Tag: Algorithm

  • Swift, Modern Concurrency

    Swift, Modern Concurrency

    ✍️ Note

    Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    Swift Concurrency Model

    The possible suspension points in your code marked with await indicate that the current piece of code might pause execution while waiting for the asynchronous function or method to return. This is also called yielding the thread because, behind the scenes, Swift suspends the execution of your code on the current thread and runs some other code on that thread instead. Because code with await needs to be able to suspend execution, only certain places in your program can call asynchronous functions or methods:

    • Code in the body of an asynchronous function, method, or property.
    • Code in the static main() method of a structure, class, or enumeration that’s marked with @main.
    • Code in an unstructured child task, as shown in Unstructured Concurrency below.
    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/

    Explicitly insert a suspension point

    Call Task.yield() method for long running operation that doesn’t contain any suspension point.

    func listPhotos(inGallery name: String) async throws -> [String] {
        // ... some asynchronous networking code ...
        // use Task.sleep for simulating networking logic
        try await Task.sleep(for: .seconds(2))
        let result = ["photo1.jpg", "photo2.jpg", "photo3.jpg"]
        return result
    }
    
    func generateSlideshow(forGallery gallery: String) async throws {
        let photos = try await listPhotos(inGallery: gallery)
        for photo in photos {
            // ... render a few seconds of video for this photo ...
            print("photo file: \(photo)")
            // Explicitly insert a suspending point
            // It allows other tasks to execute
            await Task.yield()
        }
    }
    
    Task {
        let _ = try? await generateSlideshow(forGallery: "Summer Vacation")
    }

    Wait, there is no function to resuming it explicitly?

    No.

    Suspends the current task and allows other tasks to execute.

    A task can voluntarily suspend itself in the middle of a long-running operation that doesn’t contain any suspension points, to let other tasks run for a while before execution returns to this task.

    If this task is the highest-priority task in the system, the executor immediately resumes execution of the same task. As such, this method isn’t necessarily a way to avoid resource starvation.

    Structuring long-running code this way (explicitly insert a suspension point) lets Swift balance between making progress on this task, and letting other tasks in your program make progress on their work.

    Task.yield(), Apple

    Wrap a throwing function

    When you define an asynchronous or throwing function, you mark it with async or throws, and you mark calls to that function with await or try. An asynchronous function can call another asynchronous function, just like a throwing function can call another throwing function.

    However, there’s a very important difference. You can wrap throwing code in a docatch block to handle errors, or use Result to store the error for code elsewhere to handle it. These approaches let you call throwing functions from nonthrowing code.

    Apple
    func photoList(inGallery: String) throws -> [String] {
        return ["photo1.jpg", "photo2.jpg"]
    }
    
    func photoListResult(inGallery name: String) -> Result<[String], Error> {
        return Result {
            try photoList(inGallery: name)
        }
    }

    Normal function can wrap a throwing function returning Result. But there’s no safe way to wrap asynchronous code so you can call it from synchronous code and wait for the result.

    The Swift standard library intentionally omits this unsafe functionality — trying to implement it yourself can lead to problems like subtle races, threading issues, and deadlocks. When adding concurrent code to an existing project, work from the top down. Specifically, start by converting the top-most layer of code to use concurrency, and then start converting the functions and methods that it calls, working through the project’s architecture one layer at a time. There’s no way to take a bottom-up approach, because synchronous code can’t ever call asynchronous code.

    Apple

    Above the example is working fine. But If you try to wrap a async throws function using Result, It can’t. See below example.

    Asynchronous Sequences

    let handle = FileHandle.standardInput
    for try await line in handle.bytes.lines {
        print(line)
    }

    In the same way that you can use your own types in a forin loop by adding conformance to the Sequence protocol, you can use your own types in a forawaitin loop by adding conformance to the AsyncSequence protocol.

    Apple

    Calling Asynchronous Functions in Parallel

    • Call asynchronous functions with asynclet when you don’t need the result until later in your code. This creates work that can be carried out in parallel.
    • Both await and asynclet allow other code to run while they’re suspended.
    • In both cases, you mark the possible suspension point with await to indicate that execution will pause, if needed, until an asynchronous function has returned.
    Apple

    🤯 Tasks and Task Groups

    func downloadPhoto(from url: String) async throws -> Data {
        //Download image
        try await URLSession.shared.data(from: URL(string: url)!).0
    }
    
    Task {
        let photoUrls = [
            "https://picsum.photos/200/300?grayscale",
            "https://picsum.photos/200",
            "https://picsum.photos/300"
        ]
        //async let, It implicitly create a new child task
        async let firstPhoto = downloadPhoto(from: photoUrls[0])
        async let secondPhoto = downloadPhoto(from: photoUrls[1])
        async let thirdPhoto = downloadPhoto(from: photoUrls[2])
        
        //3 child tasks are created
        let photos = try await [firstPhoto, secondPhoto, thirdPhoto]
    }

    Tasks are arranged in a hierarchy. Each task in a given task group has the same parent task, and each task can have child tasks. Because of the explicit relationship between tasks and task groups, this approach is called structured concurrency. The explicit parent-child relationships between tasks has several advantages:

    • In a parent task, you can’t forget to wait for its child tasks to complete.
    • When setting a higher priority on a child task, the parent task’s priority is automatically escalated.
    • When a parent task is canceled, each of its child tasks is also automatically canceled.
    • Task-local values propagate to child tasks efficiently and automatically.
    Apple

    Task Group

    Swift runs as many of these tasks concurrently as conditions allow.

    Apple
    Task {
        let photos = await withTaskGroup(of: Data.self) { group in
            let photoUrls = [
                "https://picsum.photos/200/300?grayscale",
                "https://picsum.photos/200",
                "https://picsum.photos/300"
            ]
            
            for photoUrl in photoUrls {
                group.addTask {
                    return try await downloadPhoto(from: photoUrl)
                }
            }
            
            var results: [Data] = []
            for await photo in group {
                results.append(photo)
            }
            return results
        }
    }

    Ops, There is an error. Because withTaskGroup doesn’t support error handling.

    withTaskGroup

    func withTaskGroup<ChildTaskResult, GroupResult>(
        of childTaskResultType: ChildTaskResult.Type,
        returning returnType: GroupResult.Type = GroupResult.self,
        body: (inout TaskGroup<ChildTaskResult>) async -> GroupResult
    ) async -> GroupResult where ChildTaskResult : Sendable

    withThrowingTaskGroup

    Task {
        let photos = try await withThrowingTaskGroup(of: Data.self) { group in
            let photoUrls = [
                "https://picsum.photos/200/300?grayscale",
                "https://picsum.photos/200",
                "https://picsum.photos/300"
            ]
            
            for photoUrl in photoUrls {
                //creates child tasks
                group.addTask {
                    return try await downloadPhoto(from: photoUrl)
                }
            }
            
            var results: [Data] = []
            for try await photo in group {
                results.append(photo)
            }
            return results
        }
    }
    

    forawaitin loop waits for the next child task to finish, appends the result of that task to the array of results, and then continues waiting until all child tasks have finished. Finally, the task group returns the array of downloaded photos as its overall result.

    Apple

    I fixed it by using withThrowingTaskGroup

    Task Cancellation

    Swift concurrency uses a cooperative cancellation model. Each task checks whether it has been canceled at the appropriate points in its execution, and responds to cancellation appropriately. Depending on what work the task is doing, responding to cancellation usually means one of the following:

    • Throwing an error like CancellationError
    • Returning nil or an empty collection
    • Returning the partially completed work

    Downloading pictures could take a long time if the pictures are large or the network is slow. To let the user stop this work, without waiting for all of the tasks to complete, the tasks need check for cancellation and stop running if they are canceled. There are two ways a task can do this: by calling the Task.checkCancellation() method, or by reading the Task.isCancelled property.

    Calling 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. For more flexibility, use the isCancelled property, which lets you perform clean-up work as part of stopping the task, like closing network connections and deleting temporary files.

    Apple
    Task {
        let photos = try await withThrowingTaskGroup(of: Optional<Data>.self) { group in
            let photoUrls = [
                "https://picsum.photos/200/300?grayscale",
                "https://picsum.photos/200",
                "https://picsum.photos/300"
            ]
            
            for photoUrl in photoUrls {
                group.addTaskUnlessCancelled {
                    return try await downloadPhoto(from: photoUrl)
                }
            }
            
            var results: [Data] = []
            for try await photo in group {
                if let photo {
                    results.append(photo)
                }
                print("🟢 downloaded")
            }
            return results
        }
    }
    • Each task is added using the TaskGroup.addTaskUnlessCancelled(priority:operation:) method, to avoid starting new work after cancellation.
    • Each task checks for cancellation before starting to download the photo. If it has been canceled, the task returns nil. <- 🤔 Need to check…
    • At the end, the task group skips nil values when collecting the results. Handling cancellation by returning nil means the task group can return a partial result — the photos that were already downloaded at the time of cancellation — instead of discarding that completed work.

    If parent task has cancelled, all the child tasks are also cancelled.

    What if I cancel a child task?

    It seems cancelling a child task doesn’t cancel the parent task.

    Let’s remind Task cancellation.

    There are two ways a task can do this: by calling the Task.checkCancellation() method, or by reading the Task.isCancelled property. Calling 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. For more flexibility, use the isCancelled property, which lets you perform clean-up work as part of stopping the task, like closing network connections and deleting temporary files.

    Apple

    Unstructured Concurrency

    Unlike tasks that are part of a task group, an unstructured task doesn’t have a parent task. You have complete flexibility to manage unstructured tasks in whatever way your program needs, but you’re also completely responsible for their correctness. To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:)initializer. To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:) class method. Both of these operations return a task that you can interact with — for example, to wait for its result or to cancel it.

    Apple

    Apple document’s example

    let newPhoto = // ... some photo data ...
    let handle = Task {
        return await add(newPhoto, toGalleryNamed: "Spring Adventures")
    }
    let result = await handle.value

    My example

    let unstructuredTask = Task { () -> Data in
        return try! await URLSession.shared.data(from: URL(string: "https://picsum.photos/100")!).0
    }
    let firstPhoto = await unstructuredTask.value

    Task Closure life cycle

    Tasks are initialized by passing a closure containing the code that will be executed by a given task.

    After this code has run to completion, the task has completed, resulting in either a failure or result value, this closure is eagerly released.

    Retaining a task object doesn’t indefinitely retain the closure, because any references that a task holds are released after the task completes. Consequently, tasks rarely need to capture weak references to values.

    For example, in the following snippet of code it is not necessary to capture the actor as weak, because as the task completes it’ll let go of the actor reference, breaking the reference cycle between the Task and the actor holding it.

    Note that there is nothing, other than the Task’s use of self retaining the actor, And that the start method immediately returns, without waiting for the unstructured Taskto finish. So once the task completes and its the closure is destroyed, the strong reference to the “self” of the actor is also released allowing the actor to deinitialize as expected.

    Apple
    struct Work: Sendable {}
    
    
    actor Worker {
        var work: Task<Void, Never>?
        var result: Work?
    
    
        deinit {
            assert(work != nil)
            // even though the task is still retained,
            // once it completes it no longer causes a reference cycle with the actor
    
    
            print("deinit actor")
        }
    
    
        func start() {
            //unstructured Task
            work = Task {
                print("start task work")
                try? await Task.sleep(for: .seconds(3))
                self.result = Work() // we captured self
                print("completed task work")
                // but as the task completes, this reference is released
            }
            // we keep a strong reference to the task
        }
    }
    
    await Actor().start()
    
    //Prints
    start task work
    completed task work
    deinit actor

    Actors

    sometimes you need to share some information between tasks. Actors let you safely share information between concurrent code.

    Like classes, actors are reference types, so the comparison of value types and reference types in Classes Are Reference Types applies to actors as well as classes. Unlike classes, actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor. For example, here’s an actor that records temperatures:

    You introduce an actor with the actor keyword, followed by its definition in a pair of braces. The TemperatureLogger actor has properties that other code outside the actor can access, and restricts the max property so only code inside the actor can update the maximum value.

    Apple
    actor TemperatureLogger {
        let label: String
        var measurements: [Int]
        private(set) var max: Int
    
    
        init(label: String, measurement: Int) {
            self.label = label
            self.measurements = [measurement]
            self.max = measurement
        }
    }
    
    let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
    
    print(await logger.max)
    //Prints "25"
    
    print(logger.max)  // Error, Should use await
    

    You create an instance of an actor using the same initializer syntax as structures and classes. When you access a property or method of an actor, you use await to mark the potential suspension point. For example:

    In this example, accessing logger.max is a possible suspension point. Because the actor allows only one task at a time to access its mutable state, if code from another task is already interacting with the logger, this code suspends while it waits to access the property.

    Apple
    extension TemperatureLogger {
        func update(with measurement: Int) {
        //part of the actor doesn’t write await when accessing the actor’s properties
            measurements.append(measurement)
            //At here, temporary inconsistent state.
            if measurement > max {
                max = measurement
            }
        }
    }
    
    
    1. Your code calls the update(with:) method. It updates the measurements array first.
    2. Before your code can update max, code elsewhere reads the maximum value and the array of temperatures.
    3. Your code finishes its update by changing max.

    In this case, the code running elsewhere would read incorrect information because its access to the actor was interleaved in the middle of the call to update(with:) while the data was temporarily invalid. You can prevent this problem when using Swift actors because they only allow one operation on their state at a time, and because that code can be interrupted only in places where await marks a suspension point. Because update(with:) doesn’t contain any suspension points, no other code can access the data in the middle of an update.

    Apple

    actor isolation

     Swift guarantees that only code running on an actor can access that actor’s local state. This guarantee is known as actor isolation.

    The following aspects of the Swift concurrency model work together to make it easier to reason about shared mutable state:

    • Code in between possible suspension points runs sequentially, without the possibility of interruption from other concurrent code.
    • Code that interacts with an actor’s local state runs only on that actor.
    • An actor runs only one piece of code at a time.

    Because of these guarantees, code that doesn’t include await and that’s inside an actor can make the updates without a risk of other places in your program observing the temporarily invalid state. For example, the code below converts measured temperatures from Fahrenheit to Celsius:

    Apple
    extension TemperatureLogger {
        func convertFahrenheitToCelsius() {
            measurements = measurements.map { measurement in
                (measurement - 32) * 5 / 9
            }
        }
    }

    writing code in an actor that protects temporary invalid state by omitting potential suspension points, you can move that code into a synchronous method. The convertFahrenheitToCelsius() method above is a synchronous method, so it’s guaranteed to never contain potential suspension points

    Apple

    Sendable Types

    Inside of a task or an instance of an actor, the part of a program that contains mutable state, like variables and properties, is called a concurrency domain.

    You mark a type as being sendable by declaring conformance to the Sendableprotocol. That protocol doesn’t have any code requirements, but it does have semantic requirements that Swift enforces. In general, there are three ways for a type to be sendable:

    • The type is a value type, and its mutable state is made up of other sendable data — for example, a structure with stored properties that are sendable or an enumeration with associated values that are sendable.
    • The type doesn’t have any mutable state, and its immutable state is made up of other sendable data — for example, a structure or class that has only read-only properties.
    • The type has code that ensures the safety of its mutable state, like a class that’s marked @MainActor or a class that serializes access to its properties on a particular thread or queue.
    • Value types
    • Reference types with no mutable storage
    • Reference types that internally manage access to their state
    • Functions and closures (by marking them with @Sendable)
    Apple
    struct TemperatureReading: Sendable {
        var measurement: Int
    }
    
    
    extension TemperatureLogger {
        func addReading(from reading: TemperatureReading) {
            measurements.append(reading.measurement)
        }
    }
    
    
    let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
    let reading = TemperatureReading(measurement: 45)
    await logger.addReading(from: reading)

    Sendable Classes

    • Be marked final
    • Contain only stored properties that are immutable and sendable
    • Have no superclass or have NSObject as the superclass

    Classes marked with @MainActor are implicitly sendable, because the main actor coordinates all access to its state. These classes can have stored properties that are mutable and nonsendable.

    Apple

    Sendable Functions and Closures

    Instead of conforming to the Sendable protocol, you mark sendable functions and closures with the @Sendable attribute. Any values that the function or closure captures must be sendable. In addition, sendable closures must use only by-value captures, and the captured values must be of a sendable type.

    In a context that expects a sendable closure, a closure that satisfies the requirements implicitly conforms to Sendable — for example, in a call to Task.detached(priority:operation:).

    You can explicitly mark a closure as sendable by writing @Sendable as part of a type annotation, or by writing @Sendable before the closure’s parameters — for example:

    Apple
    let sendableClosure = { @Sendable (number: Int) -> String in
        if number > 12 {
            return "More than a dozen."
        } else {
            return "Less than a dozen"
        }
    }

    Sendable Tuples

    To satisfy the requirements of the Sendable protocol, all of the elements of the tuple must be sendable. Tuples that satisfy the requirements implicitly conform to Sendable. by Apple

    Sendable Metatypes

    Metatypes such as Int.Type implicitly conform to the Sendable protocol.

  • Swift, OperationQueue

    Swift, OperationQueue

    ✍️ Note

    Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    OperationQueue

    An operation queue invokes its queued Operation objects based on their priority and readiness. After you add an operation to a queue, it remains in the queue until the operation finishes its task. You can’t directly remove an operation from a queue after you add it.

    Operation queues retain operations until the operations finish, and queues themselves are retained until all operations are finished. Suspending an operation queue with operations that aren’t finished can result in a memory leak.

    Apple

    Operation

    Operation objects are synchronous by default. In a synchronous operation, the operation object does not create a separate thread on which to run its task

    Apple
    class DownloadImageOperation: Operation {
        var dataTask: URLSessionDataTask!
        init(url: URL) {
            super.init()
            dataTask = URLSession.shared.dataTask(with: url) { [unowned self] data, response, error in
                print("\(self.name!) downloaded")
            }
        }
        
        override func start() {
            super.start()
            print("\(self.name!) started")
            dataTask.resume()
        }
        
        override func cancel() {
            super.cancel()
        }
    }
    let operationQueue = OperationQueue()
    
    let firstOperation = DownloadImageOperation(url: URL(string: "https://picsum.photos/200/300?grayscale")!)
    firstOperation.name = "first"
    
    let secondOperation = DownloadImageOperation(url: URL(string: "https://picsum.photos/200/300?grayscale")!)
    secondOperation.name = "second"
    
    //first operation will not start until second operation start.
    firstOperation.addDependency(secondOperation)
    operationQueue.addOperations([secondOperation, firstOperation], waitUntilFinished: false)
    
    
    //Prints
    second started
    first started
    second downloaded
    first downloaded

    The first operation depends on the second operation. This means it can’t be started if the second operation hasn’t started yet.

    operationQueue.addOperations([firstOperation, secondOperation], waitUntilFinished: false)

    If you add firstOperation first, the results are the same. second started and then first started.

    Cancel Operation

    class DownloadImageOperation: Operation {
        var dataTask: URLSessionDataTask!
        init(url: URL) {
            super.init()
            dataTask = URLSession.shared.dataTask(with: url) { [unowned self] data, response, error in
                print("🟢 \(self.name!) downloaded")
            }
        }
        
        override func start() {
            super.start()
            print("🟢 \(self.name!) started")
            dataTask.resume()
        }
        
        override func cancel() {
            super.cancel()
            print("🔴 \(self.name!) canceled")
        }
    }
    
    let operationQueue = OperationQueue()
    let firstOperation = DownloadImageOperation(url: URL(string: "https://picsum.photos/200/300?grayscale")!)
    firstOperation.name = "first"
    
    let secondOperation = DownloadImageOperation(url: URL(string: "https://picsum.photos/200/300?grayscale")!)
    secondOperation.name = "second"
    
    firstOperation.addDependency(secondOperation)
    
    operationQueue.addOperations([firstOperation, secondOperation], waitUntilFinished: false)
    operationQueue.cancelAllOperations()
    

    When you cancel operations, operations will start and finish the task. And then It removed from operationQueue.

  • Swift, Rethrows

    Swift, Rethrows

    Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations/#Rethrowing-Functions-and-Methods

    What is rethrows in Swift?

    A function or method can be declared with the rethrows keyword to indicate that it throws an error only if one of its function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.

    Apple

    Let’s check Foundation’s map function. A transform is a rethrowing function parameter.

    It means transform function can throwing an error.

    A rethrowing function or method can contain a throw statement only inside a catch clause. This lets you call the throwing function inside a docatch statement and handle errors in the catch clause by throwing a different error. In addition, the catch clause must handle only errors thrown by one of the rethrowing function’s throwing parameters. For example, the following is invalid because the catch clause would handle the error thrown by alwaysThrows().

    A throwing method can’t override a rethrowing method, and a throwing method can’t satisfy a protocol requirement for a rethrowing method. That said, a rethrowing method can override a throwing method, and a rethrowing method can satisfy a protocol requirement for a throwing method.

    Apple

    alwaysThrows function is not a function parameter. In a someFunction, only callback function parameter can throwing a error.

    enum SomeError: Error {
        case error
    }
    
    enum AnotherError: Error {
        case error
    }
    
    func alwaysThrows() throws {
        throw SomeError.error
    }

    Example 1. This one it working fine.

    func someFunction(callback: () throws -> Void) rethrows {
        do {
            try callback()
        } catch {
            throw AnotherError.error
        }
    }

    Example 2. This one also working fine

    func someFunction(callback: () throws -> Void) rethrows {
        try callback()
    }

    Example 3. This one compile error (A function declared ‘rethrows’ may only throw if its parameter does)

    func someFunction(callback: () throws -> Void) rethrows {
        do {
            try alwaysThrows()
        } catch {
            throw AnotherError.error
        }
    }

    Because alwaysThrows function is not a part of someFunction’s parameter.

    Example 4. Use try in a closure

    When you use try in a closure, You should marked try someFunction

    try someFunction {
        let decoder = JSONDecoder()
        try decoder.decode(String.self, from: Data())
        print("someFunction")
    }

    Example 5. Call a closure without using try

    If you don’t use try in a closure, It’s okay not marking try at someFuntion.

    If I change rethrows to throws in the example above, what happens?

    There is a compile error. Always use try keyword when you call a someFunction.

    Let’s make a customMap using rethrows keyword.

    Built-In map function uses rethrows keyword.

    var input = [1, 2, 3, 4, 5]
    
    extension Array {
        func customMap<T>(_ transform: ((Element) throws -> T)) rethrows -> [T] {
            var result = [T]()
            for item in self {
                let transformedValue = try transform(item)
                result.append(transformedValue)
            }
            return result
        }
    }
    
    let output = input.customMap { item in
        "\(item)"
    }
    
    let output2 = input.map { item in
        "\(item)"
    }
    

    map itself uses try keyword. Because It allows users to use try in a closure. It we are not using try keyword in a closure we can simply use it without try keyword.

  • iOS, RunLoop

    iOS, RunLoop

    Some codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    https://developer.apple.com/documentation/foundation/runloop

    Your code provides the control statements used to implement the actual loop portion of the run loop—in other words, your code provides the while or for loop that drives the run loop. Within your loop, you use a run loop object to “run” the event-processing code that receives events and calls the installed handlers.

    A run loop receives events from two different types of sources. Input sources deliver asynchronous events, usually messages from another thread or from a different application. Timer sources deliver synchronous events, occurring at a scheduled time or repeating interval. Both types of source use an application-specific handler routine to process the event when it arrives.

    Figure 3-1 shows the conceptual structure of a run loop and a variety of sources. The input sources deliver asynchronous events to the corresponding handlers and cause the runUntilDate: method (called on the thread’s associated NSRunLoop object) to exit. Timer sources deliver events to their handler routines but do not cause the run loop to exit.

    In addition to handling sources of input, run loops also generate notifications about the run loop’s behavior. Registered run-loop observers can receive these notifications and use them to do additional processing on the thread. You use Core Foundation to install run-loop observers on your threads.

    The following sections provide more information about the components of a run loop and the modes in which they operate. They also describe the notifications that are generated at different times during the handling of events.

    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

    RunLoop.mode

    common

    When you add an object to a run loop using this mode, the runloop monitors the object when running in any of the common modes. For details about adding a runloop mode to the set of common modes,

    Apple

    default

    • for handling input sources

    perform a task

    RunLoop.current.perform {
        print("Hello RunLoop")
    }
    
    RunLoop.main.perform {
        print("Hellow?")
    }

    current

    • Returns the run loop for the current thread.

    main

    • Returns the run loop of the main thread.

    Add Timer to run a code at specific time

    class RunLoopTest {
        var timer: Timer!
        init() {
            let date = Date.now.addingTimeInterval(5)
            
            //After 5 secs from not, timer will fire an event, every 2 seconds It run a function
            timer = Timer(
                fireAt: date,
                interval: 2,
                target: self,
                selector: #selector(run),
                userInfo: ["time": date],
                repeats: true
            )
            RunLoop.main.add(timer, forMode: .common)
        }
        
        @objc func run() {
            print("Hellow? : \(timer.userInfo)")
        }
        
        func stop() {
            timer.invalidate()
        }
    }
    
    RunLoopTest()
    
    //Prints
    Hellow? : Optional({
        time = "2024-03-11 15:25:30 +0000";
    })
    Hellow? : Optional({
        time = "2024-03-11 15:25:30 +0000";
    })
    Hellow? : Optional({
        time = "2024-03-11 15:25:30 +0000";
    })
    
    

    Run a timer in a RunLoop while scrolling the table

    import UIKit
    
    class ViewController: UIViewController {
        var timer: Timer!
        var firedCount = 0
        
        lazy var tableView: UITableView = {
           let tableView = UITableView()
            tableView.dataSource = self
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
            return tableView
        }()
        
        lazy var items: [Int] = {
           var items = [Int]()
            for i in 0...1000 {
                items.append(i)
            }
            return items
        }()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            timer = Timer(
                fireAt: Date.now.addingTimeInterval(5),
                interval: 0.2,
                target: self,
                selector: #selector(run),
                userInfo: nil,
                repeats: true
            )
            RunLoop.main.add(timer, forMode: .default)
            tableView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(tableView)
            NSLayoutConstraint.activate([
                tableView.topAnchor.constraint(equalTo: view.topAnchor),
                tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        }
        
        @objc func run() {
            print("🔥 timer fired: \(firedCount)")
            firedCount += 1
        }
    }
    
    extension ViewController: UITableViewDataSource {
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            items.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
            cell.textLabel?.text = "\(items[indexPath.row])"
            return cell
        }
    }
    
    //Prints
    🔥 timer fired: 0
    
    🔥 timer fired: 1
    
    🔥 timer fired: 2
    
    🔥 timer fired: 3
    🔥 timer fired: 4
    🔥 timer fired: 5
    🔥 timer fired: 6
    

    Example 1. RunLoop Mode is defaults

    Above the code, timer starts after 5 secs. And I scrolling the table for 20 secs.

    Example 2. Changed RunLoop Mode to common

    Now I understand what RunLoop mode is…

    When you use default mode, timer might not fired if user touching the screens. defaults mode handles Input source.

  • Swift, Sorting and Binary Search Algorithm

    Swift, Sorting and Binary Search Algorithm

    Useful Foundation APIs

    swapAt

    • swap the value in an array by passing the index

    Bubble Sort

    func bubbleSort(input: inout [Int]) {
        for i in 0..<input.count {
            for j in 1..<input.count {
                if input[j] < input[j - 1] {
                    input.swapAt(j, j-1)
                }
            }
        }
    }
    
    var input = [4, 5, 2, 1, 6, 8, 9, 12, 13]
    bubbleSort(input: &input)

    Every steps compares 2 values and swapped it

    Red = Sorted Item

    Time complexity is O(n^2)

    Space complexity is O(1)

    Insertion Sort

    Apple Foundation uses TimSort (InsertionSort + MergeSort)

    Insertion Sort is another O(N^2) quadratic running time algorithm

    On large datasets it is very inefficient – but on arrays with 10-20 items it is quite good. (Apple uses TimSort, It items is less than 64 then use Insertion Sort. -> https://github.com/apple/swift/blob/387580c995fc9844d4f268723bd55e22440b1a3d/stdlib/public/core/Sort.swift#L462)

    A huge advantage is that it is easy to implement

    It is more efficient than other quadratic running time sorting procedures such as bubble sort or selection sort

    It is an adaptive algorithm – It speeds up when array is already substantially sorted

    It is stable so preserves the order of the items with equal keys

    Insertion sort is an in-place algorithm – does not need any additional memory

    It is an online algorithm – It can sort an array as it receives the items for example downloading data from web

    Hybrid algorithms uses insertion sort if the subarray is small enough: Insertion sort is faster for small subarrays than quicksort

    Variant of insertion sort is shell sort

    Sometimes selection sort is better: they are very similar algorithms

    Insertion sort requires more writes because the inner loop can require shifting large sections of the sorted portion of the array

    In general insertion sort will write to the array O(N^2) times while selection sort will write only O(N) times

    For this reason selection sort may be preferable in cases where writing to memory is significantly more expensive than reading (such as with flash memory)

    https://www.udemy.com/course/algorithms-and-data-structures-in-java-part-ii/learn/lecture/4901832#questions

    func insertSort(_ input: inout [Int]) {
        var i = 1
        for i in 1..<input.count {
            var j = i
            while j-1 >= 0, input[j] < input[j-1] {
                input.swapAt(j, j-1)
                j-=1
            }
        }
    }
    var input = [-9, 4, -9, 5, 8, 12]
    insertSort(&input)
    print(input)
    
    
    var input2 = [-9, 4, -9, 5, 8, 12, -249, 241, 4, 2, 12, 24140, 539, 3, 0, -2314]
    insertSort(&input2)
    print(input2)

    Merge Sort

    Divide and Conquer

    Break down into small subproblem, Use recursion.

    It needs two functions

    • split – divide into two array
    • merge – merging two array into the sorted array
    func mergeSort(input: inout [Int]) {
        //Base case
        if input.count == 1 {
            return
        }
        let midIndex = input.count / 2
        var left = Array(repeating: 0, count: midIndex)
        var right = Array(repeating: 0, count: input.count - midIndex)
        
        //Split into 2 sub array (create sub problems)
        split(input: &input, left: &left, right: &right)
        
        //Recursive - Divide and Conquer
        mergeSort(input: &left)
        mergeSort(input: &right)
        
        //Merge all together
        merge(input: &input, left: &left, right: &right)
    }
    func split(input: inout [Int], left: inout [Int], right: inout [Int]) {
        let leftCount = left.count
        for (index, item) in input.enumerated() {
            if index < leftCount {
                left[index] = item
            }
            else {
                right[index - leftCount] = item
            }
        }
    }
    func merge(input: inout [Int], left: inout [Int], right: inout [Int]) {
        var mergeIndex = 0
        var leftIndex = 0
        var rightIndex = 0
        
        while (leftIndex < left.count && rightIndex < right.count) {
            if left[leftIndex] < right[rightIndex] {
                input[mergeIndex] = left[leftIndex]
                leftIndex += 1
            }
            else if rightIndex < right.count {
                input[mergeIndex] = right[rightIndex]
                rightIndex += 1
            }
            mergeIndex += 1
        }
        if leftIndex < left.count {
            while mergeIndex < input.count {
                input[mergeIndex] = left[leftIndex]
                mergeIndex += 1
                leftIndex += 1
            }
        }
        if rightIndex < right.count {
            while mergeIndex < input.count {
                input[mergeIndex] = right[rightIndex]
                mergeIndex += 1
                rightIndex += 1
            }
        }
    }

    Split Function

    Calculate midIndex

    Split them into two subarray

    Time complexity: O(nlogn)

    Space complexity: O(n)

    • Because It needs a copy values when merging two array into the one sorted array

    Quick Sort

    func partition(_ input: inout [Int], low: Int, high: Int) -> Int {
        let pivot = input[low]
        var l = low
        var h = high
        while l < h {
            while (input[l] <= pivot && l < h) {
                l += 1
            }
            while (input[h] > pivot) {
                h -= 1
            }
            if l < h {
                input.swapAt(l, h)
            }
        }
        //Put pivot to the right position
        input.swapAt(low, h)
        return h
    }
    
    func quickSort(_ input: inout [Int], low: Int, high: Int) {
        //base
        if low >= high {
            return
        }
        //pivot
        let pivot = partition(&input, low: low, high: high)
        
        //left
        quickSort(&input, low: low, high: pivot - 1)
    
        //right
        quickSort(&input, low: pivot + 1, high: high)
    }
    
    var input = [2, 3, 1, 4, 9]
    quickSort(&input, low: 0, high: input.count - 1)
    print(input)

    Quick Sort is also Divide and Conquer.

    Time Complexity

    • Average Time Complexity is O(nlogn)

    Space Complexity

    • O(logn) -> Extra space for the call stack in the recursive call
    • O(n) -> worst case

    Binary Search

    Search a sorted list!

    //Binary Search
    func findIndex(array: [Int], value: Int) -> Int {
        var min = 0
        var max = array.count - 1
        
        while min <= max {
            let mid = min + (max - min) / 2
            
            if value == array[mid] {
                return mid
            }
            //search right side
            if value > array[mid] {
                min = mid - 1
            }
            //search left side
            else {
                max = mid + 1
            }
        }
        return -1
    }
    
    let sortedArray = Array(0...100)
    findIndex(array: sortedArray, value: 49)
    findIndex(array: sortedArray, value: 78)
    findIndex(array: sortedArray, value: 22)
    findIndex(array: sortedArray, value: 89)
  • Swift, DispatchQueue

    Swift, DispatchQueue

    ✍️ Note

    All the codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    Apple documents

    DispatchQueue

    QoS (Quality of Service)

    • UserInteractive
      • Animations, event handling, or updates to your app’s user interface.
      • User-interactive tasks have the highest priority on the system. Use this class for tasks or queues that interact with the user or actively update your app’s user interface. For example, use this class for animations or for tracking events interactively.
    • UserInitiated
      • Prevent the user from actively using your app
      • User-initiated tasks are second only to user-interactive tasks in their priority on the system. Assign this class to tasks that provide immediate results for something the user is doing, or that would prevent the user from using your app. For example, you might use this quality-of-service class to load the content of an email that you want to display to the user.
    • Default
      • Default tasks have a lower priority than user-initiated and user-interactive tasks, but a higher priority than utility and background tasks. Assign this class to tasks or queues that your app initiates or uses to perform active work on the user’s behalf.
    • Utility
      • Utility tasks have a lower priority than default, user-initiated, and user-interactive tasks, but a higher priority than background tasks. Assign this quality-of-service class to tasks that do not prevent the user from continuing to use your app. For example, you might assign this class to long-running tasks whose progress the user does not follow actively.
    • Background
      • Background tasks have the lowest priority of all tasks. Assign this class to tasks or dispatch queues that you use to perform work while your app is running in the background.

    let concurrentQueue = DispatchQueue(label: "concurrent", qos: .userInitiated, attributes: 
    .concurrent)
    let serialQueue = DispatchQueue(label: "serial", qos: . userInitiated)

    Example 1. Perform async tasks on serialQueue

    for i in 0...3 {
      serialQueue.async {
        print("serial task(\(i)) start")
        sleep(1)
        print("serial task(\(i)) end")
      }
    }
    
    //prints
    serial task(0) start
    serial task(0) end
    
    serial task(1) start
    serial task(1) end
    
    serial task(2) start
    serial task(2) end
    
    serial task(3) start
    serial task(3) end

    It’s make sense because serialQueue can run a one task at a time.

    Example 2. Perform sync tasks on serialQueue

    for i in 0...3 {
      serialQueue.sync {
        print("serial task(\(i)) start")
        sleep(1)
        print("serial task(\(i)) end")
      }
    }
    
    //prints
    serial task(0) start
    serial task(0) end
    
    serial task(1) start
    serial task(1) end
    
    serial task(2) start
    serial task(2) end
    
    serial task(3) start
    serial task(3) end

    Results are the same as example 1

    Submits a work item for execution on the current queue and returns after that block finishes executing.

    https://developer.apple.com/documentation/dispatch/dispatchqueue/2016083-sync

    Example 3. Perform a sync task on the concurrentQueue

    for i in 0...3 {
      concurrentQueue.sync {
        print("concurrent task(\(i)) start")
        sleep(1)
        print("concurrent task(\(i)) end")
      }
    }
    
    //Prints
    concurrent task(0) start
    concurrent task(0) end
    
    concurrent task(1) start
    concurrent task(1) end
    
    concurrent task(2) start
    concurrent task(2) end
    
    concurrent task(3) start
    concurrent task(3) end

    Example 4. Perform a async task on the concurrentQueue

    for i in 0...3 {
      concurrentQueue.async {
        print("concurrent task(\(i)) start")
        sleep(1)
        print("concurrent task(\(i)) end")
      }
    }
    
    //Prints
    concurrent task(0) start
    concurrent task(3) start
    concurrent task(1) start
    concurrent task(2) start
    
    concurrent task(0) end
    concurrent task(1) end
    concurrent task(3) end
    concurrent task(2) end

    Schedules a work item for immediate execution, and returns immediately.

    https://developer.apple.com/documentation/dispatch/dispatchqueue/2016103-async

    It immediate execution and returns immediately.

    Example 5. Perform async task on the concurrentQueue and sync task on the serialQueue

    for i in 0...3 {
      concurrentQueue.async {
        print("concurrent task(\(i)) start")
        sleep(1)
        print("concurrent task(\(i)) end")
      }
                
      serialQueue.sync {
        print("serial task(\(i)) start")
        sleep(1)
        print("serial task(\(i)) end")
      }
    }
    
    //Prints
    concurrent task(0) start
    serial task(0) start
    serial task(0) end
    serial task(1) start
    concurrent task(1) start
    concurrent task(0) end
    concurrent task(1) end
    serial task(1) end
    serial task(2) start
    concurrent task(2) start
    serial task(2) end
    serial task(3) start
    concurrent task(3) start
    concurrent task(2) end
    serial task(3) end
    concurrent task(3) end

    Example 6. Run async tasks on the concurrentQueue and serialQueue

    for i in 0...3 {
       concurrentQueue.async {
         print("concurrent task(\(i)) start")
         sleep(1)
         print("concurrent task(\(i)) end")
       }
    
       serialQueue.async {
         print("serial task(\(i)) start")
         sleep(1)
         print("serial task(\(i)) end")
       }
    }
    
    //Prints
    concurrent task(0) start
    concurrent task(2) start
    concurrent task(3) start
    serial task(0) start
    concurrent task(1) start
    concurrent task(3) end
    concurrent task(0) end
    concurrent task(2) end
    serial task(0) end
    concurrent task(1) end
    serial task(1) start
    serial task(1) end
    serial task(2) start
    serial task(2) end
    serial task(3) start
    serial task(3) end

    As you can see a SerialQueue run a task and returned when task has finished either perform it on sync or async.

    What about dispatchQueue.main.async?

    for i in 0...3 {
      DispatchQueue.main.async {
        print("main task(\(i)) start")
        sleep(1)
        print("main task(\(i)) end")
      }
    }
    
    //Prints
    main task(0) start
    main task(0) end
    
    main task(1) start
    main task(1) end
    
    main task(2) start
    main task(2) end
    
    main task(3) start
    main task(3) end

    The dispatch queue associated with the main thread of the current process.

    The system automatically creates the main queue and associates it with your application’s main thread. Your app uses one (and only one) of the following three approaches to execute blocks submitted to the main queue:

    As with the global concurrent queues, calls to suspend()resume()dispatch_set_context(_:_:), and the like have no effect when used on the queue in this property.

    https://developer.apple.com/documentation/dispatch/dispatchqueue/1781006-main

    As the results, It is not returns immediately like a concurrent queue. Because It’s a serial queue. You can check it on Xcode.

    What about dispatchQueue.global().async?

    for i in 0...3 {
      DispatchQueue.global().async {
        print("global task(\(i)) start")
        sleep(1)
        print("global task(\(i)) end")
      }
    }
    
    //Prints
    global task(3) start
    global task(2) start
    global task(0) start
    global task(1) start
    
    global task(1) end
    global task(0) end
    global task(3) end
    global task(2) end

    This method returns a queue suitable for executing tasks with the specified quality-of-service level. Calls to the suspend()resume(), and dispatch_set_context(_:_:) functions have no effect on the returned queues.

    Tasks submitted to the returned queue are scheduled concurrently with respect to one another

    https://developer.apple.com/documentation/dispatch/dispatchqueue/2300077-global

    🤯 When function is returned?

    It’s a good example for understanding sync vs async.

    Before look at the example, let’s remind

    Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system. Except for the dispatch queue representing your app’s main thread, the system makes no guarantees about which thread it uses to execute a task.

    You schedule work items synchronously or asynchronously. When you schedule a work item synchronously, your code waits until that item finishes execution. When you schedule a work item asynchronously, your code continues executing while the work item runs elsewhere.

    https://developer.apple.com/documentation/dispatch/dispatchqueue

    ConcurrentQueue with sync

    runWorkItem function is returned when after finishing the sync task.

    ConcurrentQueue with async

    runWorkItem function returned and then async task starts

    SerialQueue with async

    DispatchQueue.main.async

    Custom Serial DispatchQueue

    SerialQueue with sync but what if a submitted task is delayed?

    Keep in mind

    Work submitted to dispatch queues executes on a pool of threads managed by the system. Except for the dispatch queue representing your app’s main thread, the system makes no guarantees about which thread it uses to execute a task.

    https://developer.apple.com/documentation/dispatch/dispatchqueue

    sync -> block will run and returned when task finished

    async -> block will immediately returned when task started

    syncasyncwhen function returned?
    SerialQeueuewait until task finishedwait until task finishedeither sync or async it returned when after task finished
    ConcurrentQueuewait until task finishedimmediately return when task startedsync: it returned after task finished
    async: it returned before task finished
  • Swift, ARC Automatic Reference Counting

    Swift, ARC Automatic Reference Counting

    ✍️ Note

    All the codes and contents are sourced from Apple’s official documentation. This post is for personal notes where I summarize the original contents to grasp the key concepts

    Apple document

    class Person {
        let name: String
        init(name: String) {
            self.name = name
            print("\(name) is being initialized")
        }
        deinit {
            print("\(name) is being deinitialized")
        }
    }

    Check Strong Reference Count

    Example 1

    var person = Person(name: "Shawn") //1
    CFGetRetainCount(person) //2

    Example 2

    CFGetRetainCount(Person(name: "Shawn") //1

    Example 3

    var person = Person(name: "Shawn") //2
    var person2 = person //3
    var person3 = person //4
    
    CFGetRetainCount(person) //4

    As you can see when you initiate an object, the retain count is 1. And when you assign it, retain count will be counting up.

    Strong Reference Cycle between classes

    class Person {
        let name: String
        init(name: String) { self.name = name }
        var apartment: Apartment?
        deinit { print("\(name) is being deinitialized") }
    }
    
    
    class Apartment {
        let unit: String
        init(unit: String) { self.unit = unit }
        var tenant: Person?
        deinit { print("Apartment \(unit) is being deinitialized") }
    }

    Example 1

    var person = Person(name: "Shawn")
    var apt = Apartment(unit: "3")
    
    CFGetRetainCount(person) //2
    CFGetRetainCount(apt) //2

    Example 2 (🔥 Strong Reference Cycle)

    var person = Person(name: "Shawn")
    var apt = Apartment(unit: "3")
    
    person.apartment = apt
    apt.tenant = person
    
    CFGetRetainCount(person) //3 (apt.tenant -> count up)
    CFGetRetainCount(apt) //3 (person.apartment -> count up)
    

    ✅ Solution -> weak / unowned

    What is weak reference and when to use it?

    Use a weak reference when the other instance has a shorter lifetime — that is, when the other instance can be deallocated first.

    Apple – https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting

    ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. And, because weak references need to allow their value to be changed to nil at runtime, they’re always declared as variables, rather than constants, of an optional type.

    class Person {
        let name: String
        init(name: String) { self.name = name }
        var apartment: Apartment?
        deinit { print("\(name) is being deinitialized") }
    }
    
    
    class Apartment {
        let unit: String
        init(unit: String) { self.unit = unit }
        weak var tenant: Person?
        deinit { print("Apartment \(unit) is being deinitialized") }
    }

    What is unowned reference and when to use it?

    In contrast, use an unowned reference when the other instance has the same lifetime or a longer lifetime. Unlike a weak reference, an unowned reference is expected to always have a value. As a result, marking a value as unowned doesn’t make it optional, and ARC never sets an unowned reference’s value to nil.

    Important

    Use an unowned reference only when you are sure that the reference always refers to an instance that hasn’t been deallocated.

    If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.

    Apple – https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting
    class Customer {
        let name: String
        var card: CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit { print("\(name) is being deinitialized") }
    }
    
    
    class CreditCard {
        let number: UInt64
        unowned let customer: Customer
        init(number: UInt64, customer: Customer) {
            self.number = number
            self.customer = customer
        }
        deinit { print("Card #\(number) is being deinitialized") }
    }

    Example 1

    var customer: Customer! = Customer(name: "Shawn")
    customer.card = CreditCard(number: 7788_9999_1223_1225, customer: customer)
    
    CFGetRetainCount(customer) //2 (because CreditCard's customer is defined as unowned let)
    
    customer = nil
    
    //Shawn is being deinitialized
    //Card #7788999912231225 is being deinitialized 

    unowned(unsafe)

    Swift also provides unsafe unowned references for cases where you need to disable runtime safety checks — for example, for performance reasons. As with all unsafe operations, you take on the responsibility for checking that code for safety. You indicate an unsafe unowned reference by writing unowned(unsafe). If you try to access an unsafe unowned reference after the instance that it refers to is deallocated, your program will try to access the memory location where the instance used to be, which is an unsafe operation.

    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting

    When you access the unowned(unsafe), It try to access the memory location where the instance used to be

    when use unowned, error type is different.

    Unowned Optional References

    You can mark an optional reference to a class as unowned. In terms of the ARC ownership model, an unowned optional reference and a weak reference can both be used in the same contexts. The difference is that when you use an unowned optional reference, you’re responsible for making sure it always refers to a valid object or is set to nil.

    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting
    class Department {
        var name: String
        var courses: [Course]
        init(name: String) {
            self.name = name
            self.courses = []
        }
    }
    
    
    class Course {
        var name: String
        unowned var department: Department
        unowned var nextCourse: Course?
        init(name: String, in department: Department) {
            self.name = name
            self.department = department
            self.nextCourse = nil
        }
    }

    Example

    var department: Department! = Department(name: "Horticulture")
    
    let intro = Course(name: "Survey of Plants", in: department)
    var intermediate: Course! = Course(name: "Growing Common Herbs", in: department)
    var advanced: Course = Course(name: "Caring for Tropical Plants", in: department)
    
    intro.nextCourse = intermediate
    intermediate.nextCourse = advanced
    department.courses = [intro, intermediate, advanced]
    
    //Deinit department and intermediate
    department = nil
    intermediate = nil
    
    //Try to access nextCourse
    intro.nextCourse //SIGABRT Error

    Unlike weak, when you try to access the deallocated object, It causes a crash issue. Even you set it as unowned optionals.

    Unowned References and Implicitly Unwrapped Optional Properties

    class Country {
        let name: String
        var capitalCity: City! //Unwrapped Optional Properties
        init(name: String, capitalName: String) {
            self.name = name
            //At this point, Country already initiated. So can pass self to City initializer
            self.capitalCity = City(name: capitalName, country: self)
        }
    }
    
    
    class City {
        let name: String
        unowned let country: Country //Unowned References
        init(name: String, country: Country) {
            self.name = name
            self.country = country
        }
    }

    var capitalCity: City!

    • It’s initial value is nil

    Above the example is Two-Phased initialization

    🤔 Strong Reference Cycles for Closures

    It’s such an important topic.

    A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance. This capture might occur because the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.

    This strong reference cycle occurs because closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure. In essence, it’s the same problem as above — two strong references are keeping each other alive. However, rather than two class instances, this time it’s a class instance and a closure that are keeping each other alive.

    Swift provides an elegant solution to this problem, known as a closure capture list

    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting
    class HTMLElement {
        let name: String
        let text: String?
    
        lazy var asHTML: () -> String = {
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
    
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
    
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    print(paragraph!.asHTML())
    
    //Deallocate HTMLElement
    paragraph = nil // Can't deallocate it because the reference cycle between property (asHTML) and closure's capture list

    Even though the closure refers to self multiple times (self.text and self.name), it only captures one strong reference to the HTMLElement instance.

    Apple

    Resolving Strong Reference Cycles for Closures

    Use weak or unowned at capture lists

    Defining a capture list

    Each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance (such as self) or a variable initialized with some value (such as delegate = self.delegate). These pairings are written within a pair of square braces, separated by commas.

    Place the capture list before a closure’s parameter list and return type if they’re provided:

    Apple
    lazy var someClosure = {
            [unowned self, weak delegate = self.delegate]
            (index: Int, stringToProcess: String) -> String in
        // closure body goes here
    }
    
    
    lazy var someClosure2 = {
            [unowned self, weak delegate = self.delegate] in
        // closure body goes here
    }

    🔥 Think it again, weak vs unowned in Closures

    Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.

    Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. This enables you to check for their existence within the closure’s body.

    Apple
    class HTMLElement {
        let name: String
        let text: String?
        //Use unowned self to break reference cycle
        lazy var asHTML: () -> String = {
                [unowned self] in
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
    
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
    
        deinit {
            print("\(name) is being deinitialized")
        }
    }

    Capture value from nested Closure

    class Test {}
    DispatchQueue.global().async {
      let test = Test()
       print("closure A: Start retain count is: \(CFGetRetainCount(test))")
       DispatchQueue.global().async {
         print("nested closure B retainCount: \(CFGetRetainCount(test))")
       }
       print("closure A: End")
    }
    
    //Prints
    closure A: Start retain count is: 2
    closure A: End
    nested closure B retainCount: 3

    As you can see nested closure also counting up ARC.

    But… what about this example?

    test object was already deallocated. And when you trying to access test, It should not be nil because It’s unowned.

    Conclusion

    Understanding How ARC works is very important to avoid memory leaks and crash issues. It’s also good to know what happens when objects are deallocated and How ARC handles those objects.

  • [Draft] Python cheat sheet for iOS Engineer

    [Draft] Python cheat sheet for iOS Engineer

    I asked my self Is python worth to learn for iOS development? My answer is Yes.

    Because I can use it for

    • Build scripts
    • Core ML + coreml tools
    • BE for Mobile (Django)

    Getting Principles

    • You can read the list of the python principles by importing this
    import this

    Cheatsheet

    Standard LibrarySwiftPython
    variablevar name = "Shawn"name = "Shawn"
    constantslet name = "Shawn"Not support
    But UPPERCASED Naming indicates constants It’s like a implicit rule in Python
    string"Hello World!"

    Multiline String
    """
    Hello
    Swift
    """


    Extended Delimiters
    #"Hello\n Swift"#

    It will print Hello\n Swift
    "Hello World!"
    'Hello World!'


    Swift can’t use single quote but Python can use


    formatted stringlet name = "Shawn"
    "My name is \(name)"
    name = "Shawn"
    f"My name is {name}"


    f means formatted string
    capitalizedvar greeting = “shawn baek”
    print(greeting.capitalized)
    //’Shawn Baek’
    name = “shawn baek”
    print(name.title())

    //’Shawn Baek’
    uppercased / lowercasedvar greeting = “Shawn Baek”

    print(greeting.uppercased())
    //’SHAWN BAEK’

    print(greeting.lowercased())
    //’shawn baek’
    name = “Shawn Baek”
    print(name.upper())
    //’SHAWN BAEK’

    print(name.lower())
    //’shawn baek’
    trimmingCharacters(in: .whitespacesAndNewlines)var greeting = ” Shawn Baek “

    //MARK: Remove leading / trailing spaces

    greeting.trimmingCharacters(in: .whitespacesAndNewlines)

    //’Shawn Baek’
    name = ” Shawn Baek “

    name.rstrip()
    // ‘ Shawn Baek’

    name.lstrip()
    //’Shawn Baek ‘

    name.strip()
    //’Shawn Baek’
    trimPrefixvar blogUrl = “https://shawnbaek.com&#8221;
    blogUrl.trimPrefix(“https://&#8221😉

    //’shawnbaek.com’
    blog_url = ‘https://shawnbaek.com&#8217;

    blog_url.removeprefix(‘https://&#8217😉
    //’shawnbaek.com’
    powvar number = pow(3.0, 2.0)

    print(number)
    //9.0
    number = 3.0 ** 2

    print(number)
    //9.0
    comment//Hello World#Hello World

  • What is Dynamic Programming?

    What is Dynamic Programming?

    For me, DP problem is the most challenging problem when I faced it during the interview process. To understand it I summarize basic concepts and patterns.

    Simply It can be defined,

    Dynamic Programming = Complex problems -> smaller subproblem

    Dynamic Programming vs Divide and Conquer

    Key difference

    • Dynamic Programming – has overlapping sub problems

    Dynamic Programming Paradigm

    Optimal Substructure

    In computer science, a problem is said to have optimal substructure if an optimal solution can be constructed from optimal solutions of its subproblems. This property is used to determine the usefulness of greedy algorithms for a problem.

    WIKIPEDIA

    It can solve these kinds of problems

    • Greedy approach

    Bellman Equation (Combinatorial optimization)

    Bellman showed that a dynamic optimization problem in discrete time can be stated in a recursive, step-by-step form known as backward induction by writing down the relationship between the value function in one period and the value function in the next period. The relationship between these two value functions is called the “Bellman equation”.

    WIKIPEDIA

    It can solve these kinds of problems

    • 0-1 Knapsack problem

    What is overlapping subproblems?

    Let’s see fibonacci problem.

    fib(n) = fib(n-1) + fib(n-2)
    
    //Base case
    fib(0) = 0
    fib(1) = 1

    To get the fib(n), we need to call fib(n-1) and fib(n-2) and then sum it.

    Colored boxes are overlapped sub problems. Because It was called multiple times.

    How to avoid multiple calling overlapped subproblems?

    Answer: Save the result and use it without calling a function or recalculating it.

    Top-Down Approach: Memoization (Recursion)

    Memoization you can implement using Array or Dictionary. In Swift, You have to handle it carefully like out of bounds.

    class Fibonacci {
      func fib(_ n: Int) -> Int {
        var memoization = Array(repeating: 0, count: n + 1)
        func recursiveFib(
          _ n: Int, 
          memoization: inout [Int]
        ) -> Int {
          if memoization[n] != 0 {
            return memoization[n]
          }
          if n < 2 {
            memoization[n] = n
            return n
          }
          let sum = recursiveFib(n-1, memoization: &memoization) + recursiveFib(n-2, memoization: &memoization)
          memoization[n] = sum
          return sum
        }
        return recursiveFib(n, memoization: &memoization)
      }
    }
    
    let fibonacci = Fibonacci()
    let result = fibonacci.fib(7) //Result is 13

    I prefer to use dictionary for memoization. It’s more safe and easy to save the result of subproblems.

    class Fibonacci {
      var memoization = [Int: Int]()
      func fib(_ n: Int) -> Int {
        if let value = memoization[n] {
            return value
        }
        if n < 2 {
          memoization[n] = n
          return n
        }
        let sum = fib(n-1) + fib(n-2)
        memoization[n] = sum
        return sum
      }
    }
    
    let fibonacci = Fibonacci()
    let result = fibonacci.fib(7) //Result is 13
    

    Bottom-Up Approach: Tabulation (avoid recursion)

    This approach solve the bottom subproblems first and save the results. You don’t need to use recursion.

    class Fibonacci {
      func fib(_ n: Int) -> Int {
        var dp = Array(repeating: 0, count: n + 1)
        //base
        dp[0] = 0
        dp[1] = 1
    
        for i in 2...n {
          dp[i] = dp[i-1] + dp[i-2]
        }
        return dp[n]
      }
    }
    
    let fibonacci = Fibonacci()
    let result = fibonacci.fib(7) //Result is 13

    [Working in Progress] Dynamic Programming Problem Patterns

    Step 1. Understand problem

    Step 2. Check Is this problem can be solved by splitting into Sub Problems

    Step 3. Is Sub Problems are overlapped?

    Step 4. If Yes, It’s DP problem.

    Step 5. [Hardest Part] Define a logic when to take an item and do not take an item to reach the solution. -> Don’t memorize the solutions, all the problems we need to define a proper logic by understanding problem.

    Step 6. Give a solution (including all taken items)

    Knapsack Problem

    Divisible Knapsack Problem

    • Greedy approach can be used

    0-1 Knapsack Problem (consider should I take a item or not)

    • 0: Not take an item
    • 1: Take an item

    Rod Cutting Problem

    References

    https://www.udemy.com/share/101AYS3@4sd_kGNnngOfRfF3meBvqpARWBcWTlGxzqIQuKnKpxCDk-bb017OhSQJeotE-YGg/

  • Xcode Cloud – Auto-Generated Test Notes for TestFlight Build (PR Title, Commits, and JIRA Ticket Links)

    Xcode Cloud – Auto-Generated Test Notes for TestFlight Build (PR Title, Commits, and JIRA Ticket Links)

    No more put ‘No test notes’ at Test Flight

    No test notes.

    It’s not clear what to test using this build

    Setup Xcode Cloud Workflow

    To get a Github PR infos from Xcode Cloud, You should enable settings like below

    Environment

    • Set your Github Token

    • Set Xcode Version – Latest Release

    Start Conditions -> See Apple Document

    • CI_PULL_REQUEST_NUMBER

    • CI_PULL_REQUEST_SOURCE_COMMIT

    • CI_PULL_REQUEST_TARGET_COMMIT

    To get CI_PULL_REQUEST variables You should set Pull Request Changes as Start Condition

    Post Actions

    • Set Test Flight Internal Testing

     

    ci_scripts/ci_post_clone.sh

    #!/bin/sh
    brew install jq

     

    ci_scripts/ci_post_xcodebuild.sh

    #!/bin/zsh
    #  ci_post_xcodebuild.sh
    
    # PR Description
    GITHUB_REST_API_PR_INFO=$(curl -L\
      -H "Accept: vnd.github+json" \
      -H "Authorization: Bearer $GITHUB_TOKEN" \
      -H "X-GitHub-Api-Version: 2022-11-28" \
      "https://api.github.com/repos/$CI_PULL_REQUEST_TARGET_REPO/pulls/$CI_PULL_REQUEST_NUMBER")
    
    # Fixed: parse error: Invalid string: control characters from U+0000 through U+001F must be escaped, https://stackoverflow.com/questions/52399819/invalid-string-control-characters-from-u0000-through-u001f-must-be-escaped-us
    PR_TITLE=$(printf '%s\n' "$GITHUB_REST_API_PR_INFO" | jq -r '.title')
    
    PR_DESCRIPTION=$(printf '%s\n' "GITHUB_REST_API_PR_INFO" | jq -r '.body')
    PR_DESCRIPTION_URLS=$(echo "$PR_DESCRIPTION" | grep -Eo 'https?://[^[:space:]]+')
    PR_DESCRIPTION_JIRA_TICKETS=$(echo "$PR_DESCRIPTION_URLS" | grep 'atlassian')
    
    # GIT COMMITS
    COMMIT_MESSAGES=$(git log $CI_PULL_REQUEST_TARGET_COMMIT^..$CI_PULL_REQUEST_SOURCE_COMMIT --pretty=format:"%s\n%b")
    FORMATTED_COMMIT_MESSAGES=$(echo -e "$COMMIT_MESSAGES" | awk 'NF {print}' ORS='\n')
    
    # TEST FLIGHT - What To Test
    WHAT_TO_TEST="$PR_TITLE\nCommits:\n$FORMATTED_COMMIT_MESSAGES\nJIRA Tickets:\n$PR_DESCRIPTION_JIRA_TICKETS"
    
    if [[ -d "$CI_APP_STORE_SIGNED_APP_PATH" ]]; then
      TESTFLIGHT_DIR_PATH=../TestFlight
      mkdir $TESTFLIGHT_DIR_PATH
      echo "$WHAT_TO_TEST" >! $TESTFLIGHT_DIR_PATH/WhatToTest.en-US.txt
    fi

     

    Auto-Generated Test Notes