<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Huy Pham on Medium]]></title>
        <description><![CDATA[Stories by Huy Pham on Medium]]></description>
        <link>https://medium.com/@huypham85?source=rss-b87ab2bbc03b------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Kqn3b7Y_XAkbNriRVbjAFw@2x.jpeg</url>
            <title>Stories by Huy Pham on Medium</title>
            <link>https://medium.com/@huypham85?source=rss-b87ab2bbc03b------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 24 Apr 2026 05:22:51 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@huypham85/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Best Practices for Managing Third-Party Libraries in Multi-Module iOS Apps]]></title>
            <link>https://towardsdev.com/best-practices-for-managing-third-party-libraries-in-multi-module-ios-apps-146de53ba769?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/146de53ba769</guid>
            <category><![CDATA[modularization]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[swift-package-manager]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Tue, 22 Apr 2025 08:46:42 GMT</pubDate>
            <atom:updated>2025-04-22T12:49:35.549Z</atom:updated>
            <content:encoded><![CDATA[<h3>Table of contents</h3><p>· <a href="#78a9">1. Create a Wrapper Module for the SDK (e.g. Firebase)</a><br>· <a href="#f36a">2. Wrap SDK Logic and Use Protocols</a><br>· <a href="#fc1b">3. Other Modules Import Only the Wrapper</a><br>· <a href="#36c8">Benefits of This Pattern</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*_D6u9cIIUZFSj0JLoS2y1g.png" /></figure><p>When building a multi-module iOS app using Swift Package Manager, importing third-party libraries like Firebase directly into multiple feature modules quickly becomes messy. It leads to duplicated configurations, tight coupling, and hard-to-maintain code.</p><p>The best practice is to <strong>create a wrapper module</strong> (e.g. FirebaseModule) that imports and manages all Firebase logic in one place. Then, other modules like LoginModule, Data, etc., only depend on this wrapper — <strong>they never import the third-party library directly</strong>.</p><h3>1. Create a Wrapper Module for the SDK (e.g. Firebase)</h3><p><strong>Modules/FirebaseModule/Package.swift:</strong></p><pre>// Only in the wrapper module<br>.package(url: &quot;&lt;https://github.com/firebase/firebase-ios-sdk.git&gt;&quot;, branch: &quot;main&quot;),</pre><pre>.target(<br>  name: &quot;FirebaseModule&quot;,<br>  dependencies: [<br>    .product(name: &quot;FirebaseAuth&quot;, package: &quot;firebase-ios-sdk&quot;),<br>    .product(name: &quot;FirebaseFirestore&quot;, package: &quot;firebase-ios-sdk&quot;),<br>    .product(name: &quot;FirebaseStorage&quot;, package: &quot;firebase-ios-sdk&quot;)<br>  ]<br>)</pre><h3>2. Wrap SDK Logic and Use Protocols</h3><p><strong>CommonModule/AuthService.swift:</strong></p><pre>public protocol AuthService {<br>    func login(email: String, password: String) -&gt; AnyPublisher&lt;User, Error&gt;<br>}</pre><p><strong>FirebaseModule/FirebaseAuthService.swift:</strong></p><pre>import FirebaseAuth<br>import Combine<br><br>public class FirebaseAuthService: AuthService {<br> public init() {}<br> func login(email: String, password: String) -&gt; AnyPublisher&lt;Void, Error&gt; {<br>       Future { promise in<br>           Auth.auth().signIn(withEmail: email, password: password) { result, error in<br>               if let error = error {<br>                   promise(.failure(error))<br>               } else {<br>                   promise(.success(()))<br>               }<br>           }<br>       }.eraseToAnyPublisher()<br>   }<br>}</pre><h3>3. Other Modules Import Only the Wrapper</h3><p><strong>Modules/Data/Package.swift:</strong></p><pre>.package(path: &quot;../FirebaseModule&quot;)</pre><pre>.target(<br>  name: &quot;Data&quot;,<br>  dependencies: [&quot;FirebaseModule&quot;, &quot;CommonModule&quot;, ...]<br>)</pre><p>In your Swift code:</p><pre>import FirebaseModule<br><br>let authService: AuthService = FirebaseAuthService()</pre><h3>Benefits of This Pattern</h3><ul><li>Clean separation of concerns</li><li>No direct Firebase SDK imports in features</li><li>Easier unit testing via protocols</li><li>Centralized SDK configuration</li><li>Avoids duplication and compile issues</li></ul><p>This practice applies to <strong>any third-party SDK</strong> (Firebase, Alamofire, Realm, GRDB, etc.). Keep them wrapped in a dedicated module, expose interfaces via protocols, and let your app scale cleanly.</p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=146de53ba769" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/best-practices-for-managing-third-party-libraries-in-multi-module-ios-apps-146de53ba769">Best Practices for Managing Third-Party Libraries in Multi-Module iOS Apps</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Task Tree and Task Cancellation in Swift Concurrency: Key Concepts Explained]]></title>
            <link>https://huypham85.medium.com/task-tree-and-task-cancellation-in-swift-concurrency-key-concepts-explained-b653fc88f826?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/b653fc88f826</guid>
            <category><![CDATA[swift-concurrency]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Fri, 11 Oct 2024 18:41:49 GMT</pubDate>
            <atom:updated>2024-10-11T18:41:49.791Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/779/1*S8rFKl34XbGIE-MiilE1YA.png" /></figure><h3>Table of contents</h3><p>· <a href="#c510">Task Tree</a><br>· <a href="#7e25">Task Cancellation</a></p><h3>Task Tree</h3><p>Let’s take a look at the code below</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-0GjfPTnLmZysJyx" /></figure><p>As you can see, the function printStudentInfo() has 2 child Tasks defined using async let. Inside the child Task getClassesAndScores(), it also has 2 child Tasks getClasses() and getScores() . So we have a Task tree with 3 levels here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/441/0*ISR5A9BS2xz3oVBs" /></figure><p>Although there are 5 Tasks in the parent-child relationship here, they all run simultaneously. Because the Tasks have parent-child relationships, they call this approach Structured Concurrency.</p><p>The explicit parent-child relationships between tasks have several advantages:</p><ol><li>The parent task is complete when all of its child tasks are complete.</li></ol><p>2. When setting a higher priority on a child task, the parent task’s priority is automatically escalated.</p><p>3. When a parent task is canceled, each of its child tasks is also automatically canceled.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kppuU6woxu22-dlT" /></figure><p>4. When a child task throws an error, the error propagates to the parent task.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4-SBnFK-muMLAC7j" /></figure><h3>Task Cancellation</h3><p>What happens to other tasks when a Task is canceled?</p><p>If a Task is canceled, the cancellation state cascades down from the parent to all its children. This is called cooperative cancellation. Tasks continue their work when canceled and don’t return anything. So it’s important that we should stop running canceled Tasks, we can check this canceled state in our code and terminate processing.</p><p>There are two ways a task can do this:</p><p><strong>1. by calling the Task.checkCancellation() type method</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Rq_OB_2Dm2jNV__m" /></figure><p>Calling Task.checkCancellation() throws an error if the task is canceled, a throwing task can propagate the error out of the task, stopping all of the task’s work. This has the advantage of being simple to implement and understand</p><p><strong>2. reading the Task.isCancelled type property</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/905/0*MpzRqmho2SQJ47Dt" /></figure><p>For more flexibility, use the Task.isCancelled property, which lets you perform clean-up work as part of stopping the task, like closing network connections and deleting temporary files.</p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b653fc88f826" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift Code Explained: Implementing Safe Array Subscription]]></title>
            <link>https://huypham85.medium.com/swift-code-explained-implementing-safe-array-subscription-96904d0c9728?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/96904d0c9728</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[swift-programming]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Mon, 13 May 2024 07:58:13 GMT</pubDate>
            <atom:updated>2024-05-13T07:58:13.622Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vZzMMJtRD7U-jULk1qgQFA.png" /></figure><p>When working with arrays in Swift, you might have come across a common error called “<em>Fatal error: Index out of range</em>”. This error typically occurs when you unintentionally try to access an index that is not present in the array. Actually, accessing elements from an array is not safe by default. Therefore, today we’re going to explore how to code more securely when accessing elements in an array. 😊</p><p>Let’s look at an example:</p><pre>let shoppingList = [&quot;apples&quot;, &quot;raspberries&quot;]<br>let item = shoppingList[2]</pre><p>Accessing an element this way will crash the app if the element isn’t there.</p><blockquote>We’ll define an extension on the Collection type that adds a “safe” subscript. This subscript returns an optional value, which will be nil if the index is out of bounds.</blockquote><pre>extension Collection {<br><br>    /// Returns the element at the specified index if it exists, otherwise nil.<br>    subscript (safe index: Index) -&gt; Element? {<br>        return indices.contains(index) ? self[index] : nil<br>    }<br>}</pre><p>By using “if let” statement to unwrap the optional value, we can safely access the array item without worrying about out-of-bounds errors</p><pre>if let item = shoppingList[safe: 2] {<br>    // use item<br>}</pre><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=96904d0c9728" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scheduler in Combine: Managing Asynchronous Tasks Efficiently]]></title>
            <link>https://towardsdev.com/scheduler-in-combine-managing-asynchronous-tasks-efficiently-6775895a30b3?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/6775895a30b3</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[asynchronous-programming]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Sun, 12 May 2024 06:34:47 GMT</pubDate>
            <atom:updated>2024-05-13T15:17:09.311Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fQ6QgWyT9fYQnBbHxAqdCg.png" /></figure><h3><strong>Table of contents</strong></h3><p>· <a href="#4e23">What is a scheduler</a><br>· <a href="#86a9">Types of schedulers in Combine</a><br>· <a href="#8769">DispatchQueue</a><br>· <a href="#91ce">OperationQueue</a><br>· <a href="#1f53">ImmediateScheduler</a><br>· <a href="#fbb7">Default Scheduler</a><br>· <a href="#1001">Switching Schedulers</a><br>· <a href="#f037">receive(on:)</a><br>· <a href="#d09c">subscribe(on:)</a><br>· <a href="#111b">Performing asynchronous tasks with schedulers</a><br>· <a href="#53e6">Summary</a></p><h3>What is a scheduler</h3><p>According to the <a href="https://developer.apple.com/documentation/combine/scheduler">scheduler documentation</a>, a scheduler is <strong>“a protocol that defines <em>when</em> and <em>where</em> to execute a closure.”</strong> You can use a scheduler to execute code as soon as possible, or after a future date</p><blockquote><em>Combine does not work directly with threads. Instead, it allows Publishers to operate on specific Schedulers.</em></blockquote><p>The <strong><em>where</em></strong> means current run loop, dispatch queue or operation queue.</p><p>The <strong><em>when</em></strong> means virtual time, according to the scheduler’s clock. The work performed by a scheduler will adhere to the scheduler’s clock only, which might not correspond to the real-time of the system.</p><h3>Types of Schedulers in Combine</h3><p>Combine provides several types of schedulers, all of which conform to the <strong>Scheduler</strong> protocol:</p><h3>DispatchQueue</h3><p>a DispatchQueue is a first-in-first-out queue that can accept tasks in the form of block objects and execute them serially or concurrently. You’ll commonly use serial or global queues for the background work, and the main queue for the UI-related work</p><p>The system manages work submitted to a DispatchQueue on a pool of threads. The DispatchQueue doesn’t guarantee which thread it will use for executing tasks unless the DispatchQueue represents an app’s main thread</p><p>DispatchQueue is one of the safest ways to schedule commands</p><h3>OperationQueue</h3><p>Performs the work on a specific operation queue. Similarly to the dispatch queues, use OperationQueue.main for UI work, and other queues for the background work.</p><h3>ImmediateScheduler</h3><p>An IntermediateScheduler is used to perform asynchronous operations immediately. This means that commands will be immediately executed on the application’s current threads</p><pre>import Combine<br>import Foundation<br><br>let immediateScheduler = ImmediateScheduler.shared<br>let aNum = [1, 2, 3].publisher<br>    .receive(on: immediateScheduler)<br>    .sink(receiveValue: {<br>        print(&quot;Received \($0) on thread \(Thread.current)&quot;)<br>    }<br>)</pre><p>The code snippet above is running on the main thread, so the result will be also received in the main thread</p><pre>Received 1 on thread &lt;_NSMainThread: 0x600001704040&gt;{number = 1, name = main}<br>Received 2 on thread &lt;_NSMainThread: 0x600001704040&gt;{number = 1, name = main}<br>Received 3 on thread &lt;_NSMainThread: 0x600001704040&gt;{number = 1, name = main}</pre><h3>Default Scheduler</h3><p>💡 Even if you don’t specify any scheduler, Combine provides you with the default one. The scheduler uses the same thread, where the task is performed. For example, if you perform a background task, Combine provides a scheduler that receives the task on the same background thread</p><pre>let subject = PassthroughSubject&lt;Int, Never&gt;()<br>// 1<br>let token = subject.sink(receiveValue: { value in<br>    print(Thread.isMainThread)<br>})<br>// 2<br>subject.send(1)<br>// 3<br>DispatchQueue.global().async {<br>    subject.send(2)<br>}</pre><ol><li>Print <em>true</em> if the value is received on the main thread, and <em>false</em> otherwise.</li><li>Send 1 from the main thread.</li><li>Send 2 from the background thread.</li></ol><p>It will print:</p><p>true</p><p>false</p><p>As expected, the values are received on different threads.</p><h3>Switching Schedulers</h3><p>In order not to freeze or crash when interacting with the user interface, the resource-consuming tasks should be done in the background and then handle their result on the main thread. The Combine’s way of doing this is by switching schedulers with two methods: subscribe(on:) and receive(on:)</p><h3>receive(on:)</h3><p>The receive(on:) method changes a scheduler for all publisher that comes after it is declared</p><pre>Just(1)<br>   .map { _ in print(Thread.isMainThread) }<br>   .receive(on: DispatchQueue.global())<br>   .map { print(Thread.isMainThread) }<br>   .sink { print(Thread.isMainThread) }</pre><p>it will print:</p><pre>true<br>false<br>false</pre><p>The process is visualized as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rL2T_dFcYizI6Rp_BrSqLA.jpeg" /></figure><p>All operators to the right of receive(on:) deliver elements on DispatchQueue.global() scheduler.</p><h3>subscribe(on:)</h3><p>subscribe(on:) sets where subscription work happens. It stays there unless receive(on:) changes it. Think of it as assigning work locations.</p><pre>Just(1)<br>   .subscribe(on: DispatchQueue.global())<br>   .map { _ in print(Thread.isMainThread) }<br>   .sink { print(Thread.isMainThread) }</pre><p>It will print:</p><p>false</p><p>false</p><p>Let’s visualize the process:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aWHJJhHRg2KN5hknB7w9DQ.jpeg" /></figure><p>All the operations happen on the DispatchQueue.global()scheduler</p><p>The position of subscribe(on:) doesn’t matter, as it affects the time of subscription. This snippet is equivalent to the previous one:</p><pre>Just(1) <br>   .map { _ in print(Thread.isMainThread) }<br>   .subscribe(on: DispatchQueue.global()) // Position of subscribe(on:) has changed<br>   .sink { print(Thread.isMainThread) }</pre><p>💡 You must notice that the definition of subscribe(on:) says nothing about the scheduler on which we receive values. In case a publisher emits values on a different thread, it will be received on that thread. A typical example is a data task publisher:</p><pre>URLSession.shared.dataTaskPublisher(for: URL(string: &quot;&lt;https://www.vadimbulavin.com&gt;&quot;)!)<br>   .subscribe(on: DispatchQueue.main) // Subscribe on the main thread<br>   .sink(receiveCompletion: { _ in },<br>         receiveValue: { _ in<br>           print(Thread.isMainThread) // Are we on the main thread?<br>   })</pre><p>The code will print false because the publisher emits values on a background thread. In such cases, we must use receive(on:) to specify a scheduler</p><h3>Performing asynchronous tasks with schedulers</h3><p>Let’s see how we can switch schedulers by combining subscribe(on:) and receive(on:)</p><p>Assume that we have a publisher with a long-running task:</p><pre>struct BusyPublisher: Publisher {<br>    typealias Output = Int<br>    typealias Failure = Never<br>    <br>    func receive&lt;S&gt;(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {<br>        sleep(5)<br>        subscriber.receive(subscription: Subscriptions.empty)<br>        _ = subscriber.receive(1)<br>        subscriber.receive(completion: .finished)<br>    }<br>}</pre><p>When call from the UI thread, it freezes the app for 10 seconds. Remember, Combine defaults to the same scheduler from where the element is fired:</p><pre>BusyPublisher()<br>  .sink { _ in<br>    print(&quot;Received value&quot;) <br>  }<br>print(&quot;Hello&quot;)</pre><p>As expected, Hello is printed after the value is received:</p><pre>Received value<br>Hello</pre><p>💡 So the solution for doing asynchronous work with Combine is subscribing on the background scheduler and then receiving the events on the UI Scheduler:</p><pre>BusyPublisher()<br>   .subscribe(on: DispatchQueue.global())<br>   .receive(on: DispatchQueue.main)<br>   .sink { _ in print(&quot;Received value&quot;) }<br>print(&quot;Hello&quot;)</pre><p>As expected, Hello is printed before the value is received, this means that the application is not frozen by the publisher blocking the main thread.</p><pre>Hello<br>Received value</pre><h3>Summary</h3><p>Let’s recap the main takeaways.</p><ul><li>subscribe(on:) and receive(on:) are primary multithreading methods of the Combine Swift framework.</li><li>The default scheduler uses the same thread from where the element was generated.</li><li>receive(on:) sets a scheduler for all operators coming afterward.</li><li>subscribe(on:) sets a scheduler for the whole stream, starting at the time the Publisher is subscribed to. The stream stays on the same scheduler until receive(on:) specifies another scheduler.</li><li>The position of subscribe(on:) does not matter.</li><li>Asynchronous work is typically performed by subscribing on the background scheduler and receiving values on the UI scheduler.</li></ul><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6775895a30b3" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/scheduler-in-combine-managing-asynchronous-tasks-efficiently-6775895a30b3">Scheduler in Combine: Managing Asynchronous Tasks Efficiently</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best Strategies To Handle Errors in Combine]]></title>
            <link>https://huypham85.medium.com/best-strategies-to-handle-errors-in-combine-4b455b5edd09?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/4b455b5edd09</guid>
            <category><![CDATA[error-handling]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Fri, 03 May 2024 02:54:43 GMT</pubDate>
            <atom:updated>2024-05-03T09:22:46.842Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*mtmAZ6qGgTuwXU1onDwU2A.jpeg" /></figure><h3>Table of contents</h3><p>· <a href="#b83b">Errors types in Combine</a><br>· <a href="#401e">mapError</a><br>· <a href="#0932">retry</a><br>· <a href="#08e6">catch</a><br>· <a href="#3239">replaceError</a><br>· <a href="#182a">Conclusion</a></p><p>Handling errors properly is essential for creating a robust and reliable application. Error handling in reactive programming is comparatively more complex than its imperative counterpart. But when using Combine, you’re equipped with handy operators that can help you handle the errors properly.</p><p>This article assumes you have the basic knowledge about Combine including Publisher, Subscriber,…. You can also check my series about Combine <a href="https://huypham85.medium.com/list/combine-framework-ce3638f2bf58">here</a></p><h3>Errors types in Combine</h3><p>Before we learn about handling errors strategies, it’s crucial to understand different types of errors that can occur when you use Combine</p><ul><li><strong>Publisher errors</strong>: These errors occur when a publisher fails to produce a value due to an internal error, such as a network failure or a runtime error.</li><li><strong>Operator errors</strong>: These errors occur when an operator in the pipeline fails to process a value due to an error condition, such as an invalid argument or a runtime error.</li><li><strong>Subscription errors</strong>: These errors occur when a subscriber fails to receive values due to an error condition, such as a canceled subscription or a runtime error.</li></ul><h3>mapError</h3><p>mapError operator is used for mapping an error to the expected error type</p><pre>import Combine<br>enum MyError: Error {<br>    case testError<br>}<br>enum MappedError: Error {<br>    case transformedError<br>}<br>let publisher = PassthroughSubject&lt;Int, MyError&gt;()<br>let cancellable = publisher<br>    .mapError { _ in MappedError.transformedError }<br>    .sink(receiveCompletion: { completion in<br>        switch completion {<br>        case .finished:<br>            print(&quot;Publisher completed successfully.&quot;)<br>        case .failure(let error):<br>            print(&quot;Publisher completed with error: \(error)&quot;)<br>        }<br>    }, receiveValue: { value in<br>        print(&quot;Received value: \(value)&quot;)<br>    })<br>publisher.send(1)<br>publisher.send(completion: .failure(.testError))<br>//Outputs<br>//Received value: 1<br>//Publisher completed with error: transformedError</pre><h3>retry</h3><p>Another common strategy for handling errors in Combine is to use the retry operator. You might want to use the retry operator before accepting an error when working with data requests.</p><pre>enum MyError: Error {<br>    case unknown<br>}<br><br>let url = URL(string: &quot;&lt;https://example.comm&gt;&quot;)!<br>let cancellable = URLSession.shared.dataTaskPublisher(for: url)<br>    .mapError { error -&gt; Error in<br>        if let urlError = error as? URLError, urlError.code == .networkConnectionLost {<br>            return urlError<br>        } else {<br>            return MyError.unknown<br>        }<br>    }<br>    .retry(3)<br>    .sink(receiveCompletion: { completion in<br>        switch completion {<br>        case .finished:<br>            print(&quot;Request completed successfully.&quot;)<br>        case .failure(let error):<br>            print(&quot;Request failed with error: \(error)&quot;)<br>        }<br>    }, receiveValue: { value in<br>        print(&quot;Received value: \(value)&quot;)<br>    })</pre><p>However, The <strong>retry</strong> operator in Combine does not have the same functionality as the <strong>retry(when:)</strong> operator in RxSwift. In Combine, the <strong>retry</strong> operator simply resubscribes to the upstream publisher when an error occurs, up to a specified number of times. It does not provide a mechanism to conditionally decide whether to retry based on the error that occurred.</p><h3>catch</h3><p>One of the most common strategies for handling errors in Combine is to use the catch operator. The catch operator allows you to handle errors and recover from failures in a reactive pipeline.</p><pre>let publisher = URLSession.shared.dataTaskPublisher(for: url)<br>    .map(\.data)<br>    .catch { error -&gt; Just&lt;Data&gt; in<br>        print(&quot;Error: \(error)&quot;)<br>        return Just(Data()) // return a default value if an error occurs<br>    }</pre><p>For example, if you have a publisher that retrieves data from the server, you can use catch operator to catch any errors and recover by returning a default value or retrying the request</p><h3>replaceError</h3><p>replaceError seems quite the same as the catch operator. The difference is that replaceError completely ignores the error and still returns a recovering value.</p><p>In the above example, we’re doing nothing than returning the placeholder image in case of an error.</p><pre>URLSession.shared<br>    .dataTaskPublisher(for: URL(string: &quot;&lt;https://mydomain/image_654&gt;&quot;)!)<br>    .map { result -&gt; UIImage in<br>        return UIImage(data: result.data) ?? UIImage(named: &quot;placeholder-image&quot;)!<br>    }<br>    .replaceError(with: UIImage(named: &quot;placeholder-image&quot;)!)<br>    .sink(receiveCompletion: { print(&quot;received completion: \($0)&quot;) }, receiveValue: {print(&quot;received auth: \($0)&quot;)})</pre><h3>Conclusion</h3><p>Recognizing the importance of handling both happy and unhappy scenarios, it’s vital to discuss error management strategies in Combine. Also, you can check out the code snippet featured in this article via M<a href="https://github.com/huypham85/iOSExperimentHub/tree/main/iOSExperimentHub/Playground">y Playground</a> 🙌</p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b455b5edd09" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mastering Publisher Subscriptions in Combine: A Comprehensive Guide]]></title>
            <link>https://huypham85.medium.com/mastering-publisher-subscriptions-in-combine-a-comprehensive-guide-47ed69d2a427?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/47ed69d2a427</guid>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[combine]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Fri, 19 Apr 2024 08:21:27 GMT</pubDate>
            <atom:updated>2024-04-22T07:33:36.601Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*k3cFoSmclt6BsLvn0FD5Sg.png" /></figure><h3><strong>Table of contents</strong></h3><p>· <a href="#61a0">How to create Subscriber</a><br>· <a href="#ab79">Assign</a><br>· <a href="#7562">Sink</a><br>· <a href="#3b86">Custom Subscriber</a><br>· <a href="#f63f">AnyCancellable and memory management</a><br>· <a href="#a99c">Conclusion</a><br>· <a href="#2101">References:</a></p><p>It’s time to review a little:</p><p>Subscribers will receive values from Publisher, all subscribers must inherit the Subscriber protocol.</p><pre>public protocol Subscriber {<br>  associatedtype Input<br>  associatedtype Failure: Error<br>  func receive(subscription: Subscription)<br>  func receive(_ input: Self.Input) -&gt; Subscribers.Demand<br>  func receive(completion: Subscribers.Completion&lt;Self.Failure&gt;)<br>}</pre><ul><li>Input: Type of data provided, it has to be matched the Output of Publisher</li><li>Failure: Type of data for error</li></ul><p>3 important methods:</p><ul><li>receive(subscription:) when receiving subscription from Publisher</li><li>receive(input:) when receiving value from Publisher and we will adjust the request for further data through Demand.</li><li>receive( completion:) when receiving completion from the publisher.</li></ul><p>You can subscribe to a Subscriber by using subscribe of Publisher (if you haven’t read my article about Publisher in Combine yet, you can check this <a href="https://towardsdev.com/publisher-in-combine-fbe23d994ae4">link</a>)</p><pre>publisher.subscribe(subscriber)</pre><blockquote>An important note that only when a subscriber connects does the publisher emit data</blockquote><p>Subscriber has the ability to <strong>self-destruction</strong> when subscription is disconnected. That cancellation helps memory automatically release unnecessary objects. We have 2 types of cancellation:</p><ul><li>Auto cancel by AnyCancellable when we create subscribers by sink or assign</li><li>Manually cancel by method cancel() of Subscriber</li></ul><h3>How to create Subscriber</h3><h3>Assign</h3><p>Assigns each element from a publisher to a property on an object.</p><pre>func assign&lt;Root&gt;(<br>    to keyPath: ReferenceWritableKeyPath&lt;Root, Self.Output&gt;,<br>    on object: Root<br>) -&gt; AnyCancellable</pre><p>Use the assign(to:on:) subscriber when you want to set a given property each time a publisher produces a value.</p><pre>import Combine<br>class MyModel {<br>    @Published var value: Int = 0<br>}<br>let model = MyModel()<br>var cancellable = Set&lt;AnyCancellable&gt;()<br>Just(10)<br>    .assign(to: \.value, on: model)<br>    .store(in: &amp;cancellable)<br>print(model.value)  // Output: 10</pre><p>You can use Subscribers.Assignto get the same approach, but note that the instance created by this operator <strong>maintains a strong reference to object, and sets it to </strong><strong>nil when the upstream publisher completes</strong> (either normally or with an error), you can see the below example and try it yourself in Playground</p><pre>import Combine<br>class ViewModel {<br>    var name: String = &quot;&quot;<br>    private var cancellable: Set&lt;AnyCancellable&gt; = Set()<br>    deinit {<br>        print(&quot;deinit&quot;)<br>    }<br>    init(publisher: CurrentValueSubject&lt;String, Never&gt;) {<br>        publisher.assign(to: \.name, on: self).store(in: &amp;cancellable)<br>    }<br>}<br>let publisher = CurrentValueSubject&lt;String, Never&gt;(&quot;Test&quot;)<br>var viewModel: ViewModel? = ViewModel(publisher: publisher)<br>viewModel = nil // the ViewModel object can&#39;t be released because upstream publisher hasn&#39;t finished<br>publisher.send(completion: .finished) // finish the publisher, now the ViewModel object is completely released</pre><p>I also find out an extension at <a href="http://forums.swift.org/">forums.swift.org</a> can replace the primary assign method to properly prevent the memory leak, that’s awesome!</p><pre>extension Publisher where Self.Failure == Never {<br>    public func assignNoRetain&lt;Root&gt;(to keyPath: ReferenceWritableKeyPath&lt;Root, Self.Output&gt;, on object: Root) -&gt; AnyCancellable where Root: AnyObject {<br>        sink { [weak object] (value) in<br>        object?[keyPath: keyPath] = value<br>    }<br>  }<br>}</pre><h3>Sink</h3><p>Attaches a subscriber with closure-based behavior.</p><p>Use sink(receiveCompletion:receiveValue:) to observe values received by the publisher and process them using a closure you specify.</p><pre>let publisher = Just(&quot;Hello, Combine!&quot;)<br>let cancellable = publisher<br>    .sink(receiveCompletion: { completion in<br>        switch completion {<br>        case .finished:<br>            print(&quot;Publisher completed successfully.&quot;)<br>        case .failure(let error):<br>            print(&quot;Publisher completed with error: \(error)&quot;)<br>        }<br>    }, receiveValue: { value in<br>        print(&quot;Received value: \(value)&quot;)<br>    })</pre><ul><li>The receiveValue: closure is called with the value emitted by the publisher, and the receiveCompletion: closure is called when the publisher completes.</li><li>The receiveCompletion: closure takes a Subscribers.Completion parameter, which is an enum that can be either .finished or .failure(let error). In this case, since Just cannot fail, there’s no error to handle, but in a real-world scenario, you would handle potential errors in the .failure case.</li></ul><blockquote>Please note that sink(receiveCompletion:receiveValue:) returns an AnyCancellable instance, which is stored in Set&lt;AnyCancellable&gt; to keep the subscription alive. If you don’t store this instance, the subscription is canceled immediately</blockquote><h3>Custom Subscriber</h3><p>Implementing custom subscribers in Combine allows us to define our own logic for handling values received from publishers.</p><p><strong>Step 1: Define Your Custom Subscriber</strong></p><p>This class should conform to the <strong>Subscriber</strong> protocol. The <strong>Subscriber</strong> protocol requires you to specify types for <strong>Input</strong> and <strong>Failure</strong>.</p><pre>class CustomSubscriber: Subscriber {<br>    typealias Input = Int<br>    typealias Failure = Never</pre><p><strong>Step 2: Implement the </strong><strong>receive(subscription:) Method</strong></p><p>This method is called once when the publisher is connected to the subscriber. Here, you can request a certain number of values from the publisher.</p><pre>func receive(subscription: Subscription) {<br>    subscription.request(.max(1))<br>}</pre><p><strong>Step 3: Implement the </strong><strong>receive(_:) Method</strong></p><p>This method is called each time a new value is delivered by the publisher. Here, you can handle the received value.</p><pre>func receive(_ input: Int) -&gt; Subscribers.Demand {<br>    print(&quot;Value:&quot;, input)<br>    return .none<br>}</pre><p>This mode returns Demand means that every time receives the value, Subscriber will adjust its request through Demand . With returning:</p><ul><li>none : do not get any more elements</li><li>unlimited : take all</li><li>max(n): get the next n elements</li></ul><p><strong>Step 4: Implement the </strong><strong>receive(completion:) Method</strong></p><p>This method is called when the publisher completes, either successfully or with an error. Here, you can handle the completion event.</p><pre>func receive(completion: Subscribers.Completion&lt;Never&gt;) {<br>        print(&quot;Completion: \(completion)&quot;)<br>    }<br>}</pre><p><strong>Step 5: Use Your Custom Subscriber</strong></p><p>Finally, you can use your custom subscriber with a publisher. The <strong>subscribe(_:)</strong> method is called on the publisher with the subscriber as an argument, which connects the publisher to the subscriber.</p><pre>let publisher = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].publisher<br>let subscriber = CustomSubscriber()<br>publisher.subscribe(subscriber)</pre><p>You can also get the full code of CustomSubscriber <a href="https://github.com/huypham85/iOSExperimentHub/blob/c90ec6433487d276efcd2a0bad0eac376b0848a9/iOSExperimentHub/Playground/CustomSubscriber.playground/Contents.swift">here</a></p><h3>AnyCancellable and memory management</h3><p><strong>AnyCancellable</strong> is a type-erasing cancellable object that executes a provided closure when canceled. It’s used to manage the lifetime of a subscription</p><p>Here are some key points about <strong>AnyCancellable</strong>:</p><ul><li><strong>Cancellation token</strong>: Subscriber implementations can use <strong>AnyCancellable</strong> to provide a “cancellation token” that makes it possible for a caller to cancel a publisher. This means you can stop the subscription whenever you want by calling the <strong>cancel()</strong> method.</li></ul><pre>let publisher = Just(&quot;Hello, Combine!&quot;)<br>let cancellable: AnyCancellable = publisher<br>    .sink { value in<br>        print(value)<br>    }<br><br>// When you want to cancel the subscription<br>cancellable.cancel()</pre><ul><li><strong>Automatic cancellation</strong>: An <strong>AnyCancellable</strong> instance automatically calls <strong>cancel()</strong> when de-initialized. This means if the <strong>AnyCancellable</strong> instance is deallocated, the subscription will be canceled automatically.</li></ul><pre>class MyClass {<br>    var cancellable: AnyCancellable? = nil<br><br>    init() {<br>        cancellable = Just(&quot;Hello, Combine!&quot;)<br>            .sink { value in<br>                print(value)<br>            }<br>    }<br>}<br><br>var myClass: MyClass? = MyClass()  // prints &quot;Hello, Combine!&quot;<br>myClass = nil  // `cancellable` is deallocated, so the subscription is cancelled</pre><ul><li><strong>Storing AnyCancellable instances</strong>: You can store <strong>AnyCancellable</strong> instances in a collection or a set using the <strong>store(in:)</strong> method. This is useful when you have multiple subscriptions and want to manage them together.</li><li><strong>Memory Management</strong>: When you store <strong>AnyCancellable</strong> instances in a set (or any other collection), you’re essentially creating a strong reference to those instances. As long as there’s a strong reference to an <strong>AnyCancellable</strong>, the subscription it represents stays alive. However, if you remove all references to an <strong>AnyCancellable</strong>, then those <strong>AnyCancellable</strong> instances get deallocated, and the subscriptions they represent are automatically canceled.</li></ul><pre>class MyClass {<br>    var cancellables = Set&lt;AnyCancellable&gt;()<br><br>    init() {<br>        Just(&quot;Hello, Combine!&quot;)<br>            .sink { value in<br>                print(value)<br>            }<br>            .store(in: &amp;cancellables)<br>    }<br>}<br><br>var myClass: MyClass? = MyClass()  // prints &quot;Hello, Combine!&quot;<br>myClass = nil  // MyClass instance is deinitialized, so all subscriptions are cancelled</pre><p>In this code, when myClass is set to nil, the MyClass instance is deinitialized. As a result, the <strong>cancel()</strong> method of each <strong>AnyCancellable</strong> instance will be called automatically, effectively canceling all subscriptions. This is a key feature of <strong>AnyCancellable</strong> and a reason why it’s commonly used for managing subscriptions in Combine</p><h3>Conclusion</h3><p>This article covers subscribing to publishers with assign , sink and custom subscribers. Lastly, take advantage of AnyCancellable for efficient subscription and memory management in Combine.</p><h3>References</h3><p><a href="https://developer.apple.com/documentation/combine/publisher/assign(to:on:)">https://developer.apple.com/documentation/combine/publisher/assign(to:on:)</a></p><p><a href="https://stackoverflow.com/questions/57980476/how-to-prevent-strong-reference-cycles-when-using-apples-new-combine-framework">https://stackoverflow.com/questions/57980476/how-to-prevent-strong-reference-cycles-when-using-apples-new-combine-framework</a></p><p><a href="https://developer.apple.com/documentation/combine/anycancellable">AnyCancellable | Apple Developer Documentation</a></p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=47ed69d2a427" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Essential iOS App Security Cases Developers Shouldn’t Overlook]]></title>
            <link>https://towardsdev.com/essential-ios-app-security-cases-developers-shouldnt-overlook-0339ff1bd69e?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/0339ff1bd69e</guid>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Tue, 09 Apr 2024 07:01:58 GMT</pubDate>
            <atom:updated>2024-04-10T16:16:31.751Z</atom:updated>
            <content:encoded><![CDATA[<p>Although iOS is less vulnerable than Android, it still faces security issues. It is the responsibility of the developer to secure code and data communication</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*im3mPktJY0YJe0RTdcrmEg.jpeg" /></figure><p>Here are some preliminary-level threats that every iOS developer should take care of:</p><h3>1. Screen Recording and Screen Capturing</h3><p>An attacker can record sensitive screens such as login pages to capture username and password. Also possible to record paid video content</p><p>To fix that, we can observe the UIScreen.capturedDidChangeNotification and check for UIScreen.main.isCaptured</p><p>Observe changes to isCaptured with NotificationCenter:</p><pre>NotificationCenter.default.addObserver(self, selector: #selector(screenCaptureDidChange),<br>                                       name: UIScreen.capturedDidChangeNotification,<br>                                       object: nil)</pre><p>Handle the notification:</p><pre>@objc func screenCaptureDidChange() {<br>    print(&quot;screenCaptureDidChange.. isCapturing: \\(UIScreen.main.isCaptured)&quot;)<br>    if UIScreen.main.isCaptured {<br>            //TODO: They started capturing..<br>            print(&quot;screenCaptureDidChange - is recording screen&quot;)<br>    } else {<br>        //TODO: They stopped capturing..<br>        print(&quot;screenCaptureDidChange - screen recording stoped&quot;)<br>    }<br>}</pre><h3>2. Weak Jail Break Detection</h3><p>Application logic and behavior can be compromised on JailBroken devices, which can expose the application to attacks. Relying solely on jailbreak detection methods may not be sufficient to ensure the safety of your app</p><p>3 ways to identify whether a device is jailbroken or not:</p><ol><li>Looking for unique files and applications that are installed on jailbroken devices</li></ol><pre>private var filesPathToCheck: [String] {    <br>  return [&quot;/private/var/lib/apt&quot;,<br>            &quot;/Applications/Cydia.app&quot;,<br>            &quot;/private/var/lib/cydia&quot;,<br>            &quot;/private/var/tmp/cydia.log&quot;,<br>            &quot;/Applications/RockApp.app&quot;,<br>            &quot;/Applications/Icy.app&quot;,<br>            &quot;/Applications/WinterBoard.app&quot;,<br>            &quot;/Applications/SBSetttings.app&quot;,<br>            &quot;/Applications/blackra1n.app&quot;,<br>            &quot;/Applications/IntelliScreen.app&quot;,<br>            &quot;/Applications/Snoop-itConfig.app&quot;,<br>            &quot;/usr/libexec/cydia/&quot;,<br>            &quot;/usr/sbin/frida-server&quot;,<br>            &quot;/usr/bin/cycript&quot;,<br>            &quot;/usr/local/bin/cycript&quot;,<br>            &quot;/usr/lib/libcycript.dylib&quot;,<br>            &quot;/bin/sh&quot;,<br>            &quot;/usr/libexec/sftp-server&quot;,<br>            &quot;/usr/libexec/ssh-keysign&quot;,<br>            &quot;/Library/MobileSubstrate/MobileSubstrate.dylib&quot;,<br>            &quot;/bin/bash&quot;,<br>            &quot;/usr/sbin/sshd&quot;,<br>            &quot;/etc/apt&quot;,<br>            &quot;/usr/bin/ssh&quot;,<br>            &quot;/bin.sh&quot;,<br>            &quot;/var/checkra1n.dmg&quot;,<br>            &quot;/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist&quot;,<br>            &quot;/System/Library/LaunchDaemons/com.ikey.bbot.plist&quot;,<br>            &quot;/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist&quot;,<br>            &quot;/Library/MobileSubstrate/DynamicLibraries/Veency.plist&quot;]<br>}<br><br>func isJailBrokenFilesPresentInTheDirectory() -&gt; Bool {<br>    var checkFileIfExist: Bool = false<br>    filesPathToCheck.forEach {<br>        checkFileIfExist =  fm.fileExists(atPath: $0) ? true : false<br>        if checkFileIfExist{<br>            return<br>        }<br>    }<br>    return checkFileIfExist<br>}</pre><p>2. Checking if a file can be modified outside the application bundle. Developers can use this test to check if their application follows sandboxing rules</p><pre>func canEditSandboxFilesForJailBreakDetection() -&gt; Bool {<br>    let jailBreakTestText = &quot;Test for JailBreak&quot;<br>    do {<br>      try jailBreakTestText.write(toFile:&quot;/private/jailBreakTestText.txt&quot;, atomically:true, encoding:String.Encoding.utf8)<br>      return true<br>    } catch {<br>    let resultJailBroken = isJailBrokenFilesPresentInTheDirectory()<br>    return resultJailBroken<br>    }<br>}</pre><p>3. Calling the Cydia URL scheme (Cydia://) from an application successfully, it means the device is jailbroken</p><pre>// Protocol function extended for JailBreak detection<br>func assignJailBreakCheckType() -&gt; Bool {<br>  // If it is run on simulator follow the regular flow of the app<br>  if !isSimulator {<br>    // Check if Cydia app is installed on the device<br>      guard UIApplication.shared.canOpenURL(URL(string: &quot;cydia://&quot;)!) else {<br>        return false<br>      }<br>      return true<br>    }<br>    return true<br>}</pre><h3>3. Keychain Data Protection</h3><p>On JailBroken devices, a keychain item with a vulnerable accessibility option can be easily exposed to other applications or attackers with physical access</p><p>However, developers have multiple actions to mitigate this security risk:</p><ol><li>kSecAttrAccessibleWhenUnlocked</li><li>kSecAttrAccessibleAfterFirstUnlock</li><li>kSecAttrAccessibleAlways</li><li>kSecAttrAccessibleWhenUnlockedThisDeviceOnly</li><li>kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly</li><li>kSecAttrAccessibleAlwaysThisDeviceOnly</li></ol><p>Choose the easiest or more prone to vulnerable options like kSecAttrAccessibleWhenUnlocked may lead to potential security risk</p><p>The ‘kSecAttrAccessibleAfterFirstUnlock&#39; or &#39;kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly&#39; modes should only be used when the application requires a KeyChain item for background processing.</p><h3>4. File Data Protection</h3><p>When a file is to be saved, a developer can choose from these options to better use data protection</p><ol><li>atomic: An option to write data to an auxiliary file first and then replace the original file with the auxiliary file when the write completes.</li><li>withoutOverwriting: An option that attempts to write data to a file and fails with an error if the destination file already exists.</li><li>noFileProtection: An option to not encrypt the file when writing it out.</li><li>completeFileProtection: An option to make the file accessible only while the device is unlocked.</li><li>completeFileProtectionUnlessOpen: An option to allow the file to be accessible while the device is unlocked or the file is already open.</li><li>completeFileProtectionUntilFirstUserAuthentication: An option to allow the file to be accessible after a user first unlocks the device.</li><li>fileProtectionMask: An option the system uses when determining the file protection options that the system assigns to the data.</li></ol><p>Choosing noFileProtection should lead to potential security risks.</p><p>You should use ‘completeFileProtectionUnlessOpen’ and ‘completeFileProtectionUntilFirstUserAuthentication’ to have data protection on all files.</p><p>Encrypting a file on the first write</p><pre>do {<br>    try data.write(to: fileURL, options: .completeFileProtection)<br>}<br>catch {<br>   // Handle errors.<br>}</pre><p>For an existing file, you can use either NSFileManager/FileManager or NSURL:</p><pre>try FileManager.default.setAttributes([.protectionKey: .completeFileProtection], ofItemAtPath: fileURL.path)<br>// Or<br>// cast as `NSURL` because the `fileProtection` property of `URLResourceValues` is read-only.<br>try (fileURL as NSURL).setResourceValue(URLFileProtection.complete, forKey: .fileProtectionKey)</pre><p>With Core Data, you can pass the protection type when adding the persistent store:</p><pre>try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: [NSPersistentStoreFileProtectionKey: .completeFileProtection])</pre><h3>5. Show/Hide password field</h3><p>When you identify that the screen is being recorded, use a mask over the password or any sensitive text field to protect data security. Also, hide/dismiss the keyboard while the app is being recorded</p><pre>if(isRecording){<br>    let maskView = UIView(frame: CGRect(x: 64, y: 0, width: 128, height: 128))<br>    maskView.backgroundColor = .blue<br>    maskView.layer.cornerRadius = 64<br>    yourView.mask = maskView<br>}</pre><h3>6. HTTP Request</h3><p>If the application uses an insecure communication channel (HTTP), means that an attacker on the same network as the victim could carry on a man-in-the-middle attack by injecting a 301 HTTP redirection response to an attacker-controlled server</p><h3>7. Privacy Resources Access</h3><p>The application can access user resources like contacts, location, Bluetooth device ID, camera, photos</p><p>However, this could lead to a data leak if the data is transmitted insecurely</p><p>The data must be encrypted before transmitting to the server to prevent this. In addition, it’s crucial to verify that there are no third-party libraries in use that access resources insecurely</p><h3>8. Debug Logs Enabling</h3><p>Developer should avoid printing method completion details and sensitive information in release builds</p><p>Use #if DEBUG when we want to log information for only debug builds</p><pre>let logger = Logger(subsystem: &quot;com.yourdomain.yourapp&quot;, category: &quot;yourcategory&quot;)<br>#if DEBUG<br>logger.info(&quot;This is an info message&quot;)<br>// logger.debug(&quot;This is a debug message&quot;)<br>#endif</pre><p>You can check <a href="https://www.avanderlee.com/debugging/oslog-unified-logging/">OSLog and Unified logging as recommended by Apple</a></p><h3>References</h3><p><a href="https://pratap89singh.medium.com/ios-mobile-app-security-best-practices-for-ios-mobile-developers-a7e9375d40be">IOS Mobile App Security: Best Practices for iOS Mobile Developers. | by Munendra Pratap Singh | Feb, 2024 | Medium</a></p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0339ff1bd69e" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/essential-ios-app-security-cases-developers-shouldnt-overlook-0339ff1bd69e">Essential iOS App Security Cases Developers Shouldn’t Overlook</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Publisher in Combine: Unveiling the Publisher Role]]></title>
            <link>https://towardsdev.com/publisher-in-combine-fbe23d994ae4?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/fbe23d994ae4</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[reactive-programming]]></category>
            <category><![CDATA[combine]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Mon, 08 Apr 2024 18:05:31 GMT</pubDate>
            <atom:updated>2024-04-22T07:32:50.306Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Table of contents</strong></h3><p>· <a href="#e7ca">Publishers</a><br>· <a href="#7655">Publisher from a Sequence</a><br>· <a href="#159a">Publisher from transformation</a><br>· <a href="#1b46">Publisher from the Class’s property</a><br>· <a href="#c15d">Just</a><br>· <a href="#82fc">Future</a><br>· <a href="#a7af">Subject</a><br>· <a href="#7eba">PassthroughSubject</a><br>· <a href="#bd01">CurrentValueSubject</a><br>· <a href="#bd15">Type Erasure</a><br>· <a href="#bb93">Conclusion</a></p><p>Continue with the Combine series, today we will discuss Publisher. Combine needs something that can model a data stream. This is the role of the Publisher protocol.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*15wwll6urcdC1Fo4Nvel8w.png" /></figure><p>If you haven’t read my article about Combine in general, let’s check it out: <a href="https://huypham85.medium.com/introduction-to-combine-in-ios-f1823def91b3">Introduction to Combine in iOS. What is Combine? | by Huy Pham | Apr, 2024 | Medium</a></p><p>Now we will focus on types of Publisher regularly used and how to use them</p><h3>Publishers</h3><h3>Publisher from a Sequence</h3><p>A data stream can be viewed as a sequence of events over time. Therefore a Sequence is a great source for a simple publisher. Like this example above, Array conforms to Sequence. Sequence has a property called publisher that creates a publisher that emits the element from the source sequence</p><pre>let arrayPublisher = [1, 2, 3, 4, 5].publisher</pre><p>Another example:</p><pre>let stringPublisher = &quot;Huy Pham&quot;.publisher</pre><p>The initial value is a String, after transforming to a publisher, its emitted values are characters</p><p>The characteristic of this Publisher type is that it never makes errors, so the data type for Failure is Never</p><h3>Publisher from transformation</h3><p>We can create a new publisher by using transform operators</p><pre>[1, 2, 3].publisher // publisher of integers<br>    .map(String.init) // now a publisher of strings<br>    .sink {<br>        print($0)<br>    }<br>    .store(in: &amp;cancellable)</pre><p>The map function takes the values emitted by the upstream publisher and passes them as input to theString.init function. So the result Publisher now emits a String rather than an Int</p><p>Besides, we can create publishers from other sealed struct of Publishers, be able to transform upstream data to your expected publisher. See the example below:</p><pre>// combine latest operator<br>let publisher1 = PassthroughSubject&lt;Int, Never&gt;()<br>let publisher2 = PassthroughSubject&lt;String, Never&gt;()<br>let combined = Publishers.CombineLatest(publisher1, publisher2)<br><br>// create a sequence by sequence operator <br>let numbers = [1, 2, 3, 4, 5]<br>let sequencePublisher = Publishers.Sequence&lt;[Int], Never&gt;(sequence: numbers)</pre><p>It’s we simulate the operators. I will have an article next week giving more detail about Combine’s operators.</p><h3>Publisher from the Class’s property</h3><p>@Published is a property wrapper that makes any property using it Publisher</p><p>This wrapper is class-constrained, meaning that you can only use it in instances of a class</p><pre>class ViewModel {<br>    @Published var title = &quot;the first title&quot;<br>}</pre><p>@Published doesn’t need to call send() method or access .value. When you directly change its value, it will update the value. Note that we’re using the dollar sign to access the projected value. If you’re not familiar with this technique, check Apple’s document about <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Projecting-a-Value-From-a-Property-Wrapper">Property Wrapper and projected value</a></p><pre>var cancellable = viewModel.$title.sink(receiveValue: { newTitle in<br>    print(&quot;Title changed to \(newTitle)&quot;)<br>})<br>viewModel.title = &quot;the second title&quot;</pre><pre>// Prints:<br>// Title changed to the first title<br>// Title changed to the second title</pre><p>This approach is very efficient with UIKit and SwiftUI in case you don’t want to change the architecture of your project</p><h3>Just</h3><p>Just struct creates a straightforward publisher that will emit one value and then complete</p><pre>Just(&quot;A&quot;)</pre><p>This is handy for use as the return type of a function or if you just want to emit just 1 value</p><h3>Future</h3><p>Future means a publisher that eventually produces a single value and then finishes or fails. It provides closure as Future.Promise has Result type parameter without a return value. In the successful case, the future downstream subscriber receives the element before the publishing stream finishes normally. If the result is an error, publishing terminates with that error.</p><pre>func generateAsyncRandomNumberFromFuture() -&gt; Future &lt;Int, Never&gt; {<br>    return Future() { promise in<br>        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {<br>            let number = Int.random(in: 1...10)<br>            promise(Result.success(number))<br>        }<br>    }<br>}<br>cancellable = generateAsyncRandomNumberFromFuture()<br>    .sink { number in print(&quot;Got random number \\(number).&quot;) }<br>    <br>// Prints<br>// Got random number 9</pre><p>Future can replace the callback of the function, which can allow you to express asynchronous behavior without deeply nested callbacks (callback hell)</p><h3>Subject</h3><h3>PassthroughSubject</h3><p>PassthroughSubject can be used to emit values to downstream subscribers. However, it doesn’t store or cache the most recent value</p><h3>CurrentValueSubject</h3><p>Like PassThroughSubject, it can emit values to downstream subscribers. However, unlike PassThroughSubject, CurrentValueSubject maintains and provides access to the most recent value it has received. When a new subscriber attaches to a CurrentValueSubject, it immediately receives the current value (if available) before getting any subsequent updates</p><pre>import Combine<br>class DataManager {<br>    // Create a PassThroughSubject to emit updates<br>    let passThroughSubject = PassthroughSubject&lt;String, Never&gt;()<br>    <br>    // Create a CurrentValueSubject with an initial value<br>    var currentValueSubject = CurrentValueSubject&lt;Int, Never&gt;(0)<br>    <br>    func updateData(value: String) {<br>        passThroughSubject.send(value)<br>    }<br>    <br>    func updateCurrentValue(value: Int) {<br>        currentValueSubject.send(value)<br>    }<br>}<br><br>let dataManager = DataManager()<br><br>let passThroughSubscription = dataManager.passThroughSubject.sink { value in<br>    print(&quot;Received value from PassThroughSubject: \\(value)&quot;)<br>}<br>let currentValueSubscription = dataManager.currentValueSubject.sink { value in<br>    print(&quot;Received value from CurrentValueSubject: \\(value)&quot;)<br>}<br>// subjects emit data<br>dataManager.updateData(value: &quot;Hello, World!&quot;)<br>dataManager.updateCurrentValue(value: 42)<br>// Prints<br>// Received value from PassThroughSubject: Hello, World!<br>// Received value from CurrentValueSubject: 0<br>// Received value from CurrentValueSubject: 42</pre><p>The main difference between both subjects is that a PassthroughSubject doesn’t have an initial value or a reference to the most recently published element. Therefore, new subscribers will only receive newly emitted events.</p><ul><li>A PassthroughSubject is like a doorbell push button <em>When someone rings the bell, you’re only notified when you’re at home</em></li><li>A CurrentValueSubject is like a light switch <em>When a light is turned on while you’re away, you’ll still notice it was turned on when you get back home.</em></li></ul><h3>Type Erasure</h3><p>Sometimes you want to subscribe to the publisher without knowing too much about its details or the chain operators create complicated nested type</p><pre>let publisher = Fail&lt;Int, Error&gt;(error: ErrorDomain.example)<br>    .replaceError(with: 0)<br>    .map { _ in &quot;Now I am a string&quot; }<br>    .filter { $0.contains(&quot;Now&quot;) }</pre><p>The type of this publisher is:</p><pre>Publishers.Filter&lt;Publishers.Map&lt;Publishers.ReplaceError&lt;Fail&lt;Int, Error&gt;&gt;, String&gt;&gt;</pre><p>To manage these nested types publishers have the eraseToAnyPublisher() method. This is a form of “type erasure”. This method erases the complex nested types and makes the publisher appear as a simpler AnyPublisher&lt;String, Never&gt; to any downstream subscribers.</p><p>With AnyPublisher, can’t call send(_:) method, it’s important to wrap a subject with AnyPublisher to prevent the view from sending events through it. When you use type erasure in the MVVM way, you can change the underlying publisher implementation over time without affecting existing clients.</p><h3>Conclusion</h3><p>In summary, Combine’s Publishers provides iOS developers with a powerful solution for managing asynchronous events and data streams. Mastering Publishers is essential for creating robust, scalable iOS applications with reactive capabilities.</p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fbe23d994ae4" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/publisher-in-combine-fbe23d994ae4">Publisher in Combine: Unveiling the Publisher Role</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making a completionHandler Safer in Swift]]></title>
            <link>https://huypham85.medium.com/making-a-completionhandler-safer-in-swift-ebda437736f7?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/ebda437736f7</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[swift-programming]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Wed, 03 Apr 2024 16:13:05 GMT</pubDate>
            <atom:updated>2024-04-12T02:51:12.987Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3tfI1doR2uCS6mlwjqeRMg.jpeg" /></figure><p>Asynchronous programming in Swift often involves using completionHandler within functions to handle events. While this approach is common, there are potential pitfalls that developers need to be mindful of. One such pitfall is that there is no guarantee all code paths will eventually call the completionHandler. This means if we forget to call it, the code will still build successfully, leading to unexpected behaviors or bugs.</p><p>To create safer code that ensures the completionHandler is always called, we can follow a simple two-step process:</p><ol><li>Declare a constant using the let keyword to store the result.</li><li>Use a defer statement to call the completionHandler.</li></ol><p>The defer statement is a powerful tool in Swift. It ensures that a block of code is executed when the current scope exits, which in this case is just before the function returns. This guarantees that the completionHandler will always be called, even if an error occurs during the function&#39;s execution.</p><p>Here’s an example of how you can implement this in your code:</p><pre>import Foundation<br>func fetchData(url: URL, _ completion: @escaping (Result&lt;Data, Error&gt;) -&gt; Void) {<br>    URLSession.shared.dataTask(with: url) { data, _, error in<br>        let result: Result&lt;Data, Error&gt;<br>        defer {<br>            completion(result)<br>        }<br>        guard error == nil else {<br>            result = .failure(error!)<br>            return<br>        }<br>        guard let data else {<br>            result = .failure(NetworkError.noData)<br>            return<br>        }<br>        result = .success(data)<br>    }<br>    .resume()<br>}</pre><p>In the above code, we first declare a constant result to store the outcome of our network call. We then use a defer statement to call the completionHandler with the result just before the function exits. This ensures that regardless of the path our code takes, our completionHandler is always called.</p><p>By incorporating these steps, we can create a safer and more reliable asynchronous code that guarantees our completionHandler is always executed.</p><p>Reference: <a href="https://www.youtube.com/watch?v=fH_I2jUCzvc">Here’s the trick to make a completionHandler much safer! 😌 — YouTube</a></p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ebda437736f7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Stop Using .lowercased() to Compare Strings in Swift]]></title>
            <link>https://huypham85.medium.com/stop-using-lowercased-to-compare-strings-in-swift-3867dd8fd3d2?source=rss-b87ab2bbc03b------2</link>
            <guid isPermaLink="false">https://medium.com/p/3867dd8fd3d2</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[swift-tips]]></category>
            <dc:creator><![CDATA[Huy Pham]]></dc:creator>
            <pubDate>Mon, 01 Apr 2024 16:49:00 GMT</pubDate>
            <atom:updated>2024-04-12T02:47:34.525Z</atom:updated>
            <content:encoded><![CDATA[<h3>Stop Using .lowercased() to Compare Strings in Swift</h3><p>When developing in Swift, a common task you may come across is comparing strings. However, did you know that using the .lowercased() method might not be the best way to do it?</p><p>Let’s take a look at an example where we compare two strings using .lowercased():</p><pre>import Foundation<br><br>let searchQuery = &quot;cafe&quot;<br>let databaseValue = &quot;Café&quot;<br>if searchQuery.lowercased() == databaseValue.lowercased() {<br>    //...<br>}</pre><p>At first glance, this seems like a reasonable approach. After all, the .lowercased() method is simple and easy to use. However, it has a couple of significant drawbacks.</p><p>Firstly, the .lowercased() method creates a new copy of each String every time it&#39;s called. This could potentially harm performance, especially if you&#39;re dealing with large strings or performing the operation numerous times.</p><p>Secondly, using .lowercased() only provides a case-insensitive search. It doesn&#39;t account for diacritics, such as accents. For instance, it wouldn&#39;t match &quot;cafe&quot; with &quot;Café&quot;.</p><p>So, what’s the solution? Swift provides the compare() method, allowing us to specify comparison options. This method is more flexible and can handle diacritics and case sensitivity.</p><p>Here’s how you can use it:</p><pre>import Foundation<br><br>let searchQuery = &quot;cafe&quot;<br>let databaseValue = &quot;Café&quot;<br>let comparisonResult = searchQuery.compare(<br>    databaseValue,<br>    options: [.caseInsensitive, .diacriticInsensitive]<br>)<br>if comparisonResult == .orderedSame {<br>    //...<br>}</pre><p>In this example, we’re using the .caseInsensitive and .diacriticInsensitive options to compare the strings. This means the comparison will ignore both case and diacritics, making it a more robust solution for string comparison in Swift.</p><p>In conclusion, while .lowercased() may seem like an easy solution for string comparison, it&#39;s worth considering the compare() method for a more versatile and efficient approach.</p><p>Reference: <a href="https://sarunw.com/posts/different-ways-to-compare-string-in-swift/">Different ways to compare string in Swift | Sarunw</a></p><h3>Thanks for Reading! ✌️</h3><p><em>If you have any questions or corrections, please leave a comment below or contact me via my LinkedIn account</em> <a href="https://www.linkedin.com/in/huypham85/">Pham Trung Huy</a><em>.</em></p><p><em>Happy coding 🍻</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3867dd8fd3d2" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>