<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Apple Developer Academy | Mackenzie - Medium]]></title>
        <description><![CDATA[Laboratório de Ensino e Pesquisa especializado em iOS / tvOS / watchOS - Medium]]></description>
        <link>https://medium.com/mackmobile?source=rss----776a211f46b1---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Apple Developer Academy | Mackenzie - Medium</title>
            <link>https://medium.com/mackmobile?source=rss----776a211f46b1---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 07:55:37 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/mackmobile" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[VoiceOver: O que é e passos para implementar no seu app!]]></title>
            <link>https://medium.com/mackmobile/voiceover-o-que-%C3%A9-e-passos-para-implementar-no-seu-app-44785e7838a5?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/44785e7838a5</guid>
            <category><![CDATA[acessibilidade]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[apple]]></category>
            <dc:creator><![CDATA[Marina Scabello Martin]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:28:39 GMT</pubDate>
            <atom:updated>2024-12-12T20:28:39.473Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jx9KNF10lPBgTc1j4oy_rw.jpeg" /><figcaption><a href="https://www.theverge.com/23274010/iphone-screen-reader-accessibility-how-to">https://www.theverge.com/23274010/iphone-screen-reader-accessibility-how-to</a></figcaption></figure><p><em>VoiceOver</em> é um leitor de tela baseado em gestos, que faz com que pessoas consigam interagir com a interface gráfica sem ter que ver a tela. Pessoas com baixa visão e cegas dependem do <em>VoiceOver</em> para ter um <em>feedback</em> auditivo enquanto utilizam os dispositivos com iOS.</p><p>O <em>VoiceOver</em> por padrão já é ativo nos aplicativos da App Store sem a necessidade de modificações pelo desenvolvedor, entretanto existem boas práticas que podem facilitar uso do <em>VoiceOver</em> e melhorar a experiência de pessoas que usam esse recurso dentro do seu aplicativo.</p><p>Para melhorar a experiência do usuário quando estiver utilizando o <em>VoiceOver</em> é importante utilizar a <em>feature</em> do Xcode chamada <em>accessibility inspector </em>onde é possível visualizar valores de uma view como: <em>Label, Value, Trait/Role, Default Action </em>e<em> Custom Action</em>, uma boa prática é sempre escrever bem o <em>Label</em> e também se forem valores de dinheiro sempre utilizar sifrão nos valores do <em>Value</em>.</p><p>Caso queira aprender mais sobre o assunto, selecionamos alguns <strong>recursos</strong> que podem ser úteis na sua jornada de aprendizado</p><p><strong>Acessibilidade em SwiftUI — Mais que o básico:</strong></p><p><a href="https://developer.apple.com/videos/play/wwdc2021/10119/">https://developer.apple.com/videos/play/wwdc2021/10119/</a></p><p><strong>Design — Human Interface Guidelines — VoiceOver:</strong></p><p><a href="https://developer.apple.com/design/human-interface-guidelines/accessibility#VoiceOver">https://developer.apple.com/design/human-interface-guidelines/accessibility#VoiceOver</a></p><p><strong>Ative e treine o VoiceOver no iPhone:</strong></p><p><a href="https://support.apple.com/pt-br/guide/iphone/iph3e2e415f/ios">https://support.apple.com/pt-br/guide/iphone/iph3e2e415f/ios</a></p><p><strong>Melhore a experiência do VoiceOver no seu App:</strong></p><p><a href="https://developer.apple.com/news/?id=v56qu1b3">https://developer.apple.com/news/?id=v56qu1b3</a></p><p><strong>Artigo — Melhores práticas para implementar Voice Over:</strong></p><p><a href="https://medium.com/capital-one-tech/ios-accessibility-best-practices-for-the-voiceover-user-experience-dc08112ef16">https://medium.com/capital-one-tech/ios-accessibility-best-practices-for-the-voiceover-user-experience-dc08112ef16</a></p><p><strong>Vídeo — Tutorial básico de VoiceOver e Opções avançadas:</strong></p><p><a href="https://www.youtube.com/watch?v=UK-oZerWyjc">https://www.youtube.com/watch?v=UK-oZerWyjc</a></p><p><strong>Vídeo — Como pessoas cegas usam tecnologia:</strong></p><p><a href="https://www.youtube.com/watch?v=tTg8QT4NjvA">https://www.youtube.com/watch?v=tTg8QT4NjvA</a></p><p><strong>Como Criar Experiências Espaciais Acessíveis:</strong></p><p><a href="https://developer.apple.com/videos/play/wwdc2023/10034">https://developer.apple.com/videos/play/wwdc2023/10034</a></p><p><strong>Créditos:</strong></p><p><a href="https://www.linkedin.com/in/emily-yukari-morimoto-20052b231/">Emily Yukari Morimoto</a></p><p><a href="https://www.linkedin.com/in/henriquekenzohiga/">Henrique Kenzo Higa</a></p><p><a href="https://www.linkedin.com/in/lucaszbaggiotto/">Lucas Zanatta Baggiotto</a></p><p><a href="https://www.linkedin.com/in/mariaki/">Marina Scabello Martin</a></p><p><a href="https://www.linkedin.com/in/vinicius-serpa-pansan-181b91243/">Vinicius Serpa Pansan</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=44785e7838a5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/voiceover-o-que-%C3%A9-e-passos-para-implementar-no-seu-app-44785e7838a5">VoiceOver: O que é e passos para implementar no seu app!</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Haptics: O que é e passos para implementar no seu app!]]></title>
            <link>https://medium.com/mackmobile/haptics-o-que-%C3%A9-e-passos-para-implementar-no-seu-app-181bc7b0419d?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/181bc7b0419d</guid>
            <category><![CDATA[acessibilidade]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[apple]]></category>
            <dc:creator><![CDATA[Marina Scabello Martin]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:28:06 GMT</pubDate>
            <atom:updated>2024-12-12T20:28:06.447Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/810/1*3Y1mbB08vY3zrhE6gwpVWw.jpeg" /><figcaption><a href="https://www.nextpit.com/haptic-feedback-iphone-enable-disable">https://www.nextpit.com/haptic-feedback-iphone-enable-disable</a></figcaption></figure><ul><li>O que é?</li></ul><p>É uma tecnologia tátil que transmite <em>feedback</em> a partir de vibrações, toque e <em>force feedback</em>, trazendo mais sensações ao usuário. Possibilita que a pessoa tenha uma experiência “real” a partir do objeto virtual, permitindo a percepção de coisas como, peso, pressão e textura do item digital.</p><ul><li>Como implementar em um código?</li></ul><p>Existe um modificador chamado <strong>sensoryFeedback()</strong> que permite a utilização de alguns haptics simples, que podem criar vibrações para o <em>feedback</em> tátil de algumas ações. Um exemplo é a utilização do modificador em um botão de confirmação, fazendo o iphone vibrar ao concluir a ação:</p><pre>struct ContentView: View {<br>  @State private var taskIsComplete = false<br><br>  var body: some View {<br>    Button (&quot;Mark Complete&quot;) {<br>      taskIsComplete = true<br>    }<br>    .sensoryFeedback(success, trigger: taskIsComplete)<br>  }<br>}</pre><p>Há algumas formas de transmitir o <em>feedback</em> a partir de suas vibrações no código, como:</p><p>Há algumas formas de transmitir o <em>feedback</em> a partir de suas vibrações no código, como:</p><p><strong>UIImpactFeedbackGenerator() :</strong> gera um <em>feedback </em>a partir de um toque do usuário.</p><ul><li>Dentro de <strong>UIImpactFeedbackGenerator()</strong> existem as opções de .light, .medium e .heavy, para definir a intensidade do <em>feedback</em></li></ul><p><strong>UINotificationFeedbackGenerator():</strong> <em>feedback</em> gerado a partir de uma notificação ou alerta recebido.</p><ul><li>Dentro de <strong>UINotificationFeedbackGenerator()</strong> existem as opções de .success, .warning e .error, para definir o tipo da vibração</li></ul><p><strong>UISelectionFeedbackGenerate():</strong> ao selecionar um ícone, gera um <em>feedback</em></p><ul><li>Como alterar a sensibilidade do <em>Haptic feedback</em>?</li></ul><p>Abra Configurações &gt; Acessibilidade &gt; Toque &gt; Haptic Touch &gt; Selecione Rápido ou Lento.</p><p><strong>Recursos:</strong></p><ul><li><a href="https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-haptic-effects-using-sensory-feedback">How to add haptic effects using sensory feedback</a></li><li><a href="https://www.spiceworks.com/tech/tech-general/articles/what-are-haptics/">What Are Haptics? Meaning, Types, and Importance</a></li><li><a href="https://raulferrer.dev/blog/swiftui_haptic_feedback/">Adding Hapict Feedback in SwiftUI</a></li><li><a href="https://developer.apple.com/documentation/corehaptics/delivering_rich_app_experiences_with_haptics">Delivering Rich App Experiences with Haptics</a></li><li><a href="https://www.pocket-lint.com/pt-br/celulares/noticias/apple/149457-o-que-e-a-tecnologia-do-toque-tatil-de-macas-como-funciona-e-o-que-voce-pode-fazer-com-ela/">Como alterar sensibilidade do Haptic Touch</a></li></ul><p><strong>Créditos:</strong></p><p><a href="https://www.linkedin.com/in/emily-yukari-morimoto-20052b231/">Emily Yukari Morimoto</a></p><p><a href="https://www.linkedin.com/in/henriquekenzohiga/">Henrique Kenzo Higa</a></p><p><a href="https://www.linkedin.com/in/lucaszbaggiotto/">Lucas Zanatta Baggiotto</a></p><p><a href="https://www.linkedin.com/in/mariaki/">Marina Scabello Martin</a></p><p><a href="https://www.linkedin.com/in/vinicius-serpa-pansan-181b91243/">Vinicius Serpa Pansan</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=181bc7b0419d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/haptics-o-que-%C3%A9-e-passos-para-implementar-no-seu-app-181bc7b0419d">Haptics: O que é e passos para implementar no seu app!</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como traçar uma rota em Swift a partir de um GeoJSON]]></title>
            <link>https://medium.com/mackmobile/como-tra%C3%A7ar-uma-rota-em-swift-a-partir-de-um-geojson-cc39ff5ae0f0?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/cc39ff5ae0f0</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[geojson]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Vinicius Spansan]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:26:47 GMT</pubDate>
            <atom:updated>2024-12-12T20:26:47.314Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AqH4lVGsMGU-LavHWyz79g.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@cardmapr?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">CardMapr.nl</a> on <a href="https://unsplash.com/photos/person-holding-silver-iphone-6-lMcHm_tzR3I?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></figcaption></figure><p>Primeiramente o que é um <em>GeoJSON</em>? É um tipo de arquivo derivado do <em>JSON(JavaScript Object Notation), </em>desenvolvido para agrupar dados geoespaciais em um padrão aberto. Um <em>GeoJSON</em> suporta alguns tipos de geometrias, quando se trata de como as coordenadas serão apresentadas:</p><ul><li><em>Point</em></li><li><em>LineString</em></li><li><em>Polygon</em></li><li><em>MultiPoint</em></li><li><em>MultiLineString</em></li><li><em>MultiPolygon</em></li></ul><p>Este é um exemplo de um <em>GeoJSON</em>, nesse caso ele mostra os pontos da pista de Interlagos — SP:</p><pre>{<br>&quot;type&quot;: &quot;FeatureCollection&quot;,<br>&quot;name&quot;: &quot;br-1940&quot;,<br>&quot;bbox&quot;: [ -46.700536, -23.706404, -46.694033, -23.696978 ],                                                                        <br>&quot;features&quot;: [<br>{ &quot;type&quot;: &quot;Feature&quot;, &quot;properties&quot;: { &quot;id&quot;: &quot;br-1940&quot;, &quot;Location&quot;: &quot;Sao Paulo&quot;, &quot;Name&quot;: &quot;Autódromo José Carlos Pace - Interlagos&quot;, &quot;opened&quot;: 1940, &quot;firstgp&quot;: 1973, &quot;length&quot;: 4309, &quot;altitude&quot;: 765 }, &quot;bbox&quot;: [ -46.700536, -23.706404, -46.694033, -23.696978 ], &quot;geometry&quot;: { &quot;type&quot;: &quot;LineString&quot;, &quot;coordinates&quot;: [ [ -46.699905, -23.703744 ], [ -46.699329, -23.705785 ], [ -46.699268, -23.706 ], [ -46.699207, -23.706124 ], [ -46.699069, -23.706312 ], [ -46.698998, -23.706365 ], [ -46.698896, -23.706403 ], [ -46.698795, -23.706404 ], [ -46.698684, -23.706383 ], [ -46.698626, -23.706355 ], [ -46.698446, -23.706203 ], [ -46.698271, -23.706025 ], [ -46.698202, -23.705978 ], [ -46.698112, -23.705949 ], [ -46.698021, -23.705944 ], [ -46.697889, -23.705983 ], [ -46.697609, -23.70614 ], [ -46.697387, -23.706224 ], [ -46.697222, -23.706263 ], [ -46.697071, -23.706281 ], [ -46.696933, -23.706288 ], [ -46.69673, -23.706266 ], [ -46.696575, -23.706235 ], [ -46.696384, -23.706172 ], [ -46.696205, -23.706077 ], [ -46.696028, -23.70595 ], [ -46.695885, -23.705812 ], [ -46.695772, -23.705665 ], [ -46.695688, -23.705527 ], [ -46.695618, -23.705372 ], [ -46.695344, -23.704543 ], [ -46.695228, -23.704127 ], [ -46.69503, -23.703421 ], [ -46.694788, -23.702519 ], [ -46.694363, -23.701041 ], [ -46.69421, -23.700508 ], [ -46.694102, -23.700134 ], [ -46.694052, -23.69996 ], [ -46.694033, -23.69985 ], [ -46.694045, -23.699747 ], [ -46.694073, -23.699677 ], [ -46.694134, -23.699594 ], [ -46.694244, -23.699524 ], [ -46.694372, -23.699475 ], [ -46.694557, -23.69944 ], [ -46.69511, -23.69934 ], [ -46.695326, -23.699318 ], [ -46.69546, -23.699327 ], [ -46.695624, -23.699348 ], [ -46.695779, -23.69939 ], [ -46.695916, -23.699464 ], [ -46.696046, -23.699534 ], [ -46.696176, -23.699632 ], [ -46.696301, -23.699759 ], [ -46.696472, -23.699958 ], [ -46.697034, -23.700653 ], [ -46.697364, -23.701081 ], [ -46.697645, -23.701447 ], [ -46.697953, -23.701817 ], [ -46.698115, -23.702023 ], [ -46.69824, -23.702178 ], [ -46.698327, -23.702266 ], [ -46.698424, -23.702321 ], [ -46.698562, -23.702366 ], [ -46.698714, -23.702384 ], [ -46.698942, -23.702379 ], [ -46.699094, -23.702356 ], [ -46.699215, -23.702321 ], [ -46.699323, -23.702276 ], [ -46.699508, -23.702184 ], [ -46.6996, -23.702109 ], [ -46.699661, -23.702035 ], [ -46.69972, -23.701928 ], [ -46.699765, -23.701783 ], [ -46.699821, -23.701569 ], [ -46.699867, -23.701373 ], [ -46.699929, -23.701013 ], [ -46.699941, -23.700864 ], [ -46.699938, -23.700713 ], [ -46.699899, -23.700598 ], [ -46.699836, -23.70054 ], [ -46.699792, -23.700519 ], [ -46.699737, -23.700502 ], [ -46.699662, -23.700494 ], [ -46.699583, -23.700512 ], [ -46.6995, -23.700542 ], [ -46.699276, -23.700698 ], [ -46.699186, -23.700747 ], [ -46.699116, -23.700774 ], [ -46.699041, -23.700788 ], [ -46.698972, -23.700793 ], [ -46.698896, -23.700784 ], [ -46.698792, -23.700749 ], [ -46.698669, -23.700656 ], [ -46.6986, -23.700562 ], [ -46.698563, -23.700459 ], [ -46.698557, -23.700371 ], [ -46.698572, -23.700269 ], [ -46.698614, -23.700189 ], [ -46.69871, -23.70006 ], [ -46.698797, -23.69998 ], [ -46.699104, -23.699705 ], [ -46.699262, -23.699539 ], [ -46.699371, -23.699391 ], [ -46.69945, -23.699193 ], [ -46.699511, -23.698947 ], [ -46.699539, -23.698798 ], [ -46.699554, -23.698664 ], [ -46.699544, -23.698554 ], [ -46.699497, -23.698479 ], [ -46.699458, -23.698434 ], [ -46.699395, -23.698415 ], [ -46.699343, -23.698403 ], [ -46.699276, -23.698413 ], [ -46.699198, -23.698442 ], [ -46.698823, -23.698833 ], [ -46.698499, -23.699161 ], [ -46.698443, -23.699203 ], [ -46.698322, -23.69928 ], [ -46.698211, -23.699325 ], [ -46.698119, -23.69935 ], [ -46.698006, -23.699367 ], [ -46.697901, -23.699378 ], [ -46.69778, -23.699379 ], [ -46.69759, -23.699354 ], [ -46.697445, -23.699311 ], [ -46.697291, -23.69923 ], [ -46.697135, -23.699114 ], [ -46.697048, -23.699009 ], [ -46.696955, -23.698891 ], [ -46.696706, -23.698532 ], [ -46.696431, -23.69811 ], [ -46.69612, -23.697649 ], [ -46.6961, -23.697595 ], [ -46.696095, -23.697531 ], [ -46.696114, -23.697468 ], [ -46.69615, -23.6974 ], [ -46.696219, -23.697341 ], [ -46.696382, -23.697263 ], [ -46.696619, -23.697184 ], [ -46.6971, -23.697018 ], [ -46.697194, -23.696995 ], [ -46.697314, -23.696978 ], [ -46.697556, -23.697002 ], [ -46.697768, -23.697035 ], [ -46.698232, -23.697126 ], [ -46.698386, -23.697174 ], [ -46.698692, -23.697291 ], [ -46.698843, -23.697352 ], [ -46.699203, -23.69753 ], [ -46.699346, -23.697619 ], [ -46.699468, -23.697719 ], [ -46.699592, -23.697824 ], [ -46.699693, -23.697933 ], [ -46.699783, -23.698061 ], [ -46.699865, -23.698224 ], [ -46.699931, -23.698398 ], [ -46.700035, -23.698753 ], [ -46.700148, -23.6992 ], [ -46.700223, -23.699621 ], [ -46.700352, -23.70007 ], [ -46.700435, -23.700408 ], [ -46.700507, -23.700642 ], [ -46.700531, -23.700812 ], [ -46.700536, -23.701043 ], [ -46.700522, -23.701317 ], [ -46.700489, -23.701522 ], [ -46.700433, -23.701761 ], [ -46.700336, -23.702105 ], [ -46.700053, -23.703195 ], [ -46.699905, -23.703744 ] ] } }<br>]<br>}</pre><p>Para informações mais detalhadas sobre a estrutura e funcionamento de um <em>GeoJSON </em>deixo aqui o <em>link</em> para outro artigo:</p><p><a href="https://medium.com/academy-eldoradocps/o-que-%C3%A9-e-como-funciona-o-geojson-43aca509fe4d">O que é e como funciona o GeoJSON.</a></p><p>Agora iremos utilizar ele em nosso aplicativo, queremos pegar essas coordenadas, criar uma <em>PolyLine</em> e mostra-lá em nosso mapa, nesse exemplo estarei utilizando <em>UIKit</em>, no entanto o <em>decoder</em> pode ser utilizado em <em>SwiftUI</em> também.</p><p>O primeiro passo é criar um <em>decoder</em> para o <em>GeoJSON</em>, para que o nosso aplicativo possa ler e interpretar ele.</p><p>Vamos nomear a função de <em>loadGeoJSON</em>, ela irá receber uma <em>String</em> com o nome do arquivo que foi salvo no <em>Xcode</em> e irá nos retornar um <em>overlay</em>, que é a nossa <em>MKPolyline.</em></p><p>Obs: não esqueça de importar o <em>MapKit</em> em seu projeto!</p><pre> func loadGeoJSON(filePath: String) -&gt; [MKOverlay]? {<br>        do {<br>            let data = try Data(contentsOf: URL(fileURLWithPath: filePath))<br>            let geoJSON = try MKGeoJSONDecoder().decode(data)<br>            let overlays = geoJSON.compactMap { $0 as MKGeoJSONObject }.flatMap { geoJSONObject -&gt; [MKOverlay] in<br>                if let feature = geoJSONObject as? MKGeoJSONFeature, let geometry = feature.geometry.first {<br>                    if let polyline = geometry as? MKPolyline {<br>                        return [polyline]<br>                    }<br>                }<br>                return []<br>            }<br>            return overlays<br>        } catch {<br>            print(&quot;Error loading GeoJSON: \(error)&quot;)<br>            return nil<br>        }<br>    }</pre><p>Caso tenha algum problema na hora de decodificar o <em>GeoJSON</em>, o código irá imprimir uma mensagem de erro e a função irá retornar vazia.</p><p>Veja que nesse caso o código está desenvolvido para utilizar da geometria de <em>MultiLineString</em> e criar um <em>MKPolyLine</em>, no entanto podem ser utilizadas outras geometrias compatíveis com <em>GeoJSON</em>, necessitando de poucas alterações.</p><p>Após isso, precisamos chamar a função em nosso código, aqui vai um exemplo de como podemos fazer isso, utilizando o <em>GeoJSON</em> de Interlagos.</p><pre>let routeManager = RouteManager(mapView: nil)<br><br>        if let overlays = routeManager.loadGeoJSON(filePath: Bundle.main.path(forResource: &quot;Interlagos&quot;, ofType: &quot;geojson&quot;)!) {<br>            routes.addGeoJSONRoute(name: &quot;Interlagos&quot;, overlays: overlays)<br>        }</pre><p>Nesse caso, o código pega as informações do <em>GeoJSON</em> e já salva diretamente na minha classe <em>routes</em>, que recebe os atributos de todas as minhas rotas.</p><p>Dentro da classe <em>Routes</em>, vamos criar a função que irá adicionar na tela a rota criada a partir da <em>PolyLine</em> fornecida pelo <em>GeoJSON</em>.</p><p>Vamos nomea-la como <em>addGeoJSONRoute</em>, ela irá receber o nome da rota e a <em>overlay</em> que é do tipo <em>MKOverlay</em>.</p><pre> func addGeoJSONRoute(name: String, overlays: [MKOverlay]) {<br>        var points: [RoutePoint] = []<br>        for overlay in overlays {<br>            if let polyline = overlay as? MKPolyline {<br>                for i in 0..&lt;polyline.pointCount {<br>                    let coordinate = polyline.points()[i].coordinate<br>                    let routePoint = RoutePoint(coordinate: coordinate, timestamp: Date())<br>                    points.append(routePoint)<br>                }<br>            }<br>        }<br>        addRoute(name: name, points: points)<br>    }</pre><p>E dessa maneira, fazendo as adaptações para adicionar na sua <em>ViewController</em>, já podemos ter a rota do <em>GeoJSON</em> em nosso mapa.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/738/1*zAT_G0QZusNmwhVs-Ti8Kw.png" /></figure><p>Para efetivamente criar a rota no mapa, utilizo de uma <em>extension</em> que funciona como um <em>Delegate</em> para a minha <em>MapView</em>.</p><pre>extension MapViewComponent: MKMapViewDelegate {<br>    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -&gt; MKOverlayRenderer {<br>        if let polyline = overlay as? MKPolyline {<br>            let renderer = MKPolylineRenderer(polyline: polyline)<br>            renderer.strokeColor = .blue<br>            renderer.lineWidth = 4<br>            return renderer<br>        }<br>        return MKOverlayRenderer(overlay: overlay)<br>    }<br>}</pre><p>Com apenas algumas linhas de código você agora pode ler e decodificar diferentes tipos de <em>GeoJSONs</em> e integrá-los em seus projetos de maneira eficiente. Experimente implementar esses conceitos em seus projetos e aproveite todas as funcionalidades que o <em>GeoJSON</em> oferece.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc39ff5ae0f0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/como-tra%C3%A7ar-uma-rota-em-swift-a-partir-de-um-geojson-cc39ff5ae0f0">Como traçar uma rota em Swift a partir de um GeoJSON</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como alterar volume, pitch e criar um equalizador de áudio usando AVFoundation]]></title>
            <link>https://medium.com/mackmobile/como-alterar-volume-pitch-e-criar-um-equalizador-de-%C3%A1udio-usando-avfoundation-629b993b9255?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/629b993b9255</guid>
            <category><![CDATA[audio]]></category>
            <category><![CDATA[avaudioengine]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Thiago Leandro Liporace]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:26:21 GMT</pubDate>
            <atom:updated>2024-12-12T20:26:21.810Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sBotWmgdvbcGhRvx" /><figcaption>Photo by <a href="https://unsplash.com/@arcreates?utm_source=medium&amp;utm_medium=referral">Anthony Roberts</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Neste artigo, vamos falar sobre como editar um áudio de algumas formas diferentes, como adicionando um equalizador, alterando seu volume e seu tom, usando o framework <a href="https://developer.apple.com/av-foundation/">AVFoundation</a> (AV representa Audio e Video). Não vamos comentar aqui sobre como gravar e armazenar o áudio dentro do dispositivo do usuário, portanto já assumimos que o código consegue gravar e reproduzir o áudio gravado em um <a href="https://developer.apple.com/documentation/avfaudio/avaudioplayernode">AVAudioPlayerNode</a>.</p><h3>Como alterar o volume de um player de áudio</h3><p>Primeiro, vamos criar os <em>players</em> de áudio para alterar seu volume posteriormente.</p><pre>var player1: AVAudioPlayerNode = AVAudioPlayerNode()<br>var player2: AVAudioPlayerNode = AVAudioPlayerNode()<br>var player3: AVAudioPlayerNode = AVAudioPlayerNode()<br>var player4: AVAudioPlayerNode = AVAudioPlayerNode()</pre><p>Para alterar o volume do <em>player</em>, somente é necessário alterar o atributo volume<em> </em>do <em>AVAudioPlayerNode.<br></em>Para isso, criamos uma função que recebe o volume desejado e a referência de qual player que deve ter seu volume alterado. Nesse caso, foi criado um enum que checa qual áudio foi selecionado, porém, se o seu código apenas possui um <em>player</em>, você apenas precisa receber o volume de parâmetro na função.</p><pre>func changeVolume(volume: Float, track: AudioEnum){<br>    if tracksRecorded[track] == true{<br>        <br>        switch track.numero{<br>        case 0:<br>            player1.volume = volume<br>            <br>        case 1:<br>            player2.volume = volume<br>            <br>        case 2:<br>            player3.volume = volume<br>            <br>        case 3:<br>            player4.volume = volume<br>            <br>        default:<br>            player1.volume = volume<br>            <br>        }<br>    }<br>}</pre><p>Na nossa <em>View</em>, criamos um array que armazena os valores dos volumes dos áudios.</p><p>Também criamos um <em>Slider</em> personalizado para alterar o volume, porém um Slider padrão também pode ser usado aqui.</p><pre>@State var volume: [Float] = [1,1,1,1]<br><br>//Esse slider serve para alterar o volume do primeiro áudio<br>iPhoneVolumeSlider(volume: $volume[0])<br>      .onChange(of: volume[0])<br>  {<br>      recordController.changeVolume(volume: volume[0],track: .audio1)<br>  }</pre><h3>Como alterar o pitch de um player de áudio</h3><p>Primeiro precisamos criar um <a href="https://developer.apple.com/documentation/avfaudio/avaudiounittimepitch">AVAudioUnitTimePitch</a>. Como nesse código queremos alterar o <em>pitch</em> de todos os quatro áudios, vou criar quatro AVAudioUnitTimePitch diferentes.</p><pre>var pitchControl1: AVAudioUnitTimePitch = AVAudioUnitTimePitch()<br>var pitchControl2: AVAudioUnitTimePitch = AVAudioUnitTimePitch()<br>var pitchControl3: AVAudioUnitTimePitch = AVAudioUnitTimePitch()<br>var pitchControl4: AVAudioUnitTimePitch = AVAudioUnitTimePitch()</pre><p>Para ter o <em>pitch</em> funcionando, precisamos criar duas coisas antes, uma <a href="https://developer.apple.com/documentation/avfaudio/avaudioengine">AVAudioEngine</a> e um <a href="https://developer.apple.com/documentation/avfaudio/avaudiomixernode">AVAudioMixerNode</a>, que vão servir para gerenciar todos os efeitos de áudio diferentes que vão ser adicionados aos <em>players</em>. Aqui está o código usado para criar elas.</p><pre>var mixer: AVAudioMixerNode = AVAudioMixerNode()<br>var engine: AVAudioEngine = AVAudioEngine()<br><br>func startEngine(){<br>    //Conecta o mixer na engine<br>    engine.attach(mixer)<br>    <br>    //Conecta o nodo de origem (mixer) com o de saida (engine.outputNode), se o formato for nulo ele pega o formato do arquivo de entrada<br>    engine.connect(mixer, to: engine.outputNode, format: nil)<br><br>    //A engine deve ser inicializada antes dos nodos, se houver algum erro uma mensagem de erro vai ser printada<br>    do{<br>        try engine.start()<br>    }<br>    catch {<br>        print(&quot;Error initializing engine&quot;)<br>    }<br>    <br>}</pre><p>Agora, antes de alterar o valor do <em>pitch</em> diretamente, precisamos adicionar os <em>pitchControls</em> que criamos na engine e conectar elas com o <em>player</em> e o <em>mixer</em> que também criamos.</p><p>Antes de reproduzirmos o áudio, precisamos ter o seguinte bloco de código. Nesse caso já foi adicionado o equalizador, pois todos os componentes precisam estar conectados para o App não crashar.</p><pre>var mixer: AVAudioMixerNode = AVAudioMixerNode()<br>var engine: AVAudioEngine = AVAudioEngine()<br><br><br>engine.attach(player1)<br>engine.attach(equalizer1)<br>engine.attach(pitchControl1)<br>//Conecta o Player no Equalizer<br>//Mesmo que ainda não tenhamos o equalizador criado, precisamos considerar que quando ele for criado<br>//ele já esteja adicionado na ordem a seguir<br>engine.connect(player1, to: equalizer1, format: nil)<br>engine.connect(equalizer1, to: pitchControl1, format: nil)<br>engine.connect(pitchControl1, to: mixer, format: nil)<br></pre><p>Por último, vamos criar uma função para alterar o pitch de todos os audios.</p><pre>func changePitch(pitch: Float){<br>    pitchControl1.pitch = pitch<br>    pitchControl2.pitch = pitch<br>    pitchControl3.pitch = pitch<br>    pitchControl4.pitch = pitch<br>}</pre><h3>Como criar um equalizador de áudio</h3><p>Como fizemos no pitch, precisamos criar quatro equalizadores diferentes do tipo <a href="https://developer.apple.com/documentation/avfaudio/avaudiouniteq">AVAudioUnitEQ</a>, pois cada um vai ser atrelado a um player na hora de conectar eles na engine.</p><pre>var equalizer1 : AVAudioUnitEQ = AVAudioUnitEQ()<br>var equalizer2 : AVAudioUnitEQ = AVAudioUnitEQ()<br>var equalizer3 : AVAudioUnitEQ = AVAudioUnitEQ()<br>var equalizer4 : AVAudioUnitEQ = AVAudioUnitEQ()</pre><p>Precisamos inicializar esse equalizador com algumas configurações padrões antes de podermos conectar ele na engine e alterar seus valores.</p><p><strong>IMPORTANTE: </strong>Antigamente no código, eu tinha as funções de inicializar o equalizador na função de gravar áudio, porém, ao fazer isso o código demora mais para ser executado e o gravador perdia alguns milissegundos de gravação.<br>Eu <strong>fortemente </strong>recomendo colocar o código abaixo na função de parar a gravação, pois não haverá nenhuma perda para o usuário.</p><pre>//Como nesse código queremos alterar o Bass (Baixo) e o Treble (Agudo) do som,<br>//colocamos algumas configurações iniciais de cada um deles.<br>func createEqualizer(audioEqualizer: AVAudioUnitEQ) {<br>    audioEqualizer.bypass = false<br>    <br>    let bass = audioEqualizer.bands[0]<br>    bass.filterType = .lowShelf<br>    bass.frequency = 55<br>    bass.bandwidth = 0.71<br>    bass.bypass = false<br>    <br>    let treble = audioEqualizer.bands[1]<br>    treble.filterType = .highShelf<br>    treble.frequency = 5800<br>    treble.bandwidth = 0.64<br>    treble.bypass = false<br>    <br>}<br><br>//Essa função foi recortada para mostrar apenas o equalizador<br>public func stopRecording(track: AudioEnum) {<br>        <br>    //Adiciona o equalizador no array a medida que a pessoa grava<br>    let audioEqualizer = AVAudioUnitEQ(numberOfBands: 2)<br>    <br>    //Inicializa o equalizador<br>    createEqualizer(audioEqualizer: audioEqualizer)<br>    <br>    switch track.numero{<br>    case 0:<br>        print(&quot;Gravando audio 1&quot;)<br>        equalizer1 = audioEqualizer<br>    case 1:<br>        print(&quot;Gravando audio 2&quot;)<br>        equalizer2 = audioEqualizer<br>    case 2:<br>        print(&quot;Gravando audio 3&quot;)<br>        <br>        equalizer3 = audioEqualizer<br>    case 3:<br>        print(&quot;Gravando audio 4&quot;)<br>        <br>        equalizer4 = audioEqualizer<br>    default:<br>        print(&quot;Error when recording in switch case&quot;)<br>    }<br>    <br>}</pre><p>Agora, vamos repetir o código usado no pitch, para conectar o equalizador na engine.</p><pre>engine.attach(player1)<br>engine.attach(equalizer1)<br>engine.attach(pitchControl1)<br>//Conecta o Player no Equalizer<br>engine.connect(player1, to: equalizer1, format: nil)<br>engine.connect(equalizer1, to: pitchControl1, format: nil)<br>engine.connect(pitchControl1, to: mixer, format: nil)</pre><p>Para alterar os valores do Bass (Baixo) e do Treble (Agudo), vamos criar uma função que seleciona eles e altera seu gain (o que precisamos alterar em cada um).</p><pre>//Funcao pra mudar o gain do Treble<br>func changeTreble(gain: Float, track: AudioEnum){<br>    if tracksRecorded[track] == true{<br>        <br>        switch track.numero{<br>        case 0:<br>            let treble1 = equalizer1.bands[1]<br>            treble1.gain = gain<br>        case 1:<br>            let treble2 = equalizer2.bands[1]<br>            treble2.gain = gain<br>        case 2:<br>            let treble3 = equalizer3.bands[1]<br>            treble3.gain = gain<br>        case 3:<br>            let treble4 = equalizer4.bands[1]<br>            treble4.gain = gain<br>        default:<br>            let treble1 = equalizer1.bands[1]<br>            treble1.gain = gain<br>        }<br>        <br>    }<br>}<br><br>//Funcao pra mudar o gain do Bass<br>func changeBass(gain: Float, track: AudioEnum){<br>    if tracksRecorded[track] == true{<br>        <br>        switch track.numero{<br>        case 0:<br>            let bass1 = equalizer1.bands[0]<br>            bass1.gain = gain<br>        case 1:<br>            let bass2 = equalizer2.bands[0]<br>            bass2.gain = gain<br>        case 2:<br>            let bass3 = equalizer3.bands[0]<br>            bass3.gain = gain<br>        case 3:<br>            let bass4 = equalizer4.bands[0]<br>            bass4.gain = gain<br>        default:<br>            let bass1 = equalizer1.bands[0]<br>            bass1.gain = gain<br>        }<br>    }<br>}</pre><p>Espero que este Medium tenha ensinado você a respeito de algumas funcionalidades do AVFoundation, e que você consiga aplicar o que aprendeu em seu código.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=629b993b9255" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/como-alterar-volume-pitch-e-criar-um-equalizador-de-%C3%A1udio-usando-avfoundation-629b993b9255">Como alterar volume, pitch e criar um equalizador de áudio usando AVFoundation</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Aprofundando em SwiftData]]></title>
            <link>https://medium.com/mackmobile/aprofundando-em-swiftdata-72d994d889ba?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/72d994d889ba</guid>
            <dc:creator><![CDATA[Raphael Iniesta]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:25:45 GMT</pubDate>
            <atom:updated>2024-12-12T20:25:44.939Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yAdKFLP-us5SWaM8dv5j0A.jpeg" /></figure><p>Anteriormente, escrevi um artigo discutindo os básicos de Swift Data. Você pode ver esse artigo clicando no link:</p><p><a href="https://medium.com/mackmobile/introdu%C3%A7%C3%A3o-ao-swiftdata-28f126049582">Introdução ao SwiftData</a></p><p>Se você já viu alguma coisa sobre <em>SwiftData</em>, já deve ter percebido que para cada vez que você precisa usar uma função, é preciso reescreve-la em todas as <em>Views</em> que forem utiliza-la. Isso acaba deixando o código muito poluído e extenso.</p><p>Nesse artigo vou mostrar como deixar o código mais limpo e enxuto, sem perder nenhuma funcionalidade do <em>SwiftData</em>. Abaixo tem uma pequena prévia de como vai ficar no final.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*197Nipu0oWAWcMv6GCd9JA.png" /></figure><p>Mesmo que queira tentar deixar a função em um arquivo separado, ela não vai funcionar, já que essas funções que usam SwiftData precisam estar em uma View.</p><p>E como vamos resolver isso?</p><p>Bem, podemos usar uma classe que conforme com o protocolo <em>ObservableObject</em>. Ou seja, vamos fazer uma <em>ViewModel</em> para poder componentizar a declaração e chamada de funções do SwiftData.</p><p>Sem mais enrolações. Vamos começar.</p><p>Primeiro, vamos criar um novo arquivo e chamá-lo de <em>ViewModel</em>. Só para contexto, estou utilizando o mesmo código que utilizei para fazer o outro artigo.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/283204f11e86d1cca9455424e5fc1a3b/href">https://medium.com/media/283204f11e86d1cca9455424e5fc1a3b/href</a></iframe><p>Legal, já temos a nossa ViewModel. Agora, vamos pegar todas aquelas funções que criamos anteriormente e colocar aqui dentro.</p><p>Quando fizer isso, vai notar que terão erros. Esses erros acontecem porque o <em>context</em> não foi declarado em nenhum lugar para que pudesse ser utilizado.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DxVF91q3LTiyn_ICFCfeCw.png" /><figcaption>Imagem 1</figcaption></figure><p>Vamos resolver isso da seguinte forma: Colocaremos o <em>context</em> como parâmetro nas funções, assim é possível utilizar essas funções.</p><p>Só esclarecendo que, c<em>ontext</em> é do tipo <em>ModelContext</em>. Então a nova classe ficaria assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5eedcf67ef47d58fa3e667c0883d2682/href">https://medium.com/media/5eedcf67ef47d58fa3e667c0883d2682/href</a></iframe><p>Ótimo! Já temos a nossa <em>ViewController</em>. Agora só precisamos configurar para que possamos utilizá-la no aplicativo.</p><p>Para isso, vamos até o arquivo &lt;nome do projeto&gt;App e adicionar o macro @StateObject e declarar como valor a <em>ViewModel</em> que criamos mais cedo, não esquecendo que precisamos também passar essa <em>ViewModel</em> no arquivo principal do aplicativo.</p><p>Adicionando tudo, o seu arquivo App deve ficar semelhante a isso:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1ef6ed477fe7a0a69bbd4564143d3056/href">https://medium.com/media/1ef6ed477fe7a0a69bbd4564143d3056/href</a></iframe><p>Agora, se você for no(s) arquivos do seu app, reparará que tem alguns erros em todos os lugares que alguma das funções que utilizava foi chamada. Isso acontece por dois motivos: O primeiro porque não está sendo colocado o <em>context</em> como um parâmetro; o segundo porque a <em>ViewModel</em> não foi declarada na <em>View </em>em que está sendo utilizada essas funções.</p><p>Com os erros corrigidos, o arquivo deve ficar mais ou menos assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/689093a8ba4972c9aee699b3752bd280/href">https://medium.com/media/689093a8ba4972c9aee699b3752bd280/href</a></iframe><p>Se observar, na linha 7, declarei uma variável <em>@EnviromentObjec</em> chamada <em>vm</em> que é do tipo ViewController. E então, toda vez que preciso usar uma função do SwiftData, utilizo <em>vm</em> para chama-la.</p><p>E <em>voilà, </em>temos agora um aplicativo utilizando SwiftData totalmente componentizado.</p><p>Agora é com você! Crie e explore as novas possibilidades que você tem. Se quiser o código atualizado, o link está aqui em baixo:</p><p><a href="https://github.com/RaphaelIniesta/SwiftDataDemo.git">GitHub - RaphaelIniesta/SwiftDataDemo</a></p><p><em>Keep Coding </em>e até a próxima! Tchau, tchau!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=72d994d889ba" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/aprofundando-em-swiftdata-72d994d889ba">Aprofundando em SwiftData</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift Data com UIKit]]></title>
            <link>https://medium.com/mackmobile/swift-data-com-uikit-7e68245289fa?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/7e68245289fa</guid>
            <category><![CDATA[swiftdata]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Pedro Pessuto]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:25:22 GMT</pubDate>
            <atom:updated>2024-12-12T20:25:22.160Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PZPOaSaLK__Qxbr8YOAXeQ.png" /></figure><p>Neste artigo será mostrado como utilizar <em>SwiftData</em> sem depender do <em>SwiftUI</em> em seu código. Desta maneira, por exemplo, projetos em <em>UIKit</em> poderão usufruir das vantagens sem necessariamente ter que utilizar <em>CoreData</em> para isso. O único requisito é que o dispositivo alvo tem que possuir a versão do sistema operacional superior ou igual a estas<em> iOS 17.0+, iPadOS 17.0+, macOS 14.0+, Mac Catalyst 17.0+, tvOS 17.0+, watchOS 10.0+ </em>e<em> visionOS 1.0+</em>.</p><p>O <em>SwiftData</em> é uma framework criada pela <em>Apple</em> com o propósito de salvar dados de maneira persistente no dispositivo de maneira fácil e intuitiva. Dessa forma, a framework utiliza código na linguagem moderna e permite fáceis integrações, como, por exemplo com o <em>iCloud</em> (<em>CloudKit</em>).</p><p>Em primeiro lugar é importante ressaltar que este código possui uma básica implementação de um <em>fetch</em> e <em>insert</em>, mas também é possível fazer outras operações de maneira semelhante. Neste exemplo é necessário considerar que exista uma classe (<em>Account</em>) que será guardada e um <em>Enum</em> (<em>CustomError</em>) que será utilizado para indicar erro em result.</p><p>*OBS: É possível implementar de outras maneiras, com <em>@escaping</em> e <em>DispatchQueue.main</em>, por exemplo*</p><pre>  class DataController {<br>      <br>      @MainActor<br>      public var container: ModelContainer = {<br>          let schema = Schema([<br>              Account.self,<br>          ])<br>          let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .none)<br>  <br>          do {<br>              return try ModelContainer(for: schema, configurations: [modelConfiguration])<br>          } catch {<br>              fatalError(&quot;Failed to create ModelContainer: \(error)&quot;)<br>          }<br>      }()<br>      <br>      @MainActor<br>      public func insertAccount(_ account: Account) {<br>          self.container.mainContext.insert(account)<br>      }<br>  <br>      @MainActor<br>      public func getAccounts() -&gt; Result&lt;[Account], CustomError&gt; {<br>          do {<br>              let descriptor: FetchDescriptor&lt;Account&gt; = FetchDescriptor&lt;Account&gt;()<br>              let accounts: [Account] = try self.container.mainContext.fetch(descriptor)<br>              return .success(accounts)<br>          }<br>          catch {<br>              return .failure(.fetchError)<br>          }<br>      }<br>  <br>  }</pre><p>No código foi utilizado o <strong><em>@MainActor</em></strong> em cada função, isto porque é um requisito para funcionar com o <em>SwiftData</em> e para indicar que a função deve ser utilizada na fila principal de maneira <strong>assíncrona</strong>. Dessa forma, ao chamá-la será preciso tratá-la corretamente, por exemplo, com o uso da estrutura <strong><em>Task.</em></strong></p><pre>@MainActor<br>    public var container: ModelContainer = {<br>        let schema = Schema([<br>            Account.self,<br>        ])<br>        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .none)<br><br>        do {<br>            return try ModelContainer(for: schema, configurations: [modelConfiguration])<br>        } catch {<br>            fatalError(&quot;Failed to create ModelContainer: \(error)&quot;)<br>        }<br>    }()</pre><p>Essa parte do código, a variável <em>container</em> é inicializada com um bloco que define o esquema do modelo, especifica a configuração do modelo e tenta criar uma instância de <em>ModelContainer</em>. O <em>ModelContainer</em> é o responsável por gerenciar os objetos salvos e suas interações no <em>SwiftData</em>.</p><p>O esquema do modelo representa uma estrutura<strong><em> “Schema”</em></strong> que mapeia classes para dados no armazenamento e ajuda na migração desses dados entre versões.</p><p>A configuração do modelo é definida pelo esquema e algumas outras propriedades, por exemplo: <em>“isStoredInMemoryOnly : false” </em>para indicar se os dados salvos não serão salvos permanentemente e <em>“cloudKitDatabase: .none” </em>para não utilizar no <em>CloudKit</em>.</p><p>Após a declaração do esquema e configuração do modelo é necessário instancia-lo com a utilização de um bloco <em>“do/catch”</em> para tratamento de erros.</p><pre>@MainActor<br>    public func insertAccount(_ account: Account) {<br>        self.container.mainContext.insert(account)<br>    }</pre><p>Nesse trecho de código é uma função que insere um modelo dentro do container persistente. Note que, <em>.mainContext</em> refere-se a uma propriedade de instância projetado para operar na fila principal de execução, então caso a operação seja muito pesada e possa atrapalhar na experiência do usuário, talvez uma opção melhor seja criar e operar em um <em>backgroundContext</em>.</p><pre>@MainActor<br>    public func getAccounts() -&gt; Result&lt;[Account], CustomError&gt; {<br>        do {<br>            let descriptor: FetchDescriptor&lt;Account&gt; = FetchDescriptor&lt;Account&gt;()<br>            let accounts: [Account] = try self.container.mainContext.fetch(descriptor)<br>            return .success(accounts)<br>        }<br>        catch {<br>            return .failure(.fetchError)<br>        }<br>    }</pre><p>Na função <em>getAccounts,</em> é usado um bloco de <em>“do/catch”</em> para realizar a busca no container já que temos uma operação que pode resultar em um <em>crash</em>, erro que interrompe a execução do programa. Para realizar a operação é necessário ter um <em>FetchDescriptor</em>, uma estrutura responsável por “filtrar” quais resultados devem serem buscados. Nesse caso, nenhum parâmetro é passado, assim ela retornará todos os elementos de um determinado tipo, <em>Account</em> nesse caso.</p><p><strong>Referências</strong></p><ul><li><a href="https://developer.apple.com/documentation/swiftdata">SwiftData | Apple Developer Documentation</a></li><li><a href="https://developer.apple.com/documentation/swift/mainactor">MainActor | Apple Developer Documentation</a></li><li><a href="https://developer.apple.com/documentation/swift/task">Task | Apple Developer Documentation</a></li><li><a href="https://developer.apple.com/documentation/swiftdata/schema">Schema | Apple Developer Documentation</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7e68245289fa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/swift-data-com-uikit-7e68245289fa">Swift Data com UIKit</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando uma CollectionView com células customizadas (UIKit)]]></title>
            <link>https://medium.com/mackmobile/criando-uma-collectionview-com-c%C3%A9lulas-customizadas-uikit-7c3f93ae27ab?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/7c3f93ae27ab</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[uikit]]></category>
            <dc:creator><![CDATA[Marina Scabello Martin]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:24:54 GMT</pubDate>
            <atom:updated>2024-12-12T20:24:54.498Z</atom:updated>
            <content:encoded><![CDATA[<p>Aqui você vai encontrar um tutorial simples e direto ao ponto, onde você vai acompanhar o desenvolvimento de uma <em>Collection View</em> com células customizadas, feita em <em>UIKit</em> de forma programática.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NWy61kfVq2_zLcwsQlNl2Q.jpeg" /><figcaption>Resultado das células customizadas</figcaption></figure><h3><strong>1. Criar <em>Collection View</em></strong></h3><pre>private let colectionView: UICollectionView = {<br>        let layout = UICollectionViewFlowLayout()<br>        layout.scrollDirection = .vertical<br>        <br>        let colectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)<br>        <br>        colectionView.backgroundColor = .clear<br>        <br>        colectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.identifier)<br>        colectionView.translatesAutoresizingMaskIntoConstraints = false<br>        <br>        return colectionView<br>}()</pre><p>Aqui você já deve decidir alguns fatores da sua <em>collection</em>, como a direção do <em>scroll</em>, no meu caso a vertical e a o cor de fundo do conjunto.<br><em>CollectionViewCell</em> → <em>View</em> de cada célula da collection, o item que vamos customizar.</p><h3>2. Criar um arquivo separado para <em>View</em> da sua célula customizada</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/642/1*RlucL7Jj5g1YWK-KcNvcCg.png" /><figcaption>imagem da Célula customizada</figcaption></figure><pre>class CollectionViewCell: UICollectionViewCell {<br><br> static let identifier = &quot;ActivityCell&quot;<br>}</pre><p>Aqui o <em>identifier</em> serve como o nome dessa configuração da célula.</p><p>2.1 Criar função para receber todos as variáveis da célula e iniciar as <em>Constraints</em></p><pre>func configData(title: String, description: String) {<br>        self.activityLabel.text = title<br>        self.descriptionLabel.text = description<br>               <br>        setConstraints()<br>}</pre><p>Para tornar o tutorial mais simples eu vou trabalhar apenas com as variáveis de título e descrição da célula e estas tem que ser iniciadas junto com a célula.</p><p>2.2 Criar um função de <em>Reuse</em> da célula</p><p>Para usar o <em>scroll</em> e garantir que as células não serão sempre iguais, é necessário reiniciar as células com valores vazios, o código abaixo cumpre essa função. Caso você esteja trabalhando com cores ou outras variáveis, é necessário resetá-las nesta parte do código.</p><pre>override func prepareForReuse() {<br>        super.prepareForReuse()<br>        <br>        self.background.backgroundColor = nil<br>        self.activityLabel.text = nil<br>        self.descriptionLabel.text = nil<br>        self.dateLabel.text = nil<br>}</pre><h3>3. Criar um <em>Data Source</em> para a <em>Collection</em></h3><p>Sua <em>Collection</em> precisa de um <em>Data Source</em>, este que carrega as configurações da sua <em>Collection View.</em> O trecho de código completo do DataSource se encontra abaixo, e será dividido em trechos para uma explicação mais detalhada de cada uma de suas partes.</p><pre>class ActivityCollectionDataSource: NSObject, UICollectionViewDataSource {<br>    private var activities: [String]<br>    private var title: [String]<br>    <br>    init(activities: [String], title: [String]) {<br>        self.activities = activities<br>        self.title = title<br>    }<br>    <br>    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {<br>        return activities.count<br>    }<br>    <br>    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {<br>        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as? CollectionViewCell<br>        else{<br>            fatalError(&quot;Failed to dequeue&quot;)<br>        }<br>        <br>        cell.configData(title: activities[indexPath.row], description: activities[indexPath.row])<br>        <br>        return cell<br>    }<br>}</pre><p>Para declarar e inicializar as variáveis da célula, você deve utilizar o trecho de código abaixo.</p><pre>private var activities: [String]<br>private var title: [String]<br>    <br>init(activities: [String], title: [String]) {<br>   self.activities = activities<br>   self.title = title<br>}</pre><p>A função abaixo vai realizar a contagem das células diferentes que devem ser dispostas na <em>Collection.</em></p><pre>func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {<br>   guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as? CollectionViewCell<br>   else{<br>       fatalError(&quot;Failed to dequeue&quot;)<br>   }<br>        <br>   cell.configData(title: activities[indexPath.row], description: activities[indexPath.row])<br>        <br>   return cell<br>}</pre><p>No trecho acima, a criação da célula é feita apartir da saída da mesma da fila (<em>dequeue</em>). Caso algo de errado aconteça, o <em>FatalError</em> acaba sendo ativado e o aplicativo <em>crasha.</em> <br>Como no meu aplicativo a <em>CollectionView</em> é essecial, isso foi o que mais fez sentido para a minha aplicação, mas caso ache necessário, você pode implementar um tratamento de erro.</p><p>Após o <em>dequeue</em> a configuração da célula é chamada e os parâmetros da mesma são passados pela linha (<em>row</em>) em que ela se encontra.</p><h3>4. Criar um <em>Delegate</em></h3><pre>import UIKit<br>import Foundation<br><br>class CollectionViewDelegate: NSObject, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {<br>    <br>    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {<br>        //<br>    }<br>    <br>    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -&gt; CGFloat {<br>        return 20<br>    }<br>    <br>    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexpath: IndexPath) -&gt; CGSize {<br>        return CGSize(width: UIScreen.main.bounds.width, height: 121)<br>    }<br>    <br>}</pre><p>O <em>Delegate</em> acima serve, principalmente para criar o <em>FlowLayoult</em> das células.</p><ul><li><em>minimumLineSpacingForSectionAt</em> — Trata da distância entre as células</li><li><em>sizeForItemAt</em> — Trata do tamanho da célula</li></ul><h3>5. Configurações finais</h3><p>agora só falta chamar o <em>Delegate</em> e o <em>DataSorce</em> para a <em>ViewController</em> onde a <em>Collection</em> <em>View</em> vai aparecer.</p><p>5.1 Criar as variáveis</p><pre>var collectionViewDelegate: CollectionViewDelegate = CollectionViewDelegate()<br>var collectionViewDataSource: UICollectionViewDataSource?</pre><p>5.2 Inicializar na <em>ViewDidLoad</em></p><p>é na chamada do <em>DataSource</em> que você deve chamar onde a informação que vai na célula está armazenada, aqui eu chamei de <em>activitiesTest</em> só para con firmar que a célula funcionava.</p><pre>collectionViewDataSource = ActivityCollectionDataSource(activities: activitiesTest, title: activitiesTest)<br><br>colectionView.delegate = collectionViewDelegate<br>colectionView.dataSource = collectionViewDataSource</pre><p>E esse foi o tutorial, espero que ele possa ter ajudado você com a implementação da sua <em>Collection View!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7c3f93ae27ab" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/criando-uma-collectionview-com-c%C3%A9lulas-customizadas-uikit-7c3f93ae27ab">Criando uma CollectionView com células customizadas (UIKit)</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Você já baixou um aplicativo em que nada parecia conversar visualmente?]]></title>
            <link>https://medium.com/mackmobile/voc%C3%AA-j%C3%A1-baixou-um-aplicativo-em-que-nada-parecia-conversar-visualmente-76dedc3f6319?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/76dedc3f6319</guid>
            <category><![CDATA[aplicativos]]></category>
            <category><![CDATA[mood-board]]></category>
            <category><![CDATA[identidade-visual]]></category>
            <category><![CDATA[tipografia]]></category>
            <dc:creator><![CDATA[Beatriz Moura]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:24:33 GMT</pubDate>
            <atom:updated>2024-12-12T20:24:33.656Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kbDxH4G1y-adEIRmREJbXg.png" /></figure><p>Você já baixou um aplicativo em que nada parecia conversar visualmente? Pois é, criar um <em>app</em> que tenha uma identidade visual coesa é de extrema importância para que o usuário não tenha essa sensação ao utilizar em seu aplicativo.</p><p>Não existe bem uma forma ou ordem correta de se criar uma identidade visual para o seu <em>app</em>, mas para te ajudar com os primeiros passos, vou explicar um pouco do meu processo. Espero que ajude!</p><h3>1- Quais são suas referências?</h3><p>Saber em que sua identidade se baseará, facilita muito na hora de escolher a tipografia, as cores, as ilustrações, entre outros. Por isso, recomendo que você comece montando um <em>moodboard</em>, com referências, em que você colocará imagens, texto e amostras de objetos que tenham a ver com o público-alvo e que conversem entre si.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gZ7s88veqgJw1E__qFiFXg.png" /><figcaption>Moodboard criado para a identidade do app Dandie de autocuidado masculino.</figcaption></figure><p>Com o <em>moodboard</em> feito você já terá uma base de qual estilo visual seu <em>app </em>irá seguir, se é <em>Clean</em>/minimalista, <em>Fun</em>, Retro/Vintage, <em>Illustrative</em>, entre muitos outros.</p><h3>2- Quais cores você irá utilizar?</h3><p>Seguindo seu <em>moodboard</em>, defina sua paleta de cores, pensando em como elas serão aplicadas no <em>Light</em> e no <em>Dark Mode</em>, lembrando sempre de escolher cores contrastantes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*eV0PtDsrz2_qporVZ6gctg.png" /><figcaption>Paleta de cor criada para um app de exercícios guiados</figcaption></figure><p>É bom fazer uma paleta que te possibilitará fazer todas as aplicações necessárias. No meu caso, costumo escolher as cores primárias e depois vou adicionando as variações dela.</p><h3>3- Qual tipografia irá usar?</h3><p>Escolher uma tipografia pode ser difícil, mas é uma tarefa muito importante e vale a pena gastar um pouco de tempo dando atenção a ela.<br>Saber o que você quer transmitir com seu app ajuda muito nessa escolha. Com o auxílio do seu moodboard, observe as sensações transmitidas e procure tipografias correspondentes a elas. Por exemplo, estilos como <em>Clean</em>/minimalista costumam ter fontes simples, com boa legibilidade e pouca complexidade gráfica, como glifos ou fontes cursivas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/417/1*gUq9_d0OYb7pZtWbajp6bg.jpeg" /><figcaption>Tigrafia App Clear Sharp by Bohloul Arabic Type Design</figcaption></figure><h3>4- Elementos gráficos</h3><p>Me arrisco em dizer que essa é umas das partes mais importantes da identidade, pois são os elementos gráficos que capturam o olhar. Por isso, crie apenas elementos essenciais para o <em>app</em>, afinal, queremos criar algo agradável e não visualmente sobrecarregado.<br>Aqui está um exemplo de um <em>app</em> em que usei um elemento gráfico para compor a tela a partir do próprio ícone:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YaK9QVT9sYIOG5vw6SAw7g.png" /><figcaption>App Zélar</figcaption></figure><p>Essas foram minhas dicas de hoje, realmente espero que ajude você a começar o seu projeto. Lembrando que o jeito certo de criar uma identidade é a forma como funciona melhor para você. Com calma e dedicação, tenho certeza de que encontrará o seu método. Espero que tenha gostado da dica e até logo!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=76dedc3f6319" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/voc%C3%AA-j%C3%A1-baixou-um-aplicativo-em-que-nada-parecia-conversar-visualmente-76dedc3f6319">Você já baixou um aplicativo em que nada parecia conversar visualmente?</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como usar as constraints em Swift?]]></title>
            <link>https://medium.com/mackmobile/como-usar-as-constraints-em-swift-f22c456deb6a?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/f22c456deb6a</guid>
            <category><![CDATA[viewcode]]></category>
            <category><![CDATA[constraints]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Jey Pires]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:24:28 GMT</pubDate>
            <atom:updated>2024-12-12T20:24:27.927Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2dcmWIvGS6NJS8c5" /><figcaption>Photo by <a href="https://unsplash.com/@markusspiske?utm_source=medium&amp;utm_medium=referral">Markus Spiske</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Em minha carreira como desenvolvedora eu tinha apenas experiência com SwiftUI. Logo, quando me vi em um projeto em Swift com UIkit em <em>Viewcode</em>, me deparei com um padrão de construção de layout um pouco peculiar para mim. Tive bastante dificuldade em entender a lógica de <em>layout</em> dessa linguagem, porque me deparei com as <em>constraints</em> e confesso que fiquei um pouco desesperada no início. Entretanto, usei tanto elas que desenvolvi um certo carinho e por isso resolvi criar esse Medium com o intuito de desvendar o básico delas e ensiná-las.</p><p>No UIKit, as <em>constraints</em> (ou restrições) são regras usadas para definir a posição e o tamanho de elementos de interface de usuário de forma declarativa e flexível. Elas fazem parte do Auto Layout, um sistema que permite criar layouts dinâmicos que se adaptam a diferentes tamanhos de tela e orientações de dispositivos iOS. Sendo assim, com o uso das <em>constraints</em> é possível que a visualização de elementos se ajuste a qualquer alteração de tamanho de tela sem que seja necessário colocar as informações manualmente.</p><p>Nesse sentido, o primeiro passo a se fazer é colocar translatesAutoresizingMaskIntoConstraints igual a false, logo você &quot;autoriza&quot; a criação de ajustes de tamanho no contexto usado.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d5564a7c57a234d7b910319f875754d5/href">https://medium.com/media/d5564a7c57a234d7b910319f875754d5/href</a></iframe><p>Com o exemplo a seguir, vemos o básico das <em>constraints</em>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/57801b334298cf0761d6eaf087ba3b3c/href">https://medium.com/media/57801b334298cf0761d6eaf087ba3b3c/href</a></iframe><p>Lembre-se, a construção das <em>constraints</em> deve ser legível e compreensível, um ponto que você precisa saber é que a ultima linha é necessária para ativar as <em>constrains</em> para seu layout.</p><p>Você pode colocar as <em>constraints</em> de acordo com sua <em>view</em> ou colocar elas de acordo com algum outro contexto, sendo eles: imagens, <em>labels</em> ou outros parâmetros…</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/da4e51cfcd9df8f059ec58e983007cd4/href">https://medium.com/media/da4e51cfcd9df8f059ec58e983007cd4/href</a></iframe><p>Agora vamos entender alguns tipos de <em>constraints</em>:</p><ul><li><strong><em>TopAnchor:</em></strong> refere-se a parte superior do item que você deseja modificar.</li><li><strong><em>LeadingAnchor: </em></strong>refere-se o lado no qual se inicia a leitura.</li><li><strong><em>BottomAnchor:</em> </strong>refere-se ao lado inferior do contexto no qual você quer colocar o ajuste de visualização.</li><li><strong><em>TrailingAnchor:</em></strong><em> </em>refere-se ao lado no qual se encerra a leitura.</li><li><strong><em>Center:</em></strong> refere-se ao centro, sendo necessário definir o eixo em que está trabalhando, podendo ser X ou Y, e respectivamente usar centerXAnchor ou centerYAnchor.</li></ul><p>Agora que você sabe para que servem as <em>constrains</em> e em qual contexto você deve usá-la, vai ficar muito mais fácil alterar os tamanhos do seu layout em UIKit. Agora é com você, espero ter ajudado e até uma próxima!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f22c456deb6a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/como-usar-as-constraints-em-swift-f22c456deb6a">Como usar as constraints em Swift?</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como consumir arquivos XML ou JSON em Swift?]]></title>
            <link>https://medium.com/mackmobile/como-consumir-arquivos-xml-ou-json-em-swift-fad3442edf5a?source=rss----776a211f46b1---4</link>
            <guid isPermaLink="false">https://medium.com/p/fad3442edf5a</guid>
            <category><![CDATA[xml]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[programação]]></category>
            <dc:creator><![CDATA[Gabriel Fonseca]]></dc:creator>
            <pubDate>Thu, 12 Dec 2024 20:23:35 GMT</pubDate>
            <atom:updated>2024-12-12T20:23:35.808Z</atom:updated>
            <content:encoded><![CDATA[<p>Sem usar API!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iVzXCfon1nr_6zZK" /><figcaption>Photo by <a href="https://unsplash.com/@swimstaralex?utm_source=medium&amp;utm_medium=referral">Alexander Sinn</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Para esse tutorial usarei como exemplo o conteúdo sobre a Bíblia do <a href="https://github.com/thiagobodruk/bible/commits?author=thiagobodruk">thiagobodruk</a> disponível no <em>GitHub</em>.</p><p>📌 Para ter acesso aos arquivos que serão usados, abra o link abaixo: <a href="https://github.com/thiagobodruk/bible/tree/master/json">https://github.com/thiagobodruk/bible/tree/master/json</a></p><ol><li>Em primeiro lugar, você deve fazer download do arquivo que você deseja, em <em>XML</em> ou <em>JSON</em>, escolhendo a linguagem e a versão desejada da bíblia.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jPofyn2LlxJisIMnEWGh9w.png" /><figcaption>Nesse caso, escolherei o arquivo “pt_aa.json” (Em português e na versão ARA, Almeida Revista e Atualizada)</figcaption></figure><p>2. Em segundo lugar, você deverá colocar no seu Xcode o arquivo JSON ou XML baixado.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/530/1*p2fPK7kK4-UdCPjx-csquQ.png" /><figcaption>pt_aa.json</figcaption></figure><p>3. Agora, iremos criar uma estrutura que conversará com o arquivo JSON:</p><p>▶️ Crie um arquivo chamado <em>BookModel</em>.</p><pre>import Foundation<br><br>struct BookModel: Codable {<br>    let abbrev: String<br>    let chapters: [[String]]<br>    let name: String<br>}</pre><p><strong>abbrev</strong>: Livros bíblicos abreviados (“Gn” -&gt; Gênesis).</p><p><strong>chapters</strong>: Capítulos bíblicos, sendo um <em>Array</em> com os versículos.</p><p><strong>name</strong>: Nome dos livros bíblicos.</p><p>4. Em seguida iremos realizar um <em>Decode</em> para uma estrutura que aceite os dados do JSON:</p><p>▶️ Crie um arquivo chamado <em>BibleController</em>.</p><pre>import Foundation<br><br>class BibleController {<br>    <br>    var booksAA: [BookModel] = []<br>    <br>    func fetchAA() {<br>        do {<br>            if let path = Bundle.main.path(forResource: &quot;pt_aa&quot;, ofType: &quot;json&quot;),<br>               let jsonData = try String(contentsOfFile: path).data(using: .utf8) {<br>                <br>                let books = try JSONDecoder().decode([BookModel].self, from: jsonData)<br>                self.booksAA = books<br>            }<br>        } catch {<br>            print(&quot;Error fetching data: \(error)&quot;)<br>        }<br>    }<br>}</pre><p>Veja a seguir o passo a passo do código:</p><ol><li>Crie uma variável para conversar com o BookModel.</li><li>Faça uma função de fetchAA.</li><li>Não esqueça de colocar o nome do arquivo corretamente e o seu tipo.</li></ol><p>Consumir arquivos XML ou JSON em Swift sem o uso de uma API pode parecer uma tarefa desafiadora, mas com um pouco de conhecimento e prática, você perceberá que é mais simples do que parece.</p><p>Espero que este tutorial tenha sido útil e que você se sinta mais confiante para enfrentar desafios semelhantes no futuro.</p><p>Good Code!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fad3442edf5a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mackmobile/como-consumir-arquivos-xml-ou-json-em-swift-fad3442edf5a">Como consumir arquivos XML ou JSON em Swift?</a> was originally published in <a href="https://medium.com/mackmobile">Apple Developer Academy | Mackenzie</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>