<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ iOS - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ iOS - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:30:27 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/ios/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Scale Bluetooth Across Android, iOS, and Embedded Devices ]]>
                </title>
                <description>
                    <![CDATA[ Bluetooth is one of those inventions that seems magical the first time you use it. You turn on a gadget, pair it with your phone, and suddenly they are talking to each other without a single wire in sight. Music plays through your headphones, your sm... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-scale-bluetooth-across-devices/</link>
                <guid isPermaLink="false">691742dfb6a85c7f18a5fc15</guid>
                
                    <category>
                        <![CDATA[ bluetooth ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikheel Vishwas Savant ]]>
                </dc:creator>
                <pubDate>Thu, 13 Nov 2025 23:00:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763131642774/dd2366f8-f491-4313-901e-acd4c1d937e2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Bluetooth is one of those inventions that seems magical the first time you use it. You turn on a gadget, pair it with your phone, and suddenly they are talking to each other without a single wire in sight. Music plays through your headphones, your smartwatch shows messages from your friends, and for a brief moment it feels like technology finally has its act together. Everything works and life is good.</p>
<p>Then you try to connect one more thing. Maybe a fitness band, a smart lock, or that tiny temperature sensor you ordered online because it was on sale. That is when the charm fades and reality walks in. Suddenly the connection drops, your phone cannot find the device anymore, and the once-friendly Bluetooth logo on your screen starts to feel like a taunt. You restart, you unpair, you try again, and somehow it only gets worse. What was once effortless turns into a puzzle with no clear solution.</p>
<p>Here is the secret that few people know: Bluetooth was never meant to handle the chaos we put it through today. When engineers designed it in the late 1990s, they imagined a world of simple one-to-one connections. A laptop talking to a mouse. A phone connecting to a headset. That was the whole idea. Fast-forward to the present and we are using the same technology to run entire networks of wearables, sensors, and smart appliances. We ask it to connect not just one or two devices but sometimes dozens of them at the same time, each running on different hardware and software. It is a miracle that it works at all.</p>
<p>To make things even more interesting, these devices live in very different worlds. Android devices are like an open playground where every manufacturer adds its own slide and swing set. iPhones live inside Apple’s carefully fenced garden where everything is polished but also tightly controlled. Embedded devices, like the ones built on tiny chips inside sensors or IoT boards, are the quiet introverts of the group. They have little memory, tiny batteries, and a strong preference for naps to save power. Getting all three to cooperate is a bit like trying to organize a band where one member only plays jazz, another insists on classical, and the third speaks in Morse code.</p>
<p>That is what engineers mean when they talk about scaling Bluetooth. It is not just about adding more devices. It is about making sure completely different systems can talk to each other reliably and continuously without draining their batteries or losing their minds. It requires design decisions that consider timing, power management, data formats, and even how the operating system schedules background tasks.</p>
<p>This article will guide you through that strange world. We will peel back the layers of how Bluetooth actually works and what happens when Android, iOS, and embedded devices try to share the same airwaves. We will explore why each one behaves the way it does and what you can do to build systems that stay connected instead of collapsing under their own complexity.</p>
<p>By the end, you will see that Bluetooth is not really broken. It is simply overworked. It is a polite translator trying to keep three very different languages in sync. Once you learn how to manage its quirks and give it the structure it needs, Bluetooth becomes not a source of frustration but a quiet, invisible network that holds the modern world together.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-bluetooth-has-two-personalities-meet-classic-and-ble">Bluetooth Has Two Personalities — Meet Classic and BLE</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-android-ios-and-embedded-devices-the-odd-trio">Android, iOS, and Embedded Devices — The Odd Trio</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-architecting-for-scale-herding-cats-but-wirelessly">Architecting for Scale — Herding Cats, but Wirelessly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-connection-discovery-and-data-flow-the-bluetooth-dating-game">Connection, Discovery, and Data Flow — The Bluetooth Dating Game</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-platform-quirks-and-how-to-stay-sane">Platform Quirks — And How to Stay Sane</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-security-and-privacy-at-scale">Security and Privacy at Scale</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-power-and-performance-tuning">Power and Performance Tuning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-provisioning-and-firmware-updates-welcome-to-device-kindergarten">Provisioning and Firmware Updates — Welcome to Device Kindergarten</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-debugging-monitoring-and-testing-across-platforms">Debugging, Monitoring, and Testing Across Platforms</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-architecture-example-when-bluetooth-finally-behaves">Real-World Architecture Example — When Bluetooth Finally Behaves</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-checklist-building-a-truly-scalable-bluetooth-system">Checklist — Building a Truly Scalable Bluetooth System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrap-up-lessons-from-the-field">Wrap-Up — Lessons from the Field</a></p>
</li>
</ul>
<h2 id="heading-bluetooth-has-two-personalities-meet-classic-and-ble">Bluetooth Has Two Personalities — Meet Classic and BLE</h2>
<p><img src="https://elainnovation.com/wp-content/uploads/2021/12/Bluetooth-VS-BLE-EN.jpg.webp" alt="What is the difference between Bluetooth and Bluetooth Low Energy (BLE)?" width="800" height="534" loading="lazy"></p>
<p>Before we can talk about scaling Bluetooth, we have to understand that Bluetooth itself has a bit of an identity crisis. It actually comes in two flavors: Classic Bluetooth and Bluetooth Low Energy, also called BLE. They share the same name and sometimes even live on the same chip, but under the hood they behave very differently. Think of them as twins who went to completely different schools and now have opposite personalities.</p>
<p>Classic Bluetooth is the older sibling. It was designed for steady, high-speed data streams. This is the version your headphones, speakers, and car systems use. It is reliable for sending large amounts of data like audio, but it is also chatty and power-hungry. It likes to stay connected all the time, constantly keeping the line open so it can send sound packets smoothly. You could say Classic Bluetooth is like that one friend who calls instead of texting and keeps the conversation going even when there is nothing left to say.</p>
<p>Then there is Bluetooth Low Energy, the younger, more introverted sibling. BLE was designed for devices that need to last for weeks or months on tiny batteries. It does not keep a constant connection open. Instead, it wakes up, sends or receives a little bit of data, and then goes back to sleep. It is the protocol behind fitness trackers, heart rate monitors, smart locks, and most modern IoT devices. If Classic Bluetooth is a full-time conversation, BLE is more like sending quick text messages throughout the day, short, efficient, and battery-friendly.</p>
<p>The funny thing is that even though they share the same wireless spectrum and sometimes even the same antenna, these two modes do not talk to each other directly. A BLE device cannot communicate with a Classic Bluetooth-only device. This is why your wireless headphones can pair with your phone, but your BLE heart rate monitor cannot talk to your old Bluetooth speaker. They live in the same neighborhood but never attend the same parties.</p>
<p>Most of the world’s scaling problems come from BLE, not Classic Bluetooth. Classic has been around long enough that its use cases are stable and well understood. BLE, on the other hand, is used in thousands of different kinds of devices, each with different timing requirements, power limits, and operating systems. When you try to make Android, iOS, and embedded systems all use BLE together, you are juggling three slightly different interpretations of the same rulebook.</p>
<p>To make things trickier, each platform implements BLE its own way. Android exposes it through flexible but sometimes unpredictable APIs. iOS keeps it tidy under Apple’s strict Core Bluetooth framework. Embedded devices rely on lightweight vendor stacks that can vary from chip to chip. Every one of these stacks follows the same Bluetooth specification, but like recipes written by different chefs, the results can taste a little different.</p>
<p>Understanding this dual nature is key to building anything that scales. You must know when to use Classic Bluetooth for high-speed continuous data, when to use BLE for low-power bursts, and how to design your system so that the right devices use the right mode. It is the first step in turning Bluetooth from a confusing mystery into a reliable network you can actually control.</p>
<h2 id="heading-android-ios-and-embedded-devices-the-odd-trio">Android, iOS, and Embedded Devices — The Odd Trio</h2>
<p><img src="https://cdn.dca-design.com/uploads/images/News/_full_width_content_image/105358/Bluetooth_DCA_News_Article_003.webp?v=1749036238" alt="Working with Bluetooth Low Energy across Android and iOS - News - DCA Design" width="2800" height="1999" loading="lazy"></p>
<p>Now that we know Bluetooth has two personalities, let’s meet the three characters that make scaling it so complicated: Android, iOS, and embedded devices. They all speak Bluetooth, but in their own unique accents. Sometimes they understand each other perfectly, and other times it feels like they’re arguing in three different languages while pretending they’re on the same page.</p>
<p>Let’s start with Android. Android is the enthusiastic extrovert of the group. It gives you tons of control and freedom. You can scan, connect, advertise, read, write, and basically poke around every corner of the Bluetooth stack. But that freedom comes with chaos. Because Android runs on phones made by dozens of manufacturers, each one tweaks the Bluetooth implementation a little differently. On one phone, everything works flawlessly. On another, the same code randomly drops connections or refuses to scan in the background. Even Android engineers joke that if your Bluetooth works the same on every device, you’ve probably entered a parallel universe.</p>
<p>Android is powerful but unpredictable. It’s like a sports car that can win a race on a good day but sometimes refuses to start if it doesn’t like the weather. The trick is to write code that expects weird behavior, to build your own connection queues, add retries, and prepare for the occasional glitch. Developers who survive Android Bluetooth bugs don’t just gain experience, they gain humility.</p>
<p>Then there’s iOS, Apple’s polished and opinionated perfectionist. Unlike Android, iOS is consistent. The same code usually behaves the same way across every iPhone and iPad. Apple’s Bluetooth framework, called Core Bluetooth, is beautifully organized and well-documented. But Apple also has strict rules about what you can and can’t do. Background scanning? Only in very specific cases. Advertising? Only for certain UUIDs. Access to lower-level Bluetooth layers? Absolutely not. Apple’s approach is like a luxury hotel: everything looks gorgeous, but you’re not allowed in the kitchen.</p>
<p>Working with iOS feels calm at first. Your connections are stable, your APIs are clear, and your devices behave predictably. But the moment you need to do something slightly unconventional, like connecting to multiple peripherals at once or keeping the app alive in the background, iOS politely says, “No, that’s not how we do things here.” Developers often end up performing delicate dances with background modes, notifications, and clever reconnection tricks just to make things feel seamless for users.</p>
<p>And then we have the third member of the trio: embedded devices. These are the quiet, uncomplaining ones that actually do most of the work. They live inside your smart sensors, wearables, and IoT nodes. They’re usually built around tiny chips with limited memory and low-power processors. They don’t have fancy operating systems or flashy UI frameworks. All they know is how to advertise, connect, send data, and then go back to sleep to save battery.</p>
<p>Embedded devices are loyal but easily overwhelmed. They can’t handle constant large data transfers, and they get cranky if you make them maintain too many simultaneous connections. Imagine trying to run a marathon after eating one grape, that’s what it’s like for a small BLE chip to handle too much traffic. Yet, these little devices are the backbone of every scalable Bluetooth network. They measure your heart rate, control your smart lights, and track your environmental sensors, all while running quietly in the background.</p>
<p>The real challenge begins when you try to make these three cooperate. Android wants freedom, iOS wants structure, and embedded devices just want a nap. Getting them all to work together is like managing a group project where one person writes essays at midnight, another color-codes everything, and the third forgets to charge their laptop. But when you finally get it right, when Android, iOS, and your embedded nodes connect seamlessly, it feels like magic again.</p>
<p>In the next section, we’ll explore how to actually make that happen. You’ll see how to design a Bluetooth architecture that scales gracefully across these platforms instead of collapsing into a pile of logs and retries. It’s part engineering, part patience, and part diplomacy.</p>
<h2 id="heading-architecting-for-scale-herding-cats-but-wirelessly">Architecting for Scale — Herding Cats, but Wirelessly</h2>
<p>If there’s one secret to scaling Bluetooth, it’s this: treat it like herding cats. You’ll never truly <em>control</em> it, but with enough structure, patience, and a bit of catnip (or clever engineering), you can convince all the cats to move in roughly the same direction.</p>
<p>Building a Bluetooth system that spans Android, iOS, and embedded devices isn’t just about writing code that connects things. It’s about designing <em>relationships</em>, the rules and boundaries that keep those connections healthy. The key idea here is <strong>architecture</strong>, which is a fancy word for “deciding who does what, when, and how.” Without a solid architecture, your Bluetooth project quickly turns into a tangle of callbacks, disconnections, and unanswered packets.</p>
<p>The first principle of Bluetooth architecture is <strong>abstraction</strong>. Every platform has its own Bluetooth API, but the basic idea is always the same: scan for devices, connect, exchange data, and disconnect. So instead of writing separate logic for each platform, you create one unified interface, a sort of translator layer, that hides all the messy differences underneath. In practice, this means you can write something like <code>connect(device)</code> in your app, and whether you’re on Android, iOS, or even a Raspberry Pi, the underlying code figures out how to make it happen.</p>
<p>This abstraction layer is your peacekeeper. It prevents the rest of your app from needing to know whether it’s talking to a Nordic chip on a wristband, a smart bulb using an ESP32, or an iPhone pretending to be a peripheral. When you have hundreds or thousands of devices, abstraction isn’t just convenient, it’s survival.</p>
<p>Next comes <strong>connection management</strong>. BLE connections are like toddlers: they demand constant attention and can vanish the moment you look away. A scalable Bluetooth system can’t afford to panic every time a device disconnects. Instead, you design it to expect chaos. You add automatic retries, reconnection strategies, and timeouts that gracefully handle failures instead of freezing your app. Good systems don’t assume the network will always behave, they assume it won’t.</p>
<p>Then there’s <strong>data orchestration</strong>, deciding who talks first, how much data gets sent, and how you keep multiple connections from tripping over each other. Imagine you’re a conductor in an orchestra where half the instruments fall asleep randomly to save power. You need a plan that lets each device play its part in harmony without draining its battery. That’s what managing Bluetooth data flow feels like.</p>
<p>And finally, there’s <strong>power strategy</strong>. Embedded devices live on tight energy budgets. Every scan, advertisement, and data exchange eats into their lifespan. So, your architecture must schedule communication intelligently, let devices wake up briefly, share data, and return to sleep before they burn out. The best Bluetooth systems look lazy on the surface but are actually brilliant planners underneath.</p>
<p>When you put all of this together, abstraction, connection management, orchestration, and power control, you get something that <em>scales</em>. It doesn’t matter if you’re managing three wearables or three thousand sensors. The system behaves predictably, logs issues instead of panicking, and recovers from disconnections automatically.</p>
<p>Think of it like a well-run airport. Planes (your devices) take off and land constantly. The control tower (your app’s Bluetooth manager) keeps track of who’s in the air, who’s landing next, and who needs maintenance. No single pilot needs to know everything, they just follow the protocol.</p>
<p>Scaling Bluetooth isn’t about being clever with one device. It’s about designing systems that keep working even when dozens of devices act unpredictably. You don’t tame Bluetooth by force; you do it by creating a world where even chaos feels organized.</p>
<p>In the next section, we’ll dig deeper into how these connections actually behave in real time, how devices discover each other, exchange data, and, sometimes, break up without warning.</p>
<h2 id="heading-connection-discovery-and-data-flow-the-bluetooth-dating-game">Connection, Discovery, and Data Flow — The Bluetooth Dating Game</h2>
<p>Every Bluetooth connection starts like a modern love story. One device sends out signals into the air, announcing that it’s available. Another device scans the surroundings, hoping to find something compatible. When they finally spot each other, they exchange a few polite packets, decide they’re a good match, and try to make it official with a connection. It’s wireless romance, until one of them walks away without saying goodbye.</p>
<p>This is the heart of how Bluetooth works: <strong>advertising, discovery, and connection</strong>. An embedded sensor or wearable device usually plays the role of the advertiser. It broadcasts tiny packets called advertisements that contain just enough information to say, “Hey, I’m here, and I can measure temperature or heart rate or unlock your door.” These packets are intentionally small because transmitting data takes energy, and low-power devices have to conserve every drop of battery life.</p>
<p>Meanwhile, your phone or tablet acts as the scanner, it listens to the radio waves around it, searching for those signals. When it finds one that matches what it’s looking for, it sends a request to connect. If the peripheral accepts, they move into a new relationship phase: the <strong>GATT connection</strong>. GATT stands for Generic Attribute Profile, which is basically the language they use to talk. Once connected, your phone can ask the device for specific data, like reading a heart rate measurement or writing a configuration setting.</p>
<p>Now, if all of this sounds peaceful and predictable, that’s because we haven’t talked about what happens in the real world. In reality, devices move around, signals weaken, and phones go into power-saving modes that forget they were even connected. Connections drop. Pairing sometimes fails. And when you have ten or more devices talking at once, managing all those tiny wireless conversations becomes a circus act.</p>
<p>Scaling Bluetooth is all about keeping this circus under control. You can’t force every device to stay connected forever, that would drain batteries and jam the radio channels. Instead, you design a rhythm. Devices connect only when needed, exchange data quickly, and then disconnect to rest. This constant dance of connecting and disconnecting keeps the system efficient and stable.</p>
<p>Think of it like a well-run coffee shop. Customers (phones) walk in, place their order (data request), get their coffee (response), and leave. The barista (the embedded device) doesn’t serve one person all day, it serves everyone in quick cycles. The trick is to make sure no one gets stuck waiting for their latte forever.</p>
<p>Timing is everything in this dance. If a device advertises too infrequently, the phone might not discover it in time. If it advertises too often, it wastes power. If the phone sends too many requests at once, the device might crash or slow down. Bluetooth connections live in this delicate balance between performance and efficiency.</p>
<p>When you scale, you also have to think about coordination. Imagine one phone trying to talk to ten sensors at once. You can’t have it flood them all with requests simultaneously, it needs a queue, a polite way of saying “you first, then me.” This is called <strong>connection orchestration</strong>, and it’s one of the hardest parts of scaling BLE systems.</p>
<p>And then there’s the breakup. Devices disconnect all the time, sometimes intentionally, sometimes accidentally. The best Bluetooth systems treat disconnections not as failures but as normal events. The app automatically retries, reconnects, and syncs data without asking the user to “try again.” To users, it feels seamless. Underneath, there’s a lot of quiet heroism happening, background threads, timers, and reconnection logic all working together to patch up relationships on the fly.</p>
<p>So, at its core, Bluetooth is less like a stable marriage and more like speed dating with excellent scheduling. Everyone meets briefly, exchanges information, and moves on. When done right, this model scales effortlessly. When done wrong, it’s chaos.</p>
<p>In the next section, we’ll explore the quirks that make Android, iOS, and embedded devices behave differently in this dating game, and how to keep the peace when one of them inevitably ghosts the others.</p>
<h2 id="heading-platform-quirks-and-how-to-stay-sane">Platform Quirks — And How to Stay Sane</h2>
<p>Once you start scaling Bluetooth, you’ll notice something odd. The same code that works perfectly on one device suddenly refuses to behave on another. It’s like watching identical twins argue about who gets the last slice of pizza, they may look the same, but their personalities couldn’t be more different.</p>
<p>Let’s start with Android, the unpredictable one. Android gives developers more power than any other mobile platform. You can scan however you like, filter by services, read and write any characteristic, and even customize connection intervals. But that power comes at a price. Every phone manufacturer modifies the Bluetooth stack slightly. Samsung, Pixel, OnePlus, Xiaomi, each adds its own flavor of “enhancement,” which sometimes translates to “surprise, nothing works the same.”</p>
<p>One Android phone might handle ten connections at once without blinking. Another might drop all of them the moment the screen turns off. Some versions ignore Bluetooth permissions until you grant location access. Others claim they’re scanning when they actually stopped five minutes ago. Android developers eventually stop asking <em>why</em> and simply build more logging instead. The rule of thumb with Android Bluetooth is simple: test everything, assume nothing, and expect the unexpected.</p>
<p>Then there’s iOS, which at first feels like a breath of fresh air. Apple’s Core Bluetooth framework is clean, consistent, and almost elegant. You get predictable callbacks, smooth reconnections, and well-behaved devices. But if you step outside Apple’s boundaries, you’ll quickly find invisible fences. iOS doesn’t let apps scan in the background freely. It limits how often you can advertise. And if your app tries to keep too many simultaneous connections alive, iOS politely steps in and shuts them down.</p>
<p>Apple’s philosophy is control. It wants Bluetooth connections to behave in ways that don’t drain the battery or clutter the radio. That’s great for users, but for developers it can feel like being handed the keys to a Ferrari and told you can only drive in the parking lot. It works beautifully, as long as you color inside the lines.</p>
<p>And then we have embedded devices, which are in a category of their own. These are the little chips sitting inside your wearables, sensors, or IoT gadgets. They don’t have operating systems or background processes. They just run tiny loops of firmware that listen, respond, and sleep. Their quirks are more about physics than software. If the antenna isn’t tuned properly, signals drop. If the power supply fluctuates, the radio turns off. Sometimes they disconnect simply because a human walked between two devices and absorbed the signal.</p>
<p>Embedded Bluetooth stacks also differ by manufacturer. Nordic, Espressif, Silicon Labs, Texas Instruments, each has its own libraries, quirks, and limitations. Even small changes like increasing the packet size or adjusting the advertising interval can make or break communication. It’s a careful dance between efficiency and reliability.</p>
<p>Now imagine you’re trying to get all three of these worlds to cooperate. Android wants freedom, iOS enforces discipline, and embedded devices want long naps. Building a Bluetooth system that works across all of them is like running a daycare with overachievers, rule-followers, and kids who fall asleep mid-activity. You can’t treat them all the same, but you can design a routine that keeps everyone content.</p>
<p>The secret is resilience. Instead of expecting perfect behavior, build your system around imperfections. Add retries when connections fail. Cache data so you don’t lose progress during disconnections. Keep your embedded devices simple, your mobile apps forgiving, and your logs brutally honest.</p>
<p>If you design with these quirks in mind, your Bluetooth system will feel almost magical, even though, behind the scenes, it’s a web of error handling, reconnections, and polite compromise.</p>
<p>In the next section, we’ll take a look at another side of scaling: keeping everything secure and private while all these devices whisper secrets over the air.</p>
<h2 id="heading-security-and-privacy-at-scale">Security and Privacy at Scale</h2>
<p>Once your Bluetooth system starts working reliably, there’s another challenge waiting in the wings: keeping it <strong>secure</strong>. It’s one thing to get devices talking to each other, it’s another to make sure no one else is eavesdropping on the conversation. Bluetooth security can sound intimidating, but at its core, it’s about making sure your devices trust each other and that strangers can’t sneak into the chat.</p>
<p>Let’s start with pairing. Pairing is Bluetooth’s version of saying, “Hey, can I trust you?” It’s a handshake where two devices exchange keys that let them communicate securely in the future. There are a few ways this handshake can happen. The simplest is called <em>Just Works</em>, which basically means, “We’ll trust each other without asking too many questions.” It’s convenient but about as safe as leaving your front door unlocked because you live in a nice neighborhood. For harmless gadgets like wireless speakers, that’s fine. But for medical devices or smart locks, “Just Works” can turn into “Just Got Hacked.”</p>
<p>A safer approach is <strong>Passkey Entry</strong>, where one device shows a code and the other types it in, proving they’re physically near each other. Even better is <strong>Out-of-Band (OOB)</strong> pairing, where the devices exchange security information through another method, maybe a QR code, NFC tap, or even an optical blink, before connecting over Bluetooth. OOB pairing is like verifying someone’s identity face-to-face before continuing a conversation online.</p>
<p>Once paired, devices use <strong>encryption</strong> to scramble their communication. Anyone listening nearby will hear only gibberish. The strength of that encryption depends on the version of Bluetooth being used. Modern devices using Bluetooth 4.2 or later support something called <em>LE Secure Connections</em>, which is based on advanced cryptography. Older devices use weaker methods that are easier to crack. So, if you’re building something new, never rely on outdated pairing modes.</p>
<p>But security isn’t just about encryption. It’s also about <strong>privacy</strong>. Every Bluetooth device has an address, kind of like its phone number, that it uses when broadcasting. If that address stays the same, someone could track you by following your device’s broadcasts. That’s why newer standards support <em>random address rotation</em>, where devices periodically change their Bluetooth address. Your phone and smartwatch still recognize each other, but strangers can’t follow your signal around the city.</p>
<p>When you scale Bluetooth systems, these little details become critical. A single insecure device in your network can become the weak link that compromises everything. It’s like locking every door in your house but leaving one window open. Attackers don’t need to break the whole system, they just need to find the lazy one.</p>
<p>Building security into a large Bluetooth deployment means standardizing your pairing process, using strong encryption everywhere, and handling key storage carefully. On embedded devices, that can be tricky because they have limited memory and no secure element by default. Still, even small steps help, like regenerating keys periodically and disabling “Just Works” mode for devices that control anything important.</p>
<p>On mobile platforms, the rules are slightly different. Android and iOS handle much of the heavy lifting for you, but you still have to design your app logic carefully. Always confirm which device you’re connecting to before exchanging sensitive data. Always check bonding state before sending configuration commands. In short, treat Bluetooth communication with the same seriousness you’d give to a login session or an online payment.</p>
<p>At scale, security isn’t something you bolt on later. It’s part of the system’s DNA. You can’t fix a weak handshake by adding a stronger password later. You have to start from the first pairing and make sure every connection trusts the right partner.</p>
<p>The reward is worth it. When done right, your Bluetooth network becomes invisible but secure, a quiet, encrypted web of trust that just works. No drama, no leaks, and no nearby strangers hijacking your sensors.</p>
<p>In the next section, we’ll talk about another invisible problem that decides whether your Bluetooth network lives for days or months: power. Because what good is a secure device if its battery dies halfway through the handshake?</p>
<h2 id="heading-power-and-performance-tuning">Power and Performance Tuning</h2>
<p>If you’ve ever wondered why your Bluetooth gadget dies right when you need it most, you’ve just met the oldest enemy in wireless communication: power consumption. Bluetooth may be clever, flexible, and everywhere, but it also has a bit of a caffeine problem. It loves to talk, and talking burns energy. Keeping your devices alive longer, especially when you scale, means learning the quiet art of power management.</p>
<p>At first, it’s easy to assume that Bluetooth is low power by default. After all, it’s called <strong>Bluetooth Low Energy</strong>, right? But BLE’s efficiency only shines when it’s used correctly. A poorly tuned BLE system can drain a battery faster than streaming music over Classic Bluetooth. The magic lies in controlling when devices talk, how long they talk, and how much they say each time.</p>
<p>Let’s start with the <strong>advertising interval</strong>. This is how often a device shouts, “I’m here!” into the air. If you set it to broadcast every 20 milliseconds, you’ll discover devices quickly, but you’ll also burn through the battery like it’s running a marathon. Increase the interval to once every second, and your device will last much longer, but phones may take a moment to find it. It’s a tradeoff between speed and stamina. Every system has to find its sweet spot.</p>
<p>Next comes the <strong>connection interval</strong>, how often two connected devices exchange data. This is like deciding how frequently you check your messages. If you check every second, you stay perfectly up to date but never get anything else done. If you check once every minute, you save time but risk missing something important. In Bluetooth terms, a shorter connection interval means faster communication but higher power usage. Longer intervals conserve battery but add delay. Smart systems adjust these intervals dynamically depending on what the device is doing.</p>
<p>Then there’s the <strong>MTU</strong>, or Maximum Transmission Unit, the size of each Bluetooth data packet. Bigger packets mean fewer total transmissions for large chunks of data, which can improve efficiency. But some devices, especially older ones, can’t handle large MTUs, so finding the right balance is important.</p>
<p>Power management is not just about numbers, it’s about habits. A well-designed embedded device spends most of its life asleep. It wakes up only to advertise or exchange data, then returns to rest as quickly as possible. Imagine a hummingbird darting out for a sip of nectar and then zipping back to rest before anyone notices. That’s how efficient Bluetooth devices survive on coin-cell batteries for months or even years.</p>
<p>On the phone side, energy management is just as critical, especially when your app needs to handle multiple connections. Constant scanning, reconnecting, or keeping GATT channels open drains your user’s battery, and patience. Android and iOS both have built-in mechanisms that throttle background Bluetooth activity to save power. Developers have to work with these rules, not against them. The best apps schedule scans intelligently, reconnect only when necessary, and avoid holding connections open when no data needs to be sent.</p>
<p>Scaling Bluetooth systems makes these power decisions even more important. When you have one device, wasting a bit of energy doesn’t matter. When you have hundreds of devices, each one burning just a few extra milliwatts, the total waste adds up quickly. Power efficiency becomes the difference between a network that runs for months and one that collapses after a week.</p>
<p>The golden rule of power tuning is simple: talk less, talk smarter. A Bluetooth device that knows when to speak and when to stay quiet can scale beautifully, even in large networks. It’s not about being fast all the time, it’s about being clever with timing.</p>
<p>In the next section, we’ll look at how these devices join your network in the first place and what happens when you need to update their software later. Because once your system scales, you’re not just connecting devices, you’re managing an entire population.</p>
<h2 id="heading-provisioning-and-firmware-updates-welcome-to-device-kindergarten">Provisioning and Firmware Updates — Welcome to Device Kindergarten</h2>
<p>Imagine setting up one Bluetooth device. It’s easy: you pair it, give it a name, and maybe tweak a few settings. Now imagine doing that a hundred times. Or a thousand. Suddenly, what felt like a simple task starts to look like a factory assembly line powered by frustration. That’s where <strong>provisioning</strong> comes in, the process of onboarding new devices into your Bluetooth network so they can start working right away, without manual babysitting.</p>
<p>Provisioning is like a first day at school for your devices. Each new student needs to be identified, assigned to a class, and given a name tag. In the Bluetooth world, a newly manufactured device begins life in an “unprovisioned” state. It doesn’t belong to any network yet, so it advertises with a special signal that says, “Hey, I’m new here.” When your mobile app or gateway spots that advertisement, it can connect, authenticate the device, and hand over the credentials it needs to join the system.</p>
<p>The app usually performs a few key steps during provisioning. It verifies that the device is genuine, assigns it a unique identifier, and exchanges security keys so future connections can happen securely. It might also store metadata like which room the sensor belongs to or what type of data it will report. After provisioning, the device switches to its normal operation mode, where it advertises with its new identity and starts behaving like a member of the family.</p>
<p>When you have just one or two devices, you can do all this manually. But when you scale up to hundreds or thousands, manual setup becomes impossible. That’s when you start thinking about automation, QR codes on packaging, NFC tags for instant pairing, or out-of-band provisioning where a separate channel (like Wi-Fi or a wired link) handles secure onboarding. The goal is to make provisioning quick, repeatable, and error-free, even when your factory or users are adding new devices by the dozens.</p>
<p>Once your devices are out in the world, the next challenge appears: <strong>firmware updates</strong>. Every system eventually needs to fix bugs, patch security holes, or add new features. For Bluetooth devices, this means pushing new firmware over the same wireless link, a process known as <strong>FOTA</strong>, or firmware-over-the-air updates.</p>
<p>Updating firmware over Bluetooth can be nerve-wracking. The connection is relatively slow, and interruptions can leave a device half-updated and confused about who it is. Good update systems handle this carefully. They divide the firmware into chunks, verify each piece with checksums, and only switch to the new version once the whole update has been safely received and validated. If anything fails midway, the device rolls back to the old firmware instead of bricking itself.</p>
<p>Scaling makes this even more complex. Updating ten devices is fine. Updating a thousand can overwhelm your network if you try to do them all at once. Smart systems stagger the updates in waves, track which devices have finished, and retry the ones that didn’t. Some even let devices report their status back to a central dashboard, so you can see which ones are ready and which ones are still stuck halfway through.</p>
<p>Provisioning and firmware updates might not sound glamorous, but they’re the backbone of every scalable Bluetooth system. Without smooth onboarding and reliable updates, your network slowly falls apart as devices drift out of sync or miss critical fixes.</p>
<p>Think of it this way: provisioning is how devices <em>join the family</em>, and firmware updates are how they <em>grow up</em>. Both are essential if you want your Bluetooth ecosystem to stay healthy and dependable over time.</p>
<p>In the next section, we’ll talk about what happens when something inevitably goes wrong, how to debug and monitor a network full of devices without losing your mind.</p>
<h2 id="heading-debugging-monitoring-and-testing-across-platforms">Debugging, Monitoring, and Testing Across Platforms</h2>
<p>At some point, every Bluetooth developer faces the same moment of quiet despair. The logs look fine, the devices are paired, the code hasn’t changed, and yet… nothing works. Connections fail, packets vanish, and everything that worked yesterday now refuses to cooperate. Welcome to the wonderful, mysterious world of Bluetooth debugging, a place where logic takes a vacation and patience becomes your most valuable skill.</p>
<p>Debugging Bluetooth is tricky because so much of it happens invisibly. The data is flying through the air, hopping between frequencies dozens of times per second, and all you can see is whether the connection succeeds or fails. It’s like trying to diagnose a conversation between two people whispering in another room. You can tell they’re talking, but not what they’re saying.</p>
<p>The first rule of Bluetooth debugging is simple: <strong>log everything</strong>. Log when you start scanning, when you find a device, when you connect, and when you disconnect. Log the signal strength, the UUIDs you discover, the number of bytes you read, and the time it took. Bluetooth problems rarely announce themselves loudly, they hide in tiny details. A small delay in a callback or a missing acknowledgment can reveal exactly why your system seems haunted.</p>
<p>Different platforms give you different kinds of help. Android, for example, offers detailed Bluetooth logs through developer options or tools like <code>adb</code>. You can capture the raw Bluetooth HCI logs and analyze them later to see what really happened under the hood. iOS, on the other hand, gives you less direct visibility. Apple handles most of the Bluetooth stack internally, so your only clues come from Core Bluetooth callbacks. Embedded devices often let you log directly from the firmware, showing connection events, error codes, and sometimes even packet-level information if the stack supports it.</p>
<p>Testing across platforms is just as important as debugging. You can’t assume that if it works on one phone, it will work on another. Android devices, especially, have a habit of interpreting Bluetooth timing slightly differently. A system that’s rock-solid on a Pixel may stutter on a Samsung or freeze on a low-cost tablet. The only cure is diversity, test on multiple brands, OS versions, and firmware builds until you’re confident the system behaves everywhere.</p>
<p>For embedded devices, testing is a different challenge. Because they often run continuously, you need long-term endurance tests to catch issues that only appear after hours or days of operation. You might discover that a connection fails only after 300 reconnections, or that a memory leak appears after a week of normal use. Building test rigs that automate these scenarios: connecting, disconnecting, and verifying data repeatedly, is a huge time saver.</p>
<p>Monitoring is what happens after you’ve deployed your devices into the real world. It’s like keeping a health tracker on your entire Bluetooth network. Your mobile apps or gateways can collect statistics such as signal strength, connection failures, uptime, and battery levels. That data tells you which devices are performing well and which ones might be drifting toward trouble.</p>
<p>Adding this kind of visibility pays off enormously at scale. When you’re managing hundreds of devices, it’s impossible to check each one manually. Instead, you rely on trends, for example, if one location shows consistently weak signal strength, maybe there’s interference nearby. If multiple devices drop connections at the same time, maybe the central device needs a firmware update. Monitoring transforms guesswork into insight.</p>
<p>The truth is, debugging and monitoring never really end. Even after your system is stable, new versions of Android and iOS will appear with small Bluetooth changes that break something you didn’t know could break. Treat Bluetooth maintenance like car maintenance: routine, ongoing, and essential.</p>
<p>Once you learn to capture good logs, read them calmly, and build systems that report their own health, debugging stops being a nightmare and becomes a science. Bluetooth may always be a little mysterious, but with the right tools and attitude, you can keep the ghosts out of your connection list.</p>
<p>In the next section, we’ll put everything together with a real-world example of what scaling Bluetooth actually looks like when all the pieces: mobile apps, embedded devices, and architecture, finally work in harmony.</p>
<h2 id="heading-real-world-architecture-example-when-bluetooth-finally-behaves">Real-World Architecture Example — When Bluetooth Finally Behaves</h2>
<p>Let’s take everything we’ve talked about and bring it to life with a real-world scenario. Imagine you’re building a smart factory system with hundreds of Bluetooth sensors scattered across the floor. Each sensor measures temperature, vibration, or humidity. Some are attached to machines, others hang on walls, and a few are hidden in places even the janitor doesn’t know about. Your goal is simple on paper: collect data from all these sensors, send it to a central dashboard, and keep everything running smoothly.</p>
<p>The reality, of course, is much more complicated. Each sensor is an embedded device powered by a coin-cell battery that has to last for months. They advertise periodically to announce they’re alive. Your Android or iOS tablets, placed around the factory as gateways, act as Bluetooth centrals. Their job is to scan, connect to nearby sensors, read data, and upload it to the cloud. It sounds straightforward, but you’re juggling dozens of invisible connections at once, and they all have different moods.</p>
<p>The architecture begins with careful planning. Each gateway tablet knows which part of the factory it’s responsible for. That way, you avoid overcrowding the airwaves with multiple devices trying to connect to the same sensors. The sensors use slightly staggered advertising intervals so they don’t all shout at the same time. The gateways maintain a queue, connecting to a few sensors at a time, reading data, and then disconnecting before moving on to the next group. This rotation keeps everything balanced and prevents Bluetooth traffic jams.</p>
<p>Power management is built into every step. Each sensor wakes up, advertises briefly, sends its data when connected, and goes right back to sleep. The connection interval and MTU size are tuned for efficiency, large enough for smooth data transfer, but not so large that slower devices choke. Every byte is treated like gold because every transmission costs energy.</p>
<p>The gateways handle the messy parts: reconnections, retries, and data aggregation. They buffer readings in case the Wi-Fi link to the cloud goes down and sync later when it’s back. They also monitor each sensor’s signal strength, battery level, and uptime. If a sensor hasn’t reported in a while, the system flags it automatically so a technician can check on it.</p>
<p>Now imagine scaling this setup to multiple factory buildings. Suddenly, you’re managing thousands of sensors, dozens of gateways, and countless wireless interactions. At this scale, the design choices you made early, abstracted Bluetooth logic, retry mechanisms, power optimization, and logging, are the difference between a quiet, self-running network and a system that collapses into constant reconnections.</p>
<p>When everything works as intended, something beautiful happens. The sensors collect data silently. The gateways synchronize automatically. The dashboards stay green. Nobody has to restart anything, and Bluetooth quietly fades into the background where it belongs. It’s the rare moment when technology stops demanding attention and simply does its job.</p>
<p>This kind of architecture isn’t science fiction. Companies use it in factories, hospitals, and warehouses every day. From smart lighting systems to patient monitors, Bluetooth at scale can be astonishingly reliable, but only if you treat it like a distributed system, not a single gadget. Each device is a citizen of a larger ecosystem, and your job as the architect is to keep that ecosystem healthy.</p>
<p>The biggest takeaway is that success doesn’t come from fancy algorithms or expensive hardware. It comes from the small, deliberate decisions that make your system resilient: how you handle disconnections, how you schedule connections, how you monitor performance. Scaling Bluetooth is not about avoiding problems, it’s about designing a system that recovers gracefully when problems happen.</p>
<p>In the next section, we’ll wrap up everything we’ve learned into a practical checklist, a simple guide you can use whenever you’re designing a Bluetooth system that has to survive in the wild.</p>
<h2 id="heading-checklist-building-a-truly-scalable-bluetooth-system">Checklist — Building a Truly Scalable Bluetooth System</h2>
<p>By now, you’ve seen Bluetooth in all its moods, charming, confusing, unpredictable, and surprisingly capable when handled with care. So how do you actually put everything together? What makes a Bluetooth system <em>scalable</em> instead of just “working on my desk”? The answer isn’t a single trick or secret API. It’s a mindset, a way of designing your system to expect chaos and still function gracefully when it happens.</p>
<p>The first part of that mindset is consistency. Every Bluetooth system should have one clear and stable way of communicating. Keep your data formats simple, your GATT profiles predictable, and your naming conventions sensible. If you have ten devices made by ten different vendors, make them all speak the same language. The moment one device starts improvising, the whole orchestra sounds off.</p>
<p>Next comes patience, and in Bluetooth, patience means retries. Connections drop. Devices go out of range. A phone might go to sleep or decide that scanning is no longer fashionable. Instead of treating every disconnection as a crisis, treat it as part of the process. A good Bluetooth app quietly retries in the background, restores the connection, and carries on as if nothing happened. To the user, it feels seamless. Underneath, it’s a flurry of logic keeping the experience smooth.</p>
<p>Then there’s the question of power. Remember that every advertisement and connection eats into battery life. A scalable Bluetooth system doesn’t talk all the time, it talks <em>smart</em>. It plans when to wake up, when to exchange data, and when to stay silent. Devices that last longer need fewer replacements, fewer updates, and far less human attention. Power efficiency is the hidden currency of scalability.</p>
<p>Monitoring is another essential habit. If you can’t see what’s happening inside your system, you’re flying blind. Log your connections, track your signal strengths, record how often devices drop out, and visualize it somewhere. A simple dashboard that shows which devices are healthy and which ones are struggling can save you countless hours later. When you scale, visibility turns guesswork into control.</p>
<p>Security, too, can’t be an afterthought. Use secure pairing, proper encryption, and rotating addresses. The bigger your system gets, the more interesting it becomes to people who might want to peek at it. Make sure they can’t. A secure Bluetooth network doesn’t just protect users, it protects your reputation.</p>
<p>Finally, build for change. Bluetooth isn’t static, Android and iOS update their stacks every year, chip vendors release new firmware, and new security standards appear. A scalable system doesn’t break when something changes, it adapts. That’s why abstraction layers, modular code, and updatable firmware matter so much. They keep your system flexible long after the first version ships.</p>
<p>If you do all of this, keep it consistent, patient, efficient, observable, secure, and adaptable, something magical happens. Your Bluetooth system starts to feel less like a fragile web of devices and more like a living network. It keeps running, keeps healing, and quietly gets the job done without constant supervision. That’s when you know you’ve built something that scales.</p>
<p>In the final section, we’ll step back and reflect on the bigger picture, what scaling Bluetooth really teaches us about building technology that has to work not just once, but over and over again in the messy, beautiful real world.</p>
<h2 id="heading-wrap-up-lessons-from-the-field">Wrap-Up — Lessons from the Field</h2>
<p>If you’ve made it this far, you’ve probably realized that scaling Bluetooth isn’t really about Bluetooth at all. It’s about learning how complex systems behave when they leave the comfort of your desk and enter the real world. It’s about understanding that wireless connections are not just electrical signals, they’re relationships between unpredictable, battery-powered, opinionated little machines.</p>
<p>Bluetooth gets a bad reputation because people expect it to be simple. They imagine it’s like Wi-Fi or USB, plug and play, pair and forget. But in truth, Bluetooth is more like a polite conversation at a crowded party. Everyone is talking at the same time, the music is loud, and you have to keep repeating yourself until the other person hears you correctly. When you think of it that way, it’s a miracle that it works as well as it does.</p>
<p>Scaling Bluetooth across Android, iOS, and embedded devices teaches you humility. You stop assuming things will always behave, and instead you start building systems that <em>recover</em> when they don’t. You learn that error handling is not an afterthought, it’s the main event. You discover that batteries are precious, timing is everything, and the smallest design decisions can ripple through an entire ecosystem of devices.</p>
<p>You also start to appreciate the quiet beauty of resilience. There’s something deeply satisfying about watching dozens of sensors, gateways, and phones connect, share data, and disconnect, all without human intervention. When it works, it feels effortless. You forget about the retries, the power cycles, the reconnections, and the debugging sessions that made it possible. All you see is a smooth network humming quietly in the background, doing exactly what it was meant to do.</p>
<p>And that’s the real magic of Bluetooth, not the flashy tech demos or the pairing animations, but the invisible collaboration that happens beneath the surface. It’s the heartbeat of every wearable, every sensor, every tiny device that quietly makes our lives a little easier. Scaling it isn’t just an engineering challenge; it’s a lesson in patience, design, and empathy for systems that can’t always speak for themselves.</p>
<p>So, the next time your Bluetooth device disconnects, take a breath. Somewhere in the chaos, it’s just trying to reconnect, to find its partner again and pick up where it left off. Because deep down, that’s what Bluetooth really is: a network built on trust, persistence, and tiny packets of hope flying through the air.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Secure iOS Apps in Swift: Common Security Pitfalls and How to Fix Them ]]>
                </title>
                <description>
                    <![CDATA[ These days, there are many ways attackers can try to compromise your applications. And thanks to the continued increase in cyberattacks, the demand for secure mobile applications – and by extension, secure coding – has never been higher. So if you’re... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-secure-ios-apps-in-swift-common-security-pitfalls-and-how-to-fix-them/</link>
                <guid isPermaLink="false">68ffdf1761d216440f5bb38a</guid>
                
                    <category>
                        <![CDATA[ Web Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ios app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Tray ]]>
                </dc:creator>
                <pubDate>Mon, 27 Oct 2025 21:07:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761599240278/644f6ebb-6092-4ea0-99e3-a568bfb0390c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>These days, there are many ways attackers can try to compromise your applications. And thanks to the continued increase in cyberattacks, the demand for secure mobile applications – and by extension, secure coding – has never been higher.</p>
<p>So if you’re an iOS developer, you should also learn to prioritize security at every stage of app development.</p>
<p>Swift, Apple’s modern programming language, offers a wealth of tools and frameworks that simplify development while enhancing security, but only when used correctly.</p>
<p>This article explores 10 common security pitfalls in Swift-based iOS apps and offers practical strategies to mitigate them.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before diving in, you’ll need:</p>
<ul>
<li><p>Working knowledge of Swift and iOS development.</p>
</li>
<li><p>Access to Xcode.</p>
</li>
<li><p>Basic understanding of how iOS apps communicate with servers.</p>
</li>
<li><p>Familiarity with Terminal/command line basics.</p>
</li>
</ul>
<p>The code examples are practical and explained step-by-step, making them accessible to junior developers while still offering value to experienced ones looking to strengthen their app's security.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<dl>
<ul>
<li><a href="" id="heading-what-are-the-most-prevalent-security-traps-in-swift-ios-applications">What are the Most Prevalent Security Traps in Swift iOS Applications?</a>
<ul>
<li><a href="" id="heading-1-insecure-data-storage">1. Insecure Data Storage</a></li>
<li><a href="" id="heading-2-weak-network-communication">2. Weak Network Communication</a></li>
<li><a href="" id="heading-3-improper-input-validation">3. Improper Input Validation</a></li>
<li><a href="" id="heading-4-hardcoding-secrets">4. Hardcoding Secrets</a></li>
<li><a href="" id="heading-5-insufficient-authentication-and-authorization">5. Insufficient Authentication and Authorization</a></li>
<li><a href="" id="heading-6-insecure-logging-and-error-handling">6. Insecure Logging and Error Handling</a></li>
<li><a href="" id="heading-7-ignoring-code-obfuscation-and-reverse-engineering">7. Ignoring Code Obfuscation and Reverse Engineering</a></li>
<li><a href="" id="heading-8-insecure-third-party-libraries">8. Insecure Third-Party Libraries</a></li>
<li><a href="" id="heading-9-insufficient-biometric-and-multi-factor-authentication">9. Insufficient Biometric and Multi-Factor Authentication</a></li>
<li><a href="" id="heading-10-disregarding-periodic-security-testing">10. Disregarding Periodic Security Testing</a></li>
</ul>
</li>
</ul>
</dl>

<h2 id="heading-what-are-the-most-prevalent-security-traps-in-swift-ios-applications"><strong>What are the Most Prevalent Security Traps in Swift iOS Applications?</strong></h2>
<p>Swift and iOS offer robust security features, but mistakes still happen. Following are the most common traps and how to fix them:</p>
<h3 id="heading-1-insecure-data-storage">1. Insecure Data Storage</h3>
<p>Among the most common mistakes developers make is having sensitive data stored insecurely. Passwords, tokens, or even individual user data can be left by accident in UserDefaults or local storage in unencrypted form. </p>
<p>While UserDefaults is convenient for small amounts of data, it is not secure for sensitive data as it is so easily accessible to attackers if the device is compromised.</p>
<h4 id="heading-how-to-fix">How to Fix:</h4>
<p>Use the Keychain Services API to securely store sensitive data. Keychain encrypts data and binds it to the device so that it can't be accessed by other unauthorized applications or users.</p>
<p>You can securely store credentials using libraries in Swift such as KeychainAccess or the built-in SecItemAdd and SecItemCopyMatching functions.</p>
<p>For example, this how you can store a user password in Keychain so as to ensure sensitive data is stored securely:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">do</span> {

<span class="hljs-keyword">try</span> keychain.<span class="hljs-keyword">set</span>(<span class="hljs-string">"userPassword123"</span>, key: <span class="hljs-string">"userPassword"</span>)

} <span class="hljs-keyword">catch</span> {

    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Error saving to Keychain: \(error)"</span>)

}
</code></pre>
<p>Behind the scenes, here’s what happens when you call <code>keychain.set("userPassword123", key: "userPassword")</code> inside a KeychainManager class that uses Apple’s native Security framework for storage:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Security

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">KeychainManager</span> </span>{
 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-number">_</span> value: String, key: String)</span></span> <span class="hljs-keyword">throws</span> {
     <span class="hljs-comment">// 1. Convert string to Data</span>
     <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> data = value.data(using: .utf8) <span class="hljs-keyword">else</span> {
         <span class="hljs-keyword">throw</span> <span class="hljs-type">NSError</span>(domain: <span class="hljs-string">"KeychainManager"</span>, code: -<span class="hljs-number">1</span>)
     }

     <span class="hljs-comment">// 2. Build the query dictionary</span>
     <span class="hljs-keyword">let</span> query: [<span class="hljs-type">String</span>: <span class="hljs-type">Any</span>] = [
         <span class="hljs-comment">// Store as password</span>
         kSecClass <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: kSecClassGenericPassword,
         <span class="hljs-comment">// Your app's bundle identifier</span>
         kSecAttrService <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: <span class="hljs-string">"com.yourapp.keychain"</span>,
         <span class="hljs-comment">// "userPassword"</span>
         kSecAttrAccount <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: key,
         <span class="hljs-comment">// "userPassword123" encrypted</span>
         kSecValueData <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: data,
         kSecAttrAccessible <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: kSecAttrAccessibleAfterFirstUnlock
     ]
     <span class="hljs-comment">// 3. Save to keychain (iOS encrypts it automatically)</span>
     <span class="hljs-keyword">let</span> status = <span class="hljs-type">SecItemAdd</span>(query <span class="hljs-keyword">as</span> <span class="hljs-type">CFDictionary</span>, <span class="hljs-literal">nil</span>)
     <span class="hljs-comment">// 4. Check if successful</span>
     <span class="hljs-keyword">guard</span> status == errSecSuccess <span class="hljs-keyword">else</span> {
         <span class="hljs-keyword">throw</span> <span class="hljs-type">NSError</span>(domain: <span class="hljs-string">"KeychainManager"</span>, code: <span class="hljs-type">Int</span>(status))
     }
 }
}
</code></pre>
<p>When this function runs, iOS converts the string value, such as "userPassword123", into encrypted binary data and stores it securely in the device’s Keychain database. The entry is saved under the provided key (for example, "userPassword"), and only your app can access it.</p>
<p>Behind the scenes, the Keychain leverages strong security features, including hardware-backed encryption using device-specific keys, optional biometric protection through Face ID or Touch ID, and app-level isolation to ensure that no other app can read or modify your stored credentials.</p>
<h3 id="heading-2-weak-network-communication">2. Weak Network Communication</h3>
<p>Transmitting sensitive data over the network is another area prone to vulnerabilities. Using unencrypted HTTP connections exposes your app to man-in-the-middle (MITM) attacks, allowing attackers to intercept and modify data in transit.  </p>
<p><strong>The Problem</strong>: When data travels between your app and server over an insecure connection, attackers on the same network (like public Wi-Fi) can:</p>
<ul>
<li><p>Read sensitive information (passwords, personal data, payment details)</p>
</li>
<li><p>Modify requests and responses</p>
</li>
<li><p>Impersonate your legitimate server</p>
</li>
</ul>
<h4 id="heading-how-to-fix-1">How to Fix:</h4>
<p><strong>1. Always Use HTTPS</strong><br>HTTPS encrypts all data in transit, making it unreadable to attackers. iOS's App Transport Security (ATS) enforces this by blocking insecure HTTP connections by default:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// ❌ INSECURE - HTTP connection (blocked by ATS by default)</span>
<span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"http://api.example.com/login"</span>)

<span class="hljs-comment">// ✅ SECURE - HTTPS connection</span>
<span class="hljs-keyword">let</span> url = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.example.com/login"</span>)
</code></pre>
<p>You’ll want to avoid adding ATS exceptions to your Info.plist unless absolutely necessary. If a third-party API only supports HTTP, contact them to upgrade or find a more secure alternative.</p>
<p><strong>2. Implement Certificate Pinning (Advanced Protection)</strong></p>
<p>Even with HTTPS, your app could still be vulnerable to sophisticated MITM attacks. An attacker could install a fraudulent certificate on a user's device (through malware or social engineering), for example, and intercept HTTPS traffic that appears valid. The attacker's fake certificate would be trusted by the device, allowing them to decrypt and read "secure" communications.</p>
<p>Certificate pinning solves this by making your app trust only your specific server's certificate, rejecting all others – even if they're otherwise valid.</p>
<p><strong>How Certificate Pinning Works:</strong></p>
<p>Your app stores the expected certificate (or its public key hash) and validates it during each connection:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecureNetworkManager</span>: <span class="hljs-title">NSObject</span>, <span class="hljs-title">URLSessionDelegate</span> </span>{

    <span class="hljs-comment">// Store your server's certificate hash</span>
    <span class="hljs-comment">// Get this by running: openssl x509 -in certificate.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> expectedPublicKeyHash = <span class="hljs-string">"YOUR_CERTIFICATE_HASH_HERE"</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">urlSession</span><span class="hljs-params">(
        <span class="hljs-number">_</span> session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping <span class="hljs-params">(URLSession.AuthChallengeDisposition, URLCredential?)</span></span></span> -&gt; <span class="hljs-type">Void</span>
    ) {
        <span class="hljs-comment">// Step 1: Check if this is a server trust challenge (certificate validation)</span>
        <span class="hljs-keyword">guard</span> challenge.protectionSpace.authenticationMethod == <span class="hljs-type">NSURLAuthenticationMethodServerTrust</span>,
              <span class="hljs-keyword">let</span> serverTrust = challenge.protectionSpace.serverTrust
        <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Not a certificate challenge; use default handling</span>
            completionHandler(.performDefaultHandling, <span class="hljs-literal">nil</span>)
            <span class="hljs-keyword">return</span>
        }

        <span class="hljs-comment">// Step 2: Validate that the server's certificate matches our pinned certificate</span>
        <span class="hljs-keyword">if</span> isValidServerTrust(serverTrust) {
            <span class="hljs-comment">// Certificate matches - proceed with the connection</span>
            completionHandler(.useCredential, <span class="hljs-type">URLCredential</span>(trust: serverTrust))
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Certificate doesn't match - reject the connection to prevent MITM attack</span>
            completionHandler(.cancelAuthenticationChallenge, <span class="hljs-literal">nil</span>)
        }
    }

    <span class="hljs-comment">// Validates the server's certificate against our pinned hash</span>
    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isValidServerTrust</span><span class="hljs-params">(<span class="hljs-number">_</span> serverTrust: SecTrust)</span></span> -&gt; <span class="hljs-type">Bool</span> {
        <span class="hljs-comment">// Extract the server's certificate</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> serverCertificate = <span class="hljs-type">SecTrustGetCertificateAtIndex</span>(serverTrust, <span class="hljs-number">0</span>) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }

        <span class="hljs-comment">// Get the public key from the certificate</span>
        <span class="hljs-keyword">let</span> serverPublicKey = <span class="hljs-type">SecCertificateCopyKey</span>(serverCertificate)
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> publicKey = serverPublicKey <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }

        <span class="hljs-comment">// Convert the public key to data and hash it</span>
        <span class="hljs-keyword">var</span> error: <span class="hljs-type">Unmanaged</span>&lt;<span class="hljs-type">CFError</span>&gt;?
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> publicKeyData = <span class="hljs-type">SecKeyCopyExternalRepresentation</span>(publicKey, &amp;error) <span class="hljs-keyword">as</span> <span class="hljs-type">Data?</span> <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }

        <span class="hljs-comment">// Hash the public key using SHA-256</span>
        <span class="hljs-keyword">let</span> publicKeyHash = <span class="hljs-type">SHA256</span>.hash(data: publicKeyData)
        <span class="hljs-keyword">let</span> publicKeyHashString = <span class="hljs-type">Data</span>(publicKeyHash).base64EncodedString()

        <span class="hljs-comment">// Compare with our expected hash</span>
        <span class="hljs-keyword">return</span> publicKeyHashString == expectedPublicKeyHash
    }
}
</code></pre>
<p>What this code does, step-by-step:</p>
<ol>
<li><p><code>urlSession(_:didReceive:completionHandler:)</code> – This method is called whenever your app makes an HTTPS connection. iOS asks: "Should I trust this server's certificate?"</p>
</li>
<li><p>Check authentication method – We verify this is a server trust challenge (certificate validation), not some other type of authentication.</p>
</li>
<li><p>Validate the certificate – We call <code>isValidServerTrust()</code>, which:</p>
<ul>
<li><p>Extracts the server's certificate from the connection</p>
</li>
<li><p>Gets the public key from that certificate</p>
</li>
<li><p>Hashes the public key using SHA-256</p>
</li>
<li><p>Compares the hash to our stored, expected hash</p>
</li>
</ul>
</li>
</ol>
<ol start="4">
<li>Make a decision:</li>
</ol>
<ul>
<li><p>If hashes match, then the server is legitimate. Proceed with <code>.useCredential</code>.</p>
</li>
<li><p>If hashes don't match, we have a potential MITM attack. Cancel with <code>.cancelAuthenticationChallenge</code>.</p>
</li>
</ul>
<p>So how does this prevent MITM attacks? Even if an attacker installs a fraudulent certificate on the user's device and intercepts traffic, their certificate's hash won't match your pinned hash. Your app will reject the connection, preventing the attacker from decrypting your traffic.</p>
<p><strong>3. Additional Protection: Recommend a VPN on Public Wi-Fi</strong></p>
<p>You can also recommend that users connect through a VPN when on public Wi-Fi for an added layer of security, and provide guidance on <a target="_blank" href="https://surfshark.com/blog/how-to-use-a-vpn">how to use a VPN</a> effectively to keep their data safe.</p>
<p>For developers, maintaining strong app security also depends on having a well-optimized system, learning <a target="_blank" href="https://cleanmymac.com/blog/macos-tahoe-slow">how to speed up a slow Mac</a> can help ensure smoother builds, faster testing, and a more secure overall development workflow.</p>
<h3 id="heading-3-improper-input-validation">3. Improper Input Validation</h3>
<p>Some developers neglect correct input validation, leading to a number of vulnerabilities including SQL injection, remote code execution, and data corruption.</p>
<p>While Swift has strong typing support, some developers don’t sanitize user input or API responses. Incorporating real-time <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-email-validation-api-for-flask-user-authentication/">API email validation</a> ensures that users provide legitimate, properly formatted email addresses before they are stored or processed, reducing both security risks and data quality issues.</p>
<h4 id="heading-how-to-fix-2">How to Fix:</h4>
<p>Input validation is your first line of defense against malicious data. Here's how to protect your iOS applications:</p>
<p><strong>1. Validate User Input with Patterns</strong></p>
<p>Always validate user input using regular expressions or predefined patterns before processing. For example, when accepting email addresses:</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isValidEmail</span><span class="hljs-params">(<span class="hljs-number">_</span> email: String)</span></span> -&gt; <span class="hljs-type">Bool</span> {
    <span class="hljs-keyword">let</span> emailRegex = <span class="hljs-string">"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"</span>
    <span class="hljs-keyword">let</span> emailPredicate = <span class="hljs-type">NSPredicate</span>(format: <span class="hljs-string">"SELF MATCHES %@"</span>, emailRegex)
    <span class="hljs-keyword">return</span> emailPredicate.evaluate(with: email)
}

<span class="hljs-comment">// Only accept properly formatted data</span>
<span class="hljs-keyword">guard</span> isValidEmail(userEmail) <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Reject invalid input</span>
    <span class="hljs-keyword">return</span>
}
</code></pre>
<p>This ensures that only properly formatted data is accepted, preventing malformed or malicious input from entering your system.</p>
<p><strong>2. Sanitize API Responses</strong></p>
<p>Never trust external data. Always validate and sanitize API responses before using them:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> userAge = apiResponse[<span class="hljs-string">"age"</span>] <span class="hljs-keyword">as</span>? <span class="hljs-type">Int</span>,
   userAge &gt;= <span class="hljs-number">0</span> &amp;&amp; userAge &lt;= <span class="hljs-number">150</span> {
    <span class="hljs-comment">// Safe to use</span>
    user.age = userAge
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Handle invalid data appropriately</span>
    <span class="hljs-keyword">throw</span> <span class="hljs-type">ValidationError</span>.invalidAge
}
</code></pre>
<p><strong>3. Use Parameterized Queries (Most Critical)</strong></p>
<p>The most dangerous mistake is building database queries through string concatenation. Consider this vulnerable code:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// ❌ NEVER DO THIS - Vulnerable to SQL Injection</span>
<span class="hljs-keyword">let</span> username = userInput  <span class="hljs-comment">// Could be: "admin' OR '1'='1"</span>
<span class="hljs-keyword">let</span> query = <span class="hljs-string">"SELECT * FROM users WHERE username = '\(username)'"</span>
database.execute(query)
</code></pre>
<p>If a malicious user enters admin' OR '1'='1 as their username, the query becomes:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">WHERE</span> username = <span class="hljs-string">'admin'</span> <span class="hljs-keyword">OR</span> <span class="hljs-string">'1'</span>=<span class="hljs-string">'1'</span>
</code></pre>
<p>This would return all users in the database instead of just one, potentially exposing sensitive data. The secure solution uses parameterized queries:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// ✅ SAFE - Using parameterized queries</span>
<span class="hljs-keyword">let</span> query = <span class="hljs-string">"SELECT * FROM users WHERE username = ?"</span>
database.execute(query, withArgumentsIn: [username])
</code></pre>
<p>In this approach, the <code>?</code> is a placeholder that the database treats as a parameter, not as part of the SQL command. The username value is passed separately in the <code>withArgumentsIn</code> array.</p>
<p>This means that even if a user tries to inject SQL code like <code>admin' OR '1'='1</code>, the database will treat the entire string as a literal username to search for – not as executable SQL code. The database engine automatically escapes any special characters, completely eliminating the risk of SQL injection.</p>
<p>By separating the query structure from the data, parameterized queries ensure that user input can never alter the intended logic of your SQL statements.</p>
<h3 id="heading-4-hardcoding-secrets">4. Hardcoding Secrets</h3>
<p>API keys, credentials, or private tokens hard-coded in the source code is another serious security mistake. Attackers can extract such secrets from compiled binaries using reverse-engineering tools, especially for apps released to the public.</p>
<p>Once exposed, these credentials can be used to access your backend services, potentially leading to data breaches or unauthorized charges.</p>
<h4 id="heading-the-problem-hardcoded-secrets">The Problem – Hardcoded Secrets:</h4>
<pre><code class="lang-swift"><span class="hljs-comment">// NEVER DO THIS</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">APIClient</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> apiKey = <span class="hljs-string">"1234567890abcdef"</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> secretToken = <span class="hljs-string">"sk_live_51H..."</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeRequest</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// These secrets are embedded in your binary</span>
        <span class="hljs-keyword">let</span> headers = [<span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Bearer \(apiKey)"</span>]
    }
}
</code></pre>
<p><strong>How to Fix:</strong></p>
<p>Never store sensitive credentials directly in your code. Here are secure alternatives:</p>
<p><strong>Solution 1: Fetch Secrets from Backend at Runtime</strong></p>
<p>The most secure approach is to never store secrets on the client at all. Instead, authenticate users and let your backend make authorized API calls:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">APIClient</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> sessionToken: <span class="hljs-type">String?</span>

    <span class="hljs-comment">// User logs in and receives a temporary session token</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">authenticateUser</span><span class="hljs-params">(email: String, password: String)</span></span> async <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">try</span> await backend.login(email: email, password: password)
        <span class="hljs-comment">// Store only a temporary, user-specific session token</span>
        <span class="hljs-keyword">self</span>.sessionToken = response.sessionToken
    }

    <span class="hljs-comment">// Backend handles the actual API calls with the real API key</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchUserData</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">UserData</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> token = sessionToken <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">AuthError</span>.notAuthenticated
        }

        <span class="hljs-comment">// Your backend receives this request, validates the session token,</span>
        <span class="hljs-comment">// then uses its own API keys to fetch data from third-party services</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> await backend.getUserData(sessionToken: token)
    }
}
</code></pre>
<p><strong>How this works:</strong></p>
<p>Your app never knows the actual API keys. When a user needs data, your app sends a request to your own backend server with a session token. Your backend validates the token, then uses its own securely stored API keys to make the actual third-party API calls. This way, the real secrets never leave your server.</p>
<p><strong>Solution 2: Environment Variables or Config Files (Development Only)</strong></p>
<p>For development environments, use .xcconfig files that are excluded from version control:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Secrets.xcconfig (add to .gitignore!)</span>
<span class="hljs-type">API_KEY</span> = your_dev_api_key_here
<span class="hljs-type">API_SECRET</span> = your_dev_secret_here

<span class="hljs-comment">// Access in your code through Info.plist</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span> </span>{
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> apiKey: <span class="hljs-type">String</span> = {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> key = <span class="hljs-type">Bundle</span>.main.object(forInfoDictionaryKey: <span class="hljs-string">"API_KEY"</span>) <span class="hljs-keyword">as</span>? <span class="hljs-type">String</span> <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"API_KEY not found in configuration"</span>)
        }
        <span class="hljs-keyword">return</span> key
    }()
}
</code></pre>
<p><strong>Important</strong>: This approach is only suitable for non-production environments! Never ship production API keys with your app, even in config files.</p>
<h3 id="heading-5-insufficient-authentication-and-authorization">5. Insufficient Authentication and Authorization</h3>
<p>Relying on client-side authentication and authorization checks is risky. Attackers can cause the app to bypass these checks and access in an unauthorized way by brute forcing or tampering with the app/runtime.</p>
<h4 id="heading-how-to-fix-3">How to Fix:</h4>
<ul>
<li><p>Do authentication and authorization on the server side instead of the client-side.</p>
</li>
<li><p>Use JWT (JSON Web Tokens) or OAuth 2.0 for authenticated user login.</p>
</li>
<li><p>Token expiration and refresh logic needs to be implemented in order to minimize the likelihood of token theft.</p>
</li>
</ul>
<p><strong>Example: Securely sending JWT:</strong></p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> request = <span class="hljs-type">URLRequest</span>(url: apiURL)

request.setValue(<span class="hljs-string">"Bearer \(jwtToken)"</span>, forHTTPHeaderField: <span class="hljs-string">"Authorization"</span>)
</code></pre>
<h3 id="heading-6-insecure-logging-and-error-handling">6. Insecure Logging and Error Handling</h3>
<p>Extensive and insecure logging practices, as well as uncaught exceptions, can lead to the exposure of sensitive information including usernames, passwords, and API keys.</p>
<h4 id="heading-how-to-fix-4">How to Fix:</h4>
<ul>
<li><p>Log sensitive information carefully.</p>
</li>
<li><p>Implement controlled error management and provide the minimum amount of information in user-presented messages.</p>
</li>
<li><p>Implement secure logging libraries that mask or encrypt personal data.</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-keyword">do</span> {
    <span class="hljs-keyword">try</span> someRiskyOperation()
} <span class="hljs-keyword">catch</span> {
    <span class="hljs-comment">// Log error securely</span>
    <span class="hljs-type">Logger</span>.log(<span class="hljs-string">"Operation failed: \(error.localizedDescription)"</span>)
}
</code></pre>
<h3 id="heading-7-ignoring-code-obfuscation-and-reverse-engineering">7. Ignoring Code Obfuscation and Reverse Engineering</h3>
<p>Swift binaries can be reverse-engineered to expose sensitive business logic, algorithms, or hidden secrets. Attackers use tools like Hopper Disassembler, class-dump, or IDA Pro to decompile your app and analyze how it works internally. This risk is often underestimated, especially for smaller apps, but any app can be a target.</p>
<p>This means that when you compile a Swift app, the resulting binary contains:</p>
<ul>
<li><p>Class names and method signatures</p>
</li>
<li><p>String literals (URLs, error messages, keys)</p>
</li>
<li><p>The structure of your code logic</p>
</li>
<li><p>Algorithm implementations</p>
</li>
</ul>
<p>An attacker can extract this information and use it to understand your app's authentication flow and bypass it, copy your proprietary algorithms, find hardcoded API endpoints or keys you thought were "hidden", discover premium features to unlock without paying, and so on.</p>
<p><strong>Why It's Bad – Real Example:</strong></p>
<p>Let's imagine you have a premium feature check in your app:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FeatureManager</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isPremiumUser</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Bool</span> {
        <span class="hljs-comment">// Check if user has premium access</span>
        <span class="hljs-keyword">let</span> hasSubscription = <span class="hljs-type">UserDefaults</span>.standard.bool(forKey: <span class="hljs-string">"premium_unlocked"</span>)
        <span class="hljs-keyword">return</span> hasSubscription
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">unlockPremiumFeature</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">guard</span> isPremiumUser() <span class="hljs-keyword">else</span> {
            showPaywall()
            <span class="hljs-keyword">return</span>
        }
        <span class="hljs-comment">// Show premium content</span>
        showPremiumContent()
    }
}
</code></pre>
<p>An attacker could reverse-engineer your app and discover that the method <code>isPremiumUser()</code> controls access, and that it simply checks a <code>UserDefaults</code> key called <code>premium_unlocked</code>. They would then know that they could use runtime manipulation tools to set this value to true, bypassing your paywall entirely.</p>
<p><strong>How to Fix:</strong></p>
<p><strong>1. Use Swift Compiler Optimizations</strong></p>
<p>Enable optimization flags that strip debugging symbols and make the binary harder to read:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// In your build settings:</span>
<span class="hljs-comment">// - Set "Optimization Level" to "-O" (or -Osize) for release builds</span>
<span class="hljs-comment">// - Enable "Strip Debug Symbols During Copy" = YES</span>
<span class="hljs-comment">// - Set "Strip Style" to "All Symbols"</span>
</code></pre>
<p>This removes function names and makes the compiled code less readable – though class/method names remain partially visible.</p>
<p><strong>2. Use Symbol Obfuscation Tools</strong></p>
<p>Tools like SwiftShield can rename your classes, methods, and properties to meaningless names:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Before obfuscation (readable to attackers):</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FeatureManager</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isPremiumUser</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Bool</span> { ... }
}

<span class="hljs-comment">// After obfuscation (harder to understand):</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">a7f3b2</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">x9k2m</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Bool</span> { ... }
}
</code></pre>
<p>While this doesn't prevent reverse engineering, it makes it significantly harder for attackers to understand what the code does.</p>
<p><strong>3. Move Sensitive Logic to the Server (Best Practice)</strong></p>
<p>Instead of checking premium status locally, verify it server-side:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// ✅ Secure approach - Server validates everything</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FeatureManager</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">unlockPremiumFeature</span><span class="hljs-params">()</span></span> async {
        <span class="hljs-keyword">do</span> {
            <span class="hljs-comment">// Server checks if user truly has premium access</span>
            <span class="hljs-keyword">let</span> hasAccess = <span class="hljs-keyword">try</span> await backend.verifyPremiumAccess(userId: currentUserId)

            <span class="hljs-keyword">if</span> hasAccess {
                showPremiumContent()
            } <span class="hljs-keyword">else</span> {
                showPaywall()
            }
        } <span class="hljs-keyword">catch</span> {
            <span class="hljs-comment">// Handle error</span>
            showPaywall()
        }
    }
}
</code></pre>
<p><strong>How this works:</strong></p>
<p>Your backend maintains the source of truth about premium access. Even if an attacker reverse-engineers your app and tries to bypass the check, the server will reject unauthorized requests. The app becomes just a UI layer, while all critical decisions happen server-side – where attackers can't manipulate them.</p>
<p>The key principle is to assume your app code is public: never rely on client-side checks for security-critical operations like payments, access control, or authentication. Use obfuscation to make reverse engineering harder, but ultimately move sensitive logic to your secure backend</p>
<h3 id="heading-8-insecure-third-party-libraries">8. Insecure Third-Party Libraries</h3>
<p>Third-party libraries are at risk if they are hacked or outdated. Developers might inadvertently prioritize app functionality over potential security risks from dependencies, and <a target="_blank" href="https://airbyte.com/top-etl-tools-for-sources/etl-tools">ETL tools</a> can further help by streamlining the monitoring and processing of dependency-related data to identify vulnerabilities more efficiently.</p>
<p>On a broader scale, implementing strong data center security practices ensures that even if third-party components introduce risks, the underlying infrastructure remains resilient against attacks.</p>
<h4 id="heading-how-to-fix-5">How to Fix:</h4>
<ul>
<li><p>Use only high-quality, well-maintained libraries.</p>
</li>
<li><p>Update dependencies and monitor CVEs (Common Vulnerabilities and Exposures).</p>
</li>
<li><p>Audit library code if it handles sensitive data.</p>
</li>
</ul>
<h3 id="heading-9-insufficient-biometric-and-multi-factor-authentication">9. Insufficient Biometric and Multi-Factor Authentication</h3>
<p>Most applications rely on passwords alone, which are vulnerable to being hacked. Enabling biometrics like Face ID or Touch ID enhances security for users.</p>
<h4 id="heading-how-to-fix-6">How to Fix:</h4>
<ul>
<li><p>Tie LocalAuthentication framework for biometric authentication.</p>
</li>
<li><p>Combine biometrics with server-based authentication for multifactor authentication (MFA).</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> LocalAuthentication

<span class="hljs-keyword">let</span> context = <span class="hljs-type">LAContext</span>()

<span class="hljs-keyword">var</span> error: <span class="hljs-type">NSError?</span>

<span class="hljs-keyword">if</span> context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &amp;error) {

    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,

                        localizedReason: <span class="hljs-string">"Access your account"</span>) { success, authError <span class="hljs-keyword">in</span>

        <span class="hljs-type">DispatchQueue</span>.main.async {

         <span class="hljs-keyword">if</span> success {

                <span class="hljs-comment">// Proceed securely</span>

         } <span class="hljs-keyword">else</span> {

                <span class="hljs-comment">// Handle failure (authError may contain the reason)</span>

                <span class="hljs-built_in">print</span>(<span class="hljs-string">"Authentication failed: \(authError?.localizedDescription ?? "</span><span class="hljs-type">Unknown</span> error<span class="hljs-string">")"</span>)

         }

     }

}

} <span class="hljs-keyword">else</span> {

<span class="hljs-comment">// Biometrics not available, check error for details</span>

    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Biometrics unavailable: \(error?.localizedDescription ?? "</span><span class="hljs-type">Unknown</span> error<span class="hljs-string">")"</span>)

}
</code></pre>
<h3 id="heading-10-disregarding-periodic-security-testing">10. Disregarding Periodic Security Testing</h3>
<p>Apps, despite following best practices during development, often contain unexplored vulnerabilities that emerge from complex interactions, third-party dependencies, or newly discovered attack vectors. Regular security testing is absolutely essential to discover these vulnerabilities before attackers exploit them.</p>
<p>Security testing should happen at multiple stages, using accessible tools and practices:</p>
<ol>
<li><p><strong>Automated security scans:</strong> Run automatically with every build to catch common issues.</p>
</li>
<li><p><strong>Self-conducted code audits:</strong> Regular security-focused reviews of your own code using established guidelines.</p>
</li>
<li><p><strong>Vulnerability scanning tools:</strong> Use free tools like MobSF to analyze your app for security flaws.</p>
</li>
<li><p><strong>Dependency audits:</strong> Checking third-party libraries for known security vulnerabilities.</p>
</li>
</ol>
<p><strong>How to Fix:</strong></p>
<p><strong>1. Implement Automated Security Scans in CI/CD</strong></p>
<p>Integrate security scanning tools into your continuous integration pipeline so every code change is automatically checked:</p>
<pre><code class="lang-swift"># <span class="hljs-type">Example</span>: <span class="hljs-type">GitHub</span> <span class="hljs-type">Actions</span> workflow <span class="hljs-keyword">for</span> automated security scanning
name: <span class="hljs-type">Security</span> <span class="hljs-type">Scan</span>

on: [push, pull_request]

jobs:
  security-scan:
    runs-on: macos-latest

    steps:
      - name: <span class="hljs-type">Checkout</span> code
        uses: actions/checkout@v3

      - name: <span class="hljs-type">Run</span> <span class="hljs-type">MobSF</span> <span class="hljs-type">Security</span> <span class="hljs-type">Scan</span>
        run: |
          # <span class="hljs-type">Mobile</span> <span class="hljs-type">Security</span> <span class="hljs-type">Framework</span> - scans <span class="hljs-keyword">for</span> common vulnerabilities
          docker run -v $(pwd):/app opensecurity/mobile-security-framework-mobsf

      - name: <span class="hljs-type">Dependency</span> <span class="hljs-type">Vulnerability</span> <span class="hljs-type">Check</span>
        run: |
          # <span class="hljs-type">Check</span> <span class="hljs-type">CocoaPods</span>/<span class="hljs-type">SPM</span> dependencies <span class="hljs-keyword">for</span> known <span class="hljs-type">CVEs</span>
          brew install dependency-check
          dependency-check --scan ./<span class="hljs-type">Podfile</span>.lock --format <span class="hljs-type">JSON</span>

      - name: <span class="hljs-type">Secret</span> <span class="hljs-type">Detection</span>
        run: |
          # <span class="hljs-type">Detect</span> accidentally committed secrets
          brew install truffleHog
          truffleHog filesystem . --json

      - name: <span class="hljs-type">Fail</span> build on critical issues
        run: |
          <span class="hljs-keyword">if</span> grep -q <span class="hljs-string">"CRITICAL"</span> security-report.json; then
            echo <span class="hljs-string">"Critical security issues found!"</span>
            exit <span class="hljs-number">1</span>
          fi
</code></pre>
<p><strong>Automated scans check for:</strong></p>
<ul>
<li><p>Hardcoded API keys, tokens, or passwords</p>
</li>
<li><p>Insecure network configurations (allowing HTTP instead of HTTPS)</p>
</li>
<li><p>Weak cryptographic algorithms</p>
</li>
<li><p>Known vulnerabilities in third-party libraries</p>
</li>
<li><p>Improper SSL/TLS certificate validation</p>
</li>
<li><p>Insecure data storage (storing sensitive data in UserDefaults)</p>
</li>
<li><p>Excessive app permissions</p>
</li>
</ul>
<p><strong>Example output from an automated scan:</strong></p>
<pre><code class="lang-swift"><span class="hljs-type">Security</span> <span class="hljs-type">Scan</span> <span class="hljs-type">Results</span>:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[<span class="hljs-type">CRITICAL</span>] <span class="hljs-type">Hardcoded</span> <span class="hljs-type">API</span> <span class="hljs-type">Key</span> <span class="hljs-type">Found</span>
  <span class="hljs-type">File</span>: <span class="hljs-type">APIClient</span>.swift:<span class="hljs-number">15</span>
  <span class="hljs-type">Issue</span>: <span class="hljs-type">API</span> key <span class="hljs-string">"sk_live_abc123..."</span> detected <span class="hljs-keyword">in</span> source code

[<span class="hljs-type">HIGH</span>] <span class="hljs-type">Insecure</span> <span class="hljs-type">HTTP</span> <span class="hljs-type">Connection</span>
  <span class="hljs-type">File</span>: <span class="hljs-type">NetworkManager</span>.swift:<span class="hljs-number">42</span>
  <span class="hljs-type">Issue</span>: <span class="hljs-type">App</span> allows cleartext <span class="hljs-type">HTTP</span> traffic to api.example.com
  <span class="hljs-type">Fix</span>: <span class="hljs-type">Enforce</span> <span class="hljs-type">HTTPS</span> or add exception to <span class="hljs-type">Info</span>.plist <span class="hljs-keyword">if</span> <span class="hljs-keyword">required</span>

[<span class="hljs-type">MEDIUM</span>] <span class="hljs-type">Weak</span> <span class="hljs-type">Encryption</span> <span class="hljs-type">Algorithm</span>
  <span class="hljs-type">File</span>: <span class="hljs-type">DataEncryption</span>.swift:<span class="hljs-number">28</span>
  <span class="hljs-type">Issue</span>: <span class="hljs-type">Using</span> <span class="hljs-type">MD5</span> <span class="hljs-keyword">for</span> hashing (cryptographically broken)
  <span class="hljs-type">Fix</span>: <span class="hljs-type">Use</span> <span class="hljs-type">SHA</span>-<span class="hljs-number">256</span> or better

[<span class="hljs-type">LOW</span>] <span class="hljs-type">Outdated</span> <span class="hljs-type">Dependency</span>
  <span class="hljs-type">Library</span>: <span class="hljs-type">Alamofire</span> <span class="hljs-number">4.2</span>.<span class="hljs-number">0</span>
  <span class="hljs-type">Issue</span>: <span class="hljs-type">Known</span> vulnerability <span class="hljs-type">CVE</span>-<span class="hljs-number">2021</span>-<span class="hljs-number">12345</span>
  <span class="hljs-type">Fix</span>: <span class="hljs-type">Update</span> to version <span class="hljs-number">5.6</span>.<span class="hljs-number">0</span> or later
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
<span class="hljs-type">Build</span> <span class="hljs-type">Failed</span>: <span class="hljs-number">2</span> critical issues must be fixed before deployment
</code></pre>
<p><strong>2. Use MobSF (Mobile Security Framework) for Vulnerability Analysis</strong></p>
<p>MobSF is a free, automated tool that analyzes your iOS app for security issues:</p>
<pre><code class="lang-swift"># <span class="hljs-type">Install</span> and run <span class="hljs-type">MobSF</span> locally
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p <span class="hljs-number">8000</span>:<span class="hljs-number">8000</span> opensecurity/mobile-security-framework-mobsf

# <span class="hljs-type">Upload</span> your .ipa file through the web interface at localhost:<span class="hljs-number">8000</span>
# <span class="hljs-type">MobSF</span> will analyze and provide a detailed security report
</code></pre>
<p>What MobSF checks:</p>
<ul>
<li><p>Binary analysis for hardcoded secrets</p>
</li>
<li><p>Insecure data storage patterns</p>
</li>
<li><p>Weak cryptographic implementations</p>
</li>
<li><p>Insecure network connections</p>
</li>
<li><p>Code quality and security best practices</p>
</li>
<li><p>Compliance with security standards</p>
</li>
</ul>
<p><strong>3. Conduct Regular Code Audits Using OWASP MSTG</strong></p>
<p>Use the OWASP Mobile Security Testing Guide as a checklist to audit your own code:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Example: Following OWASP MSTG recommendations for secure storage</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecureStorage</span> </span>{
    <span class="hljs-comment">// ❌ Insecure - UserDefaults is not encrypted</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">saveTokenInsecurely</span><span class="hljs-params">(<span class="hljs-number">_</span> token: String)</span></span> {
        <span class="hljs-type">UserDefaults</span>.standard.<span class="hljs-keyword">set</span>(token, forKey: <span class="hljs-string">"authToken"</span>)
    }

    <span class="hljs-comment">// ✅ Secure - Using Keychain as OWASP recommends</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">saveTokenSecurely</span><span class="hljs-params">(<span class="hljs-number">_</span> token: String)</span></span> <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> data = token.data(using: .utf8)!
        <span class="hljs-keyword">let</span> query: [<span class="hljs-type">String</span>: <span class="hljs-type">Any</span>] = [
            kSecClass <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: kSecClassGenericPassword,
            kSecAttrAccount <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: <span class="hljs-string">"authToken"</span>,
            kSecValueData <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: data,
            kSecAttrAccessible <span class="hljs-keyword">as</span> <span class="hljs-type">String</span>: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
        ]

        <span class="hljs-type">SecItemDelete</span>(query <span class="hljs-keyword">as</span> <span class="hljs-type">CFDictionary</span>)
        <span class="hljs-keyword">let</span> status = <span class="hljs-type">SecItemAdd</span>(query <span class="hljs-keyword">as</span> <span class="hljs-type">CFDictionary</span>, <span class="hljs-literal">nil</span>)

        <span class="hljs-keyword">guard</span> status == errSecSuccess <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">StorageError</span>.saveFailed
        }
    }
}
</code></pre>
<p><strong>OWASP MSTG Checklist for Self-Audit:</strong></p>
<ul>
<li><p>[ ] Are all sensitive data encrypted at rest?</p>
</li>
<li><p>[ ] Is HTTPS enforced for all network calls?</p>
</li>
<li><p>[ ] Are certificates properly validated?</p>
</li>
<li><p>[ ] Is sensitive data excluded from logs?</p>
</li>
<li><p>[ ] Are API keys and secrets not hardcoded?</p>
</li>
<li><p>[ ] Is user input validated and sanitized?</p>
</li>
<li><p>[ ] Are authentication tokens stored securely in Keychain?</p>
</li>
</ul>
<p><strong>4. Automated Dependency Scanning</strong></p>
<p>Monitor your dependencies continuously for newly discovered vulnerabilities:</p>
<pre><code class="lang-swift"># <span class="hljs-type">For</span> <span class="hljs-type">CocoaPods</span> projects
gem install cocoapods-audit
pod audit

# <span class="hljs-type">For</span> <span class="hljs-type">Swift</span> <span class="hljs-type">Package</span> <span class="hljs-type">Manager</span>
# <span class="hljs-type">Use</span> <span class="hljs-type">GitHub</span> <span class="hljs-type">Dependabot</span> (free <span class="hljs-keyword">for</span> <span class="hljs-keyword">public</span> repos) or
brew install swift-outdated
swift-outdated
</code></pre>
<p>And set up automated alerts with these tools:</p>
<ul>
<li><p><strong>GitHub Dependabot:</strong> Automatically creates PRs when vulnerable dependencies are detected (free)</p>
</li>
<li><p><strong>Snyk</strong>: Free tier available for open-source projects</p>
</li>
<li><p><strong>OWASP Dependency-Check:</strong> Free command-line tool</p>
</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Developing secure iOS apps using Swift is all about forward-thinking. You should do all you can to avoid these common errors, like insecure storage of data, poor network communication, hard-coded secrets, or poor authentication.</p>
<p>Using Keychain for confidential information, requiring HTTPS, input validation, and multifactor authentication are all steps that decrease risk.</p>
<p>Regular testing for security vulnerabilities and limiting third-party library use can also further enhance your app's security.</p>
<p>Security is a continuous responsibility. Swift provides tools, but the developers need to apply those tools carefully. Security being tackled right from the start protects users' information, creates trust, and protects the reputation of the application.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn SwiftUI and Create an iOS App From Scratch ]]>
                </title>
                <description>
                    <![CDATA[ Learn how to create a complete iOS app from scratch using SwiftUI and Xcode. We just posted a course on the freeCodeCamp.org YouTube channel that will teach you to build a feature-rich movie and TV browsing app with a dynamic home screen, powerful se... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-swiftui-and-create-an-ios-app-from-scratch/</link>
                <guid isPermaLink="false">68f7d59ad8e7070995147d16</guid>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SwiftUI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 21 Oct 2025 18:48:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761072521870/418af7e2-c265-4088-8432-19dad9d1b194.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Learn how to create a complete iOS app from scratch using SwiftUI and Xcode.</p>
<p>We just posted a course on the freeCodeCamp.org YouTube channel that will teach you to build a feature-rich movie and TV browsing app with a dynamic home screen, powerful search, and detail screens that play YouTube trailers.</p>
<p>You will also implement a download manager to save titles for offline viewing using SwiftData. By integrating The Movie Database and YouTube APIs, this project provides excellent hands-on practice with SwiftUI, API integration, and modern data storage. Carlos Valentin created this course.</p>
<p>Here are the sections in this course:</p>
<ul>
<li><p>Essentials</p>
</li>
<li><p>Networking</p>
</li>
<li><p>API Networking Requests</p>
</li>
<li><p>Navigation</p>
</li>
<li><p>Search</p>
</li>
<li><p>SwiftData</p>
</li>
</ul>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/-VC3hIEL7eQ">the freeCodeCamp.org YouTube channel</a> (4-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/-VC3hIEL7eQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Submit an App to the iOS App Store ]]>
                </title>
                <description>
                    <![CDATA[ The iOS App Store submission process can feel like a daunting maze, but it doesn’t have to be. We just posted a course on the freeCodeCamp.org YouTube channel is designed to be your guide through this entire process, from start to finish. Shad Rayhan... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-submit-an-app-to-the-ios-app-store/</link>
                <guid isPermaLink="false">68beece9aaba834ff6c89ad9</guid>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Mon, 08 Sep 2025 14:49:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757341462696/49823d35-ea00-400e-b90a-2df3f6edc5c6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The iOS App Store submission process can feel like a daunting maze, but it doesn’t have to be.</p>
<p>We just posted a course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel is designed to be your guide through this entire process, from start to finish. Shad Rayhan Mazumder developed this course.</p>
<p>Shad starts by explaining the traditional, manual method. This course will walk you through every essential step, explaining the foundational concepts of application IDs, certificates, and provisioning profiles. You’ll learn exactly what you need to do to generate the right credentials, register your devices, and get your app running on a physical iPhone or iPad for testing. The course also covers distributing ad hoc versions for testers before diving into the final steps for a public release.</p>
<p>The course then introduces the powerful concepts of Continuous Integration and Continuous Deployment (CI/CD), specifically focusing on Apple’s own Xcode Cloud. You will learn how to push your latest changes to a GitHub repository and have your app automatically build, test, and distribute itself to your testers without you lifting another finger.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/9vkkJ4tC4SQ">the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9vkkJ4tC4SQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Apple Code Signing Handbook ]]>
                </title>
                <description>
                    <![CDATA[ In this handbook, I’ll demystify the Apple app code signing process. Apple's ecosystem is powerful, but its distribution mechanisms – with various identifiers, certificates, and profiles – can appear complex. This guide attempts to make that journey ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/apple-code-signing-handbook/</link>
                <guid isPermaLink="false">68489084490c709f6b3070ed</guid>
                
                    <category>
                        <![CDATA[ Apple ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ macOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sravan Karuturi ]]>
                </dc:creator>
                <pubDate>Tue, 10 Jun 2025 20:07:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749585600223/49e9c922-0b5d-4a98-a619-eedfd7a8b617.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this handbook, I’ll demystify the Apple app code signing process. Apple's ecosystem is powerful, but its distribution mechanisms – with various identifiers, certificates, and profiles – can appear complex. This guide attempts to make that journey more manageable and straightforward for you.</p>
<p>Throughout this handbook, you will learn how to:</p>
<ul>
<li><p>Correctly establish and manage an app's unique identity.</p>
</li>
<li><p>Understand the roles of different Apple developer certificates and how to create and manage them.</p>
</li>
<li><p>Differentiate between various types of provisioning profiles and know when to use each one.</p>
</li>
</ul>
<p>This guide is geared towards new developers who want to learn how the code signing process works, but it should also be useful experienced developers who want or need to refresh their memory.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>While there are no hard prerequisites to understanding the certificates, bundles, and provisioning profiles for distributing on Apple platforms, it helps to have an Apple developer account to follow along.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-app-ids-bundle-ids-your-apps-identity">App IDs, Bundle IDs – Your App’s Identity</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-distribution-a-deep-dive-into-certificates">Understanding Distribution: A Deep Dive into Certificates</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-bridge-between-everything-provisioning-profiles">Bridge between everything: Provisioning Profiles</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-device-management-development-and-ad-hoc-builds">Device management – Development and Ad Hoc Builds</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-possibilities-enabling-capabilities-and-services">Possibilities: Enabling Capabilities and Services</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-app-ids-bundle-ids-your-apps-identity"><strong>App IDs, Bundle IDs — Your App’s Identity</strong></h2>
<p>The Bundle ID and the corresponding App ID registered with Apple form the basis of an application’s identity. Establishing these correctly from the beginning is very important, as errors or misconfigurations here can lead to significant complications down the line, particularly once you’ve submitted your app to App Store Connect.</p>
<h3 id="heading-understanding-cfbundleidentifier-bundle-id">Understanding <code>CFBundleIdentifier</code> (Bundle ID)</h3>
<h4 id="heading-what-is-the-bundle-id">What is the “Bundle ID”?</h4>
<p>Think of the Bundle ID as a unique name or a fingerprint for your app. The <code>CFBundleIdentifier</code>, more commonly known as the <strong>Bundle ID</strong>, is a string that uniquely identifies your application.</p>
<p>This identifier is not just a name – it serves multiple crucial purposes.</p>
<ul>
<li><p>The operating system relies on it to apply specific preferences and settings to an app.</p>
</li>
<li><p>This is used to launch the application from other apps etc.</p>
</li>
<li><p>It plays an essential role in the validation of an app's code signature, ensuring the app's integrity and authenticity.</p>
</li>
<li><p>The Bundle ID defined in an app's Info.plist file must exactly match the Bundle ID registered for the app in App Store Connect for successful submission and distribution.</p>
</li>
</ul>
<p>The Bundle ID string must adhere to specific character limitations: it can only contain alphanumeric characters <code>A-Z, a-z, 0-9</code>, hyphens <code>-</code>, and periods <code>.</code>. It's important to note that Bundle IDs are treated as <strong>case-insensitive</strong> by the system.</p>
<h3 id="heading-how-to-choose-and-format-your-bundle-id-reverse-dns-and-best-practices">How to Choose and Format Your Bundle ID (Reverse-DNS and Best Practices)</h3>
<p>Apple highly recommends, and it is standard practice, to use a reverse-DNS (Domain Name System) format for Bundle IDs.</p>
<p>A common example would be <code>com.yourcompanyname.appname</code>. This convention leverages the global uniqueness of domain names to help ensure the global uniqueness of Bundle IDs.</p>
<p>If an organization uses its unique domain name (for example, <code>sravan.gg</code> becomes <code>gg.sravan</code> ) as the prefix, and the app name is unique within that organization, the resulting Bundle ID (for example, <code>gg.sravan.mycoolapp</code> ) is highly likely to be unique worldwide.</p>
<p><strong>Sidenote</strong>: While Xcode won’t stop you from creating something like <code>com.google.mapping</code> or something like that even if you don’t work at Google, this will most likely get rejected when it goes through the AppStore review process. This is because this implies ownership of that domain. So, while it’s technically possible when starting out, you shouldn’t use domains that don’t belong to you.</p>
<p>The fundamental nature of the Bundle ID as a unique, system-wide identifier – coupled with its immutability after an app is first uploaded to App Store Connect – means that you should treat its selection with the same seriousness as choosing a <strong>permanent, unchangeable identifier</strong> for a critical entity. A mistake in the Bundle ID after this point can necessitate creating an entirely new app listing on the App Store.</p>
<h3 id="heading-app-ids-in-the-apple-developer-portal-explicit-vs-wildcard">App IDs in the Apple Developer Portal: Explicit vs. Wildcard</h3>
<h4 id="heading-which-one-do-you-need">Which One Do You Need?</h4>
<p>In the Apple Developer Portal, developers register an "App ID." This App ID is a record that links one or more applications from a single development team to specific app services (capabilities) and is used in provisioning profiles. We’ll learn more about this in the following sections.</p>
<p>There are two main types of App IDs:</p>
<ul>
<li><p><strong>Explicit App ID:</strong> This type is used for a single application. The Bundle ID specified within an explicit App ID must be an exact match for the CFBundleIdentifier in the app's Info.plist file (for example, <code>com.mycompany.myapp</code>). Explicit App IDs are required for apps that use many of Apple's specific services and capabilities, such as In-App Purchases (which are enabled by default for explicit App IDs), Push Notifications, iCloud, HealthKit, and Sign in with Apple.</p>
</li>
<li><p><strong>Wildcard App ID:</strong> This type can be used for a set of applications that share a common Bundle ID prefix. It contains an asterisk (*) as the last part of its Bundle ID string (for example, <code>com.mycompany.*</code>). This wildcard App ID would match any app whose Bundle ID starts with <code>com.mycompany.</code>, such as <a target="_blank" href="http://com.mycompany.app"><code>com.mycompany.app</code></a> or <code>com.mycompany.utility</code>. But you can’t use wildcard App IDs if the app requires services or capabilities that mandate an explicit App ID.</p>
</li>
</ul>
<p>The choice between an explicit and a wildcard App ID has significant implications. The App ID acts as a central registration point, and the capabilities are "enabled" for this registration – more on capabilities later in this handbook.</p>
<p>You can think of an explicit App ID as a specific key designed to unlock extra "keyholes" (capabilities). A wildcard App ID, being more generic, might not fit these extra keyholes. If you choose a wildcard App ID initially for convenience, but you need a feature requiring an explicit App ID (like Push Notifications) later, you’ll be forced to create a new explicit App ID and reconfigure associated settings and provisioning profiles.</p>
<p>So, make sure you carefully consider your current and future app features when selecting an App ID type. The following table provides a quick comparison**.**</p>
<p>My personal recommendation is always go with explicit App Ids unless you need the flexibility of wildcard app ids.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Explicit App ID</strong></td><td><strong>Wildcard App ID</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Bundle ID Match</strong></td><td>Exact match (for example, <a target="_blank" href="http://com.foo.bar">com.foo.bar</a>)</td><td>Suffix match (for example, <a target="_blank" href="http://com.foo">com.foo</a>.*)</td></tr>
<tr>
<td><strong>Use Case</strong></td><td>Single app</td><td>Set of apps with similar base ID</td></tr>
<tr>
<td><strong>Capabilities</strong></td><td>Supports all capabilities</td><td>Limited (cannot use services requiring explicit IDs)</td></tr>
<tr>
<td><strong>Uniqueness</strong></td><td>Globally unique identifier for one specific app</td><td>Identifies a group of apps</td></tr>
</tbody>
</table>
</div><h3 id="heading-step-by-step-how-to-register-your-app-id-in-the-apple-developer-portal">Step-by-Step: How to Register Your App ID in the Apple Developer Portal</h3>
<p>To register an App ID, an you’ll need an <strong>Apple Developer Program membership</strong>. Also, the actions must be performed by someone with an Account Holder or Admin role.</p>
<p>The process is as follows:</p>
<ol>
<li><p>Sign in to the Apple Developer Portal and navigate to "Certificates, Identifiers &amp; Profiles," then select "Identifiers" from the sidebar.</p>
</li>
<li><p>Click the “Add button (+)” to create a new identifier.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748642247245/a24b527f-e810-4a9c-b75a-dcd3d189b1d1.png" alt="Picture depicting the Add button" class="image--center mx-auto" width="1130" height="404" loading="lazy"></p>
</li>
<li><p>Select "App IDs" from the list of options and click "Continue."</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748642283885/851f64f3-e608-4fb7-9f31-bd30adb64beb.png" alt="851f64f3-e608-4fb7-9f31-bd30adb64beb" class="image--center mx-auto" width="1628" height="704" loading="lazy"></p>
</li>
<li><p>Make sure that the "App" type is selected (it usually is by default) and click "Continue."</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748642318142/a7b28529-bbe6-4240-953e-836de3e948ac.png" alt="App type selection" class="image--center mx-auto" width="1526" height="766" loading="lazy"></p>
</li>
<li><p>Enter a "Description" for the App ID. This is for your reference within the portal (for example, "My very cool App ID").</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748642392862/a5322cf5-3d75-4b0b-93bf-d46dd1ce8afe.png" alt="App Id registration screen" class="image--center mx-auto" width="2446" height="698" loading="lazy"></p>
</li>
<li><p>Choose the "App ID Type": "Explicit" or "Wildcard."</p>
</li>
<li><p>For an "Explicit App ID," enter the exact Bundle ID that will be used in your Xcode project (for example, <code>com.yourcompany.yourapp</code>). For a "Wildcard App ID," enter a Bundle ID suffix ending with an asterisk (for example, <code>com.yourcompany.*</code>).</p>
</li>
<li><p>Scroll down to the "Capabilities" section and select the checkboxes for any app services your app will use. Some capabilities might require further configuration at this stage or later. (Again, we’ll cover app capabilities in more detail later on).</p>
</li>
<li><p>Click "Continue," review all the details carefully, and then click "Register" to finalize the App ID creation.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748642432661/2052a435-ed0e-404a-9178-7d6541fc9421.png" alt="Confirm the App ID screen" class="image--center mx-auto" width="2486" height="708" loading="lazy"></p>
</li>
</ol>
<h3 id="heading-how-to-manage-your-bundle-id-xcode-app-store-connect-and-the-point-of-no-return">How to Manage Your Bundle ID: Xcode, App Store Connect, and the Point of No Return</h3>
<p>The Bundle ID specified in an Xcode project is critical. To set it:</p>
<ol>
<li><p>In the Xcode project navigator, select the target for your app.</p>
</li>
<li><p>Open the "Signing &amp; Capabilities" tab.</p>
</li>
<li><p>Expand the "Signing" section.</p>
</li>
<li><p>In the "Bundle Identifier" text field, enter the Bundle ID. This identifier must precisely match the Bundle ID associated with an explicit App ID registered in the Developer Portal, or conform to the pattern of a wildcard App ID if applicable.</p>
</li>
</ol>
<p>It's important to understand the difference between the "Bundle ID" (or <code>CFBundleIdentifier</code>) in the Xcode project and the "App ID" registered in the Developer Portal. The "App ID" in the developer portal is an entity that <em>contains</em> a “Bundle ID” string (either explicit or wildcard). The string in your Xcode project's "Bundle Identifier" field must match this contained string.</p>
<p>When preparing for distribution via TestFlight or the App Store, you’ll need to create an app record in App Store Connect. The Bundle ID you enter during this app record creation must exactly match the Bundle ID in the Xcode project.</p>
<h4 id="heading-a-critical-warning-immutability-after-first-upload">A Critical Warning: Immutability After First Upload</h4>
<p>This is a point of no return: Once you upload a build of an app to App Store Connect, the Bundle ID for that app record <strong>cannot be changed</strong>.</p>
<p>In addition, after an upload, you can’t delete the associated explicit App ID registered in the Developer Portal. This immutability highlights the need for <em>careful planning and verification</em> of the Bundle ID before any uploads occur.</p>
<p>If you prefer programmatic management or automation, the App Store Connect API provides resources for managing Bundle IDs. You can <a target="_blank" href="https://developer.apple.com/documentation/appstoreconnectapi">read more on that here</a>.</p>
<h2 id="heading-understanding-distribution-a-deep-dive-into-certificates"><strong>Understanding Distribution: A Deep Dive into Certificates</strong></h2>
<h3 id="heading-what-are-certificates">What are Certificates?</h3>
<p>Certificates are digital credentials that verify a <strong>developer's identity</strong> – that is, you – to Apple and, by extension, to the app users.</p>
<p>They are fundamental to Apple's code signing process, which is mandatory for all apps to ensure they originate from a <strong>known source</strong> and have not been tampered with since being signed.</p>
<h3 id="heading-what-is-code-signing-ensuring-trust-and-integrity">What is Code Signing: Ensuring Trust and Integrity</h3>
<p>Code signing is you as a developer signing the app with your signature. It is the process of attaching a digital signature to an app's code. This signature assures users of two key things:</p>
<ol>
<li><p><strong>Authenticity:</strong> The app was created by an identified Apple developer (an individual or a team).</p>
</li>
<li><p><strong>Integrity:</strong> The app's code has not been altered or corrupted since it was signed by the developer.</p>
</li>
</ol>
<p>The process involves using a private key, securely held by the developer (you), to create the signature. The corresponding public key, embedded within the developer's certificate (issued by Apple), is used by the system to verify this signature.</p>
<p>This system of identity verification and integrity checking is crucial. The developer's certificate, issued by Apple as a Certificate Authority (CA), vouches for their identity. The code signing process, using hashing and encryption, ensures that any modification to the code after signing would invalidate the signature.</p>
<p>For app developers, benefits of code signing include removing warnings on macOS for apps distributed outside the Mac App Store, providing a smoother user experience. It is a mandatory requirement for listing applications on any of Apple's App Stores. It also enhances security of the app as it acts as a deterrent against malicious tampering.</p>
<h3 id="heading-types-of-certificates-development-distribution-and-developer-id">Types of Certificates: Development, Distribution, and Developer ID</h3>
<p>Apple provides different types of certificates for various stages of development and methods of distribution. Each of them has a distinct role to play throughout the app development process.</p>
<h4 id="heading-1-development-certificates-for-example-apple-development">1. Development Certificates (for example, "Apple Development"):</h4>
<ul>
<li><p><strong>Purpose:</strong> Used to sign apps during the development phase, allowing them to be installed and run on a limited number of <em>registered test devices</em> and simulators for debugging and testing.</p>
</li>
<li><p><strong>Identifies:</strong> Typically identifies an individual developer through their developer ID.</p>
</li>
<li><p><strong>Used with:</strong> Development provisioning profiles – more on this later.</p>
</li>
</ul>
<h4 id="heading-2-distribution-certificates-for-example-apple-distribution">2. Distribution Certificates (for example, "Apple Distribution"):</h4>
<ul>
<li><p><strong>Purpose:</strong> Used to sign apps intended for distribution, either through Ad Hoc methods (to a limited set of <em>registered testers</em>) or for submission to the App Store.</p>
</li>
<li><p><strong>Identifies:</strong> The development team via the team identifier.</p>
</li>
<li><p><strong>Use Cases:</strong></p>
<ol>
<li><p><strong>App Store:</strong> For signing the final version of an app that will be uploaded to App Store Connect for TestFlight beta testing or release on the App Store (iOS, macOS, tvOS, watchOS). These are used with App Store provisioning profiles – more on this later.</p>
</li>
<li><p><strong>Ad Hoc:</strong> For signing apps that will be distributed to a <em>limited number of registered test devices outside of the App Store or TestFlight</em>. These are used with Ad Hoc provisioning profile. More on this later.</p>
</li>
</ol>
</li>
</ul>
<h4 id="heading-3-developer-id-certificates-for-mac-apps-distributed-outside-the-mac-app-store">3. Developer ID Certificates (for Mac apps distributed outside the Mac App Store):</h4>
<ul>
<li><p><strong>Purpose:</strong> Specifically for macOS developers who wish to distribute their applications directly to users (for example, from their own website) rather than through the Mac App Store. Gatekeeper on macOS recognizes apps signed with a Developer ID certificate, assuring users that the app is from a known developer and has not been tampered with.</p>
</li>
<li><p><strong>Types:</strong></p>
<ol>
<li><p><strong>Developer ID Application:</strong> Used to sign the Mac application bundle (.app) itself.</p>
</li>
<li><p><strong>Developer ID Installer:</strong> Used to sign a Mac Installer Package (.pkg) that contains the signed application.</p>
</li>
<li><p><strong>Limit:</strong> Developers can create up to five Developer ID Application certificates and up to five Developer ID Installer certificates.</p>
</li>
</ol>
</li>
</ul>
<p>The following table summarizes these certificate types:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Certificate Type</strong></td><td><strong>Issued To</strong></td><td><strong>Primary Purpose</strong></td><td><strong>Used With Provisioning Profile Type</strong></td><td><strong>Key Use Cases</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Apple Development</td><td>Individual Dev ID</td><td>Develop &amp; debug on registered devices</td><td>Development</td><td>Xcode builds for local testing, running on personal/team test devices.</td></tr>
<tr>
<td>Apple Distribution</td><td>Team ID</td><td>Submit app to App Store / Ad Hoc distribution</td><td>App Store, Ad Hoc</td><td>Final builds for TestFlight, App Store submission, or QA/client Ad Hoc builds.</td></tr>
<tr>
<td>Developer ID Application</td><td>Team ID</td><td>Sign Mac app for distribution outside Mac App Store</td><td><strong>Developer ID Provisioning</strong> <strong>Profile</strong> if the app utilizes specific capabilities (e.g., Push Notifications, Associated Domains).</td><td>Distributing Mac software directly to users (for example, from website).</td></tr>
<tr>
<td>Developer ID Installer</td><td>Team ID</td><td>Sign Mac Installer Pkg for distribution outside Mac App Store</td><td>N/A. (The app inside the package may need a profile).</td><td>Distributing Mac software in a .pkg installer directly to users.</td></tr>
<tr>
<td>APNs / Service Keys (.p8)</td><td>Team ID</td><td>Secure communication with specific Apple services</td><td>N/A for app signing</td><td>Push Notifications, MusicKit, DeviceCheck and so on. (Token-based authentication)</td></tr>
</tbody>
</table>
</div><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748216973656/76df3f64-c84e-4195-a092-37c1143d8b1b.png" alt="Create a new certificate screen in App Store Connect" class="image--center mx-auto" width="1768" height="1384" loading="lazy"></p>
<h3 id="heading-how-to-create-an-apple-certificate-an-overview">How to Create an Apple Certificate – An Overview</h3>
<p>Here’s a general outline of how you create an Apple Certificate:</p>
<ul>
<li><p>Generate a Certificate Signing Request (CSR) on your Mac. (Yes you need a mac.)</p>
</li>
<li><p>You upload this CSR in AppStoreConnect as a part of creating the certificate.</p>
</li>
<li><p>Download the certificate from AppStoreConnect once it’s issued.</p>
</li>
<li><p>Install the certificate into your Keychain.</p>
</li>
</ul>
<p>Now we’ll go through each step in more detail. This part is very important, since we have to save some of the files generated locally or we lose the ability to transfer these certificates. This would mean revoking and re-issuing certificates (I have done this more times than I’d like to admit).</p>
<h4 id="heading-how-to-create-a-certificate-signing-request-csr">How to Create a Certificate Signing Request (CSR)</h4>
<p>A Certificate Signing Request (CSR) is a fancy name for an encrypted block of text containing information about who’s requesting the certificate (like your name and the public key). These are widely used in the cryptography world.</p>
<p>For our purposes, you’ll generate a CSR on your Mac and then submit it to Apple to request a digital certificate. The CSR generation process also creates a new public/private key pair on the Mac – the private key is stored in Keychain Access and is used for the eventual code signing.</p>
<p>To create a CSR using Keychain Access on macOS:</p>
<ol>
<li><p>Launch Keychain Access (you can find it at <code>/Applications/Utilities/</code> or use spotlight).</p>
</li>
<li><p>From the menu bar, choose Keychain Access &gt; Certificate Assistant &gt; Request a Certificate From a Certificate Authority.... (Here the Certificate Authority would be Apple).</p>
</li>
<li><p>In the dialog, enter your email address and a common name for the key (for example, "My Mac Key" or "[Your Name] Dev Key"). This name is primarily for your identification in the Keychain.</p>
</li>
<li><p>Leave the "CA Email Address" field empty – we won’t email it to the Certificate Authority (Apple).</p>
</li>
<li><p>Select the "Saved to disk" option and click "Continue".</p>
</li>
<li><p>Save the file, which will have a .certSigningRequest extension. The corresponding private key is now stored in the login keychain. <strong>This private key is irreplaceable by Apple and you must store it yourself.</strong></p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748288861336/50f20da3-69d9-476d-97e7-331f9b9b5c76.png" alt="Dialog for the CSR creation" class="image--center mx-auto" width="684" height="510" loading="lazy"></p>
<h4 id="heading-how-to-generate-and-download-your-apple-certificates">How to Generate and Download Your Apple Certificates</h4>
<p>Once you’ve created a CSR, you can request a certificate from the Apple Developer Portal:</p>
<ol>
<li><p>Navigate to "Certificates, Identifiers &amp; Profiles" and select "Certificates".</p>
</li>
<li><p>Click the add button (+).</p>
</li>
<li><p>Choose the desired certificate type</p>
</li>
<li><p>Follow the prompts, and when asked, upload the .certSigningRequest file generated earlier.</p>
</li>
<li><p>After Apple processes the request, the certificate will be available for download as a .cer file.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748289386364/78f46b4e-b232-4484-98c2-dcb75120fd61.png" alt="Prompt to upload the CSR after selecting the type of certificate" class="image--center mx-auto" width="1293" height="319" loading="lazy"></p>
</li>
</ol>
<p>To install the certificate, double-click the downloaded .cer file. It will be added to the Keychain Access application – usually appearing in the "login" keychain under the "My Certificates" category, where it should be paired with the private key generated during the CSR generation process earlier.</p>
<p>You can see my certificate and private key in the image below for reference.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748289120657/38f711dd-887a-4fae-844d-e389c65234cf.png" alt="An example of how your certificate and the private key will look like in the keychain" class="image--center mx-auto" width="905" height="44" loading="lazy"></p>
<p>To recap, the CSR certifies that you generated the request from your mac. The certificate certifies that Apple (in this case, an intermediary like the "Apple Worldwide Developer Relations Certification Authority") confirms that they verified the CSR and that it is indeed you who will sign with the certificate (<code>.cer</code>) file.</p>
<p>This is enforced by only you having access to the private key – if you lose it, you cannot use this certificate anymore.</p>
<p>So, if you use this certificate (and the private key) to sign an app, the app store / operating system knows that it is you for sure since Apple confirmed it.</p>
<h3 id="heading-how-to-store-your-keys-what-are-p12-files">How to Store Your Keys: What are .p12 Files?</h3>
<p>As I mentioned in the previous section, to code sign an app you need your certificate (containing the public key) and the corresponding private key. This is created along with the CSR, and you can find it in the <code>Keychain Access</code> app.</p>
<p>We call the combination of the certificate and the private key a digital identity. This proves your identity when you sign an app with them.</p>
<h4 id="heading-p12-files-personal-information-exchange">.p12 Files (Personal Information Exchange):</h4>
<p>A .p12 file is a password-protected archive format used to bundle a certificate along with its private key. Its primary purposes are:</p>
<ul>
<li><p>Backing up the digital identity in case you lose access to your Mac.</p>
</li>
<li><p>Transferring the digital identity to another Mac (for example, for another team member or a new development machine).</p>
</li>
<li><p>Providing the identity to automated build systems or third-party build services.</p>
</li>
</ul>
<p>Historically, I have stored the .p12 file on a shared drive with my team and shared the password to it verbally – you can also store it in a local backup disk.</p>
<p>Great. So how do you create one?</p>
<h4 id="heading-to-export-a-p12-file-from-keychain-access">To export a .p12 file from Keychain Access:</h4>
<ol>
<li><p>Open Keychain Access, select the "login" keychain, and go to the "My Certificates" category.</p>
</li>
<li><p>Locate the desired certificate. It should have an expandable disclosure triangle indicating an associated private key (look at the image of my certificate above).</p>
</li>
<li><p>Select <em>both</em> the certificate and its private key (or right-click the certificate and choose "Export").</p>
</li>
<li><p>Right-click and choose "Export [X] items...".</p>
</li>
<li><p>In the save dialog, choose the "Personal Information Exchange (.p12)" file format.</p>
</li>
<li><p>Assign a strong password to protect the .p12 file. This password will be required when importing the file elsewhere. It is crucial for security.</p>
</li>
<li><p>Save the file to a secure location.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748297124625/f9d2cfe0-3538-405e-8fb0-af08276c4326.png" alt="Image of exporting my certificate and private key as a .p12 file" class="image--center mx-auto" width="468" height="266" loading="lazy"></p>
</li>
</ol>
<h2 id="heading-bridge-between-everything-provisioning-profiles"><strong>Bridge Between Everything: Provisioning Profiles</strong></h2>
<p>Provisioning profiles are the final link between an App ID, developer certificates, and, in some cases, a list of specific test devices. They act as a permission slip, authorizing an app signed with a particular certificate to be installed and run either on designated devices or to be submitted to the App Store.</p>
<h3 id="heading-what-exactly-is-a-provisioning-profile">What Exactly is a Provisioning Profile?</h3>
<p>A provisioning profile is a <code>.mobileprovision</code> (for iOS / VisionOS) or <code>.provisionprofile</code> (for macOS) file that holds several key pieces of information:</p>
<ul>
<li><p><strong>The App ID:</strong> Specifies which application (or set of applications, if using a wildcard App ID) the profile applies to.</p>
</li>
<li><p><strong>Certificates:</strong> Contains one or more developer or distribution certificates that can be used to sign the app.</p>
</li>
<li><p><strong>Device UDIDs (for Development and Ad Hoc):</strong> For profiles intended for testing on specific devices, it includes a list of the Unique Device Identifiers (UDIDs) of those authorized devices – more on devices in the next section.</p>
</li>
<li><p><strong>Entitlements:</strong> A list of app services or capabilities (like Push Notifications, iCloud, App Groups) that the app is permitted to use. These are derived from the capabilities enabled for the <em>associated App ID</em>.</p>
</li>
</ul>
<p>You can open the file using <code>vim</code> or any editor to see parts of the content which include the App Id, Operating Systems, Certificates, and so on.</p>
<p>The operating system checks the provisioning profile at app launch to ensure the app is authorized to run on the current device and use the requested services. If the profile is missing, invalid, or doesn't match the app's signature or the device, the app will not launch.</p>
<p>They are difference from certificates, because certificates are tied to you as a developer. But provisioning profiles are to a specific app – with specific capabilities to a specific developer and maybe on specific devices.</p>
<p>If any of these change (let’s say you added a capability or your certificate expired, for example), you’ll need to generate the provisioning profile again. These are the files you will work with the most out of all the above, and any change can cause your profile to become invalid.</p>
<h3 id="heading-types-of-provisioning-profiles-development-ad-hoc-app-store-and-enterprise">Types of Provisioning Profiles: Development, Ad Hoc, App Store, (and Enterprise)</h3>
<h3 id="heading-types-of-provisioning-profiles-development-ad-hoc-app-store-and-enterprise-1"><strong>Types of Provisioning Profiles: Development, Ad Hoc, App Store, (and Enterprise)</strong></h3>
<p>Just like certificates, we have multiple types of provisioning profiles. Similar to certificates, there can be development and distribution provisioning profiles.</p>
<p>Since we also keep track of the devices a profile is supposed to run, we have several kinds of distribution profiles based on which devices it should run on.</p>
<p>We also have special profiles like “Enterprise” which will add additional capabilities (like main camera access on the Vision Pro) but will restrict your app distribution methods to enterprise only.</p>
<p>We will go over each of these types now. Feel free to skip to the one that you’re looking for.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Profile Type</strong></td><td><strong>Purpose</strong></td><td><strong>Required Certificate Type(s)</strong></td><td><strong>Device Registration Required?</strong></td><td><strong>Distribution Method</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Development</strong></td><td>Install &amp; debug on registered devices during development (need Xcode to install).</td><td>Development</td><td>Yes</td><td>Xcode run, local device deployment.</td></tr>
<tr>
<td><strong>Ad Hoc</strong></td><td>Distribute to a limited number of registered test devices (no need for Xcode).</td><td>Distribution</td><td>Yes</td><td>Manual install (for example, via link, email, MDM) for testers.</td></tr>
<tr>
<td><strong>App Store Connect</strong></td><td>Submit app to App Store Connect for TestFlight or App Store release.</td><td>Distribution</td><td>No</td><td>Upload to App Store Connect.</td></tr>
<tr>
<td><strong>Enterprise</strong></td><td>Distribute proprietary apps to employees within an organization.</td><td>Enterprise (Distribution)</td><td>No (subject to program terms)</td><td>Internal distribution (e.g., private portal, MDM).</td></tr>
<tr>
<td><strong>Developer ID</strong></td><td>Allows a macOS app that is distributed outside the App Store to use advanced features</td><td>Developer ID</td><td>No</td><td>Outside the Mac App Store (for example, a web page, USB, MDM )</td></tr>
</tbody>
</table>
</div><h4 id="heading-development-provisioning-profile"><strong>Development Provisioning Profile:</strong></h4>
<ul>
<li><p><strong>Allows</strong> an app to be installed and debugged on specific devices registered in the developer's account during the active development phase. More on device registration later.</p>
</li>
<li><p><strong>Contains</strong> an App ID, one or more development certificates, and a list of registered device UDIDs.</p>
</li>
<li><p><strong>Created</strong> manually in the Apple Developer Portal or generated automatically by Xcode if <code>Automatically manage signing</code> is enabled.</p>
</li>
</ul>
<h4 id="heading-ad-hoc-provisioning-profile"><strong>Ad Hoc Provisioning Profile:</strong></h4>
<ul>
<li><p><strong>Allows</strong> distribution of an app to a limited number of registered test devices <strong>without</strong> requiring Xcode for installation. This is ideal for distributing builds to QA teams, beta testers, or clients for feedback.</p>
</li>
<li><p><strong>Contains</strong> an App ID (often an explicit App ID, or an Xcode-managed one like <code>XC Wildcard</code> or <code>XC</code>), a single distribution certificate, and a list of registered device UDIDs.</p>
</li>
<li><p><strong>Created</strong> manually in the Developer Portal or managed by Xcode's automatic signing.</p>
</li>
</ul>
<h4 id="heading-app-store-connect-provisioning-profile"><strong>App Store Connect Provisioning Profile:</strong></h4>
<ul>
<li><p><strong>Required</strong> to sign an app for submission to App Store Connect. This is the pathway for distributing apps via TestFlight for broader beta testing and for official release on the App Store.</p>
</li>
<li><p><strong>Contains</strong> an explicit App ID (or an App ID that matches the app's bundle ID, including Xcode-managed App IDs), and a single distribution certificate. <em>Device UDIDs are not included in this profile type since this is meant for broader distribution.</em></p>
</li>
<li><p><strong>Created</strong> manually in the Developer Portal or managed by Xcode's automatic signing.</p>
</li>
</ul>
<h4 id="heading-enterprise-provisioning-profile"><strong>Enterprise Provisioning Profile:</strong></h4>
<ul>
<li><p>Exclusively for members of the <strong>Apple Developer Enterprise Program</strong>. It allows developers of these orgs to distribute proprietary, in-house applications directly to their employees, bypassing the public App Store.</p>
</li>
<li><p>Note: This program has stringent enrollment criteria and is strictly for internal distribution within the enrolled organization – these apps cannot be pushed to AppStore.</p>
</li>
</ul>
<h4 id="heading-developer-id-provisioning-profile"><strong>Developer ID Provisioning Profile:</strong></h4>
<ul>
<li><p><strong>Required</strong> to utilize certain Apple services or advanced capabilities like Push Notifications, CloudKit, Sign in with Apple, or specific iCloud services.</p>
</li>
<li><p><strong>Contains</strong> an App ID, a Developer ID distribution certificate, the entitlements authorized for the app.</p>
</li>
<li><p><strong>Created</strong> manually in the Developer Portal – will not be added automatically by Xcode’s automatic signing.</p>
</li>
</ul>
<h3 id="heading-how-to-create-and-manage-provisioning-profiles">How to Create and Manage Provisioning Profiles</h3>
<p>Creating and managing provisioning profiles usually requires an Account Holder or Admin role in the Apple Developer Program. You also need a configured App ID, the appropriate certificate(s), and for Development or Ad Hoc profiles, a list of registered device UDIDs.</p>
<p>If you are new developer, my recommendation is to read this article completely, then get back to this section once you have your devices setup.</p>
<p>General steps for manual creation in the Developer Portal:</p>
<ol>
<li><p>Navigate to "Certificates, Identifiers &amp; Profiles" and select "Profiles".</p>
</li>
<li><p>Click the add button (+).</p>
</li>
<li><p>Select the type of provisioning profile to create (for example, "iOS App Development," "Ad Hoc," "App Store").</p>
</li>
<li><p>Choose the App ID you’re targeting from the dropdown list.</p>
</li>
<li><p>Select the certificate(s) to include in the profile. Development profiles can include multiple development certificates – so you can include all the team member certificates here. Ad Hoc and App Store profiles include a single distribution certificate.</p>
</li>
<li><p>If creating a Development or Ad Hoc profile, select the registered devices to include.</p>
</li>
<li><p>Provide a name for the provisioning profile (this is for identification in the portal and Xcode).</p>
</li>
<li><p>Click "Generate" and then "Download" the <code>.mobileprovision</code> or <code>.provisionprofile</code> file.</p>
</li>
</ol>
<p>You need to make downloaded profiles available to Xcode. You can often do this by double-clicking the downloaded file or by refreshing profiles within Xcode's account settings (Preferences &gt; Accounts).</p>
<p>I really like Xcode's "Automatically manage signing" feature and it can simplify profile management by a lot. It creates and updates profiles as needed. But, understanding the manual process is crucial for troubleshooting because when things go wrong, it is straightforward to debug the issue with this knowledge.</p>
<p>Provisioning profiles will become invalid and require regeneration if:</p>
<ul>
<li><p>The capabilities of the associated App ID are changed – let’s say you added a new capability.</p>
</li>
<li><p>An included certificate expires or is revoked.</p>
</li>
<li><p>For Development/Ad Hoc profiles, if devices are added or removed from the registered list in a way that affects the profile's device set, or if the profile's own expiration date is reached. When such changes occur, you have to edit the profile (if possible) or delete it and recreate it in the Developer Portal, then re-download it and install it again. While this may seem like a complicated step, it’s straightforward if you do it a couple of times.</p>
</li>
</ul>
<h2 id="heading-device-management-development-and-ad-hoc-builds"><strong>Device Management — Development and Ad Hoc Builds</strong></h2>
<p>For testing applications on physical Apple hardware outside of Testflight or AppStore, you’ll need to register the Unique Device Identifiers (UDIDs) of your test devices with your Apple Developer account. This registration is a necessary step for creating Development and Ad Hoc provisioning profiles.</p>
<h3 id="heading-why-you-need-to-register-test-devices">Why You Need to Register Test Devices</h3>
<p>Development and Ad Hoc provisioning profiles are specifically tied to a list of registered devices. An app signed with this profile can be installed directly without going through App Store process. This means that you need to register devices you intend to develop on. This restricts bad faith actors from releasing apps widely without developer and App Store supervision.</p>
<p>The UUID of a device is like a physical address (think Mac Address). If you don’t include this in the provisioning profile you used to sign an app package, it cannot be installed on that device.</p>
<p>Let’s go over the steps to do that.</p>
<h3 id="heading-how-to-find-your-devices-udid-unique-device-identifier">How to Find Your Device's UDID (Unique Device Identifier)</h3>
<p>A UDID is a unique 40-character hexadecimal string (for older devices) or a 25-character string (format XXXXXXXX-XXXXXXXXXXXXXXXX) that uniquely identifies a specific iPhone, iPad, Apple Watch, Apple TV, Vision Pro or Mac.</p>
<p>There are several ways to find a device's UDID:</p>
<ul>
<li><p><strong>Xcode:</strong> Connect the device to a Mac running Xcode. Open Xcode and navigate to Window &gt; Devices and Simulators. Select the connected device from the list on the left. The UDID will be displayed as the "Identifier" in the device information panel.</p>
</li>
<li><p><strong>Finder (macOS Catalina and later):</strong> Connect the iOS or iPadOS device to a Mac. Open Finder and select the device from the sidebar under "Locations." The UDID may be displayed directly, or it might be necessary to click on the line of text beneath the device's name (which shows model, storage, and OS version) to cycle through to display the UDID.</p>
</li>
<li><p><strong>iTunes (older macOS versions):</strong> For Macs running macOS Mojave or earlier, connect the device and open iTunes. Select the device icon when it appears. In the "Summary" tab, click on the "Serial Number" field; this will change to display the UDID.</p>
</li>
<li><p><strong>Apple Silicon Macs:</strong> When registering an Apple Silicon Mac, it's important to look for the "Provisioning UDID," which can be found in System Information under Hardware &gt; Provisioning UDID.</p>
</li>
<li><p><strong>Other Ways:</strong> There are some websites that will install a profile on to your device to get the UUID – so as an absolute last resort, you can do this. <em>But I highly recommend doing it in the one of the official ways to avoid any potential issues.</em></p>
</li>
</ul>
<h3 id="heading-how-to-register-devices-in-the-apple-developer-portal">How to Register Devices in the Apple Developer Portal</h3>
<p>Device registration is managed through the "Certificates, Identifiers &amp; Profiles" section of the Apple Developer Portal (developer.apple.com) and typically requires an Account Holder or Admin role.</p>
<p>To manually register a single device:</p>
<ol>
<li><p>Sign in to the Apple Developer Portal and navigate to "Certificates, Identifiers &amp; Profiles," then select "Devices" from the sidebar.</p>
</li>
<li><p>Click the add button (+) to register a new device.</p>
</li>
<li><p>Select the correct platform for the device (for example, iOS, macOS, tvOS, watchOS).</p>
</li>
<li><p>Enter a descriptive "Device Name" (this is for your reference, for example, "Sravan’s iPhone 11 Pro") and the device's UDID obtained in the previous step.</p>
</li>
<li><p>Click "Continue," review the information to make sure everything is correct, and then click "Register".</p>
</li>
</ol>
<p>For registering multiple devices, the portal supports uploading a specially formatted text file (a .txt or a .deviceids file) containing device names and UDIDs.</p>
<p>If "Automatically manage signing" is enabled in Xcode, Xcode can automatically register a connected device when it's selected as a build target. This is the way I managed all of my personal projects and devices. On the other hand, the file upload was really useful at my workplace to keep track of all the devices and add them at once.</p>
<h3 id="heading-understanding-device-limits-and-annual-resets">Understanding Device Limits and Annual Resets</h3>
<p>The Apple Developer Program imposes limits on the number of devices that can be registered for testing:</p>
<ul>
<li><p><strong>Annual Limit:</strong> Each membership year, a development team can register up to 100 devices for each product family (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro, Mac). If you are a large team, this can potentially bottleneck you. When we ran into this issue, we created a new development team that could be split so that it didn’t have too much interdependence. There is no other way as far as I know, other than asking Apple and appealing them.</p>
</li>
<li><p><strong>Disabling Devices:</strong> While a device can be disabled in the portal during the membership year, doing so <strong>does not free up its slot or increase the number of available devices for that year</strong>. This part is frustrating but I think this is the only way they can enforce the 100 device limit to avoid people swapping devices. They should just provide a pathway to increase the limit, really. Disabling a device will, however, invalidate any provisioning profiles that include it, requiring those profiles to be regenerated.</p>
</li>
<li><p><strong>Resetting Device List (Start of New Membership Year):</strong> At the beginning of a new membership year, Account Holders, Admins, and App Managers are given a one-time option when they first sign in to "Certificates, Identifiers &amp; Profiles" to remove devices from their list. This allows them to "reset" their available device count back to 100 for each product family. You can choose to remove specific devices or all registered devices. <strong>This is your one chance per year to remove unused devices completely and free up slots for new devices.</strong></p>
</li>
<li><p><strong>Membership Expiration:</strong> If a developer program membership is nearing expiration and is not planned for renewal, the Account Holder will have an option, starting 30 days before expiration, to download a copy of their registered device list. They can also opt to have all devices removed from the account immediately upon membership expiration. If no action is taken, devices are typically removed automatically 180 days after membership expiration.</p>
</li>
</ul>
<h2 id="heading-possibilities-enabling-capabilities-and-services"><strong>Possibilities: Enabling Capabilities and Services</strong></h2>
<p>App Capabilities (or App Services) are features provided by Apple that we (as developers) can integrate into our applications to extend functionality and provide richer user experiences. Examples include iCloud storage, Push Notifications, Sign in with Apple, Apple Pay and HealthKit integration. Enabling these often requires explicit configuration for an app's App ID in the Apple Developer Portal and within the Xcode project.</p>
<h3 id="heading-why-you-should-use-capabilities">Why You Should Use Capabilities</h3>
<p>Making full use of these App capabilities can set your app apart from other apps in a very noticeable way. You can use Apple Wallet integration if you want users to scan a membership card. You can use journaling suggestions if you want to prompt them to journal something. You can use iCloud Storage to lean further into inter-device synchronization.</p>
<p>When you enable a capability for an App ID, it results in specific entitlements being added to the app's provisioning profile. These entitlements are permissions that the operating system checks at runtime to ensure the app is authorized to use the requested service.</p>
<h3 id="heading-how-to-configure-capabilities-for-your-app-id-apple-developer-portal">How to Configure Capabilities for Your App ID (Apple Developer Portal)</h3>
<p>Enabling and configuring capabilities is typically done by an Account Holder or Admin in the Apple Developer Portal (developer.apple.com).</p>
<ol>
<li><p>Navigate to "Certificates, Identifiers &amp; Profiles" and select "Identifiers."</p>
</li>
<li><p>Choose the App ID for which capabilities need to be configured.</p>
</li>
<li><p>In the App ID's settings, there will be a "Capabilities" tab. Select the checkboxes for the capabilities the app requires.</p>
</li>
<li><p>Many capabilities require additional configuration steps. For these, a "Configure" or "Edit" button will usually appear next to the capability once selected. Examples include:</p>
</li>
</ol>
<ul>
<li><p><strong>App Groups:</strong> Requires creating or selecting an app group identifier to allow data sharing between a main app and its extensions, or between different apps from the same developer.</p>
</li>
<li><p><strong>Apple Pay:</strong> Requires associating one or more Merchant IDs with the App ID.</p>
</li>
<li><p><strong>iCloud:</strong> May require choosing Xcode version compatibility and creating or assigning iCloud containers for Key-Value or Document storage</p>
</li>
<li><p><strong>Sign in with Apple:</strong> May require configuring the App ID as a primary app or grouping it with an existing primary App ID, and optionally providing a server-to-server notification endpoint URL.</p>
</li>
</ul>
<ol start="5">
<li>After configuring all selected capabilities, click "Save." A warning dialog may appear, which needs confirmation to finalize the changes.</li>
</ol>
<p><strong>Enabling a capability in the Developer Portal is only one part of the process.</strong> You’ll also need to add and configure it within the app's target in the Xcode project, under the "Signing &amp; Capabilities" tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748480139418/6a4007b3-01bd-484a-865c-8c5e728e15e0.png" alt="Showing the Signing &amp; Capabilities screen in Xcode" class="image--center mx-auto" width="614" height="114" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748480260906/e0dcec33-24ce-448b-91be-b79f5638e6fc.png" alt="Screenshot showing the Capabilities selector in Xcode" class="image--center mx-auto" width="793" height="608" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748480340624/ac56896a-0fb0-4cb0-a3fc-c894a255794a.png" alt="Screenshot of Xcode showing three capabilities. " class="image--center mx-auto" width="1187" height="434" loading="lazy"></p>
<ol>
<li><p>Navigate to the project settings and select “Signing &amp; Capabilities”.</p>
</li>
<li><p>Press the “+ Capability” button to select the capability.</p>
</li>
<li><p>Once selected, the capability should appear in the pane. Depending on the capability, you might want to configure it further.</p>
</li>
</ol>
<p>This Xcode step integrates the necessary frameworks, adds entitlements files to the project, and adjusts build settings.</p>
<h3 id="heading-how-enabling-capabilities-affects-your-provisioning-profiles">How Enabling Capabilities Affects Your Provisioning Profiles</h3>
<p>Changes to an App ID's enabled capabilities have a direct and significant impact on its associated provisioning profiles.</p>
<ul>
<li><p><strong>Invalidation:</strong> When a capability is enabled, disabled, or its configuration is modified for an App ID, <strong>all existing provisioning profiles that use that App ID immediately become invalid</strong>.</p>
</li>
<li><p><strong>Regeneration Required:</strong> These invalidated provisioning profiles must be regenerated (either by editing and re-saving them in the Developer Portal or by having Xcode's automatic signing handle it). The regenerated profiles will then include the updated set of entitlements corresponding to the newly configured capabilities.</p>
</li>
<li><p><strong>Platform Impact:</strong> Enabling a capability for an App ID that is used across multiple platforms (for example, an iOS app and its watchOS companion) will affect the provisioning profiles for all eligible platforms that use that App ID.</p>
</li>
</ul>
<p>This is something to keep in mind. Especially when it comes to distribution profiles since those are usually manually managed.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>While all of these might seem daunting, Apple’s automatic process should handle most of it. But I highly recommend learning how everything works so that you can debug it in case something goes wrong. I also highly recommend using manually created profiles for distribution.</p>
<p>While signing and handling certificates is not the most exciting part of the App development process, it is a necessary skill to have. In my next article, I will go over distributing an app from start to finish (which includes these processes and more restrictions).</p>
<p>You can follow me at <a class="user-mention" href="https://hashnode.com/@sravankaruturi">Sravan Karuturi</a> for my other posts.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Prepare for your iOS interview ]]>
                </title>
                <description>
                    <![CDATA[ Preparing for an iOS developer interview can be a daunting task, especially when you're trying to master both conceptual questions and practical coding challenges. Whether you're just starting your iOS development journey or gearing up for your next ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/prepare-for-your-ios-interview/</link>
                <guid isPermaLink="false">681d1bbfb1018a470d0d8a02</guid>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 08 May 2025 21:01:51 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746737982577/1c47e446-ea9d-4e63-a54d-55c6d49e0b53.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Preparing for an iOS developer interview can be a daunting task, especially when you're trying to master both conceptual questions and practical coding challenges. Whether you're just starting your iOS development journey or gearing up for your next big career move, understanding the most commonly asked interview questions and knowing how to answer them effectively can give you a major advantage.</p>
<p>We just published a course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel that will teach you all about the top 10 most frequently asked iOS interview questions, complete with sample answers and code demonstrations. Presented by Richard Topchii, this mock interview format course features Daniel as the interviewer and covers everything from foundational concepts to nuanced language features in Swift.</p>
<p>The course kicks off with an overview and a discussion of why these specific questions were chosen, followed by a deep dive into essential iOS concepts like the View Controller life cycle, and the differences between structs and classes, which is a crucial topic for understanding memory management and data handling in Swift. You’ll also learn the distinctions between <code>.frame</code> and <code>.bounds</code> in UIViews, an area where developers are often tested for their understanding of layout behavior.</p>
<p>Beyond the fundamentals, the course explores more advanced topics such as Protocol-Oriented Programming, MVC architecture, and important Swift keywords like <code>lazy</code>, <code>weak</code>, <code>unowned</code>, and <code>@escaping</code>. These are often tricky areas that interviewers use to assess a candidate's depth of knowledge and real-world coding experience. Plus, with real coding questions on the <code>defer</code> statement and GCD / <code>DispatchQueue</code>, you'll get to see how to write performant and safe concurrent code.</p>
<p>By the end of the video, you’ll also get a summary and feedback section that ties all the concepts together, helping reinforce what you've learned. Watch the full course on <a target="_blank" href="https://youtu.be/SLIlyAy-aZs">the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/SLIlyAy-aZs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Address Common Accessibility Challenges in iOS Mobile Apps Using SwiftUI ]]>
                </title>
                <description>
                    <![CDATA[ Mobile apps are essential tools in daily life, making accessibility a top priority. However, many apps still do not provide inclusive experiences for people with disabilities. This article highlights nine common accessibility challenges in mobile app... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-address-ios-accessibility-challenges-using-swiftui/</link>
                <guid isPermaLink="false">673dc0d9ed2a01b66ee9f37c</guid>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SwiftUI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Namaswi Chandarana ]]>
                </dc:creator>
                <pubDate>Wed, 20 Nov 2024 10:58:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/9e9PD9blAto/upload/43ed1bb84a1c0abad81192c63e920503.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Mobile apps are essential tools in daily life, making accessibility a top priority. However, many apps still do not provide inclusive experiences for people with disabilities.</p>
<p>This article highlights nine common accessibility challenges in mobile apps and demonstrates how SwiftUI features can help developers address these issues effectively.</p>
<p>Each challenge is paired with a SwiftUI solution, sample code, and testing tips to guide developers in creating accessible and user-friendly apps.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-mobile-apps-accessibility-issues-and-swiftui-solutions">Mobile Apps Accessibility Issues and SwiftUI Solutions</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-missing-labels-and-descriptions">Missing Labels and Descriptions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-insufficient-color-contrast">Insufficient Color Contrast</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-small-touch-targets">Small Touch Targets</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-inaccessible-navigation">Inaccessible Navigation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lack-of-feedback-for-actions">Lack of Feedback for Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-complex-or-confusing-user-interfaces">Complex or Confusing User Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lack-of-support-for-assistive-technologies">Lack of Support for Assistive Technologies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-poorly-implemented-accessibility-features">Poorly Implemented Accessibility Features</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-insufficient-customization-options">Insufficient Customization Options</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-references">References</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-mobile-apps-accessibility-issues-and-swiftui-solutions">Mobile Apps Accessibility Issues and SwiftUI Solutions</h2>
<h3 id="heading-missing-labels-and-descriptions">Missing Labels and Descriptions</h3>
<ul>
<li><p><strong>Challenge</strong>: Many apps lack appropriate labels or descriptions for buttons, images, and other interactive elements, making it difficult for screen readers to communicate their purpose to visually impaired users. Without these labels, users might struggle to understand the app’s functionality.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: SwiftUI’s <code>.accessibilityLabel(_:)</code> modifier allows developers to assign clear, descriptive labels to interactive elements. These labels improve navigation and understanding by giving screen readers the necessary context.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">Label</span>(<span class="hljs-string">"Shop"</span>, systemImage: <span class="hljs-string">"cart"</span>)
      .accessibilityLabel(<span class="hljs-string">"Go to Shop"</span>)
</code></pre>
</li>
<li><p><strong>Testing</strong>: Enable VoiceOver on an iOS device, navigate through the app, and ensure each element has an accurate label. VoiceOver should read labels clearly to help users understand each element’s purpose without needing additional explanation.</p>
</li>
</ul>
<h3 id="heading-insufficient-color-contrast">Insufficient Color Contrast</h3>
<ul>
<li><p><strong>Challenge</strong>: Low contrast between text and background colors can make it difficult for users with visual impairments to read the content, especially for those with color vision deficiencies or low vision.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Use SwiftUI’s dynamic system colors (<code>.primary</code> and <code>.secondary</code>), which automatically adapt to the light or dark mode setting on the device, ensuring good readability.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">Text</span>(<span class="hljs-string">"Shop"</span>)
      .foregroundColor(.primary)  <span class="hljs-comment">// Adapts to light or dark mode automatically</span>
</code></pre>
</li>
<li><p>If custom colors are necessary, test them against WCAG standards for color contrast, using tools like Color Contrast Analyzer.</p>
</li>
<li><p><strong>Testing</strong>: Use Xcode’s Accessibility Inspector to verify contrast, and ensure that text remains readable in both light and dark modes. WCAG guidelines recommend a minimum contrast ratio of 4.5:1 for normal text.</p>
</li>
</ul>
<h3 id="heading-small-touch-targets">Small Touch Targets</h3>
<ul>
<li><p><strong>Challenge</strong>: Small buttons or other touch areas can be difficult for users with motor impairments to interact with accurately. Elements that are too small may require more precision than some users can provide.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Set minimum touch sizes by adding padding or using <code>.frame(minWidth:minHeight:)</code> to ensure a comfortable touch target size.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">Button</span>(action: { <span class="hljs-comment">/* Action */</span> }) {
      <span class="hljs-type">Text</span>(<span class="hljs-string">"Tap Me"</span>)
          .frame(minWidth: <span class="hljs-number">44</span>, minHeight: <span class="hljs-number">44</span>)
  }.padding()
</code></pre>
</li>
<li><p><strong>Testing</strong>: Manually interact with touch elements in the app on an iOS device. Ensure they are easily tappable without precise effort. Verify touch target size with the Accessibility Inspector to confirm they meet recommended minimums (44x44 points).</p>
</li>
</ul>
<h3 id="heading-inaccessible-navigation">Inaccessible Navigation</h3>
<ul>
<li><p><strong>Challenge</strong>: Apps with limited navigability can cause frustration for users who rely on screen readers or keyboards. Without a clear reading order, navigating through the interface becomes challenging.</p>
</li>
<li><p><strong>SwiftUI Techniques for Accessible Navigation</strong>:</p>
<ul>
<li><p><strong>Group Elements</strong> with <code>.accessibilityElement(children:)</code>: Combine related elements into a single accessible unit for more streamlined navigation.</p>
<pre><code class="lang-swift">  <span class="hljs-type">VStack</span> {
      <span class="hljs-type">Text</span>(<span class="hljs-string">"Profile"</span>)
      <span class="hljs-type">Image</span>(<span class="hljs-string">"profile_picture"</span>)
  }
  .accessibilityElement(children: .combine)
</code></pre>
</li>
<li><p><strong>Set Focus</strong> with <code>.accessibilityFocused</code>: Programmatically control focus on specific elements.</p>
<pre><code class="lang-swift">  <span class="hljs-type">Text</span>(<span class="hljs-string">"Special Announcement"</span>)
      .accessibilityFocused($isFocused)
</code></pre>
</li>
<li><p><strong>Custom Actions</strong> with <code>.accessibilityAction</code>: Add specific actions for interactive controls like sliders or steppers.</p>
<pre><code class="lang-swift">  <span class="hljs-type">Slider</span>(value: $value)
      .accessibilityAction(named: <span class="hljs-string">"Increase"</span>) { value += <span class="hljs-number">10</span> }
</code></pre>
</li>
<li><p><strong>Hide Decorative Elements</strong> with <code>.accessibilityHidden</code>: Exclude non-essential visuals from screen readers.</p>
<pre><code class="lang-swift">  <span class="hljs-type">Image</span>(<span class="hljs-string">"decorative_image"</span>)
      .accessibilityHidden(<span class="hljs-literal">true</span>)
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Testing</strong>: Enable VoiceOver and use swipe gestures to confirm the intended focus order. Also, use a connected keyboard or switch control to test smooth transitions and confirm navigability.</p>
</li>
</ul>
<h3 id="heading-lack-of-feedback-for-actions">Lack of Feedback for Actions</h3>
<ul>
<li><p><strong>Challenge</strong>: Without feedback, users with visual or hearing impairments may struggle to confirm if an action has completed. Feedback like haptic, auditory, or visual cues can enhance usability.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Use <code>.accessibilityHint</code> to provide additional information about the action that will occur.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">Button</span>(<span class="hljs-string">"Submit"</span>) {
      <span class="hljs-comment">// Submit action</span>
  }.accessibilityHint(<span class="hljs-string">"Submits the form"</span>)
</code></pre>
</li>
<li><p><strong>Testing</strong>: Use VoiceOver to ensure that hints are read immediately after labels. Check that users can understand what each button does without extra explanation.</p>
</li>
</ul>
<h3 id="heading-complex-or-confusing-user-interfaces">Complex or Confusing User Interfaces</h3>
<ul>
<li><p><strong>Challenge</strong>: Cluttered interfaces can be overwhelming, particularly for users with cognitive impairments, who may struggle to navigate or process information effectively.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Simplify layouts and use <code>.accessibilitySortPriority</code> to organize the reading order logically.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">VStack</span> {
      <span class="hljs-type">Text</span>(<span class="hljs-string">"Main Content"</span>)
          .accessibilitySortPriority(<span class="hljs-number">1</span>)
      <span class="hljs-type">Button</span>(<span class="hljs-string">"Secondary Action"</span>)
          .accessibilitySortPriority(<span class="hljs-number">2</span>)
  }
</code></pre>
</li>
<li><p><strong>Testing</strong>: Use VoiceOver to verify the logical reading order and ensure only relevant elements are accessible. Use <code>.accessibilityHidden</code> to hide decorative elements that do not add meaningful information.</p>
</li>
</ul>
<h3 id="heading-lack-of-support-for-assistive-technologies">Lack of Support for Assistive Technologies</h3>
<ul>
<li><p><strong>Challenge</strong>: Inadequate support for screen readers or other assistive technologies can make apps unusable for some users.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Group elements with <code>.accessibilityElement(children: .combine)</code> for cohesive navigation. This improves readability and usability for screen reader users.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">VStack</span> {
      <span class="hljs-type">Text</span>(<span class="hljs-string">"Profile"</span>)
      <span class="hljs-type">Image</span>(<span class="hljs-string">"profile_picture"</span>)
  }
  .accessibilityElement(children: .combine)
</code></pre>
</li>
<li><p><strong>Testing</strong>: Check with VoiceOver that grouped elements are announced as a single unit, improving navigation flow for visually impaired users.</p>
</li>
</ul>
<h3 id="heading-poorly-implemented-accessibility-features">Poorly Implemented Accessibility Features</h3>
<ul>
<li><p><strong>Challenge</strong>: Without regular testing and updates, accessibility features can degrade over time, negatively impacting the user experience.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Regular testing with VoiceOver and Xcode’s Accessibility Inspector helps maintain effective functionality.</p>
</li>
<li><p><strong>Testing</strong>: Conduct regular testing to detect regressions or improvements needed for accessibility. Recheck VoiceOver usability after UI updates to confirm features remain effective.</p>
</li>
</ul>
<h3 id="heading-insufficient-customization-options">Insufficient Customization Options</h3>
<ul>
<li><p><strong>Challenge</strong>: Limited customization options, such as font size or color schemes, restrict usability for users with specific visual needs.</p>
</li>
<li><p><strong>SwiftUI Solution</strong>: Use <code>.dynamicTypeSize()</code> to allow text scaling based on the user’s preferred settings.</p>
</li>
<li><p><strong>Example</strong>:</p>
<pre><code class="lang-swift">  <span class="hljs-type">Text</span>(<span class="hljs-string">"Adjustable Text"</span>)
      .dynamicTypeSize(.xxxLarge)
</code></pre>
</li>
<li><p><strong>Testing</strong>: Adjust text size in iOS Accessibility settings, and ensure the app’s text scales correctly without truncating or overlapping, preserving readability.</p>
</li>
</ul>
<h3 id="heading-references">References</h3>
<ol>
<li><p><strong>Apple Developer Documentation: SwiftUI Accessibility</strong></p>
<ul>
<li><p>Comprehensive guide to accessibility in SwiftUI, covering accessibility properties like <code>.accessibilityLabel</code>, <code>.accessibilityHint</code>, <code>.accessibilityElement</code>, and more.</p>
</li>
<li><p><a target="_blank" href="https://developer.apple.com/documentation/swiftui/accessibility">SwiftUI Accessibility Guide</a></p>
</li>
</ul>
</li>
<li><p><strong>Apple Human Interface Guidelines: Accessibility</strong></p>
<ul>
<li><p>Apple's best practices for designing accessible apps, including color contrast and touch target size recommendations.</p>
</li>
<li><p><a target="_blank" href="https://developer.apple.com/design/human-interface-guidelines/accessibility/overview/">Apple Human Interface Guidelines: Accessibility</a></p>
</li>
</ul>
</li>
<li><p><strong>Color Contrast Analyzer</strong></p>
<ul>
<li><p>A tool for testing contrast ratios to ensure color accessibility compliance with WCAG standards.</p>
</li>
<li><p>Color Contrast Analyzer</p>
</li>
</ul>
</li>
<li><p><strong>VoiceOver and Accessibility Inspector</strong></p>
<ul>
<li><p>Tools for testing accessibility features, available in iOS and Xcode for simulating screen reader usage and checking accessibility properties.</p>
</li>
<li><p><a target="_blank" href="https://support.apple.com/guide/voiceover/welcome/mac">VoiceOver Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://developer.apple.com/documentation/accessibility-testing/accessibility-inspector">Accessibility Inspector Documentation</a></p>
</li>
</ul>
</li>
<li><p><strong>Chandarana, N., &amp; Gada, T. (2024). Accessibility Challenges in Current Mobile Applications: A Comprehensive Overview.</strong></p>
<ul>
<li><p>This journal paper provides an in-depth analysis of common accessibility challenges faced in mobile applications, discussing real-world examples and potential solutions for developers.</p>
</li>
<li><p><em>International Journal of Innovative Research in Computer and Communication Engineering.</em></p>
</li>
</ul>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ In App Update - How to Notify Users of App Updates in Flutter ]]>
                </title>
                <description>
                    <![CDATA[ When you roll out a new version of your application, you want your users to know about it. Whether you fixed a critical bug, added a new feature, or the application just runs smoother or faster – they need to know.  As application developers, we want... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/in-app-update-the-flutter-way/</link>
                <guid isPermaLink="false">66ba5026f8a814ef73b78bc4</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ application ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tomer ]]>
                </dc:creator>
                <pubDate>Fri, 18 Nov 2022 21:50:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/clarisse-meyer-xXiKQ2AavlY-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you roll out a new version of your application, you want your users to know about it. Whether you fixed a critical bug, added a new feature, or the application just runs smoother or faster – they need to know. </p>
<p>As application developers, we want all of our users to use the most recent version of our application.</p>
<p>But how can we make sure that users are aware of a new version of our application?</p>
<p>The answer to that question is quite simple: Why not inform them when a new version of the application is out? </p>
<p>You can do this in various ways:</p>
<ul>
<li>Use a push notification</li>
<li>Let them know when the application is launched</li>
</ul>
<p>We won’t be dealing with push notifications in this article. Instead we'll focus on showing how you can (using a package or two) show a dialog to your users informing them that a new version of the application is out and how to deal with the update.</p>
<h2 id="heading-wait-isnt-this-already-included">Wait, Isn’t This Already Included?</h2>
<p>You would think that this kind of functionality should already be included in the modern mobile OS systems. And you would be right – but only for Android. </p>
<p>iOS does not (currently) give developers the ability to see if there is a new version of the application and notify users about it. In Android, you have the <a target="_blank" href="https://developer.android.com/guide/playcore/in-app-updates/kotlin-java">In-App Update library</a> that is part of the Google Play libraries.</p>
<p>Because of this, and because Flutter supports both platforms, I am going to go over two prominent packages that help you handle version updates to your application:</p>
<ol>
<li><a target="_blank" href="https://pub.dev/packages/upgrader">Upgrader</a></li>
<li><a target="_blank" href="https://pub.dev/packages/in_app_update">In App Update</a></li>
</ol>
<p>Both can get you the desired result, but they vary widely in how they do it.</p>
<p>Before we start, <strong>it is crucial to understand that you must have a version of your application that was installed directly from the Google Play store</strong>. This is required since both packages rely on Google Play services and its ability to verify the owner of the application. </p>
<p>If you fail to do so, you will see the following error when trying to use one of the packages:</p>
<blockquote>
<p>_Install Error(-10): The app is not owned by any user on this device. An app is “owned” if it has been acquired from Play. (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_APP_NOT<em>OWNED)</em></p>
</blockquote>
<h2 id="heading-how-to-use-the-in-app-update-package">How to Use The In App Update Package</h2>
<p>Right off the bat you should know that this package will only work on Android. This is because it relies on the in app update library for its inner workings. </p>
<p>This package is basically a wrapper for the Android library. Below are its exposed API methods:</p>
<ul>
<li><code>Future&lt;AppUpdateInfo&gt; checkForUpdate()</code>: Checks if there's an update available</li>
<li><code>Future&lt;AppUpdateResult&gt; performImmediateUpdate()</code>: Performs an immediate update (full-screen)</li>
<li><code>Future&lt;AppUpdateResult&gt; startFlexibleUpdate()</code>: Starts a flexible update (background download)</li>
<li><code>Future&lt;void&gt; completeFlexibleUpdate()</code>: Actually installs an available flexible update</li>
</ul>
<p>✋ If you want to read more about the differences between an immediate update or a flexible update, head over <a target="_blank" href="https://developer.android.com/guide/playcore/in-app-updates">here</a>.</p>
<h3 id="heading-how-to-set-up-the-package">How to Set Up the Package</h3>
<p>First, add the package to your pubspec.yaml file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">in_app_update:</span> <span class="hljs-string">^3.0.0</span>
</code></pre>
<p>Then perform pub get.</p>
<p>Inside your application, where you intend to perform the logic to handle in app updates, add the following import:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:in_app_update/in_app_update.dart'</span>;
</code></pre>
<p>We will first need to add logic that checks if our application has an update. To do that, we will use the <strong>checkForUpdate</strong> method. Its return value is a Future which contains information about the availability and progress of an app update. </p>
<p>We can check if an update is available by using the <a target="_blank" href="https://developer.android.com/reference/com/google/android/play/core/install/model/UpdateAvailability">updateAvailability</a> property. If an update is available, it will have the value of <strong>UPDATE_AVAILABLE</strong>. So, your method may look like this:</p>
<pre><code class="lang-dart">InAppUpdate.checkForUpdate().then((updateInfo) {
  <span class="hljs-keyword">if</span> (updateInfo.updateAvailability == UpdateAvailability.updateAvailable) {
      <span class="hljs-comment">//Logic to perform an update </span>
  }
});
</code></pre>
<p>Next, we need to decide which kind of update we want to trigger – either a flexible or an immediate update. </p>
<p>Going for an immediate update should be reserved for an application update that is critical for your users. That may mean a version that fixes a critical bug or offers a new feature. </p>
<p>To start an immediate update, we can use the <strong>performImmediateUpdate</strong> method. This method returns a <a target="_blank" href="https://developer.android.com/reference/com/google/android/play/core/ktx/AppUpdateResult">AppUpdateResult</a> enum that lets you know if the update was successful or not. </p>
<p>Before calling this method we need to check if we are allowed to run an immediate update. We do that by accessing the <strong>immediateUpdateAllowed</strong> flag on the AppUpdateInfo object.</p>
<p>If we want to trigger a flexible update, we use the <strong>startFleixbleUpdate</strong> method. This runs in the background and similar to the immediate update method. It also returns an AppUpdateResult enum. </p>
<p>If in this scenario the update was successful, we need to call the <strong>completeFlexibleUpdate</strong> method to install the update to our application.</p>
<p>So, if we look at the code snippet above and add the logic for the different types of updates, it will look like this:</p>
<pre><code class="lang-dart">InAppUpdate.checkForUpdate().then((updateInfo) {
  <span class="hljs-keyword">if</span> (updateInfo.updateAvailability == UpdateAvailability.updateAvailable) {
      <span class="hljs-keyword">if</span> (updateInfo.immediateUpdateAllowed) {
          <span class="hljs-comment">// Perform immediate update</span>
          InAppUpdate.performImmediateUpdate().then((appUpdateResult) {
              <span class="hljs-keyword">if</span> (appUpdateResult == AppUpdateResult.success) {
                <span class="hljs-comment">//App Update successful</span>
              }
          });
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (updateInfo.flexibleUpdateAllowed) {
        <span class="hljs-comment">//Perform flexible update</span>
        InAppUpdate.startFlexibleUpdate().then((appUpdateResult) {
              <span class="hljs-keyword">if</span> (appUpdateResult == AppUpdateResult.success) {
                <span class="hljs-comment">//App Update successful</span>
                InAppUpdate.completeFlexibleUpdate();
              }
          });
      }
  }
});
</code></pre>
<h2 id="heading-how-to-use-the-upgrader-package">How to Use The Upgrader Package</h2>
<p>As opposed to the first option, this one offers a solution for both iOS and Android. It relies on gathering data from the store and checking it against the current data from the application itself. </p>
<p>Instead of having an API to query the data, this package has widgets that perform the logic under the hood.</p>
<h3 id="heading-how-to-set-up-the-package-1">How to Set Up the Package</h3>
<p>First, add the package to your pubspec.yaml file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">upgrader:</span> <span class="hljs-string">^5.0.0</span>
</code></pre>
<p>Then perform pub get.</p>
<p>Inside your application, where you intend to perform the logic to handle in app updates, add the following import:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:upgrader/upgrader.dart'</span>;
</code></pre>
<p>The main difference between these two options is just a UI one, so pick the one that fits the most for you. </p>
<p>To integrate this package, you will need to wrap your body widget with either <strong>UpgradeAlert</strong> or <strong>UpgradeCard</strong>. Below is an example:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
      <span class="hljs-keyword">return</span> MaterialApp(
        title: applicationName,
        home: UpgradeAlert(                  <span class="hljs-comment">/// <span class="markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">------------------</span></span></span></span></span>
          child: MainPage(
              key: Key(<span class="hljs-string">"YOUR_KEY"</span>),
              title: applicationName
          ),
        )
      );
    }
}
</code></pre>
<p>If a new version of your application is available in the store, you will see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To test things out, make sure you add this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> Upgrader.clearSavedSettings()
</code></pre>
<p>inside your main method in your main.dart file.</p>
<p>Just so you are aware, there are a ton of configurations that you can set for the Upgrader package. I highly recommend you go and check these out.</p>
<h2 id="heading-how-to-test-the-packages">How to Test the Packages</h2>
<p>Regardless of which package you choose to work with, you need to know that your logic functions properly. </p>
<p>But how can you do that without releasing an official version of your application? You can use the internal testing option in Google Play Console. By releasing a new version of your application to internal testers, it will not be a public one and will allow you to test out the upgrading functionality.</p>
<p>Here is what you need to do:</p>
<ol>
<li>Log in to your Google Play Console account and head into the application you are working on to have the updating logic</li>
<li>Under Setup → Internal App Sharing, go to Manage Testers and make sure to allow testers to download and install the shared application. You can either choose to do so via link or by email.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-1.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Then, go to Testing → Internal Testing and click on the Create new release button (top right).</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Once you have performed a release, you can head back to the main Internal Testing page and click on the Testers tab. There you will see a list containing tester emails (empty right now). Click on the blue arrow icon.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-3.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="5">
<li>In this screen you can add yourself to be an internal tester (in the Add email addresses).</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-4.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="6">
<li>When you are done, you can head back to the Internal Testing window. Scroll down to the bottom and you will see the <strong>How testers join your test</strong> and you will see a Copy link button.</li>
</ol>
<p>You can now click the button and send yourself the link so you will be able to download the new version of your application.</p>
<p>If you fail to do one of the above steps, the link generated will lead to a not found (Error 404) page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-5.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you did everything successfully, you will see the following when you click on the generated link:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-6.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you see this error:</p>
<blockquote>
<p>_Install Error(-6): The download/install is not allowed, due to the current device state (e.g. low battery, low disk space, …). (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_INSTALL_NOT<em>ALLOWED)</em></p>
</blockquote>
<p>It might mean you are running your application on an emulated device and you need to have Google Play Store installed on it and be logged in.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>I wrote this article because I had to go through the same process when integrating the in app update package with my own application. </p>
<p>You are welcome to check it out at the <a target="_blank" href="https://play.google.com/store/apps/details?id=com.tomerpacific.birthday_calendar">Google Play Store</a>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/1-7.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And see the entire source code here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/TomerPacific/BirthdayCalendar">https://github.com/TomerPacific/BirthdayCalendar</a></div>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement a GameKit Leaderboard in SwiftUI ]]>
                </title>
                <description>
                    <![CDATA[ By Saamer Mansoor In this article we will talk about why and how to implement the GameCenter's Leaderboard within your app.  Why GameCenter is Making a Huge Revival You can make iPhone games without a scoreboard, but leaderboards can help make the ga... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-a-leaderboard-in-swiftui/</link>
                <guid isPermaLink="false">66d460c537bd2215d1e245bb</guid>
                
                    <category>
                        <![CDATA[ Apple ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Games ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SwiftUI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 19 Aug 2022 21:00:48 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/IMG_45B142A26F90-1-copy.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Saamer Mansoor</p>
<p>In this article we will talk about why and how to implement the <a target="_blank" href="https://developer.apple.com/design/human-interface-guidelines/technologies/game-center/leaderboards/">GameCenter's Leaderboard</a> within your app. </p>
<h2 id="heading-why-gamecenter-is-making-a-huge-revival">Why GameCenter is Making a Huge Revival</h2>
<p>You can make iPhone games without a scoreboard, but leaderboards can help make the game feel more competitive, like people are competing against one another around the World. </p>
<p>Instead of creating and managing your own backend, the GameCenter Leaderboard allows you to scale with traffic infinitely, skip an entire login page for authorization, get the Image, Name, and friends playing the same game – all without your users having to enter anything. </p>
<p>Especially with iOS 16, <a target="_blank" href="https://developer.apple.com/game-center/">Apple is investing more in improving it</a>, and driving more app usage, like through Push Notifications when your friend beats your score in the game.</p>
<p>In my journey of learning SwiftUI, I have been creating and publishing apps, because IMO that's the best way to learn. </p>
<p>There wasn't much updated documentation on how to do a lot of this, especially none with SwiftUI nor with the <a target="_blank" href="https://www.freecodecamp.org/news/make-rest-api-call-in-swiftui-in-2-minutes/">advent of async and await in Swift</a>. So I consolidated and simplified it for everyone to build amazing apps. So feel free to invite me to test your apps too!</p>
<h3 id="heading-pre-requisites">Pre-Requisites:</h3>
<ul>
<li>You'll need to have an <a target="_blank" href="https://developer.apple.com/programs/">Apple Developer</a> paid account</li>
<li>You have to create the <a target="_blank" href="https://support.magplus.com/hc/en-us/articles/203808708-iOS-Creating-App-IDs">App Id for your app</a> in the provisioning profiles section of the Apple Developer Portal</li>
<li>You have to <a target="_blank" href="https://support.staffbase.com/hc/en-us/articles/115003481992-Creating-an-App-Profile-in-App-Store-Connect">create the App in the iTunes Connect Connect</a> portal</li>
</ul>
<h2 id="heading-how-to-implement-your-ios-leaderboard-in-6-steps">How to Implement Your iOS Leaderboard in 6 Steps</h2>
<p>Most of the code logic for the leaderboard is in <a target="_blank" href="https://github.com/StairMasterClimber/mobile/blob/main/StairStepperMaster/StairStepperMaster/Views/LeadersTileView.swift">this file if you want to skip ahead</a>. Here's the steps as follows:</p>
<h3 id="heading-1-how-to-create-the-app-store-connect-leaderboard">1. How to Create the App Store Connect Leaderboard</h3>
<p><img src="https://user-images.githubusercontent.com/8262287/180824532-2e27ca8a-c1c0-4676-b439-f3ab09887271.png" alt="image" width="1348" height="754" loading="lazy">
<em>Screenshot from the Apple iTunes Connect Portal</em></p>
<p>Once you have created the app in the App Store Connect portal successfully, go to the Services tab for the app -&gt; and make sure you're in the GameCenter page.</p>
<p>Then add a new leaderboard using the "+" sign, which can either be "Classic" (scores never reset) or "Recurring" (scores reset based on your frequency settings).</p>
<p>Most games prefer a recurring leaderboard so that the leaderboard isn't cluttered with older impossible to reach high scores.</p>
<p>The LeaderboardID you input there is the one that you need to use in all the places in the code that ask for it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-122.png" alt="Image" width="600" height="400" loading="lazy">
<em>Details required to create a new Leaderboard</em></p>
<h3 id="heading-2-how-to-set-up-gamecenter-authentication">2. How to Set Up GameCenter Authentication</h3>
<p>First, you'll need to authenticate users to GameCenter in order for any of this functionality to work.</p>
<p>So we'll use this code to do that, which basically makes sure that you (GKLocalPlayer.local) are authenticated, or prints an error if there is one:</p>
<pre><code>func authenticateUser() {
    GKLocalPlayer.local.authenticateHandler = { vc, error <span class="hljs-keyword">in</span>
        guard error == nil <span class="hljs-keyword">else</span> {
            print(error?.localizedDescription ?? <span class="hljs-string">""</span>)
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre><p>If the user is authenticated, you will see a little popup in the UI. If not, the user will be taken to a page to login to their GameCenter account.</p>
<p><img src="https://user-images.githubusercontent.com/8262287/180823235-cafefcfa-3d25-46e5-8524-d7f475b9a000.png" alt="image" width="464" height="94" loading="lazy">
<em>A sign that displays when a user is logged in</em></p>
<h3 id="heading-3-how-to-display-leaderboard-items-in-the-ui">3. How to Display Leaderboard Items in the UI</h3>
<p>In order to get the data away from the GameCenter ViewController leaderboards (GKLeaderboard), you need to use the <code>loadLeaderboards</code> . </p>
<p>You can switch up the <code>loadEntries</code> function from <code>.global</code> to <code>.friends</code> in order to only pull your friends. </p>
<p>You can also retrieve the image for each player by iterating over each player and performing a <code>loadPhoto</code>. </p>
<p>Using <code>NSRang(1...5)</code>, you can choose how many players to display. This pulls the users with the highest 5 scores from the leaderboard and returns none if there's no users, such as in the case when the cycle refreshes for a recurring Leaderboard.</p>
<p>This is what pulling data from a leaderboard could look like if you take advantage of async-await:</p>
<pre><code>func loadLeaderboard() <span class="hljs-keyword">async</span> {
    playersList.removeAll()
    Task{
        <span class="hljs-keyword">var</span> playersListTemp : [Player] = []
        <span class="hljs-keyword">let</span> leaderboards = <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> GKLeaderboard.loadLeaderboards(IDs: [leaderboardIdentifier])
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> leaderboard = leaderboards.filter ({ $<span class="hljs-number">0.</span>baseLeaderboardID == self.leaderboardIdentifier }).first {
            <span class="hljs-keyword">let</span> allPlayers = <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> leaderboard.loadEntries(<span class="hljs-keyword">for</span>: .global, <span class="hljs-attr">timeScope</span>: .allTime, <span class="hljs-attr">range</span>: NSRange(<span class="hljs-number">1.</span>.<span class="hljs-number">.5</span>))
            <span class="hljs-keyword">if</span> allPlayers<span class="hljs-number">.1</span>.count &gt; <span class="hljs-number">0</span> {
                <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> allPlayers<span class="hljs-number">.1</span>.asyncForEach { leaderboardEntry <span class="hljs-keyword">in</span>
                    <span class="hljs-keyword">var</span> image = <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> leaderboardEntry.player.loadPhoto(<span class="hljs-keyword">for</span>: .small)
                    playersListTemp.append(Player(name: leaderboardEntry.player.displayName, <span class="hljs-attr">score</span>:leaderboardEntry.formattedScore, <span class="hljs-attr">image</span>: image))
                                print(playersListTemp)
                    playersListTemp.sort{
                        $<span class="hljs-number">0.</span>score &lt; $<span class="hljs-number">1.</span>score
                    }
                }
            }
        }
        playersList = playersListTemp            
    }
}
</code></pre><p><img src="https://user-images.githubusercontent.com/8262287/180823292-2dee4f9a-4894-4442-9241-2ad1c84b1cf7.png" alt="image" width="622" height="240" loading="lazy">
<em>You can get leaderboard data into your app</em></p>
<h3 id="heading-4-how-to-call-functionality-in-swiftui-as-the-viewpage-appears">4. How to Call Functionality in SwiftUI as the View/Page Appears</h3>
<p>You can take advantage of the <code>onAppear</code> <a target="_blank" href="https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear">lifecycle function of the view</a> to actually make the calls to authenticate and load, but you can also do it on the tap of a button if you prefer that:</p>
<pre><code>.onAppear(){
    <span class="hljs-keyword">if</span> !GKLocalPlayer.local.isAuthenticated {
        authenticateUser()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> playersList.count == <span class="hljs-number">0</span> {
        Task{
            <span class="hljs-keyword">await</span> loadLeaderboard()
        }
    }
}
</code></pre><h3 id="heading-5-how-to-load-the-submitted-scores">5. How to Load the Submitted Scores</h3>
<p>In order to load the scores, you need to submit them as well. The <code>submitScore</code> function can help you with that.</p>
<ul>
<li>The <code>flightsClimbed</code> variable should contain the score that you would like to submit.</li>
<li>GameKit makes sure to only display your best score for the life of the leaderboard.</li>
<li>The <code>leaderboardId</code> contains the value that you manually enter in your App Store Connect account:</li>
</ul>
<pre><code>func leaderboard() <span class="hljs-keyword">async</span>{
    Task{
        <span class="hljs-keyword">try</span> <span class="hljs-keyword">await</span> GKLeaderboard.submitScore(
            flightsClimbed,
            <span class="hljs-attr">context</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">player</span>: GKLocalPlayer.local,
            <span class="hljs-attr">leaderboardIDs</span>: [<span class="hljs-string">"com.tfp.stairsteppermaster.flights"</span>]
        )
    }
    calculateAchievements()
}
</code></pre><h3 id="heading-6-how-to-display-the-gamecenter-viewcontroller-portal">6. How to display the GameCenter ViewController Portal</h3>
<p>When you're logged into GameCenter, a little annoying icon appears in the top right of your screen. When you tap on it, you are taken to the GameCenter ViewController. Luckily you can hide it if it's not part of your design, using <code>GKAccessPoint.shared.isActive = false</code>. </p>
<p>Since the GameCenter UI is a UIKit <code>ViewController</code> and not a simple SwiftUI <code>View</code>, you need to create this <a target="_blank" href="https://www.hackingwithswift.com/books/ios-swiftui/wrapping-a-uiviewcontroller-in-a-swiftui-view">UIViewControllerRepresentable</a> first (as you can <a target="_blank" href="https://github.com/StairMasterClimber/mobile/blob/main/StairStepperMaster/StairStepperMaster/Views/GameCenterView.swift">see here</a>), to launch GameCenter using a different button, </p>
<p>Once you add that file to your project, you can display the GameCenter portal simply using this: <code>GameCenterView(format: gameCenterViewControllerState)</code> where gameCenterViewControllerState can be help you go to a detail page in GameCenter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Frame-3-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>GameCenter's Leaderboard View</em></p>
<h2 id="heading-things-to-keep-in-mind-while-using-gamecenters-leaderboards">Things to Keep in Mind While using GameCenter's Leaderboards:</h2>
<ul>
<li>Simulator Debugging – For some reason the authenticate to GameCenter is extremely slow on a simulator, so it might make sense to even create a mock of data when using the simulator.</li>
<li>Challenges – You can't programmatically issue GameKit Challenges to your friends anymore <a target="_blank" href="https://developer.apple.com/documentation/gamekit/gkscore/1520610-issuechallenge">due to deprecation</a>. Instead, you have to do those manually within the user's GameCenter dashboard against GameKit Achievements. Also, there's no way to view challenges you have sent. </li>
<li>Achievements – Leaderboards are different from the GameKit Achievements, which is calculated and displayed differently, but a <a target="_blank" href="https://github.com/StairMasterClimber/mobile/blob/18283a68e1c5cac4e270a85b03853887b3950156/StairStepperMaster/StairStepperMaster/Views/AchievementTileView.swift#L113">lot easier</a>. Those can also be pulled into the app as well, as you can see below:</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Frame-2-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>GameKit Challenges and Achievements</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>You can try out the free open-source <a target="_blank" href="https://stairmasterclimber.com/app">Stair Master Climber iPhone Health &amp; Fitness app</a> that I shared above. I would love to know what you think so that we can learn together. </p>
<p>Feel free to reach out to me on <a target="_blank" href="https://twitter.com/StairMasterApp">social media</a> or by <a target="_blank" href="mailto:hi@stairmasterclimber.com">email</a> if you have any questions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use UISearchController in iOS Apps ]]>
                </title>
                <description>
                    <![CDATA[ By Sai Balaji K Hello everyone! In this article we are going to learn how to use UISearchController in iOS Apps. What are we going to build? We are going to build a movie search application which uses the TMDB API to fetch movie info and display it u... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-use-uisearchcontroller-in-ios-apps/</link>
                <guid isPermaLink="false">66d460c7b3016bf139028d85</guid>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ios app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Xcode ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 24 Mar 2022 15:55:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/03/Screenshot-2022-03-21-at-8.11.31-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sai Balaji K</p>
<p>Hello everyone! In this article we are going to learn how to use UISearchController in iOS Apps.</p>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We are going to build a movie search application which uses the <a target="_blank" href="https://www.themoviedb.org/">TMDB</a> API to fetch movie info and display it using a UICollectionView based on a user's search query.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Open up Xcode and create a new blank iOS App project – make sure you select UIKit and not SwiftUI.</p>
<p>In this app we are going to use the MVC Pattern so organise the project by creating the following groups and Swift files:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/Screenshot-2022-03-21-at-8.16.02-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now close your Xcode project. Open up the terminal and move to your project directory. Here we need to add <a target="_blank" href="https://cocoapods.org/pods/SDWebImage">SD WebImage</a> Cocoa Pods to asynchronously download and cache the movie poster images.</p>
<p>Type the following command in the terminal:</p>
<pre><code>pod init
</code></pre><p>Now when you list the contents of the directory, you can see that there is a new Podfile. Open the file using any text editor (here I've used Vim). Edit your Podfile so that it looks similar to the below image. Save and close the Podfile.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/Screenshot-2022-03-21-at-8.20.28-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that we have specified the SD WebImage, we can install the dependencies by running the below command:</p>
<pre><code>pod install
</code></pre><p>As you can see, we have successfully added the SD WebImage pod in our iOS project. Now run the below command to open our project in Xcode.</p>
<pre><code>open PROJECT_NAME.xcworkspace
</code></pre><p>After opening Xcode, make sure you build your project by hitting Command+B.</p>
<h2 id="heading-how-to-design-the-user-interface-using-uikit-and-programmatic-ui"><strong>How to Design the User Interface using UIKit and Programmatic UI</strong></h2>
<p>Our app needs three UIElements navigation bars to hold the search bar, UISearchBarController for actual search, and a UICollectionView to display the search results.</p>
<p>Open up your Scenedelegate.swift file and add the following code inside it which will connect to the session method:</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">scene</span><span class="hljs-params">(<span class="hljs-number">_</span> scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)</span></span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> scene = (scene <span class="hljs-keyword">as</span>? <span class="hljs-type">UIWindowScene</span>) <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
        window = <span class="hljs-type">UIWindow</span>(windowScene: scene)       window?.rootViewController=<span class="hljs-type">UINavigationController</span>(rootViewController:<span class="hljs-type">HomeVC</span>())
        window?.makeKeyAndVisible()
    }
</code></pre>
<p>Since we are using a programatic UI, first we need to mention our Root View controller – that is, the first screen which will be displayed when the user launches the app. </p>
<p>Here in this app we're using only one View Controller, so we wrap it inside a UINavigationController. This provides a navigation bar where we can place our UISearchController.</p>
<p>Open up the HomeVC.swift file and add the following properties:</p>
<pre><code class="lang-swift"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> <span class="hljs-type">SearchBar</span>: <span class="hljs-type">UISearchController</span> = {
        <span class="hljs-keyword">let</span> sb = <span class="hljs-type">UISearchController</span>()
        sb.searchBar.placeholder = <span class="hljs-string">"Enter the movie name"</span>
        sb.searchBar.searchBarStyle = .minimal
        <span class="hljs-keyword">return</span> sb
    }()

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> <span class="hljs-type">MovieCollectionView</span>: <span class="hljs-type">UICollectionView</span> = {
        <span class="hljs-keyword">let</span> layout = <span class="hljs-type">UICollectionViewFlowLayout</span>()
        layout.scrollDirection = .vertical
        layout.itemSize = <span class="hljs-type">CGSize</span>(width: <span class="hljs-type">UIScreen</span>.main.bounds.width/<span class="hljs-number">3</span> - <span class="hljs-number">10</span>, height: <span class="hljs-number">200</span>)
        <span class="hljs-keyword">let</span> cv = <span class="hljs-type">UICollectionView</span>(frame: .zero, collectionViewLayout: layout)
        cv.register(<span class="hljs-type">MovieCell</span>.<span class="hljs-keyword">self</span>, forCellWithReuseIdentifier: <span class="hljs-type">MovieCell</span>.<span class="hljs-type">ID</span>)
        <span class="hljs-keyword">return</span> cv
    }()
</code></pre>
<p>First we create our UISearchController and configure its properties such as placeholder text and style. </p>
<p>Then we create a UICollectionView and specify the type of layout our collection view should use. In this case it is UICollectionViewFlowLayout and other properties such as scroll direction, item size, and specifying a custom CollectionView cell class which we will create later in our project.</p>
<p>Inside the HomeVC class create a new function and add the following code to configure auto-layout constraints programmatically for our UICollectionView:</p>
<pre><code class="lang-swift">    <span class="hljs-comment">//MARK: - HELPERS</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">configureUI</span><span class="hljs-params">()</span></span>{
        <span class="hljs-type">MovieCollectionView</span>.translatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>
        <span class="hljs-type">MovieCollectionView</span>.topAnchor.constraint(equalTo: view.topAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MovieCollectionView</span>.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MovieCollectionView</span>.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MovieCollectionView</span>.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = <span class="hljs-literal">true</span>
    }
</code></pre>
<p>First we say that we don't need to convert the auto resizing mask into the constraints. Then we pin our collection view to all four sides of our View Controller.</p>
<p>Inside the <code>viewDidLoad()</code> method add the following lines of code:</p>
<pre><code class="lang-swift">  <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">viewDidLoad</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">super</span>.viewDidLoad()

        navigationItem.title  = <span class="hljs-string">"Movie Search"</span>
        view.backgroundColor = .systemBackground
        <span class="hljs-type">SearchBar</span>.searchResultsUpdater = <span class="hljs-keyword">self</span>
        navigationItem.searchController = <span class="hljs-type">SearchBar</span>
        view.addSubview(<span class="hljs-type">MovieCollectionView</span>)
        <span class="hljs-type">MovieCollectionView</span>.delegate = <span class="hljs-keyword">self</span>
        <span class="hljs-type">MovieCollectionView</span>.dataSource = <span class="hljs-keyword">self</span>
        configureUI()
    }
</code></pre>
<p>Here we first specify the title for our ViewController followed by the background color which is the systemBackground color. If the device is in light mode it shows a white background. If it is in dark mode, then it shows a dark background. </p>
<p>Then we set current the view controller as the search result updater, then add our SearchController to the navigation bar, add the UICollectionView to the ViewController, and setup delegate and datasource. Finally we pin the UICollectionView by using auto-layout.</p>
<p>Create an extension for HomeVC and implement the UISearchResultsUpdating protocol and its stub method updateSearchResults.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">HomeVC</span>: <span class="hljs-title">UISearchResultsUpdating</span></span>{

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateSearchResults</span><span class="hljs-params">(<span class="hljs-keyword">for</span> searchController: UISearchController)</span></span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> query = searchController.searchBar.text <span class="hljs-keyword">else</span>{<span class="hljs-keyword">return</span>}

        }

    }


}
</code></pre>
<p>The <code>updateSearchResults()</code> method will be called whenever the text entered in the search bar changes or when the user taps the search button on their keyboard.</p>
<p>Next we need to create that custom UICollectionView cell. Inside the MovieCell.swift file, add the following code:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> UIKit
<span class="hljs-keyword">import</span> SDWebImage

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MovieCell</span>: <span class="hljs-title">UICollectionViewCell</span></span>{

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> <span class="hljs-type">ID</span> = <span class="hljs-string">"MovieCell"</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> <span class="hljs-type">MoviePosterImageView</span>: <span class="hljs-type">UIImageView</span> = {
        <span class="hljs-keyword">let</span> imageView = <span class="hljs-type">UIImageView</span>()
        imageView.contentMode = .scaleAspectFit
      <span class="hljs-comment">//  imageView.image = UIImage(systemName: "house")</span>
        <span class="hljs-keyword">return</span> imageView
    }()

    <span class="hljs-keyword">override</span> <span class="hljs-keyword">init</span>(frame: <span class="hljs-type">CGRect</span>) {
        <span class="hljs-keyword">super</span>.<span class="hljs-keyword">init</span>(frame: frame)
        addSubview(<span class="hljs-type">MoviePosterImageView</span>)
        configureUI()
    }

    <span class="hljs-keyword">required</span> <span class="hljs-keyword">init</span>?(coder: <span class="hljs-type">NSCoder</span>) {
        <span class="hljs-built_in">fatalError</span>(<span class="hljs-string">"init(coder:) has not been implemented"</span>)
    }

}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">MovieCell</span></span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">configureUI</span><span class="hljs-params">()</span></span>{
        <span class="hljs-type">MoviePosterImageView</span>.translatesAutoresizingMaskIntoConstraints = <span class="hljs-literal">false</span>
        <span class="hljs-type">MoviePosterImageView</span>.topAnchor.constraint(equalTo: topAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MoviePosterImageView</span>.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MoviePosterImageView</span>.leftAnchor.constraint(equalTo: leftAnchor).isActive = <span class="hljs-literal">true</span>
        <span class="hljs-type">MoviePosterImageView</span>.rightAnchor.constraint(equalTo: rightAnchor).isActive = <span class="hljs-literal">true</span>
    }
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateCell</span><span class="hljs-params">(posterURL: String?)</span></span>{
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> posterURL = posterURL {
            <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> <span class="hljs-type">CompleteURL</span> = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://image.tmdb.org/t/p/w500/\(posterURL)"</span>) <span class="hljs-keyword">else</span> {<span class="hljs-keyword">return</span>}
            <span class="hljs-keyword">self</span>.<span class="hljs-type">MoviePosterImageView</span>.sd_setImage(with: <span class="hljs-type">CompleteURL</span>)
        }

    }
}
</code></pre>
<p>Here we create our custom collection view cell by sub-classing the UICollectionView class and implementing the <code>init()</code> functions. </p>
<p>We create a UIImageView to display the movie poster image and setup auto-layout constraints for it. Then we create a user defined function which takes the Movie poster URL string a parameter and downloads it asynchronously without affecting the UI Thread/Main thread. It does this by using the SD WebImage CocoaPod which we added earlier.</p>
<h2 id="heading-how-to-set-up-our-api">How to Set Up Our API</h2>
<p>Before moving on, you'll need to get your API key for the <a target="_blank" href="https://www.themoviedb.org/">TMDB</a> API by creating an account (it's free). We are going to use the Movie Search end point of the API which takes the API Key and movie name as parameters.</p>
<pre><code class="lang-url">https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&amp;query=batman
</code></pre>
<p>You can examine the API response by running it in Postman.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/Screenshot-2022-03-22-at-8.52.57-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-create-a-model-for-the-api-response">How to Create a Model for the API Response</h2>
<p>Now we get a JSON response from the API. We need to decode them to Swift which we can do by creating a model struct that implements the Codable protocol.</p>
<p>WE can generate the model struct for our JSON response easily by using JSON to Swift websites. Here is the model code for the API response – you can just copy and paste it inside the Model.swift file:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">TrendingTitleResponse</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> results: [<span class="hljs-type">Title</span>]
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Title</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">let</span> media_type: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> original_name: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> original_title: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> poster_path: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> overview: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> vote_count: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">let</span> release_date: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> vote_average: <span class="hljs-type">Double</span>
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">YoutubeSearchResponse</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> items: [<span class="hljs-type">VideoElement</span>]
}


<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">VideoElement</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> id: <span class="hljs-type">IdVideoElement</span>
}


<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">IdVideoElement</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> kind: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> videoId: <span class="hljs-type">String</span>
}
</code></pre>
<h2 id="heading-how-to-perform-http-requests-using-swift">How to Perform  HTTP Requests Using Swift</h2>
<p>Now we need to write some Swift code to perform HTTP GET requests which return the JSON response of the API. </p>
<p>Swift provides a URLSession class which makes it easier to write networking code without needing any third party libraries like AFNetworking, AlamoFire, and so on.</p>
<p>Open APIService.swift and add the following code:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">APIService</span></span>{
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> shared = <span class="hljs-type">APIService</span>()
    <span class="hljs-keyword">let</span> session = <span class="hljs-type">URLSession</span>(configuration: .<span class="hljs-keyword">default</span>)

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getMovies</span><span class="hljs-params">(<span class="hljs-keyword">for</span> Query: String,completion:@escaping<span class="hljs-params">([Title]?,Error?)</span></span></span>-&gt;<span class="hljs-type">Void</span>){
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> <span class="hljs-type">FormatedQuery</span> = <span class="hljs-type">Query</span>.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)  <span class="hljs-keyword">else</span>{<span class="hljs-keyword">return</span>}

        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span>  <span class="hljs-type">SEARCH_URL</span> = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://api.themoviedb.org/3/search/movie?api_key=API_KEY_HERE&amp;query=\(FormatedQuery)"</span>) <span class="hljs-keyword">else</span> {<span class="hljs-built_in">print</span>(<span class="hljs-string">"INVALID"</span>)
            <span class="hljs-keyword">return</span>}



        <span class="hljs-keyword">let</span> task = session.dataTask(with: <span class="hljs-type">SEARCH_URL</span>) { data, response, error <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> error = error {
                <span class="hljs-built_in">print</span>(error.localizedDescription)
                completion(<span class="hljs-literal">nil</span>,error)
            }
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> data = data {
                <span class="hljs-keyword">do</span>{
                    <span class="hljs-keyword">let</span> decodedData = <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">TrendingTitleResponse</span>.<span class="hljs-keyword">self</span>, from: data)
                 <span class="hljs-comment">//   print(decodedData)</span>
                    completion(decodedData.results,<span class="hljs-literal">nil</span>)
                }
                <span class="hljs-keyword">catch</span>{
                    <span class="hljs-built_in">print</span>(error)
                }
            }
        }
        task.resume()
    }
}
</code></pre>
<p>Here we created a class named API Service with the singleton pattern so we need an instance for this class as a static member of the class. Then we created a session for our networking task with the default configuration, followed by a user defined method getMovies(). </p>
<p>Then we created our networking task – in this case we need to perform HTTP GET requests which can be performed using the <code>dataTask()</code> method of the URLSession class. It takes the URL as a parameter and gives a completion handler which contains data returned from the API, error data if any error has occurred, and a response which has HTTP Response information such as status codes and their corresponding messages. </p>
<p>If there is any error, then we escape out of this function with error data. If not, then we decode our JSON data based on our Swift Model and escape out of this function with the decoded data.</p>
<h2 id="heading-how-to-display-the-search-results-on-the-uicollectionview">How to Display the Search Results on the UICollectionView</h2>
<p>In HomeVC.swift, create a private property which is an array of Title objects. These will hold each movie's info returned by the API.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> <span class="hljs-type">Movies</span> = [<span class="hljs-type">Title</span>]()
</code></pre>
<p>In HomeVC.swift create an extension for the HomeVC class and implement the UIColletionViewDelegate and UICollectionViewDatasource protocols. Then implement numberOfItemsInSection (which is equal to the number of movies returned by the API) and cellForItemAt (which actually populates the cell with API responses, like download and set the poster image).</p>
<pre><code class="lang-swift"> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">collectionView</span><span class="hljs-params">(<span class="hljs-number">_</span> collectionView: UICollectionView, numberOfItemsInSection section: Int)</span></span> -&gt; <span class="hljs-type">Int</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">Movies</span>.<span class="hljs-built_in">count</span>
    }
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">collectionView</span><span class="hljs-params">(<span class="hljs-number">_</span> collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)</span></span> -&gt; <span class="hljs-type">UICollectionViewCell</span> {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> cell = collectionView.dequeueReusableCell(withReuseIdentifier: <span class="hljs-type">MovieCell</span>.<span class="hljs-type">ID</span>, <span class="hljs-keyword">for</span>: indexPath) <span class="hljs-keyword">as</span>? <span class="hljs-type">MovieCell</span>{
           <span class="hljs-comment">// cell.backgroundColor = .systemBackground</span>

            cell.updateCell(posterURL: <span class="hljs-type">Movies</span>[indexPath.row].poster_path)
            <span class="hljs-keyword">return</span> cell
        }
        <span class="hljs-keyword">return</span> <span class="hljs-type">UICollectionViewCell</span>()

    }
</code></pre>
<p>Finally we need to make actual API call which we do inside the <code>updateSearchResults()</code> delegate method which we have implemented previously. Inside that method add the following code:</p>
<pre><code class="lang-swift">    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateSearchResults</span><span class="hljs-params">(<span class="hljs-keyword">for</span> searchController: UISearchController)</span></span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> query = searchController.searchBar.text <span class="hljs-keyword">else</span>{<span class="hljs-keyword">return</span>}
        <span class="hljs-type">APIService</span>.shared.getMovies(<span class="hljs-keyword">for</span>:query.trimmingCharacters(<span class="hljs-keyword">in</span>: .whitespaces)) { titles, error <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> titles = titles {
                <span class="hljs-keyword">self</span>.<span class="hljs-type">Movies</span> = titles
                <span class="hljs-type">DispatchQueue</span>.main.async {
                    <span class="hljs-keyword">self</span>.<span class="hljs-type">MovieCollectionView</span>.reloadData()
                }

            }
        }

    }
</code></pre>
<p>Here, whenever the user types in the search bar or presses the search button, we make an HTTP GET request to fetch a movie (based on the name entered in the search bar). Then we reload the CollectionView which updates the collection view cells with the movie poster. </p>
<p>Note that we need to do this in the Main Thread/UI Thread, because by default iOS automatically makes HTTP request in background thread. This means that we need to use UI/Main Thread to update our UI elements. </p>
<p>Now run your app in the simulator to see the result:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/03/ezgif.com-gif-maker--3-.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Congratulations! You have learned to use UISearchController in iOS apps.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Make a Simple Async GET REST API call in SwiftUI ]]>
                </title>
                <description>
                    <![CDATA[ By Saamer Mansoor In this tutorial for beginners, you will learn the basics of using SwiftUI to make API calls using the popular Internet Chuck Norris DataBase (ICNDB) as an example. It will display a joke quickly and easily using Swift and SwiftUI. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/make-rest-api-call-in-swiftui-in-2-minutes/</link>
                <guid isPermaLink="false">66d460c7d7a4e35e384349ab</guid>
                
                    <category>
                        <![CDATA[ Apple ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SwiftUI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 02 Dec 2021 18:32:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/Frame-12-3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Saamer Mansoor</p>
<p>In this tutorial for beginners, you will learn the basics of using SwiftUI to make API calls using the popular Internet Chuck Norris DataBase (ICNDB) as an example. It will display a joke quickly and easily using Swift and SwiftUI. </p>
<p>You'll see how the cross-platform framework SwiftUI lets us use the exact same code across iOS, iPadOS, macOS, watchOS, App Clips and tvOS, which otherwise would have been impossible.</p>
<p>Along with that, you will use <a target="_blank" href="https://developer.apple.com/documentation/swift/swift_standard_library/concurrency/updating_an_app_to_use_swift_concurrency">async-await</a> that was introduced in Swift 5.5, which works for newer operating systems including iPhones running iOS &gt; v15.0. This really simplifies our work of making data network calls asynchronously on click of a button without freezing the UI thread. </p>
<p>I will share the code changes you'll need to make first. Then in the following section, I will share a brief analysis of the code so beginners can understand what's going on as well.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Group-1-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>tvOS app running the code displays a button that retrieves the joke on click</em></p>
<h2 id="heading-how-to-make-api-calls-in-swift-and-swiftui">How to Make API Calls in Swift and SwiftUI</h2>
<p>First, you'll need a Mac to install Xcode. Once it's installed, open Xcode and create a new project. Then select "App" for iOS, macOS, tvOS, or watchOS.</p>
<h3 id="heading-contentview">ContentView</h3>
<p>Just update your existing ContentView SwiftUI file to add a Button and use the <em>State</em> variable to refresh the text displayed as the joke returns from ICNDB API:</p>
<pre><code class="lang-swiftui">import Foundation
import SwiftUI
struct ContentView: View {
    @State private var joke: String = ""
    var body: some View {
        Text(joke)
        Button {
            Task {
                let (data, _) = try await URLSession.shared.data(from: URL(string:"https://api.chucknorris.io/jokes/random")!)
                let decodedResponse = try? JSONDecoder().decode(Joke.self, from: data)
                joke = decodedResponse?.value ?? ""
            }
        } label: {
            Text("Fetch Joke")
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
struct Joke: Codable {
    let value: String
}
</code></pre>
<h3 id="heading-fetch-a-joke">Fetch a joke!</h3>
<p>If you press build/play, the app will build in whatever platform you selected above:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Screen-Shot-2021-12-01-at-4.42.11-AM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshots of watchOS, macOS, and iOS apps running the same exact code</em></p>
<h2 id="heading-code-analysis">Code Analysis</h2>
<p>If you go to the random joke URL, you'll notice that the data is in JSON format. You can copy that and use a JSON Linter to view its structure to figure out what property of the Joke object is needed. </p>
<p>Based on that, you determine the code above. You use the Codable protocol (aka interfaces) to go from a JSON data object to an actual Swift class or struct, and you create properties for the data you want to store (value in our case). </p>
<p>JSONDecoder helps us parse the JSON string using the Codable object. This works regardless of platform because the page that loads on launching the app has the same name <em>ContentView</em> regardless of platform.</p>
<h3 id="heading-app-clips">App Clips</h3>
<p><a target="_blank" href="https://developer.apple.com/app-clips/">App Clips</a> are Apple's latest way of using native app functionality using an "App Clip Code" without having to download the whole application from the App Store.</p>
<p>App Clips work similar to an iOS app – the only difference is that you don't create a new App Clip project. You just need to add the App Clip as a target to an existing iOS app by going to File-&gt;New-&gt;Target-&gt;iOS-&gt;App Clip when an existing iOS app is open in Xcode. </p>
<p>If you you wondering about iPhone/iPad <a target="_blank" href="https://support.apple.com/en-us/HT207122">Widgets</a>, well they don't animate. So button clicks will just open the corresponding app and can't update text through an external API independently. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned how to make RESTful GET API calls from SwiftUI in the simplest possible way! </p>
<p>Feel free to reach out to me if you have any questions. I figured this out using another article and I thought of simplifying it further. So for more details and ways to make this code more complex, check out that article:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.raywenderlich.com/25013447-async-await-in-swiftui">https://www.raywenderlich.com/25013447-async-await-in-swiftui</a></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ iPhone Downloads Folder – Where are my Downloads? iOS and iPad ]]>
                </title>
                <description>
                    <![CDATA[ iPhone and iPad both have an app called Files where you can locate all of your files from services including iCloud Drive, Dropbox, and more.  If you want to access the Downloads folder, you can find it inside the Files app. In this article, I will s... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/iphone-downloads-folder-where-are-my-downloads-ios-and-ipad/</link>
                <guid isPermaLink="false">66b8d9d3167e676eda514f6b</guid>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iphone ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jessica Wilkins ]]>
                </dc:creator>
                <pubDate>Thu, 14 Oct 2021 21:59:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/10/kevin-bhagat-Co-usQ-kpO0-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>iPhone and iPad both have an app called Files where you can locate all of your files from services including iCloud Drive, Dropbox, and more. </p>
<p>If you want to access the Downloads folder, you can find it inside the Files app. In this article, I will show you how to access the Files app and the Downloads folder. </p>
<h2 id="heading-how-to-locate-the-files-app-on-iphone-and-ipad">How to locate the Files App on iPhone and iPad</h2>
<p>If you have an iPhone and iPad running iOS 11 or later, the Files app is automatically loaded into your device.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_620DBD8F833B-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can also use the search feature to find the Files app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_43A9B3862235-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-locate-the-downloads-folder-on-iphone-and-ipad">How to locate the Downloads folder on iPhone and iPad</h2>
<p>Step 1: Open the Files app</p>
<p>Step 2: Click on the Browse </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_B02829DF17F7-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Step 3: Click On My iPhone under Locations</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_3475EB49148B-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Step 4: Click on the Downloads folder</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_243C9B5BA317-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then you can see all of your downloaded files.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_AE67A8A7D212-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you hold your finger down on the file you want to select, then you can see a list of available options. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/IMG_29103C3000C3-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now you can copy, delete, share, rename, and perform many other options on those files.</p>
<p>Thanks for reading, and happy browsing!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Delete Albums on iPhone ]]>
                </title>
                <description>
                    <![CDATA[ If you want to delete a photo album on an iPhone, it only takes a few simple steps.  In this short article, I will walk you through the six steps to delete an album from your iPhone.  Step 1: Go to the Photos app on your phone First, ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-delete-albums-on-iphone/</link>
                <guid isPermaLink="false">66b8d97ff805ffd579552e8a</guid>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iphone ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jessica Wilkins ]]>
                </dc:creator>
                <pubDate>Wed, 01 Sep 2021 17:53:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/arnel-hasanovic-4oWSXdeAS2g-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you want to delete a photo album on an iPhone, it only takes a few simple steps. </p>
<p>In this short article, I will walk you through the six steps to delete an album from your iPhone. </p>
<h3 id="heading-step-1-go-to-the-photos-app-on-your-phone">Step 1: Go to the Photos app on your phone</h3>
<p>First, you need to open the photos app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/photos-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-2-go-to-albums">Step 2: Go to Albums</h3>
<p>Next, go to the "Albums" tab in Photos.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/click-albums.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-3-click-see-all">Step 3: Click "See All"</h3>
<p>To see all your albums, you'll need to click the "See All" button located at the top right hand corner</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/click-see-all.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-4-edit-the-albums">Step 4: Edit the Albums</h3>
<p>Then you'll click on Edit located at the top right hand corner.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/click-edit.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-5-select-the-albums-you-want-to-delete">Step 5: Select the album(s) you want to delete</h3>
<p>Click on the red circled dash on the album you want to delete. This will select it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/click-red-dash.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-6-delete-the-albums">Step 6: Delete the album(s)</h3>
<p>Click the red Delete Album text and the album will be deleted.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/confirm-delete.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>It is important to note that if you delete an album it does not delete the photos from your phone. </p>
<p>If you want to delete a photo, then you will need to choose the photo from the library and click on the trash can icon on the bottom right hand corner. </p>
<h2 id="heading-why-cant-i-delete-all-of-the-albums-from-my-phone">Why can't I delete all of the albums from my phone?</h2>
<p>There are certain albums on the iPhone that you cannot remove. If you followed the steps above, you will notice that there is no red dash above the Recents or Favorites albums.</p>
<p>Those are the default albums that already come on the iPhone. Other default albums like Screenshots and Selfies cannot be deleted, either. </p>
<p>You can only delete albums that you have created on your phone yourself. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add the Realm Database to an iOS CRUD App using Swift ]]>
                </title>
                <description>
                    <![CDATA[ By Sai Balaji K Hello, everyone! In this article we are going to learn how to add the Realm database to an iOS app.  We'll create a simple ToDo app so you can learn how to perform CRUD (Create, Read, Update, Delete) operations in the Realm database. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/add-realm-database-to-ios-crud-app-with-with-swift/</link>
                <guid isPermaLink="false">66d460c5230dff0166905863</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ios app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 27 Apr 2021 18:21:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/605aa5a8687d62084bf6b611.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sai Balaji K</p>
<p>Hello, everyone! In this article we are going to learn how to add the Realm database to an iOS app. </p>
<p>We'll create a simple ToDo app so you can learn how to perform CRUD (Create, Read, Update, Delete) operations in the Realm database.</p>
<h2 id="heading-what-is-realm">What is Realm?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-8.09.31-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Realm is an open-source mobile database which is developer friendly and easy to use. You can also use it as an alternative to Core Data in iOS apps. </p>
<p>Realm is a cross platform mobile database, which means that you can use it in native Android and iOS apps and also in cross platform apps like those created using React Native. It supports Objective-C, Swift, Java, Kotlin, C#, and JavaScript.</p>
<h2 id="heading-how-to-set-up-realm-in-your-ios-project">How to Set Up Realm in Your iOS Project</h2>
<p>We can add Realm to our iOS project using SPM (the Swift Package Manager), Cocoa Pods, or Carthage. Here, we are going to use Cocoa Pods to add Realm Pod to our iOS project.</p>
<ol>
<li>Open up Xcode and create a blank iOS app project with UIKit and Swift without using Core Data.</li>
<li>Now close Xcode and open up the terminal. Navigate to your project directory using the terminal.</li>
<li>Run the following command to create a PodFile.</li>
</ol>
<pre><code class="lang-command">pod init
</code></pre>
<ol start="4">
<li>Now when you list the contents of the directory you can see that there is a new Podfile. Open the file using any text editor (here I've used Vim). Edit your Podfile so that it looks similar to the below image. Save and close the Podfile.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-8.23.07-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that we have specified the dependence for Realm DB, we can install the dependencies by running the below command:</p>
<pre><code class="lang-command">pod install
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-8.26.21-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As you can see, we have successfully added the Realm DB dependency in our iOS project. Now run the below command to open our project in Xcode.</p>
<pre><code class="lang-command">open YOUR_APP_NAME.xcworkspace
</code></pre>
<p>Note: after opening Xcode, make sure you build your project by pressing Command+B.</p>
<h2 id="heading-how-to-design-your-user-interface-in-realm">How to Design Your User Interface in Realm</h2>
<p>We are going to keep our app's UI simple. Open up Main.storyboard and create a simple UI as shown below by adding a table view with a prototype cell. Then embed a navigation controller and create the IBOutlets for the tableview in the ViewController.swift file:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-8.32.42-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-create-a-data-model-in-realm">How to Create a Data Model in Realm</h2>
<p>In our ToDo app, each task has a task name and a task id. We are going to create a Model class to represent the todo task. In the project navigator right click and create a new Swift file, and add the below code.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> Foundation
<span class="hljs-keyword">import</span> RealmSwift


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ToDoTask</span>:<span class="hljs-title">Object</span>
</span>{
    <span class="hljs-meta">@objc</span> <span class="hljs-keyword">dynamic</span> <span class="hljs-keyword">var</span> tasknote: <span class="hljs-type">String?</span>
    <span class="hljs-meta">@objc</span> <span class="hljs-keyword">dynamic</span> <span class="hljs-keyword">var</span> taskid: <span class="hljs-type">String?</span>
}
</code></pre>
<p>Her we created our model class named ToDoTask. It inherits the Object class which is a class that comes with RealmDB. This class handles all the under the hood processes of saving the data created using this model class in the database.</p>
<p>We also added two properties: <code>tasknote</code>, which is the task to be done, and <code>taskid</code> – both of type string. <code>@objc</code> means that your Swift code is visible to Objective C and <code>dynamic</code> means you want to use Objective C dynamic dispatch.</p>
<h2 id="heading-basic-crud-app-functions">Basic CRUD App functions</h2>
<p>Our app will perform the following functions:</p>
<ol>
<li>Get input from the user using AlertViewController.</li>
<li>Add the input to the database and also to the table view.</li>
<li>Allow the user to edit their input.</li>
<li>Swipe to delete a row to remove the data from both the table view and the database.</li>
<li>Fetch all the data (if present) from the database and display it in the table view.</li>
</ol>
<h3 id="heading-how-to-get-input-from-the-user-using-alertviewcontroller">How to get input from the user using AlertViewController</h3>
<p>Open up <code>ViewController.swift</code> and add the below code inside the <code>ViewDidLoad()</code> method. And create a new function called <code>addTask()</code> and add the code to display the alert view controller with a text box to get input from the user. </p>
<p>Now when the right bar button is pressed it will call the <code>addTask()</code> function which will display the <code>alertviewcontroller</code> with a text field to get user input.</p>
<pre><code class="lang-swift">navigationItem.rightBarButtonItem = <span class="hljs-type">UIBarButtonItem</span>(image: .add, style: .done, target: <span class="hljs-keyword">self</span>, action: #selector(addTask))

navigationController?.navigationBar.prefersLargeTitles = <span class="hljs-literal">true</span>

title = <span class="hljs-string">"RealmDB"</span>
</code></pre>
<pre><code class="lang-swift"><span class="hljs-meta">@objc</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">addTask</span><span class="hljs-params">()</span></span>
    { 
        <span class="hljs-keyword">let</span> ac = <span class="hljs-type">UIAlertController</span>(title: <span class="hljs-string">"Add Note"</span>, message: <span class="hljs-literal">nil</span>, preferredStyle: .alert)

        ac.addTextField(configurationHandler: .<span class="hljs-keyword">none</span>)

        ac.addAction(<span class="hljs-type">UIAlertAction</span>(title: <span class="hljs-string">"Add"</span>, style: .<span class="hljs-keyword">default</span>, handler: { (<span class="hljs-type">UIAlertAction</span>) <span class="hljs-keyword">in</span>

              <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> text = ac.textFields?.first?.text
            {
                <span class="hljs-built_in">print</span>(text)
            }

        }))
        ac.addAction(<span class="hljs-type">UIAlertAction</span>(title: <span class="hljs-string">"Cancel"</span>, style: .cancel, handler: <span class="hljs-literal">nil</span>))
        present(ac, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }
</code></pre>
<h3 id="heading-how-to-add-the-input-to-the-database-and-table-view">How to add the input to the database and table view</h3>
<p>To save data into Realm, first we need obtain an instance for Realm through which we can access all the methods needed for CRUD operations. Create a property of type Realm in the <code>ViewController.swift</code> file and initialise it in the <code>viewDidLoad()</code> method.</p>
<pre><code class="lang-swift"> <span class="hljs-keyword">var</span> realmDB: <span class="hljs-type">Realm!</span>
</code></pre>
<pre><code class="lang-swift"> <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">viewDidLoad</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">super</span>.viewDidLoad()

        navigationItem.rightBarButtonItem = <span class="hljs-type">UIBarButtonItem</span>(image: .add, style: .done, target: <span class="hljs-keyword">self</span>, action: #selector(addTask))
        navigationController?.navigationBar.prefersLargeTitles = <span class="hljs-literal">true</span>
        title = <span class="hljs-string">"RealmDB"</span>

        realmDB = <span class="hljs-keyword">try</span>! <span class="hljs-type">Realm</span>()

    }
</code></pre>
<p>Create an empty array of type our DataModel (ToDoTask). This array will hold all the tasks which need to be added to the table view and database. </p>
<p>Now inside the <code>addTask()</code> function modify the Add action closure so that it gets the user input and creates a random ID for that input. Then append it to our array and save it to the database.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> tasks = [<span class="hljs-type">ToDoTask</span>]()
</code></pre>
<pre><code class="lang-swift"> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> text = ac.textFields?.first?.text
            {
                <span class="hljs-comment">//Add data to data model array</span>
                <span class="hljs-keyword">let</span> t = <span class="hljs-type">ToDoTask</span>()
                t.taskid = <span class="hljs-type">UUID</span>().uuidString
                t.tasknote = text
                <span class="hljs-keyword">self</span>.tasks.append(t)

                <span class="hljs-comment">//Add data to database</span>
                <span class="hljs-keyword">try</span>! <span class="hljs-keyword">self</span>.realmDB.write {
                    <span class="hljs-keyword">self</span>.realmDB.add(t)
                }
                <span class="hljs-comment">//Update table view UI</span>
                <span class="hljs-keyword">self</span>.tasktv.reloadData()
            }
</code></pre>
<p>Now when you run the app, the data will be saved in the database. But it will not show in table view because we have not implemented the delegate methods. </p>
<p>Make the ViewController class implement the <code>UITableViewDelegate</code> and <code>UITableViewDataSource</code> protocols and add the protocol stubs. </p>
<p>Now inside the <code>numberOfRowsInSection</code> method, return the count of our tasks array which gives the number of rows to be added to the table view. This is equal to the number of elements in tasks array.</p>
<pre><code class="lang-swift"> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">tableView</span><span class="hljs-params">(<span class="hljs-number">_</span> tableView: UITableView, numberOfRowsInSection section: 
 Int)</span></span> -&gt; <span class="hljs-type">Int</span> 
 {
        <span class="hljs-keyword">return</span> tasks.<span class="hljs-built_in">count</span>;
 }
</code></pre>
<p>The next thing we need to do is specify the content in each row. We can do this using the <code>cellForRowAt</code> delegate method. Here we dequeue a cell using the identifier which we have mentioned in storyboard and specify the label text as the tasks array element tasknote property.</p>
<pre><code class="lang-swift"> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">tableView</span><span class="hljs-params">(<span class="hljs-number">_</span> tableView: UITableView, cellForRowAt indexPath: IndexPath)</span></span> -&gt; <span class="hljs-type">UITableViewCell</span> {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> cell = tableView.dequeueReusableCell(withIdentifier: <span class="hljs-string">"cell"</span>)
        {
            cell.textLabel?.text = tasks[indexPath.row].tasknote
            <span class="hljs-keyword">return</span> cell
        }
        <span class="hljs-keyword">return</span> <span class="hljs-type">UITableViewCell</span>()
    }
</code></pre>
<h3 id="heading-how-to-allow-users-to-edit-their-input">How to allow users to edit their input</h3>
<p>Now we have to allow the user to edit their entered tasks and update the changes in both the database and UI. We can do this using similar methods of getting input from the user. Implement the <code>didSelectRowAt</code> delegate method which will be called when user taps the table view row. </p>
<p>Add the below code which displays an <code>AlertViewController</code> with a text view. Then update the content of the cell with the entered text, and at the same time update the database contents.</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">tableView</span><span class="hljs-params">(<span class="hljs-number">_</span> tableView: UITableView, didSelectRowAt indexPath: IndexPath)</span></span> {

        <span class="hljs-keyword">let</span> tasktomodify = tasks[indexPath.row]
        <span class="hljs-keyword">let</span> ac = <span class="hljs-type">UIAlertController</span>(title: <span class="hljs-string">"Update task"</span>, message: <span class="hljs-literal">nil</span>, preferredStyle: .alert)

        ac.addTextField(configurationHandler: .<span class="hljs-keyword">none</span>)
        ac.addAction(<span class="hljs-type">UIAlertAction</span>(title: <span class="hljs-string">"Ok"</span>, style: .<span class="hljs-keyword">default</span>, handler: { (<span class="hljs-type">UIAlertAction</span>) <span class="hljs-keyword">in</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> text = ac.textFields?.first?.text
            {
                <span class="hljs-keyword">if</span>(!text.isEmpty)
                {
                <span class="hljs-keyword">try</span>! <span class="hljs-keyword">self</span>.realmDB.write({
                    tasktomodify.tasknote = text
                })
                <span class="hljs-keyword">self</span>.tasktv.reloadData()
                }
            }
        }))

        present(ac, animated: <span class="hljs-literal">true</span>, completion: <span class="hljs-literal">nil</span>)
    }
</code></pre>
<h3 id="heading-how-to-swipe-to-delete-a-row-amp-remove-the-data-from-both-the-table-view-and-database">How to swipe to delete a row &amp; remove the data from both the table view and database</h3>
<p>Here we are going to implement a swipe to delete feature in our table view so that users can delete their tasks. But under the hood when the user swipe deletes a table view row then it should delete the data from database, data model array, and update the UI of the table view. </p>
<p>We can do this by implementing the <strong>commit editingStyle</strong> delegate method and adding the following code:</p>
<pre><code class="lang-swift"> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">tableView</span><span class="hljs-params">(<span class="hljs-number">_</span> tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)</span></span> {
        <span class="hljs-keyword">if</span> editingStyle == .delete
        {
            <span class="hljs-keyword">let</span> tasktoDelete = tasks[indexPath.row]
            <span class="hljs-keyword">try</span>! realmDB.write({
                realmDB.delete(tasktoDelete)
                <span class="hljs-keyword">self</span>.tasks.remove(at: indexPath.row)
                <span class="hljs-keyword">self</span>.tasktv.deleteRows(at: [indexPath], with: .fade)
            })
        }
    }
</code></pre>
<h3 id="heading-how-to-fetch-all-the-data-if-present-from-the-database-and-display-it-in-the-table-view">How to fetch all the data (if present) from the database and display it in the table view</h3>
<p>Now we are going to implement our last operation, Read. Whenever the user launches the app, it should fetch data from the database (if data is present) and display it in the table view. </p>
<p>We can do this by creating a function <code>getTodo</code> in the view controller swift file and adding the following code inside it:</p>
<pre><code class="lang-swift"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">getTodos</span><span class="hljs-params">()</span></span>
    {
       <span class="hljs-comment">//Get all the data from the database</span>
        <span class="hljs-keyword">let</span> notes = realmDB.objects(<span class="hljs-type">ToDoTask</span>.<span class="hljs-keyword">self</span>)

        <span class="hljs-comment">//Clear the model data array to prevent duplicates</span>
        <span class="hljs-keyword">self</span>.tasks.removeAll()

        <span class="hljs-comment">/*If the fetched data is not empty then add it to model data array and update the UI */</span>
        <span class="hljs-keyword">if</span>(!notes.isEmpty)
        {
        <span class="hljs-keyword">for</span> n <span class="hljs-keyword">in</span> notes
        {

            <span class="hljs-keyword">self</span>.tasks.append(n)

        }
            <span class="hljs-keyword">self</span>.tasktv.reloadData()
        }


    }
</code></pre>
<h2 id="heading-bonus-tip-how-to-view-your-database-content-in-an-ios-simulator">Bonus Tip: How to View Your Database Content in an iOS Simulator</h2>
<p>Now when you run your app, you can see that it works as expected. But how can we check if the data is really stored in the database? We can use an app called MongoDB Realm Studio through which we can view our data stored in the Realm database of the simulator.</p>
<blockquote>
<p>Note that this method works only when you test the app using iOS simulator</p>
</blockquote>
<p>In the <code>viewDidLoad()</code> method, add the below line of code which will print the real file path of our app:</p>
<pre><code class="lang-swift"> <span class="hljs-built_in">print</span>(realmDB.configuration.fileURL!)
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-9.48.59-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now copy the file path printed in the console, open up the terminal, and run the following command:</p>
<pre><code class="lang-command">open REALM_FILE_PATH_HERE
</code></pre>
<blockquote>
<p>Make sure you have downloaded MongoDB Realm Studio from the browser before running the above command.</p>
</blockquote>
<p>Now it will open the RealmFile of the app in MongoDB Realm Studio. This will display the data stored in the database in a table format. </p>
<p>If you make changes to your data by editing or deleting the task, the changes will be reflected in the MongoDB Realm Studio app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-24-at-9.55.29-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Congratulations! You have made a simple app which implements CRUD operations in iOS apps.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Operating System Definition ]]>
                </title>
                <description>
                    <![CDATA[ An operating system, or OS, is what manages all the software on your computer. Common operating systems for desktop computers are Windows, macOS, and Linux. And for mobile devices, Android and iOS are popular operating systems. When you first turn on... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/operating-system-os-definition/</link>
                <guid isPermaLink="false">66c35c4c71e87702d4e5b71a</guid>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Computers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ macOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tech Terms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 06 Apr 2021 06:32:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/60752d27776bd507fe31ecc6.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>An operating system, or OS, is what manages all the software on your computer.</p>
<p>Common operating systems for desktop computers are Windows, macOS, and Linux. And for mobile devices, Android and iOS are popular operating systems.</p>
<p>When you first turn on your computer or device, the operating system is the first thing that runs. It then manages the hardware, handles common tasks, and allocates resources that all the other software uses.</p>
<p>Before operating systems, computers were much simpler. They ran programs that were printed on a series or punch cards.</p>
<p>Like today, computers back then were often made up of very different hardware. They had different CPUs, memory, storage, and other things. So if you wanted to run your program on another computer, you had to rewrite the entire program to handle the different hardware.</p>
<p>But with an operating system, you could write your program once to run on that operating system. Then other computers or devices running the same OS could run your program.</p>
<p>For example, if you create an app for Android, the Android OS will handle common tasks like opening a window, and give your app enough memory and storage to run. Android also gives you ways to interact with hardware like the camera or touch screen.</p>
<p>Then when other people download your app, it shouldn't matter if they have a Samsung or Huawei phone – your app should work the same way on both devices.</p>
<h2 id="heading-related-tech-terms">Related Tech Terms:</h2>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/pc-definition/">PC Definition</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/macintosh-definition/">Macintosh Definition</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/software-definition/">Software Definition</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
