├── Whats-New-In-Swift-5-7.playground ├── Pages │ ├── Effectful read-only properties.xcplaygroundpage │ │ ├── Resources │ │ │ └── highscores │ │ └── Contents.swift │ ├── Async SwiftUI.xcplaygroundpage │ │ ├── timeline.xctimeline │ │ └── Contents.swift │ ├── Async await.xcplaygroundpage │ │ ├── Resources │ │ │ └── AddAsync.png │ │ └── Contents.swift │ ├── Async sequences.xcplaygroundpage │ │ ├── timeline.xctimeline │ │ └── Contents.swift │ ├── Introduction.xcplaygroundpage │ │ └── Contents.swift │ ├── ActorsCount.xcplaygroundpage │ │ └── Contents.swift │ ├── Sendable and @Sendable closures.xcplaygroundpage │ │ └── Contents.swift │ ├── DoCatchTryThrows.xcplaygroundpage │ │ └── Contents.swift │ ├── async let bindings.xcplaygroundpage │ │ └── Contents.swift │ ├── LoadImage.xcplaygroundpage │ │ └── Contents.swift │ ├── Continuations for interfacing async tasks with synchronous code.xcplaygroundpage │ │ └── Contents.swift │ ├── StructClassActor.xcplaygroundpage │ │ └── Contents.swift │ ├── Actors.xcplaygroundpage │ │ └── Contents.swift │ └── Structured concurrency.xcplaygroundpage │ │ └── Contents.swift ├── playground.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── ios.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── contents.xcplayground ├── Shared ├── Assets.xcassets │ ├── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── MulitSwift.xcdatamodeld │ ├── .xccurrentversion │ └── Shared.xcdatamodel │ │ └── contents ├── MulitSwiftApp.swift ├── ContenvView.swift ├── ImageService.swift ├── ImageViewUI.swift ├── Persistence.swift ├── ViewModel.swift └── DemoListViewUI.swift ├── ImageService.swift ├── MulitSwift.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── ios.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── MulitSwift--iOS--Info.plist ├── README.md └── .gitignore /Whats-New-In-Swift-5-7.playground/Pages/Effectful read-only properties.xcplaygroundpage/Resources/highscores: -------------------------------------------------------------------------------- 1 | score 2 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ImageService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageService.swift 3 | // ImageService 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /MulitSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async SwiftUI.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async await.xcplaygroundpage/Resources/AddAsync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerY/MulitSwift/HEAD/Whats-New-In-Swift-5-7.playground/Pages/Async await.xcplaygroundpage/Resources/AddAsync.png -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Shared/MulitSwift.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Shared.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /MulitSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MulitSwift--iOS--Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Shared/MulitSwiftApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MulitSwiftApp.swift 3 | // Shared 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct MulitSwiftApp: App { 12 | let persistenceController = PersistenceController.shared 13 | var body: some Scene { 14 | WindowGroup { 15 | ContentView() 16 | .environment(\.managedObjectContext, persistenceController.container.viewContext) 17 | } 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/xcuserdata/ios.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Whats-New-In-Swift-5-5 (Playground).xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Shared/MulitSwift.xcdatamodeld/Shared.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async sequences.xcplaygroundpage/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Shared/ContenvView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeList.swift 3 | // TimeList 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ContentView: View { 12 | 13 | var body: some View { 14 | TabView { 15 | ImageViewUI() 16 | .tabItem { 17 | Label("Photo", systemImage: "photo") 18 | } 19 | DemoListViewUI() 20 | .tabItem{ 21 | Label("List", systemImage: "list.clipboard") 22 | } 23 | } 24 | 25 | } 26 | 27 | } 28 | 29 | struct ContentView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | ContentView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Shared/ImageService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // File 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | @MainActor 12 | class ImageService: ObservableObject { 13 | // This is already reactive. 14 | @Published private(set) var url = URL(string: "https://source.unsplash.com/random/300x200") 15 | @Published private(set) var count = 0 16 | 17 | // From Swift 5.5 18 | func inCount() async { 19 | try? await Task.sleep(nanoseconds: 2_000_000_000) // called every two sec. 20 | count += 1 21 | url = URL(string: "https://source.unsplash.com/random/300x200?sig=\(Int.random(in: 1..<100))") 22 | } 23 | 24 | //@MainActor 25 | func starTimer() async { 26 | for _ in 0...100 { 27 | await inCount() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Shared/ImageViewUI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageView.swift 3 | // MulitSwift 4 | // 5 | // Created by Siamak Ashrafi on 8/21/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ImageViewUI: View { 11 | @ObservedObject var imageService = ImageService() 12 | 13 | var body: some View { 14 | VStack(alignment: .leading, spacing: 20) { 15 | Text("Count \(imageService.count) Timer!") 16 | 17 | AsyncImage(url: imageService.url) { image in 18 | image.resizable() 19 | //.aspectRatio(contentMode:.fit) 20 | } placeholder: { 21 | ProgressView() 22 | }.task { 23 | await imageService.starTimer() 24 | } 25 | } 26 | } 27 | } 28 | 29 | struct ImageView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | ImageViewUI() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | These two pleople should get all the credit. 2 | Please support them! 3 | Material Provided by: [Nick](https://www.nicksarno.com/) & [Paul](https://www.hackingwithswift.com/about) 4 | 5 | # What’s new in Swift 5.5 - 5.7 6 | 7 | * Created by [Paul Hudson](https://twitter.com/twostraws) – [Hacking with Swift](https://www.hackingwithswift.com) 8 | 9 | 10 | * Created by [Nick Sarno](https://www.nicksarno.com/) - [SwitfulThinking](https://github.com/SwiftfulThinking) - [Support the work](https://www.buymeacoffee.com/nicksarno/) 11 | 12 | This playground is designed to showcase new features introduced in Swift 5.5 - 5.7. 13 | 14 | 15 | 16 | Topics Covered 17 | * Review: Do / Try / Catch / Throw(s) 18 | * Review: Stack vs Heap - Struct vs Class vs Actor - Using Threads 19 | * Async / Await - Background tasks on various threads 20 | * Async Let bindings - Doing multiple tasks at once 21 | * Basic structured concurrency - Sleep, Cancel, Task Groups 22 | * Continuations for interfacing async tasks with synchronous code 23 | * Actors 24 | * Swift structured concurrency with SwiftUI 25 | * Async sequences 26 | * AsyncAlgorithms: async zip & merge 27 | 28 | Ending with: 29 | Instruments 14 beta Profiler for Swift concurrency 30 | 31 | Video of session: https://github.com/developerY/MulitSwift 32 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Introduction.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | # What’s new in Swift 5.5 - 5.7 3 | 4 | * Created by [Paul Hudson](https://twitter.com/twostraws) – [Hacking with Swift](https://www.hackingwithswift.com) 5 | 6 | 7 | * Created by [Nick Sarno](https://www.nicksarno.com/) - [SwitfulThinking](https://github.com/SwiftfulThinking) - [Support the work](https://www.buymeacoffee.com/nicksarno/) 8 | 9 | This playground is designed to showcase new features introduced in Swift 5.5 - 5.7. 10 | 11 | These two pleople should get all the credit. 12 | Please support them! 13 | 14 |   15 | * [Do try catch](DoCatchTryThrows) 16 | * [Struct, Class, Actor - Stack / Heap](StructClassActor) 17 | * [Async SwiftUI](Async%20SwiftUI) 18 | * [Async await](Async%20await) 19 | * [Async sequences](Async%20sequences) 20 | * [Effectful read-only properties](Effectful%20read-only%20properties) 21 | * [Structured concurrency](Structured%20concurrency) 22 | * [`async let` bindings](async%20let%20bindings) 23 | * [Continuations for interfacing async tasks with synchronous code](Continuations%20for%20interfacing%20async%20tasks%20with%20synchronous%20code) 24 | * [Actors](Actors) 25 | * [Global actors](Global%20actors) 26 | * [Sendable and @Sendable closures](Sendable%20and%20@Sendable%20closures) 27 | * [`if` for postfix member expressions](if%20for%20postfix%20member%20expressions) 28 | * [Allow interchangeable use of `CGFloat` and `Double` types](Allow%20interchangeable%20use%20of%20CGFloat%20and%20Double%20types) 29 | * [Codable synthesis for enums with associated values](Codable%20synthesis%20for%20enums%20with%20associated%20values) 30 | * [`lazy` now works in local contexts](lazy%20now%20works%20in%20local%20contexts) 31 | * [Extend property wrappers to function and closure parameters](Extend%20property%20wrappers%20to%20function%20and%20closure%20parameters) 32 | * [Extending static member lookup in generic contexts](Extending%20static%20member%20lookup%20in%20generic%20contexts) 33 | 34 |   35 | 36 | [Next >](@next) 37 | */ 38 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Effectful read-only properties.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Effectful read-only properties 8 | 9 | [SE-0310](https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md) upgrades Swift’s read-only properties to support the `async` and `throws` keywords, either individually or together, making them significantly more flexible. 10 | 11 | To demonstrate this, we could create a `BundleFile` struct that attempts to load the contents of a file in our app’s resource bundle. Because the file might not be there, might be there but can’t be read for some reason, or might be readable but so big it takes time to read, we could mark the `contents` property as `async throws` like this: 12 | */ 13 | import Foundation 14 | import SwiftUI 15 | 16 | enum FileError: Error { 17 | case missing, unreadable 18 | } 19 | 20 | struct BundleFile { 21 | let filename: String 22 | let stringToSave = "The string I want to save" 23 | 24 | 25 | var contents: String { 26 | get async throws { 27 | guard let url = Bundle.main.url(forResource: filename /*try nil*/, withExtension: nil) else { 28 | throw FileError.missing 29 | } 30 | 31 | 32 | do { 33 | return try String(contentsOf: url) 34 | } catch { 35 | throw FileError.unreadable 36 | } 37 | } 38 | } 39 | } 40 | /*: 41 | Because `contents` is both async and throwing, we must use `try await` when trying to read it: 42 | */ 43 | func printHighScores() async throws { 44 | let file = BundleFile(filename: "highscores") 45 | try await print("This is the contents = '\(file.contents)'") 46 | } 47 | 48 | Task { 49 | do { 50 | print ("High scores bundle", try await printHighScores()) 51 | } catch { 52 | print("Expected error: \(error).") 53 | } 54 | } 55 | /*: 56 | 57 |   58 | 59 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 60 | */ 61 | -------------------------------------------------------------------------------- /Shared/Persistence.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Persistence.swift 3 | // Shared 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import CoreData 9 | 10 | struct PersistenceController { 11 | static let shared = PersistenceController() 12 | 13 | static var preview: PersistenceController = { 14 | let result = PersistenceController(inMemory: true) 15 | let viewContext = result.container.viewContext 16 | for _ in 0..<10 { 17 | let newItem = Item(context: viewContext) 18 | newItem.timestamp = Date() 19 | } 20 | do { 21 | try viewContext.save() 22 | } catch { 23 | // Replace this implementation with code to handle the error appropriately. 24 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 25 | let nsError = error as NSError 26 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 27 | } 28 | return result 29 | }() 30 | 31 | let container: NSPersistentContainer 32 | 33 | init(inMemory: Bool = false) { 34 | container = NSPersistentContainer(name: "MulitSwift") 35 | if inMemory { 36 | container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") 37 | } 38 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 39 | if let error = error as NSError? { 40 | // Replace this implementation with code to handle the error appropriately. 41 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 42 | 43 | /* 44 | Typical reasons for an error here include: 45 | * The parent directory does not exist, cannot be created, or disallows writing. 46 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 47 | * The device is out of space. 48 | * The store could not be migrated to the current model version. 49 | Check the error message to determine what the actual problem was. 50 | */ 51 | fatalError("Unresolved error \(error), \(error.userInfo)") 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/ActorsCount.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | import SwiftUI 5 | 6 | 7 | class CounterClass { 8 | private var value = 0 9 | 10 | func getVal() -> Int {return value} 11 | func increment() {value = value + 1} 12 | func dec() {value = value - 1} 13 | 14 | } 15 | let counterClass = CounterClass() 16 | 17 | 18 | for _ in 1...100 { 19 | counterClass.increment() // data race 20 | 21 | Task.detached { 22 | counterClass.increment() // data race 23 | counterClass.dec() // data race 24 | } 25 | 26 | counterClass.dec() // data race 27 | 28 | Task.detached { 29 | counterClass.increment() // data race 30 | counterClass.dec() // data race 31 | } 32 | } 33 | print("Counter Class", counterClass.getVal()) 34 | 35 | 36 | sleep(2) 37 | 38 | struct CounterStruct { 39 | private var value = 0 40 | 41 | func getVal() -> Int {return value} 42 | mutating func increment() {value = value + 1} 43 | mutating func dec() {value = value - 1} 44 | 45 | } 46 | 47 | let counterStruct = CounterStruct() 48 | 49 | Task.detached { 50 | var counter = counterStruct 51 | counter.increment() // always prints 1 52 | counter.dec() // data race 53 | 54 | } 55 | 56 | Task.detached { 57 | var counter = counterStruct 58 | counter.increment() // always prints 1 59 | counter.dec() // data race 60 | 61 | } 62 | print("Counter Struct", counterStruct.getVal()) 63 | 64 | sleep(2) 65 | 66 | 67 | /* 68 | There is no await so do not have to thread reentery. 69 | */ 70 | actor CounterActor { 71 | var value = 0 72 | 73 | func printVal() -> Int { return value } 74 | 75 | func increment() {value = value + 1} 76 | func dec(){value = value - 1} 77 | 78 | } 79 | 80 | let counterActor = CounterActor() 81 | 82 | for _ in 1...10 { 83 | 84 | Task.detached { 85 | await counterActor.increment() // always prints 1 86 | } 87 | Task.detached { 88 | await counterActor.increment() // always prints 1 89 | await counterActor.dec() // data race 90 | 91 | } 92 | 93 | Task.detached { 94 | await counterActor.dec() // always prints 1 95 | } 96 | 97 | Task.detached { 98 | await counterActor.increment() // always prints 1 99 | await counterActor.dec() // data race 100 | 101 | } 102 | } 103 | sleep(2) 104 | 105 | Task { 106 | await print("Counter Actor", counterActor.printVal()) 107 | } 108 | 109 | 110 | 111 | 112 | //: [Next](@next) 113 | -------------------------------------------------------------------------------- /Shared/ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel.swift 3 | // MulitSwift 4 | // 5 | // Created by Siamak Ashrafi on 8/21/22. 6 | // 7 | 8 | import Foundation 9 | import AsyncAlgorithms 10 | 11 | 12 | // Just put @MainActor Here 13 | //@MainActor // This solves everything. 14 | class ViewModel: ObservableObject { 15 | let persistenceController = PersistenceController.shared 16 | let exmURL = "https://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0" 17 | let exmURL1 = "https://www.7timer.info/bin/astro.php?lon=13.2&lat=123.1&ac=0&unit=metric&output=json&tzshift=0" 18 | 19 | let a = [1,2,3] 20 | let b = ["a", "b", "c"] 21 | let c = ["😀","😡"] 22 | let d = [5,7,9,10] 23 | 24 | @MainActor @Published var zipList : [String] = [] // Comment out @MainActor 25 | @MainActor @Published var mergeList : [Int] = [] 26 | 27 | /*func TestCall() { 28 | let appleFeed = URL(string: exmURL)!.lines 29 | let nasdaqFeed = URL(string: exmURL1)!.lines 30 | Task { 31 | print("Stated") 32 | for try await (apple, nasdaq) in zip(appleFeed, nasdaqFeed) { 33 | 34 | print("APPL: \(apple) NASDAQ: \(nasdaq)") 35 | } 36 | print("End") 37 | } 38 | }*/ 39 | 40 | func taskZip() { 41 | print("Stated Zip") 42 | Task { 43 | for try await (a, b, c) in zip(a.send(), b.send(),c.send()) { 44 | await MainActor.run(body: { 45 | zipList.append("aSend: \(a) bSend: \(b) cSend \(c)") 46 | }) 47 | // if a is equal to this break 48 | // break 49 | } 50 | 51 | } 52 | print("End Zip") 53 | } 54 | 55 | func deleteZip() { 56 | Task { 57 | await MainActor.run(body: { 58 | zipList.removeAll() 59 | }) 60 | } 61 | } 62 | 63 | func taskMerge() { 64 | print("Stated Merge") 65 | Task { 66 | 67 | for try await myNums in merge(a.send(), d.send()) { 68 | await MainActor.run(body: { 69 | mergeList.append(myNums) 70 | }) 71 | } 72 | 73 | } 74 | print("End Merge") 75 | } 76 | 77 | func deleteMerge() { 78 | Task { 79 | await MainActor.run(body: { 80 | mergeList.removeAll() 81 | }) 82 | } 83 | } 84 | 85 | } 86 | private extension Array { 87 | func send() -> AsyncStream { 88 | AsyncStream {continuation in 89 | Task { 90 | for value in self { 91 | continuation.yield(value) 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Sendable and @Sendable closures.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Sendable and @Sendable closures 8 | 9 | [SE-0302](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md) adds support for “sendable” data, which is data that **can safely be transferred to another thread**. This is accomplished through a new `Sendable` protocol, and an `@Sendable` attribute for functions. 10 | 11 | Many things are inherently safe to send across threads: 12 | 13 | - All of Swift’s core value types, including `Bool`, `Int`, `String`, and similar. 14 | - Optionals, where the wrapped data is a value type. 15 | - Standard library collections that contain value types, such as `Array` or `Dictionary`. 16 | - Tuples where the elements are all value types. 17 | - Metatypes, such as `String.self`. 18 | 19 | These have been updated to conform to the `Sendable` protocol. 20 | 21 | As for custom types, it depends what you’re making: 22 | 23 | - Actors automatically conform to `Sendable` because they handle their synchronization internally. 24 | - Custom structs and enums you define will also automatically conform to `Sendable` if they contain only values that also conform to `Sendable`, similar to how `Codable` works. 25 | - Custom classes can conform to `Sendable` as long as they either inherits from `NSObject` or from nothing at all, all properties are constant and themselves conform to `Sendable`, and they are marked as `final` to stop further inheritance. 26 | 27 | Swift lets us use the `@Sendable` attribute on functions or closure to mark them as working concurrently, and will enforce various rules to stop us shooting ourself in the foot. For example, the operation we pass into the `Task` initializer is marked `@Sendable`, which means this kind of code is allowed because the value captured by `Task` is a constant: 28 | */ 29 | import Foundation 30 | import SwiftUI 31 | 32 | func printScore() async { 33 | let score = 1 // var 34 | 35 | Task { print(score) } 36 | Task { print(score) } 37 | } 38 | /*: 39 | However, that code would *not* be allowed if `score` were a variable, because it could be accessed by one of the tasks while the other was changing its value. 40 | 41 | You can mark your own functions and closures using `@Sendable`, which will enforce similar rules around captured values: 42 | */ 43 | func runLater(_ function: @escaping @Sendable () -> Void) -> Void { 44 | DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: function) 45 | DispatchQueue.global().asyncAfter(deadline: .now() + 2, execute: function) 46 | DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: function) 47 | } 48 | 49 | var num = 2 50 | print("Start") 51 | runLater { 52 | print("hi", num + 2) 53 | } 54 | sleep(5) 55 | print("End") 56 | 57 | /*: 58 | 59 |   60 | 61 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 62 | */ 63 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | }, 93 | { 94 | "idiom" : "mac", 95 | "scale" : "1x", 96 | "size" : "16x16" 97 | }, 98 | { 99 | "idiom" : "mac", 100 | "scale" : "2x", 101 | "size" : "16x16" 102 | }, 103 | { 104 | "idiom" : "mac", 105 | "scale" : "1x", 106 | "size" : "32x32" 107 | }, 108 | { 109 | "idiom" : "mac", 110 | "scale" : "2x", 111 | "size" : "32x32" 112 | }, 113 | { 114 | "idiom" : "mac", 115 | "scale" : "1x", 116 | "size" : "128x128" 117 | }, 118 | { 119 | "idiom" : "mac", 120 | "scale" : "2x", 121 | "size" : "128x128" 122 | }, 123 | { 124 | "idiom" : "mac", 125 | "scale" : "1x", 126 | "size" : "256x256" 127 | }, 128 | { 129 | "idiom" : "mac", 130 | "scale" : "2x", 131 | "size" : "256x256" 132 | }, 133 | { 134 | "idiom" : "mac", 135 | "scale" : "1x", 136 | "size" : "512x512" 137 | }, 138 | { 139 | "idiom" : "mac", 140 | "scale" : "2x", 141 | "size" : "512x512" 142 | } 143 | ], 144 | "info" : { 145 | "author" : "xcode", 146 | "version" : 1 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/swift,xcode,swiftpackagemanager 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift,xcode,swiftpackagemanager 3 | .DS_Store 4 | *.xccheckout 5 | *.xcscmblueprint 6 | 7 | 8 | ### Swift ### 9 | # Xcode 10 | # 11 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 12 | 13 | ## User settings 14 | xcuserdata/ 15 | 16 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 17 | *.xcscmblueprint 18 | *.xccheckout 19 | 20 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 21 | build/ 22 | DerivedData/ 23 | *.moved-aside 24 | *.pbxuser 25 | !default.pbxuser 26 | *.mode1v3 27 | !default.mode1v3 28 | *.mode2v3 29 | !default.mode2v3 30 | *.perspectivev3 31 | !default.perspectivev3 32 | 33 | ## Obj-C/Swift specific 34 | *.hmap 35 | 36 | ## App packaging 37 | *.ipa 38 | *.dSYM.zip 39 | *.dSYM 40 | 41 | ## Playgrounds 42 | timeline.xctimeline 43 | playground.xcworkspace 44 | 45 | # Finder 46 | .DS_Store 47 | 48 | # Swift Package Manager 49 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 50 | # Packages/ 51 | # Package.pins 52 | # Package.resolved 53 | # *.xcodeproj 54 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 55 | # hence it is not needed unless you have added a package configuration file to your project 56 | # .swiftpm 57 | 58 | .build/ 59 | 60 | # CocoaPods 61 | # We recommend against adding the Pods directory to your .gitignore. However 62 | # you should judge for yourself, the pros and cons are mentioned at: 63 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 64 | # Pods/ 65 | # Add this line if you want to avoid checking in source code from the Xcode workspace 66 | #*.xcworkspace 67 | 68 | # Carthage 69 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 70 | # Carthage/Checkouts 71 | 72 | Carthage/Build/ 73 | 74 | # Accio dependency management 75 | Dependencies/ 76 | .accio/ 77 | 78 | # fastlane 79 | # It is recommended to not store the screenshots in the git repo. 80 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 81 | # For more information about the recommended setup visit: 82 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 83 | 84 | fastlane/report.xml 85 | fastlane/Preview.html 86 | fastlane/screenshots/**/*.png 87 | fastlane/test_output 88 | 89 | # Code Injection 90 | # After new code Injection tools there's a generated folder /iOSInjectionProject 91 | # https://github.com/johnno1962/injectionforxcode 92 | 93 | iOSInjectionProject/ 94 | 95 | ### SwiftPackageManager ### 96 | Packages 97 | xcuserdata 98 | *.xcodeproj 99 | xcshareddata/ 100 | 101 | 102 | ### Xcode ### 103 | 104 | ## Xcode 8 and earlier 105 | 106 | ### Xcode Patch ### 107 | *.xcodeproj/* 108 | !*.xcodeproj/project.pbxproj 109 | !*.xcodeproj/xcshareddata/ 110 | !*.xcworkspace/contents.xcworkspacedata 111 | /*.gcno 112 | **/xcshareddata/WorkspaceSettings.xcsettings 113 | 114 | MockingbirdMocks 115 | 116 | # End of https://www.toptal.com/developers/gitignore/api/swift,xcode,swiftpackagemanager 117 | -------------------------------------------------------------------------------- /MulitSwift.xcodeproj/xcuserdata/ios.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MulitSwift (iOS).xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | MulitSwift (macOS).xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | Whats-New-In-Swift-5-5 (Playground) 1.xcscheme 18 | 19 | isShown 20 | 21 | orderHint 22 | 3 23 | 24 | Whats-New-In-Swift-5-5 (Playground) 10.xcscheme 25 | 26 | isShown 27 | 28 | orderHint 29 | 12 30 | 31 | Whats-New-In-Swift-5-5 (Playground) 11.xcscheme 32 | 33 | isShown 34 | 35 | orderHint 36 | 13 37 | 38 | Whats-New-In-Swift-5-5 (Playground) 12.xcscheme 39 | 40 | isShown 41 | 42 | orderHint 43 | 14 44 | 45 | Whats-New-In-Swift-5-5 (Playground) 13.xcscheme 46 | 47 | isShown 48 | 49 | orderHint 50 | 15 51 | 52 | Whats-New-In-Swift-5-5 (Playground) 14.xcscheme 53 | 54 | isShown 55 | 56 | orderHint 57 | 16 58 | 59 | Whats-New-In-Swift-5-5 (Playground) 2.xcscheme 60 | 61 | isShown 62 | 63 | orderHint 64 | 4 65 | 66 | Whats-New-In-Swift-5-5 (Playground) 3.xcscheme 67 | 68 | isShown 69 | 70 | orderHint 71 | 5 72 | 73 | Whats-New-In-Swift-5-5 (Playground) 4.xcscheme 74 | 75 | isShown 76 | 77 | orderHint 78 | 6 79 | 80 | Whats-New-In-Swift-5-5 (Playground) 5.xcscheme 81 | 82 | isShown 83 | 84 | orderHint 85 | 7 86 | 87 | Whats-New-In-Swift-5-5 (Playground) 6.xcscheme 88 | 89 | isShown 90 | 91 | orderHint 92 | 8 93 | 94 | Whats-New-In-Swift-5-5 (Playground) 7.xcscheme 95 | 96 | isShown 97 | 98 | orderHint 99 | 9 100 | 101 | Whats-New-In-Swift-5-5 (Playground) 8.xcscheme 102 | 103 | isShown 104 | 105 | orderHint 106 | 10 107 | 108 | Whats-New-In-Swift-5-5 (Playground) 9.xcscheme 109 | 110 | isShown 111 | 112 | orderHint 113 | 11 114 | 115 | Whats-New-In-Swift-5-5 (Playground).xcscheme 116 | 117 | isShown 118 | 119 | orderHint 120 | 2 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async SwiftUI.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | // @MainActor - runs on the main thread 3 | /*: 4 | Clean Code with MVVM and SwiftUI 5 | */ 6 | 7 | import Foundation 8 | import SwiftUI 9 | import PlaygroundSupport 10 | 11 | var greeting = "SwiftUI" 12 | 13 | func addEverSec(count : Int) async -> Int { 14 | try? await Task.sleep(nanoseconds: 2_000_000_000) 15 | let num = count + 1 16 | return num 17 | } 18 | 19 | // : From Nick 20 | // Created by Nick Sarno on 4/4/22 and adapted here: 21 | // Thread Sanitizer - under MulitSwift(iOS) icon on the top running bar 22 | 23 | //protocol GlobalActor 24 | @globalActor final class MyFirstGlobalActor { 25 | //static var shared: Self.ActorType { get } 26 | static var shared = MyNewDataManager() // <- Access point 27 | } 28 | 29 | actor MyNewDataManager { // cannot make a class 30 | func getDataFromDatabase() -> [String] { 31 | return ["One", "Two", "Three", "Four", "Five"] 32 | } 33 | } 34 | 35 | // @MainActor works but is waistful 36 | class GlobalActorBootcampViewModel: ObservableObject { 37 | // Must run on Main Actor (main thread/ UI Thread) 38 | @MainActor @Published var dataArray: [String] = [] 39 | 40 | 41 | let manager = MyFirstGlobalActor.shared 42 | @MyFirstGlobalActor func getData() { 43 | 44 | // HEAVY COMPLEX METHODS 45 | Task { 46 | 47 | // Run on the Global Actor (must await) 48 | let data = await manager.getDataFromDatabase() 49 | print("Global Actor : \(Thread.current)") 50 | 51 | // Run back on the Main Actor 52 | await MainActor.run(body: { 53 | print("Main Actor : \(Thread.current)") 54 | self.dataArray = data // @Published on @MainActor 55 | }) 56 | } 57 | } 58 | 59 | } 60 | 61 | 62 | struct BadCounterView: View { 63 | @State var count = 0 64 | 65 | var body: some View { 66 | VStack { 67 | Text("This is the count ") 68 | Text("\(count)") 69 | }.task { // cancle when view goes away 70 | 71 | // try Task.checkCancellation() 72 | 73 | for i in 1...10 { 74 | print("task!!! \(i) \n") 75 | count = await(addEverSec(count: count)) 76 | } 77 | }.onAppear { 78 | 79 | Task { // will NOT cancle when view goes away 80 | for i in 1...10 { 81 | print("Task \(i)") 82 | count = await(addEverSec(count: count)) 83 | } 84 | 85 | } 86 | } 87 | } 88 | 89 | } 90 | 91 | struct ContentView: View { 92 | @State private var counterOn = false 93 | 94 | @StateObject private var viewModel = GlobalActorBootcampViewModel() 95 | 96 | 97 | var body: some View { 98 | VStack { 99 | // Toggle 100 | Toggle(isOn: $counterOn) { 101 | Text("Vibrate on Ring") 102 | } 103 | 104 | if (counterOn) { 105 | BadCounterView() 106 | } else { 107 | Text("Counter Off") 108 | } 109 | 110 | // List of Text 111 | ForEach(viewModel.dataArray, id: \.self) { 112 | Text($0) 113 | .font(.headline) 114 | } 115 | 116 | }.frame(minWidth: 200, minHeight: 300) 117 | .onAppear { 118 | Task { 119 | await viewModel.getData() 120 | } 121 | } 122 | } 123 | 124 | } 125 | 126 | 127 | PlaygroundPage.current 128 | .setLiveView(ContentView()) 129 | 130 | 131 | //: [Next](@next) 132 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/DoCatchTryThrows.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | // 3 | // DoCatchTryThrows 4 | // 5 | // 6 | // Orig Created by Nick Sarno on 3/31/22. 7 | // Copied by Yours truly (Ash). 8 | // 9 | 10 | import Foundation 11 | 12 | var greeting = "Do Try" 13 | 14 | enum ValidationError: Error { 15 | case tooShort 16 | case tooLong 17 | } 18 | 19 | // MARK: return tuple 20 | 21 | // does not throw and has two returns 22 | // protocol Error : Sendable (A type whose values can safely be passed across concurrency domains by copying.) 23 | func getTitle() -> (title: String?, error: Error?) { 24 | if isTitle { 25 | return ("NEW TEXT!", nil) 26 | } else { 27 | return (nil, ValidationError.tooLong) 28 | } 29 | } 30 | 31 | let isTitle: Bool = true // false 32 | let returnedValue = getTitle() 33 | if let newTitle = returnedValue.title { 34 | print(newTitle) 35 | } else if let error = returnedValue.error { 36 | print(error.localizedDescription) 37 | } 38 | 39 | // MARK: Return Result 40 | 41 | // does not throw but uses the Result iOS 8.0+ (Swift 5.0?) 42 | // "Until Swift 5.0 added the Result type, it was harder to send back errors with completion handlers" - Paul 43 | func getTitle2() -> Result { 44 | if isTitle2 { 45 | return .success("NEW TEXT!") 46 | } else { 47 | return .failure(ValidationError.tooShort) 48 | } 49 | } 50 | 51 | let isTitle2: Bool = true // false 52 | let result = getTitle2() 53 | switch result { 54 | case .success(let newTitle): 55 | print(newTitle) 56 | case .failure(let error): 57 | print(error.localizedDescription) 58 | } 59 | 60 | 61 | // MARK: Throws -- try? 62 | 63 | // throws 64 | func getTitle3() throws -> String { 65 | if isTitle3 { // isFalse 66 | return "NEW TEXT!" 67 | } else { 68 | throw ValidationError.tooShort 69 | } 70 | } 71 | 72 | // must use try? to see the all throws 73 | func getTitle4() throws -> String { 74 | if isTitle4 { 75 | return "FINAL TEXT!" 76 | } else { 77 | throw ValidationError.tooLong 78 | } 79 | } 80 | 81 | // return here only once. 82 | let isTitle3 = true // false 83 | let isTitle4 = true // false 84 | do { 85 | let newTitle:String? = try getTitle3() // try? will continue to execute after failure 86 | if let newTitle = newTitle { 87 | print(newTitle) 88 | } 89 | 90 | let finalTitle = try getTitle4() 91 | print(finalTitle) 92 | } catch { 93 | print(error.localizedDescription) 94 | } 95 | 96 | do { 97 | try getTitle3() 98 | } catch { 99 | print(error.localizedDescription) 100 | } 101 | 102 | do { 103 | try? getTitle4() 104 | } catch { //'catch' block is unreachable because no errors are thrown in 'do' block 105 | print(error.localizedDescription) 106 | } 107 | 108 | 109 | 110 | func alwaysGetTitle() -> String { // Important to SwiftUI 111 | // return try getTitle4() // Errors thrown from here are not handled 112 | 113 | // type docat 114 | do { 115 | return try getTitle4() //Errors thrown from here are not handled because the enclosing catch is not exhaustive 116 | } catch ValidationError.tooShort { 117 | return("short error: \(ValidationError.tooShort).") 118 | } catch ValidationError.tooLong { 119 | return("long error: \(ValidationError.tooLong).") 120 | }catch { // this need to be exhaustive becuase you can not have a typed Error 121 | return("Unexpected error: \(error).") 122 | } 123 | } 124 | 125 | // build for SwiftUI View 126 | print ("always returns a string = \(alwaysGetTitle())") 127 | 128 | 129 | // what if we throw? 130 | 131 | func alwaysThrows() throws { 132 | print("throwing") 133 | throw ValidationError.tooLong 134 | } 135 | 136 | func callThowFunc() throws { 137 | // do not need do{}catch{} if throwing 138 | // do { 139 | try alwaysThrows() 140 | // } catch { print("\(error)") throw ValidationError.tooLong } 141 | } 142 | 143 | // try callThowFunc() 144 | 145 | //: [Next](@next) 146 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/async let bindings.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # `async let` bindings 8 | 9 | [SE-0317](https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md) introduces the ability to create and await child tasks using the simple syntax `async let`. This is particularly useful as an alternative to task groups where you’re dealing with heterogeneous result types – i.e., if you want tasks in a group to return different kinds of data. 10 | 11 | To demonstrate this, we could create a struct that has three different types of properties that will come from three different async functions: 12 | */ 13 | import Foundation 14 | import SwiftUI 15 | 16 | struct UserData { 17 | let username: String 18 | let friends: [String] 19 | let highScores: [Int] 20 | let ans : Int 21 | } 22 | 23 | func getUser() async -> String { 24 | print("Get Name DONE!") 25 | return "Taylor Swift" 26 | } 27 | 28 | func getHighScores() async -> [Int] { 29 | print("Get HS DONE!") 30 | return [42, 23, 16, 15, 8, 4] 31 | } 32 | 33 | func getFriends() async -> [String] { 34 | print("Get Friends DONE!") 35 | return ["Eric", "Maeve", "Otis"] 36 | } 37 | 38 | func fibNum1000() async -> Int { 39 | let ans = await fib(of: 10) 40 | print("fib 1000 done") 41 | return ans 42 | } 43 | 44 | // Started first but others end first 45 | func fib(of number: Int) async -> Int { 46 | if number < 2 { return number } 47 | async let first = fib(of: number - 2) 48 | async let second = fib(of: number - 1) 49 | return await first + second 50 | } 51 | /*: 52 | If we wanted to create a `User` instance from all three of those values, `async let` is the easiest way – it run each function concurrently, wait for all three to finish, then use them to create our object. 53 | 54 | Here’s how it looks: 55 | */ 56 | func printUserDetails() async { // three diff types. 57 | async let num1001 = fibNum1000() // starts immediate 58 | async let username = getUser() 59 | async let scores = getHighScores() 60 | async let friends = getFriends() 61 | 62 | let user = await UserData(username: username, friends: friends, highScores: scores, /*ans:5*/ ans:num1001) 63 | 64 | print("Hello, my name is \(user.username), and I have \(user.friends.count) friends!") 65 | } 66 | 67 | Task { 68 | await printUserDetails() 69 | } 70 | sleep(2) 71 | /*: 72 | **Important:** You can only use `async let` if you are already in an async context, and if you don’t explicitly await the result of an `async let` Swift will implicitly wait for it when exiting its scope. 73 | 74 | When working with throwing functions, you *don’t* need to use `try` with `async let` – that can automatically be pushed back to where you await the result. Similarly, the `await` keyword is also implied, so rather than typing `try await someFunction()` with an `async let` you can just write `someFunction()`. 75 | 76 | To demonstrate this, we could write an async function to recursively calculate numbers in the Fibonacci sequence. This approach is hopelessly naive because without memoization we’re just repeating vast amounts of work, so to avoid causing everything to grind to a halt we’re going to limit the input range from 0 to 22: 77 | */ 78 | enum NumberError: Error { 79 | case outOfRange 80 | } 81 | 82 | enum TestName :Error { 83 | case bad 84 | } 85 | 86 | func fibonacci(of number: Int) async throws -> Int { 87 | if number < 0 || number > 22 { 88 | throw NumberError.outOfRange 89 | } 90 | 91 | if number < 2 { return number } 92 | async let first = fibonacci(of: number - 2) 93 | async let second = fibonacci(of: number - 1) 94 | return try await first + second // do not need to type:: 95 | //try await first + try await second 96 | } 97 | 98 | func getName(tag: Bool) async throws -> String { 99 | guard tag == true else {throw TestName.bad} 100 | return "Swift" 101 | } 102 | 103 | func getNumber (tag: Bool) async throws -> Int { 104 | guard tag == true else {throw TestName.bad} 105 | return 27 106 | } 107 | 108 | Task { 109 | do { 110 | try await print("Name \(getName(tag: true)) Num \(getNumber(tag: true))") 111 | print("Name \(try await getName(tag: true)) Num \(try await getNumber(tag: true))") 112 | } catch { 113 | print("Error",error) 114 | } 115 | } 116 | /*: 117 | In that code the recursive calls to `fibonacci(of:)` are implicitly `try await fibonacci(of:)`, but we can leave them off and handle them directly on the following line. 118 | 119 |   120 | 121 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 122 | */ 123 | -------------------------------------------------------------------------------- /Shared/DemoListViewUI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Shared 4 | // 5 | // Created by iOS Developer on 8/15/21. 6 | // 7 | 8 | import SwiftUI 9 | import CoreData 10 | 11 | struct DynamicListViewUI: View { 12 | @Environment(\.managedObjectContext) private var viewContext 13 | 14 | @FetchRequest( 15 | sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], 16 | animation: .default) 17 | private var items: FetchedResults 18 | 19 | var body: some View { 20 | NavigationView { 21 | List { 22 | ForEach(items) { item in 23 | NavigationLink { 24 | Text("Item at \(item.timestamp!, formatter: itemFormatter)") 25 | } label: { 26 | Text(item.timestamp!, formatter: itemFormatter) 27 | } 28 | } 29 | .onDelete(perform: deleteItems) 30 | } 31 | .toolbar { 32 | #if os(iOS) 33 | ToolbarItem(placement: .navigationBarTrailing) { 34 | EditButton() 35 | } 36 | #endif 37 | ToolbarItem { 38 | Button(action: addItem) { 39 | Label("Add Item", systemImage: "plus") 40 | } 41 | } 42 | } 43 | Text("Select an item") 44 | } 45 | 46 | } 47 | 48 | private func addItem() { 49 | withAnimation { 50 | let newItem = Item(context: viewContext) 51 | newItem.timestamp = Date() 52 | 53 | do { 54 | try viewContext.save() 55 | } catch { 56 | // Replace this implementation with code to handle the error appropriately. 57 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 58 | let nsError = error as NSError 59 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 60 | } 61 | } 62 | } 63 | 64 | private func deleteItems(offsets: IndexSet) { 65 | withAnimation { 66 | offsets.map { items[$0] }.forEach(viewContext.delete) 67 | 68 | do { 69 | try viewContext.save() 70 | } catch { 71 | // Replace this implementation with code to handle the error appropriately. 72 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 73 | let nsError = error as NSError 74 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 75 | } 76 | } 77 | } 78 | } 79 | 80 | struct ZipMergeViewUI: View { 81 | @StateObject private var viewModel = ViewModel() 82 | 83 | var body: some View { 84 | VStack { 85 | HStack { 86 | Button("get zip"){ 87 | viewModel.taskZip() 88 | }.buttonStyle(.borderedProminent) 89 | Button("delete zip"){ 90 | viewModel.deleteZip() 91 | }.foregroundColor(.red) 92 | } 93 | List { 94 | ForEach(viewModel.zipList, id:\.self) { str in 95 | Text(str) 96 | } 97 | } 98 | Spacer() 99 | HStack { 100 | Button("get merge"){ 101 | print("call merge") 102 | viewModel.taskMerge() 103 | }.buttonStyle(.borderedProminent) 104 | Button("delete merge"){ 105 | viewModel.deleteMerge() 106 | }.foregroundColor(.red) 107 | } 108 | List { 109 | ForEach(viewModel.mergeList, id:\.self) { num in 110 | Text("This is the merge \(num)") 111 | } 112 | } 113 | 114 | 115 | } 116 | } 117 | } 118 | 119 | struct DemoListViewUI: View { 120 | 121 | var body: some View { 122 | 123 | VStack { 124 | ZipMergeViewUI() 125 | DynamicListViewUI() 126 | } 127 | 128 | } 129 | 130 | } 131 | 132 | private let itemFormatter: DateFormatter = { 133 | let formatter = DateFormatter() 134 | formatter.dateStyle = .short 135 | formatter.timeStyle = .medium 136 | return formatter 137 | }() 138 | 139 | struct ContentViewList_Previews: PreviewProvider { 140 | static var previews: some View { 141 | DemoListViewUI().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async sequences.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Async sequences 8 | 9 | [SE-0298](https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md) introduces the ability to loop over asynchronous sequences of values using a new `AsyncSequence` protocol. This is helpful for places when you want to process values in a sequence as they become available rather than precomputing them all at once – perhaps because they take time to calculate, or because they aren’t available yet. 10 | 11 | Using `AsyncSequence` is almost identical to using `Sequence`, with the exception that your types should conform to `AsyncSequence` and `AsyncIterator`, and your `next()` method should be marked `async`. When it comes time for your sequence to end, make sure you send back `nil` from `next()`, just as with `Sequence`. 12 | 13 | For example, we could make a `DoubleGenerator` sequence that starts from 1 and doubles its number every time it’s called: 14 | */ 15 | // Just like sequence 16 | // Iteration uses Swift Concurrency 17 | // Interator can Throw 18 | // It has map / filter / reduce 19 | 20 | //https://github.com/apple/swift-async-algorithms/tree/main/Sources/AsyncAlgorithms/AsyncAlgorithms.docc/Guides 21 | 22 | 23 | import SwiftUI 24 | // iOS 13.0+ 25 | /// An AsyncSequence resembles the Sequence type — offering a list of values you can step through one at a time — and adds asynchronicity. 26 | struct DoubleGenerator: AsyncSequence { 27 | typealias Element = Int 28 | 29 | struct AsyncIterator: AsyncIteratorProtocol { 30 | var current = 1 31 | 32 | // meets protocol 33 | mutating func next() async -> Int? { 34 | defer { current &*= 2 } 35 | 36 | if current < 0 { 37 | //print(current) overflow turns negative 38 | return nil 39 | } else { 40 | return current 41 | } 42 | } 43 | } 44 | 45 | func makeAsyncIterator() -> AsyncIterator { 46 | AsyncIterator() 47 | } 48 | } 49 | /*: 50 | **Tip:** If you just remove “async” from everywhere it appears in that code, you have a valid `Sequence` doing exactly the same thing – that’s how similar these two are. 51 | 52 | Once you have your asynchronous sequence, you can loop over its values by using `for await` in an async context, like this: 53 | */ 54 | func printAllDoubles() async -> [Int] { 55 | var nums: [Int] = [] 56 | for await number in DoubleGenerator() { // FOR AWAIT !!! asynchronous for loop! 57 | nums.append(number) 58 | } 59 | // It could wait forever here ... 60 | return nums.filter({$0 % 4 == 0}) // Filter 61 | } 62 | 63 | print("We start") 64 | var myNums:[Int] = [] 65 | Task { 66 | myNums = await printAllDoubles() 67 | print("\nPrinting numbers:") 68 | myNums.forEach{ num in 69 | print(num) 70 | } 71 | } 72 | print("Done but still running ... and printing nothing !!! \(myNums)") // FIXME: We get nothing here! 73 | 74 | /*: 75 | The `AsyncSequence` protocol also provides default implementations of a variety of common methods, such as `map()`, `compactMap()`, `allSatisfy()`, and more. For example, we could check whether our generator outputs a specific number like this: 76 | */ 77 | let doubles = DoubleGenerator() 78 | 79 | func containsExactNumber() async { 80 | let match = await doubles.contains(16_777_216) 81 | print("We found a match", match) 82 | } 83 | 84 | func summingNumbers() async { 85 | let sum = await doubles.reduce(0, +) // REDUCE 86 | print("Sum val ", sum) 87 | } 88 | 89 | func mapNumbers() { 90 | let twoBigger = doubles.map { value in 91 | return 92 | } 93 | print("Map of doubles \(twoBigger)") 94 | } 95 | 96 | print("\n\n\n") 97 | // MARK: Summing Numbers 98 | mapNumbers() 99 | Task { 100 | await summingNumbers() 101 | await containsExactNumber() 102 | //await mapNumbers() 103 | } 104 | 105 | 106 | 107 | // Async Publisher 108 | 109 | // Async Stream 110 | 111 | 112 | // Async Algorithums 113 | // Processing values over time 114 | // Zip 115 | 116 | extension Array { 117 | func send() -> AsyncStream { 118 | AsyncStream {continuation in 119 | Task { 120 | for value in self { 121 | continuation.yield(value) 122 | } 123 | } 124 | } 125 | } 126 | } 127 | 128 | 129 | // * Combines values produced into tuples 130 | let a = [1,2,3] 131 | let b = ["a", "b", "c"] 132 | let c = ["😀","😡"] 133 | 134 | for await item in b.send() { // FOR AWAIT !!! asynchronous for loop! 135 | print(item) 136 | } 137 | // Zip and Merge do not work in Playground 138 | sleep(20) 139 | /*: 140 | Again, you need to be in an async context to use this. 141 | 142 |   143 | 144 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 145 | */ 146 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/LoadImage.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | /*: 3 | ``` 4 | AsyncImage(url: URL(string: "https://example.com/icon.png")) { image in 5 | image.resizable() 6 | } placeholder: { 7 | ProgressView() 8 | } 9 | .frame(width: 50, height: 50) 10 | ``` 11 | */ 12 | 13 | import Foundation 14 | import SwiftUI 15 | import Combine 16 | import PlaygroundSupport 17 | 18 | 19 | // MARK: From Nick 20 | // MARK: Closure 21 | let url = URL(string: "https://picsum.photos/200")! 22 | 23 | //Passed to the completion handler 24 | func handleResponse(data: Data?, response: URLResponse?) -> UIImage? { 25 | guard 26 | let data = data, 27 | let image = UIImage(data: data), 28 | let response = response as? HTTPURLResponse, 29 | response.statusCode >= 200 && response.statusCode < 300 else { 30 | return nil 31 | } 32 | return image 33 | } 34 | 35 | /* 36 | 1. anytime you type @escaping --- be cautious --- This is for the programmer not the compiler 37 | 2. anytime you type [weak self] --- be cautious --- This is tricky 38 | */ 39 | func downloadWithEscaping(completionHandler: @escaping (_ image: UIImage?, _ error: Error?) -> ()) { 40 | URLSession.shared.dataTask(with: url) { /*[weak self]*/ data, response, error in 41 | let image = handleResponse(data: data, response: response) 42 | completionHandler(image, error) 43 | } 44 | .resume() 45 | } 46 | 47 | // MARK: Combine 48 | // Using Combine URLSession -- Very Clean but does not throw 49 | func downloadWithCombine() -> AnyPublisher { 50 | URLSession.shared.dataTaskPublisher(for: url) 51 | .map(handleResponse) 52 | .mapError({ $0 }) 53 | .eraseToAnyPublisher() 54 | } 55 | 56 | 57 | // MARK: Async 58 | // Using async await URLSession -- Very Clean 59 | func downloadWithAsync() async throws -> UIImage? { 60 | do { 61 | // iOS 7.0+ but the added async version (like many of there oler APIs) 62 | let (data, response) = try await URLSession.shared.data(from: url, delegate: nil) 63 | return handleResponse(data: data, response: response) 64 | } catch { 65 | throw error 66 | } 67 | } 68 | 69 | struct AsycnImgView: View { 70 | @State var msg = "blank" 71 | @State var img: UIImage? 72 | 73 | var body: some View { 74 | VStack(alignment: .leading, spacing: 20) { 75 | Text(msg) 76 | if let goodImg = img { 77 | Image(uiImage:goodImg) 78 | .resizable() 79 | .scaledToFit() 80 | .frame(width: 100, height: 200) 81 | } 82 | }.onAppear() { 83 | Task { 84 | do { 85 | if let downLoadImg = try await downloadWithAsync() { 86 | img = downLoadImg 87 | msg = "Done loading image" 88 | } 89 | }catch { 90 | msg = error.localizedDescription 91 | } 92 | } 93 | msg = "Loading image" 94 | } 95 | 96 | } 97 | } 98 | 99 | struct SwiftUIAsyncImage : View { 100 | @State var msg = "blank" 101 | @State var imgURL = "https://picsum.photos/100" 102 | var body: some View { 103 | VStack(alignment: .leading, spacing: 20) { 104 | Text(msg) 105 | // iOS 15.0+ 106 | 107 | AsyncImage(url: URL(string:"https://picsum.photos/100")) { phase in 108 | if let image = phase.image { 109 | image // Displays the loaded image. 110 | } else if phase.error != nil { 111 | Color.red // Indicates an error. 112 | } else { 113 | Color.blue // Acts as a placeholder. 114 | } 115 | } 116 | 117 | 118 | // Amazing rotating image viewer in a few lines of code 119 | AsyncImage(url: URL(string:imgURL)) { image in 120 | image.resizable() 121 | .aspectRatio(contentMode:.fit) 122 | } placeholder: { 123 | ProgressView() 124 | }.task { 125 | while(true) { 126 | try? await Task.sleep(nanoseconds: 4_000_000_000) 127 | let rand = Int.random(in: 100...150) 128 | imgURL = "https://picsum.photos/\(rand)" 129 | print("This is the URL \(imgURL)") 130 | } 131 | } 132 | } 133 | .frame(minWidth: 100, 134 | minHeight: 200) 135 | } 136 | } 137 | 138 | 139 | 140 | 141 | struct ContentView: View { 142 | var body: some View { 143 | 144 | VStack() { 145 | AsycnImgView() 146 | SwiftUIAsyncImage() 147 | }.frame(minWidth: 100, 148 | minHeight: 500) 149 | } 150 | } 151 | 152 | 153 | PlaygroundPage.current 154 | .setLiveView(ContentView()) 155 | 156 | //: [Next](@next) 157 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Continuations for interfacing async tasks with synchronous code.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Continuations for interfacing async tasks with synchronous code 8 | 9 | [SE-0300](https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md) introduces new functions to help us adapt older, completion handler-style APIs to modern async code. 10 | 11 | For example, this function returns its values asynchronously using a completion handler: 12 | */ 13 | import Foundation 14 | import SwiftUI 15 | 16 | 17 | let msg = ["Swift 5.5 release", "Apple acquires Apollo"] 18 | 19 | func fetchLatestNews(completion: @escaping ([String]) -> Void) { 20 | print("Fetching News ...") 21 | //DispatchQueue.main.async { 22 | print("GCD ...") 23 | completion(msg) 24 | //} 25 | } 26 | /*: 27 | If you wanted to use that using async/await you might be able to rewrite the function, but there are various reasons why that might not be possible – it might come from an external library, for example. 28 | 29 | Continuations allow us to create a shim between the completion handler and async functions so that we wrap up the older code in a more modern API. For example, the `withCheckedContinuation()` function creates a new continuation that can run whatever code you want, then call `resume(returning:)` to send a value back whenever you’re ready – even if that’s part of a completion handler closure. 30 | 31 | So, we could make a second `fetchLatestNews()` function that is async, wrapping around the older completion handler function: 32 | */ 33 | func fetchLatestNews() async -> [String] { // wapper 34 | await withCheckedContinuation { continuation in 35 | print("async / await ...") 36 | fetchLatestNews { items in // the oringal code 37 | continuation.resume(returning: items) // Swift will check that is called once! 38 | } 39 | } 40 | } 41 | /*: 42 | With that in place we can now get our original functionality in an async function, like this: 43 | */ 44 | func printNews() async -> [String] { 45 | print("Start async") 46 | let items = await fetchLatestNews() // So much better 47 | print("End async") 48 | return items 49 | } 50 | 51 | 52 | Task { 53 | let items = await printNews() 54 | print("These are the items") 55 | for item in items { 56 | print(item) 57 | } 58 | } 59 | print("No items here") 60 | /*: 61 | The term “checked” continuation means that Swift is performing runtime checks on our behalf: are we calling `resume()` once and only once? This is important, because if you never resume the continuation then you will leak resources, but if you call it twice then you’re likely to hit problems. 62 | 63 | **Important:** To be crystal clear, you *must* resume your continuation exactly once. 64 | 65 | As there is a runtime performance cost of checking your continuations, Swift also provides a `withUnsafeContinuation()` function that works in exactly the same way except does *not* perform runtime checks on your behalf. This means Swift won’t warn you if you forget to resume the continuation, and if you call it twice then the behavior is undefined. 66 | 67 | Because these two functions are called in the same way, you can switch between them easily. So, it seems likely people will use `withCheckedContinuation()` while writing their functions so Swift will emit warnings and even trigger crashes if the continuations are used incorrectly, but some may then switch over to `withUnsafeContinuation()` as they prepare to ship if they are affected by the runtime performance cost of checked continuations. 68 | 69 | More from Paul Hudson 70 | [url](https://www.hackingwithswift.com/quick-start/concurrency/how-to-use-continuations-to-convert-completion-handlers-into-async-functions) 71 | */ 72 | 73 | /*: 74 | From Nick 75 | */ 76 | 77 | // NEW WAY 78 | func getData(url: URL) async throws -> Data { 79 | do { 80 | let (data, _) = try await URLSession.shared.data(from: url, delegate: nil) 81 | return data 82 | } catch { 83 | throw error 84 | } 85 | } 86 | 87 | 88 | // OLD WAY with withCheckedThrowingContinuation 89 | func getDataOld(url: URL) async throws -> Data { 90 | 91 | return try await withCheckedThrowingContinuation { continuation in 92 | URLSession.shared.dataTask(with: url) { data, response, error in 93 | if let data = data { 94 | continuation.resume(returning: data) 95 | } else if let error = error { 96 | continuation.resume(throwing: error) 97 | } else { // catch all errors 98 | continuation.resume(throwing: URLError(.badURL)) //MUST RESUME the continuation exactly ONCE 99 | } 100 | } 101 | .resume() // a dataTask you must call resume. Very different form continuation.resume() 102 | } 103 | } 104 | 105 | func runWithCheckedThrowingContinuation() { 106 | Task { 107 | guard let url = URL(string: "https://picsum.photos/300") else { return } 108 | 109 | do { 110 | let data = try await getDataOld(url: url) 111 | 112 | if let image = UIImage(data: data) { 113 | await MainActor.run(body: { // move to main thread to pretend it is on the UI 114 | print("This is the address of the image \(image)") 115 | }) 116 | } 117 | } catch { 118 | print("This is the error \(error)") 119 | } 120 | } 121 | } 122 | 123 | runWithCheckedThrowingContinuation() 124 | 125 | // MARK: Just one last example to experiment 126 | func getHeartImageFromDatabase(completionHandler: @escaping (_ image: UIImage) -> ()) { 127 | DispatchQueue.main.asyncAfter(deadline: .now() + 5) { 128 | completionHandler(UIImage(systemName: "heart.fill")!) 129 | } 130 | } 131 | 132 | 133 | // Add throws to this ... 134 | func getHeartImageFromDatabase() async -> UIImage { 135 | await withCheckedContinuation { continuation in 136 | getHeartImageFromDatabase { image in 137 | continuation.resume(returning: image) // YOU MUST RESUME the task exactly ONCE 138 | } 139 | } 140 | } 141 | 142 | Task { 143 | await getHeartImageFromDatabase() 144 | } 145 | 146 | 147 | /*: 148 |   149 | 150 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 151 | */ 152 | 153 | 154 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/StructClassActor.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | /*: 4 | Copied from Nick Sarno 5 | 6 | [Treads / Stacks / Heap](https://www.backblaze.com/blog/whats-the-diff-programs-processes-and-threads/) 7 | 8 | Links: 9 | * https://blog.onewayfirst.com/ios/posts/2019-03-19-class-vs-struct/ 10 | * https://stackoverflow.com/questions/24217586/structure-vs-class-in-swift-language 11 | * https://medium.com/@vinayakkini/swift-basics-struct-vs-class-31b44ade28ae 12 | * https://stackoverflow.com/questions/24217586/structure-vs-class-in-swift-language/59219141#59219141 13 | * https://stackoverflow.com/questions/27441456/swift-stack-and-heap-understanding 14 | * https://stackoverflow.com/questions/24232799/why-choose-struct-over-class/24232845 15 | * https://www.backblaze.com/blog/whats-the-diff-programs-processes-and-threads/ 16 | * https://medium.com/doyeona/automatic-reference-counting-in-swift-arc-weak-strong-unowned-925f802c1b99 17 | 18 | VALUE TYPES: 19 | - Struct, Enum, String, Int, etc. 20 | - Stored in the Stack 21 | - Faster 22 | - Thread safe! 23 | - When you assign or pass value type a new copy of data is created 24 | 25 | REFERENCE TYPES: 26 | - Class, Function, Actor 27 | - Stored in the Heap 28 | - Slower, but synchronized 29 | - NOT Thread safe (by default) 30 | - When you assign or pass reference type a new reference to original instance will be created (pointer) 31 | 32 | - - - - - - - - - - - - - - 33 | 34 | STACK: 35 | - Stores Value types 36 | - Variables allocated on the stack are stored directly to the memory, and access to this memory is very fast 37 | - Each thread has it's own stack! 38 | 39 | HEAP: 40 | - Stores Reference types 41 | - Shared across threads! 42 | 43 | - - - - - - - - - - - - - - 44 | 45 | STRUCT: 46 | - Based on VALUES 47 | - Can be mutated 48 | - Stored in the Stack! 49 | 50 | CLASS: 51 | - Based on REFERENCES (INSTANCES) 52 | - Stored in the Heap! 53 | - Inherit from other classes 54 | 55 | ACTOR: 56 | - Same as Class, but thread safe! 57 | 58 | - - - - - - - - - - - - - - 59 | 60 | Structs: Data Models, Views 61 | 62 | Classes: ViewModels 63 | 64 | Actors: Shared 'Manager' and 'Data Stores' 65 | 66 | 67 | */ 68 | 69 | import Foundation 70 | import SwiftUI 71 | import PlaygroundSupport 72 | 73 | var greeting = "Actors" 74 | 75 | actor DataMananger { 76 | func getDataFromDatabase() {} // this is thread safe 77 | } 78 | 79 | /*: Your ViewModel must be a class to conform to ObservableObject */ 80 | class currentViewModel : ObservableObject { 81 | @Published var info: String = "my Info" 82 | 83 | init(){ 84 | print("ViewModel Init") 85 | } 86 | } 87 | 88 | /*: Classes */ 89 | 90 | class MyClass { 91 | var title: String 92 | 93 | init(title: String) { 94 | self.title = title 95 | } 96 | 97 | func updateTitle(newTitle: String) { 98 | title = newTitle 99 | } 100 | } 101 | 102 | private func classTest1() { 103 | print("classTest1") 104 | let objectA = MyClass(title: "Starting title!") 105 | print("ObjectA: ", objectA.title) 106 | 107 | print("Pass the REFERENCE of objectA to objectB.") 108 | let objectB = objectA 109 | print("ObjectB: ", objectB.title) 110 | 111 | objectB.title = "Second title!" 112 | print("ObjectB title changed.") 113 | 114 | print("ObjectA: ", objectA.title) 115 | print("ObjectB: ", objectB.title) 116 | } 117 | classTest1() 118 | printDivider() 119 | 120 | // Different Classes 121 | private func classTest2() { 122 | print("classTest2") 123 | 124 | let class1 = MyClass(title: "Title1") 125 | print("Class1: ", class1.title) 126 | class1.title = "TitleClass1" 127 | print("Class1: ", class1.title) 128 | 129 | //let class2 = class1 same class 130 | 131 | let class2 = MyClass(title: "Title2") 132 | print("Class2: ", class2.title) 133 | class2.updateTitle(newTitle: "TitleClass2") 134 | print("Class2: ", class2.title) 135 | 136 | 137 | } 138 | classTest2() 139 | printDivider() 140 | 141 | /*: Structs */ 142 | 143 | struct MyStructVar { 144 | var title: String // change to let 145 | } 146 | 147 | struct MyStructLet { 148 | let title: String 149 | } 150 | 151 | // Updating a struct creats a new struct. 152 | private func structTest1() { 153 | 154 | let structA = MyStructVar(title: "Starting title!") 155 | print("StructA: ", structA.title) 156 | 157 | print("Pass the VALUES of structA to structB.") 158 | var structB = structA // change to let 159 | print("StructB: ", structB.title) 160 | 161 | structB.title = "Second title!" 162 | print("StructB title changed.") 163 | 164 | print("StructA: ", structA.title) 165 | print("StructB: ", structB.title) 166 | 167 | } 168 | 169 | structTest1() 170 | printDivider() 171 | 172 | // Immutable struct 173 | struct CustomStruct { 174 | let title: String 175 | 176 | func updateTitle(newTitle: String) -> CustomStruct { 177 | CustomStruct(title: newTitle) 178 | } 179 | } 180 | 181 | struct MutatingStruct { 182 | private(set) var title: String 183 | 184 | init(title: String) { 185 | self.title = title 186 | } 187 | 188 | mutating func updateTitle(newTitle: String) { 189 | title = newTitle 190 | } 191 | } 192 | 193 | private func structTest2() { 194 | print("structTest2") 195 | 196 | var structLet = MyStructLet(title: "Title1.1") 197 | print("Struct1: ", structLet.title) 198 | // structLet.title = "Title1.2" // Uncommnet to see error 199 | print("Struct1: ", structLet.title) 200 | 201 | let structLet1 = MyStructVar(title: "Title1.1") 202 | print("Struct1: ", structLet1.title) 203 | // structLet1.title = "Title1.2" // Uncomment to see errro 204 | print("Struct1: ", structLet1.title) 205 | 206 | 207 | var struct1 = MyStructVar(title: "Title1.1") 208 | print("Struct1: ", struct1.title) 209 | struct1.title = "Title1.2" 210 | print("Struct1: ", struct1.title) 211 | 212 | // Create a new Struct 213 | var /*NOT let*/ struct2 = CustomStruct(title: "CustomTitle2.1") 214 | print("Struct2: ", struct2.title) 215 | struct2 = CustomStruct(title: "CustomTitle2.2") 216 | print("Struct2: ", struct2.title) 217 | 218 | // Update the title same as making a new Struct 219 | var struct3 = CustomStruct(title: "CustomTitle3.1") 220 | print("Struct3: ", struct3.title) 221 | struct3 = struct3.updateTitle(newTitle: "CustomTitle3.2") 222 | print("Struct3: ", struct3.title) 223 | 224 | 225 | // Even this makes a new Struct 226 | var struct4 = MutatingStruct(title: "MutatingTitle1.1") 227 | print("Struct4: ", struct4.title) 228 | struct4.updateTitle(newTitle: "MutatingTitle2.1") 229 | print("Struct4: ", struct4.title) 230 | } 231 | 232 | structTest2() 233 | printDivider() 234 | 235 | actor MyActor { 236 | var title: String 237 | 238 | init(title: String) { 239 | self.title = title 240 | } 241 | 242 | func updateTitle(newTitle: String) { 243 | title = newTitle 244 | } 245 | } 246 | 247 | private func actorTest1() { 248 | Task { 249 | print("actorTest1") 250 | let objectA = MyActor(title: "Starting title!") 251 | await print("ObjectA: ", objectA.title) // must await an actor - only one thread at a time 252 | 253 | print("Pass the REFERENCE of objectA to objectB.") 254 | let objectB = objectA 255 | await print("ObjectB: ", objectB.title) 256 | 257 | await objectB.updateTitle(newTitle: "Second title!") 258 | print("ObjectB title changed.") 259 | 260 | await print("ObjectA: ", objectA.title) 261 | await print("ObjectB: ", objectB.title) 262 | } 263 | } 264 | 265 | actorTest1() 266 | 267 | 268 | /*struct MyView : View { 269 | var body :some View { 270 | Text("hi") 271 | } 272 | }*/ 273 | 274 | //PlaygroundPage.current.setLiveView(MyView()) 275 | 276 | // Use asyncImage to load our image 277 | 278 | private func printDivider() { 279 | print(""" 280 | 281 | - - - - - - - - - - - - - - - - - 282 | 283 | """) 284 | } 285 | //: [Next](@next) 286 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Actors.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Actors 8 | 9 | [SE-0306](https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md) introduces actors, which are conceptually similar to classes that are safe to use in concurrent environments. This is possible because Swift ensures that mutable state inside your actor is only ever accessed by a single thread at any given time, which helps eliminate a variety of serious bugs right at the compiler level. 10 | 11 | To demonstrate the problem actors solve, consider this Swift code that creates a `RiskyCollector` class able to trade cards from their deck with another collector: 12 | */ 13 | import Foundation 14 | 15 | class RiskyCollector { 16 | let name:String 17 | var deck: Set 18 | 19 | init(name:String, deck: Set) { 20 | self.deck = deck 21 | self.name = name 22 | } 23 | 24 | func send(card selected: String, to person: RiskyCollector) -> Bool { 25 | var didTransfer = false 26 | print("sent to \(person.name) ") 27 | guard deck.contains(selected) else { 28 | print("\(name) dose not have \(selected)") 29 | return false } 30 | 31 | // Critical Section 32 | print("--- Deck has \(selected) --- for \(person.name)") 33 | // sleep(1) 34 | if let removed = deck.remove(selected) { 35 | print("The deck tranfered \(removed) for \(person.name)") 36 | didTransfer = true 37 | } else { 38 | print("The deck is was wrong for \(person.name)") 39 | didTransfer = false 40 | } 41 | person.transfer(card: selected) 42 | 43 | return didTransfer 44 | } 45 | 46 | func transfer(card: String) { 47 | deck.insert(card) 48 | } 49 | 50 | func printDeck() { 51 | print("\(name) has \(deck)") 52 | } 53 | } 54 | /*: 55 | In a single-threaded environment that code is safe: we check whether our deck contains the card in question, remove it, then add it to the other collector’s deck. However, in a multi-threaded environment our code has a potential race condition, which is a problem whereby the results of the code will vary as two separate parts of our code run side by side. 56 | 57 | If we call `send(card:to:)` more than once at the same time, the following chain of events can happen: 58 | 59 | 1. The first thread checks whether the card is in the deck, and it is so it continues. 60 | 2. The second thread also checks whether the card is in the deck, and it is so it continues. 61 | 3. The first thread removes the card from the deck and transfer it to the other person. 62 | 4. The second thread attempts to remove the card from the deck, but actually it’s already gone so nothing will happen. However, it still transfers the card to the other person. 63 | 64 | In that situation one player loses a card while the other gains *two* cards, and if that card happened to be a Black Lotus from Magic the Gathering then you’ve got a big problem! 65 | 66 | Actors solve this problem by introducing *actor isolation*: stored properties and methods cannot be read from outside the actor object unless they are performed asynchronously, and stored properties cannot be *written* from outside the actor object at all. The async behavior isn’t there for performance; instead it’s because Swift automatically places these requests into a queue that is processed sequentially to avoid race conditions. 67 | 68 | So, we could rewrite out `RiskyCollector` class to be a `SafeCollector` actor, like this: 69 | */ 70 | actor SafeCollector { 71 | var deck: Set 72 | let name: String 73 | 74 | init(name:String, deck: Set) { 75 | self.deck = deck 76 | self.name = name 77 | } 78 | 79 | func send(card selected: String, to person: SafeCollector) async -> Bool { 80 | 81 | var didTransfer = false 82 | print("sent to \(person.name) ") 83 | guard deck.contains(selected) else { 84 | print("\(name) dose not have \(selected)") 85 | return false 86 | } 87 | 88 | print("--- Deck has \(selected) --- for \(person.name)") 89 | 90 | // This is dangerous because of thread reentry 91 | // try? await Task.sleep(nanoseconds: 1_000_000_000) 92 | 93 | if let removed = deck.remove(selected) { 94 | print("The deck tranfered \(removed) for \(person.name)") 95 | didTransfer = true 96 | } else { 97 | print("The deck is was wrong for \(person.name)") 98 | didTransfer = false 99 | } 100 | await person.transfer(card: selected) 101 | 102 | return didTransfer 103 | } 104 | 105 | // Actor-isolated instance method 'transfer(card:)' referenced must be isolated actor instance 106 | func transfer(card: String) { 107 | deck.insert(card) 108 | } 109 | 110 | func printDeck() { 111 | print("\(name) has \(deck)") 112 | } 113 | 114 | nonisolated func staticData() -> String { 115 | return "data that never changes" 116 | } 117 | 118 | func dynamicData() -> String { 119 | return "data that changes \(Int.random(in: 0...300))" 120 | } 121 | 122 | } 123 | /*: 124 | There are several things to notice in that example: 125 | 126 | 1. Actors are created using the new `actor` keyword. This is a new concrete nominal type in Swift, joining structs, classes, and enums. 127 | 2. The `send()` method is marked with `async`, because it will need to suspend its work while waiting for the transfer to complete. 128 | 3. Although the `transfer(card:)` method is *not* marked with `async`, we still need to *call* it with `await` because it will wait until the other `SafeCollector` actor is able to handle the request. 129 | 130 | To be clear, an actor can use its own properties and methods freely, asynchronously or otherwise, but when interacting with a different actor it must always be done asynchronously. With these changes Swift can ensure that all actor-isolated state is never accessed concurrently, and more importantly this is done at compile time so that safety is guaranteed. 131 | 132 | Actors and classes have some similarities: 133 | 134 | - Both are reference types, so they can be used for shared state. 135 | - They can have methods, properties, initializers, and subscripts. 136 | - They can conform to protocols and be generic. 137 | - Any properties and methods that are static behave the same in both types, because they have no concept of `self` and therefore don’t get isolated. 138 | 139 | Beyond actor isolation, there are two other important differences between actors and classes: 140 | 141 | - Actors do not currently support inheritance, which makes their initializers much simpler – there is no need for convenience initializers, overriding, the `final` keyword, and more. This might change in the future. 142 | - All actors implicitly conform to a new `Actor` protocol; no other concrete type can use this. This allows you to restrict other parts of your code so it can work only with actors. 143 | 144 | The best way I’ve heard to explain how actors differ from classes is this: “actors pass messages, not memory.” So, rather than one actor poking directly around in another’s properties or calling their methods, we instead send a message asking for the data and let the Swift runtime handle it for us safely. 145 | 146 |  */ 147 | 148 | private var deck: Set = ["car", "boat", "plane", "house"] 149 | private var emptyDeck: Set = [] 150 | 151 | 152 | print("start risky") 153 | let sam = RiskyCollector(name:"Sam", deck: deck) 154 | let tim = RiskyCollector(name:"Tim", deck: emptyDeck) 155 | let adam = RiskyCollector(name:"adam", deck: emptyDeck) 156 | 157 | DispatchQueue.global().async { 158 | print("sam give item tim ", sam.send(card: "car", to:tim)) 159 | } 160 | // sleep (1) // simple sleep fixed the timing issue 161 | DispatchQueue.global().async { 162 | print("sam give item to adam", sam.send(card: "car", to:adam)) 163 | } 164 | sleep(5) 165 | sam.printDeck() 166 | tim.printDeck() 167 | adam.printDeck() 168 | 169 | 170 | print("\n\nSafe\n") 171 | let samSafe = SafeCollector(name: "safeSam", deck: deck) 172 | let timSafe = SafeCollector(name: "safeTim", deck: emptyDeck) 173 | let adamSafe = SafeCollector(name:"safeAdam", deck: emptyDeck) 174 | 175 | // print(samSafe.dynamicData()) //Actor-isolated instance method 'dynamicData()' can not be referenced from a non-isolated context 176 | 177 | print("called without await = \(adamSafe.staticData()) \n\n") // can call this from anywhere 178 | 179 | 180 | Task { 181 | let safeTim = await samSafe.send(card: "car", to:timSafe) 182 | print("safeTim got car \(safeTim)") 183 | } 184 | 185 | Task { 186 | let safeAdam = await samSafe.send(card: "car", to:adamSafe) 187 | print("safeAdam got car \(safeAdam)") 188 | } 189 | 190 | 191 | sleep(5) 192 | Task { 193 | await samSafe.printDeck() 194 | await timSafe.printDeck() 195 | await adamSafe.printDeck() 196 | } 197 | 198 | 199 | /*: [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 200 | */ 201 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Async await.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Async await 8 | 9 | [SE-0296](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md) introduces asynchronous (async) functions into Swift, allowing us to run complex asynchronous code almost is if it were synchronous. This is done in two steps: marking async functions with the new `async` keyword, then calling them using the `await` keyword, similar to other languages such as C# and JavaScript. 10 | 11 | To see how async/await helps the language, it’s helpful to look at how we solved the same problem previously. Completion handlers are commonly used in Swift code to allow us to send back values after a function returns, but they had tricky syntax as you’ll see. 12 | 13 | For example, if we wanted to write code that fetched 100,000 weather records from a server, processes them to calculate the average temperature over time, then uploaded the resulting average back to a server, we might have written this: 14 | */ 15 | import Foundation 16 | import SwiftUI 17 | import PlaygroundSupport 18 | 19 | // @escaping because outlives function 20 | // Return a array of doubles 21 | func fetchWeatherHistory(completion: @escaping ([Double]) -> Void) { 22 | // Complex networking code here; we'll just send back 100 random temperatures 23 | DispatchQueue.global().async { 24 | let results = (1..<100).map { _ in Double.random(in: -10...30) } 25 | 26 | completion(results) // records at the call sight 27 | } 28 | } 29 | 30 | // Return a single Double value 31 | func calculateAverageTemperature(for records: [Double], completion: @escaping (Double) -> Void) { 32 | // Sum our array then divide by the array size 33 | DispatchQueue.global().async { 34 | let total = records.reduce(0, +) 35 | let average = total / Double(records.count) 36 | completion(average) 37 | } 38 | } 39 | 40 | // Return a singe String 41 | func upload(result: Double, completion: @escaping (String) -> Void) { 42 | // More complex networking code; we'll just send back "OK" 43 | DispatchQueue.global().async { 44 | completion("OK") 45 | } 46 | } 47 | /*: 48 | I’ve substituted actual networking code with fake values because the networking part isn’t relevant here. What matters is that each of those functions can take some time to run, so rather than blocking execution of the function and returning a value directly we instead use a completion closure to send something back only when we’re ready. 49 | 50 | When it comes to using that code, we need to call them one by one in a chain, providing completion closures for each one to continue the chain, like this: 51 | */ 52 | // takes on argument a closure 53 | fetchWeatherHistory { records in // returns an array of doubles 54 | 55 | // takes two arguments ( 1. array of double 2. a closure ) 56 | calculateAverageTemperature(for: records) { average in // takes array of doubles & returns double 57 | 58 | // Takes two arguments ( 1. double 2. a closure ) 59 | upload(result: average) { response in // takes a double and returns a String 60 | print("Grand Dispatch server: average \(average) response: \(response)") 61 | } 62 | } 63 | } 64 | /*: 65 | Hopefully you can see the problems with this approach: 66 | 67 | - It’s possible for those functions to call their completion handler more than once, or forget to call it entirely. 68 | - The parameter syntax `@escaping (String) -> Void` can be hard to read. 69 | - At the call site we end up with a so-called pyramid of doom, with code increasingly indented for each completion handler. 70 | - Until Swift 5.0 added the `Result` type, it was harder to send back errors with completion handlers. 71 | 72 | From Swift 5.5, we can now clean up our functions by marking them as asynchronously returning a value rather than relying on completion handlers, like this: 73 | */ 74 | func fetchWeatherHistory() async -> [Double] { 75 | (1...100).map { _ in Double.random(in: -10...30) } 76 | } 77 | 78 | func calculateAverageTemperature(for records: [Double]) async -> Double { 79 | let total = records.reduce(0, +) 80 | let average = total / Double(records.count) 81 | return average 82 | } 83 | 84 | func upload(result: Double) async -> String { 85 | "OK" 86 | } 87 | /*: 88 | That has already removed a lot of the syntax around returning values asynchronously, but at the call site it’s even cleaner: 89 | */ 90 | func processWeather() async { 91 | let records = await fetchWeatherHistory() 92 | let average = await calculateAverageTemperature(for: records) 93 | let response = await upload(result: average) 94 | print("Task Server average: \(average) response: \(response)") 95 | } 96 | 97 | Task { 98 | await processWeather() 99 | } 100 | /*: 101 | As you can see, all the closures and indenting have gone, making for what is sometimes called “straight-line code” – apart from the `await` keywords, it looks just like synchronous code. 102 | 103 | There are some straightforward, specific rules about the way async functions work: 104 | 105 | - Synchronous functions cannot simply call async functions directly – it wouldn’t make sense, so Swift will throw an error. 106 | - Async functions can call other async functions, but they can also call regular synchronous functions if they need to. 107 | - If you have async and synchronous functions that can be called in the same way, Swift will prefer whichever one matches your current context – if the call site is currently async then Swift will call the async function, otherwise it will call the synchronous function. 108 | 109 | That last point is important, because it allows library authors to provide both synchronous and asynchronous versions of their code without having to name the async functions specially. 110 | 111 | The addition of `async`/`await` fits perfectly alongside `try`/`catch`, meaning that async functions and initializers can throw errors if needed. The only proviso here is that Swift enforces a particular order for the keywords, and that order is *reversed* between call site and function. 112 | 113 | For example, we might have functions that attempt to fetch a number of users from a server, and save them to disk, both of which might fail by throwing errors: 114 | */ 115 | enum UserError: Error { 116 | case invalidCount, dataTooLong 117 | } 118 | 119 | func fetchUsers(count: Int) async throws -> [String] { 120 | if count > 3 { 121 | // Don't attempt to fetch too many users 122 | throw UserError.invalidCount 123 | } 124 | 125 | // Complex networking code here; we'll just send back up to `count` users 126 | return Array(["Antoni", "Karamo", "Tan"].prefix(count)) // try 4 127 | } 128 | 129 | func save(users: [String]) async throws -> String { 130 | let savedUsers = users.joined(separator: ",") 131 | 132 | if savedUsers.count > 32 { 133 | throw UserError.dataTooLong 134 | } else { 135 | // Actual saving code would go here 136 | return "Saved \(savedUsers)!" 137 | } 138 | } 139 | /*: 140 | As you can see, both those functions are marked `async throws` – they are asynchronous functions, and they might throw errors. 141 | 142 | When it comes to *calling* them the order of keywords is flipped to `try await` rather than `await try`, like this: 143 | */ 144 | func updateUsers() async { 145 | do { 146 | let users = try await fetchUsers(count: 3) // try 4 147 | let result = try await save(users: users) 148 | print(result) 149 | } catch { 150 | print("Oops!") 151 | } 152 | } 153 | 154 | Task { 155 | await updateUsers() 156 | } 157 | /*: 158 | So, “asynchronous, throwing” in the function definition, but “throwing, asynchronous” at the call site – think of it as unwinding a stack. Not only does `try await` read a little more naturally than `await try`, but it’s also more reflective of what’s actually happening: we’re waiting for some work to complete, and when it *does* complete it might end up throwing. 159 | 160 | With async/await now in Swift itself, the `Result` type introduced in Swift 5.0 becomes much less important as one of its primary benefits was improving completion handlers. That doesn’t mean `Result` is useless, because it’s still the best way to store the result of an operation for later evaluation. 161 | 162 | **Important:** Making a function asynchronous doesn’t mean it magically runs concurrently with other code, which means unless you specify otherwise calling multiple async functions will still run them sequentially. 163 | 164 | All the `async` functions you’ve seen so far have in turn been called by other `async` functions, which is intentional: taken by itself this Swift Evolution proposal does not actually provide any way to run asynchronous code from a synchronous context. Instead, this functionality is defined in a separate Structured Concurrency proposal, although hopefully we’ll see some major updates to Foundation too. 165 | 166 |  */ 167 | 168 | /*: From Nick 169 | 170 | Understanding how threads move from background Global to Main 171 | */ 172 | 173 | class AsyncAwaitBootcampViewModel: ObservableObject { 174 | 175 | @Published var dataArray: [String] = [] 176 | 177 | // Running on the Main Thread 178 | func runonMain() { 179 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 180 | self.dataArray.append("Run On Main : \(Thread.current)") 181 | } 182 | } 183 | 184 | func runningOnGlobalToMain() { 185 | DispatchQueue.global().asyncAfter(deadline: .now() + 2) { 186 | let run = "Run On Global : \(Thread.current)" 187 | self.dataArray.append(run) 188 | 189 | DispatchQueue.main.async { 190 | let run2 = "Run back on Main : \(Thread.current)" 191 | self.dataArray.append(run2) 192 | } 193 | } 194 | } 195 | 196 | func addAuthor1() async { 197 | let author1 = "Author1 : \(Thread.current)" 198 | self.dataArray.append(author1) 199 | 200 | try? await Task.sleep(nanoseconds: 2_000_000_000) 201 | 202 | let author2 = "Author2 : \(Thread.current)" 203 | 204 | await MainActor.run(body: { 205 | self.dataArray.append(author2) 206 | 207 | let author3 = "Author3 : \(Thread.current)" 208 | self.dataArray.append(author3) 209 | }) 210 | } 211 | 212 | func addSomething() async { 213 | 214 | try? await Task.sleep(nanoseconds: 2_000_000_000) 215 | 216 | let something1 = "Something1 : \(Thread.current))" 217 | 218 | await MainActor.run(body: { 219 | self.dataArray.append(something1) 220 | 221 | let something2 = "Something2 : \(Thread.current)" 222 | self.dataArray.append(something2) 223 | }) 224 | 225 | } 226 | 227 | 228 | } 229 | 230 | struct AsyncAwaitBootcamp: View { 231 | 232 | @StateObject private var viewModel = AsyncAwaitBootcampViewModel() 233 | 234 | var body: some View { 235 | List { 236 | ForEach(viewModel.dataArray, id: \.self) { data in 237 | Text(data) 238 | } 239 | } 240 | .task { 241 | // The task is cancled when the View is closed 242 | let finalText = "Task on View : \(Thread.current)" 243 | viewModel.dataArray.append(finalText) 244 | } 245 | .onAppear { 246 | // Running on the Main Thread 247 | // viewModel.runonMain() 248 | // viewModel.runningOnGlobalToMain() 249 | Task { 250 | 251 | // These run in sequence 252 | await viewModel.addAuthor1() 253 | await viewModel.addSomething() 254 | 255 | let finalText = "FINAL TEXT : \(Thread.current)" 256 | viewModel.dataArray.append(finalText) 257 | } 258 | 259 | } 260 | 261 | } 262 | } 263 | 264 | PlaygroundPage.current.setLiveView(AsyncAwaitBootcamp()) 265 | 266 | /*: 267 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 268 | */ 269 | -------------------------------------------------------------------------------- /Whats-New-In-Swift-5-7.playground/Pages/Structured concurrency.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | 3 | 4 |   5 | 6 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 7 | # Structured concurrency 8 | 9 | [SE-0304](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md) introduces a whole range of approaches to execute, cancel, and monitor concurrent operations in Swift, and builds upon the work introduced by async/await and async sequences. 10 | 11 | For easier demonstration purposes, here are a couple of example functions we can work with – an async function to simulate fetching a certain number of weather readings for a particular location, and a synchronous function to calculate which number lies at a particular position in the Fibonacci sequence: 12 | */ 13 | import Foundation 14 | import SwiftUI 15 | import PlaygroundSupport 16 | 17 | enum LocationError: Error { 18 | case unknown_location 19 | } 20 | 21 | func waitDoneBeforeNext() { // do not put async here :-) 22 | sleep(2) // wait for 5 sec before letting next func start 23 | print("\n\n") 24 | } 25 | 26 | /* 27 | Network call 28 | */ 29 | func getWeatherReadings(for location: String) async throws -> [Double] { 30 | switch location { 31 | case "London": 32 | return (1...10).map { _ in Double.random(in: 6...26) } 33 | case "Rome": 34 | return (1...10).map { _ in Double.random(in: 10...32) } 35 | case "San Francisco": 36 | return (1...10).map { _ in Double.random(in: 12...20) } 37 | default: 38 | throw LocationError.unknown_location 39 | } 40 | } 41 | 42 | /* Long running task */ 43 | func fibonacci(of number: Int) -> Int { 44 | var first = 0 45 | var second = 1 46 | 47 | for _ in 0.. String { 80 | 81 | let task1 = Task { () -> [Int] in // takes void and returns an array of Int 82 | var numbers = [Int]() 83 | for i in 0..<5 { 84 | let result = fibonacci(of: i) 85 | numbers.append(result) 86 | } 87 | return numbers // Task returns values when asked but already running. 88 | } 89 | 90 | let result1 = await task1.value // let task1: Task<[Int], Never> 91 | 92 | 93 | print("Fibonacci sequence (5): \(result1)") 94 | return "DONE" 95 | } 96 | 97 | func returnDemo() { 98 | print("we start Fib(5) task") 99 | Task { 100 | await print("run Fib(5) ", printFibonacciSequence()) 101 | print("now we can use the results") 102 | } 103 | print("we end but the code is still running so we can not use the results here :-)") 104 | } 105 | // returnDemo() //waitDoneBeforeNext() 106 | /*: 107 | As you can see, I’ve needed to explicitly write `Task { () -> [Int] in` so that Swift understands that the task is going to return, but if your task code is simpler that isn’t needed. For example, we could have written this and gotten exactly the same result: 108 | */ 109 | let task1 = Task { 110 | (0..<12).map(fibonacci) // understood return 111 | } 112 | 113 | 114 | func noReturnDemo() { 115 | Task { 116 | print("running task1 Fib(12) with no return", await task1.value) 117 | } 118 | } 119 | // noReturnDemo() waitDoneBeforeNext() 120 | 121 | /*: 122 | Again, the task starts running as soon as it’s created, and the `printFibonacciSequence()` function will continue running on whichever thread it was while the Fibonacci numbers are being calculated. 123 | 124 | **Tip:** Our task's operation is a non-escaping closure because the task immediately runs it rather than storing it for later, which means if you use `Task` inside a class or a struct you don’t need to use `self` to access properties or methods. 125 | 126 | When it comes to reading the finished numbers, `await task1.value` will make sure execution of `printFibonacciSequence()` pauses until the task’s output is ready, at which point it will be returned. If you don’t actually care what the task returns – if you just want the code to start running and finish whenever – you don’t need to store the task anywhere. 127 | 128 | For task operations that throw uncaught errors, reading your task’s `value` property will automatically also throw errors. So, we could write a function that performs two pieces of work at the same time then waits for them both to complete: 129 | */ 130 | func runMultipleCalculations() async throws { 131 | let task1 = Task { // Started here 132 | (0..<7).map(fibonacci) 133 | } 134 | 135 | let task2 = Task { // Started here 136 | try await getWeatherReadings(for: "Rome") 137 | } 138 | 139 | let task3 = Task { // Started here 140 | try await getWeatherReadings(for: "Does not exist") 141 | } 142 | 143 | let value1 = await task1.value // value 1 is ready to use 144 | let value2 : [Double] = try await task2.value // value 2 is ready to use 145 | let result1 : Result<[Int], Never> = await task1.result 146 | let result2 : Result<[Double], Error> = await task2.result 147 | print("Fibonacci (7): r \(result1) v \(value1) \n Rome weather: v \(value2)") 148 | 149 | // becuase throws do not need a do block 150 | let result3 = try await task3.value // resualt 3 is ready to use with error 151 | print("result3 \(result3)") 152 | } 153 | 154 | func runMultiTaskDemo() { 155 | Task { 156 | // catch not error? 157 | //do { 158 | try await runMultipleCalculations() 159 | //} catch { print("\(error)")} 160 | } 161 | } 162 | // runMultiTaskDemo() // waitDoneBeforeNext() 163 | /*: 164 | Swift provides us with the built-in task priorities of `high`, `default`, `low`, and `background`. The code above doesn’t specifically set one so it will get `default`, but we could have said something like `Task(priority: .high)` to customize that. If you’re writing just for Apple’s platforms, you can also use the more familiar priorities of `userInitiated` in place of high, and `utility` in place of `low`, but you *can’t* access `userInteractive` because that is reserved for the main thread. 165 | 166 | As well as just running operations, `Task` also provides us with a handful of static methods to control the way our code runs: 167 | 168 | - Calling `Task.sleep()` will cause the current task to sleep for a specific number of nanoseconds. Until something better comes along, this means writing 1_000_000_000 to mean 1 second. 169 | - Calling `Task.checkCancellation()` will check whether someone has asked for this task to be cancelled by calling its `cancel()` method, and if so throw a `CancellationError`. 170 | - Calling `Task.yield()` will suspend the current task for a few moments in order to give some time to any tasks that might be waiting, which is particularly important if you’re doing intensive work in a loop. 171 | 172 | You can see both sleeping and cancellation in the following code example, which puts a task to sleep for one second then cancels it before it completes: 173 | */ 174 | func cancelSleepingTask() async { 175 | // Error @frozen struct Task where Success : Sendable, Failure : Error 176 | let task : Task = Task { () -> String in 177 | print("Starting Sleep/Cancle") 178 | try await Task.sleep(nanoseconds: 1_000_000_000) // sleep for 1 sec. 179 | try Task.checkCancellation() 180 | return "Done" 181 | } 182 | 183 | // The task has started, but we'll cancel it while it sleeps 184 | task.cancel() // NOTE: Comment out 185 | 186 | do { 187 | let value = try await task.value 188 | let result = await task.result 189 | print("Value: \(value) with result \(result)") 190 | } catch { 191 | print("Task was cancelled.", error) // NOTE: <--We see this 192 | } 193 | 194 | // do not need do block becuse we are not trying to use the value! 195 | let result = await task.result 196 | // await task.value // will cause an error because try is missing 197 | print("Result \(result)") 198 | 199 | } 200 | 201 | func sleepingDemo() { 202 | Task { 203 | print("Calling sleeping/cancle task", await cancelSleepingTask()) 204 | } 205 | } 206 | //sleeping() // waitDoneBeforeNext() 207 | 208 | 209 | /*: 210 | In that code, `Task.checkCancellation()` will realize the task has been cancelled and immediately throw `CancellationError`, but that won’t reach us until we attempt to read `task.value`. 211 | 212 | **Tip:** Use `task.result` to get a `Result` value containing the task’s success and failure values. For example, in the code above we’d get back a `Result`. This does *not* require a `try` call because you still need to handle the success or failure case. 213 | 214 | For more complex work, you should create *task groups* instead – collections of tasks that work together to produce a finished value. 215 | 216 | To minimize the risk of programmers using task groups in dangerous ways, they don’t have a simple public initializer. Instead, task groups are created using functions such as `withTaskGroup()`: call this with the body of work you want done, and you’ll be passed in the task group instance to work with. Once inside the group you can add work using the `async()` method, and it will start executing immediately. 217 | 218 | **Important:** You should not attempt to copy that task group outside the body of `withTaskGroup()` – the compiler can’t stop you, but you’re just going to make problems for yourself. 219 | 220 | To see a simple example of how task groups work – along with demonstrating an important point of how they order their operations, try this: 221 | */ 222 | func printTaskGroupMessage() async { 223 | let taskGroupString = await withTaskGroup(of: String.self) { group -> String in 224 | group.addTask { "Hello" } // a closure that returns a String 225 | 226 | group.addTask { "From" } 227 | group.addTask { "A" } 228 | 229 | // in the middel 230 | group.addTask { await printFibonacciSequence() } 231 | 232 | group.addTask { "Task" } 233 | group.addTask { "Group" } 234 | 235 | var collected = [String]() 236 | 237 | for await value in group { 238 | // await Task.sleep(2_000_000) // this might scramble the words 239 | collected.append(value) 240 | } 241 | let ans = collected.joined(separator: " ") 242 | 243 | return ans 244 | } 245 | 246 | print("This is the collected String: ", taskGroupString) 247 | } 248 | 249 | func taskGroupDemo() { 250 | Task{ 251 | print("Start Task Group Message: ", await printTaskGroupMessage()) 252 | } 253 | } 254 | // taskGroupDemo() // waitDoneBeforeNext() 255 | /*: 256 | That creates a task group designed to produce one finished string, then queues up several closures using the `async()` method of the task group. Each of those closures returns a single string, which then gets collected into an array of strings, before being joined into one single string and returned for printing. 257 | 258 | **Tip:** All tasks in a task group must return the same type of data, so for complex work you might find yourself needing to return an enum with associated values in order to get exactly what you want. A simpler alternative is introduced in a separate Async Let Bindings proposal. 259 | 260 | Each call to `async()` can be any kind of function you like, as long as it results in a string. However, although task groups automatically wait for all the child tasks to complete before returning, when that code runs it’s a bit of a toss up what it will print because the child tasks can complete in any order – we’re as likely to get “Hello From Task Group A” as we are “Hello A Task Group From”, for example. 261 | 262 | If your task group is executing code that might throw, you can either handle the error directly inside the group or let it bubble up outside the group to be handled there. That latter option is handled using a different function, `withThrowingTaskGroup()`, which must be called with `try` if you haven’t caught all the errors you throw. 263 | 264 | For example, this next code sample calculates weather readings for several locations in a single group, then returns the overall average for all locations: 265 | */ 266 | func printAllWeatherReadings() async { // remove do block. Add throws and try 267 | let cities = ["London", "Rome", "San Francisco"] 268 | do { 269 | print("Calculating average weather…") 270 | 271 | let result = try await withThrowingTaskGroup(of: [Double].self) { group -> String in 272 | group.addTask { 273 | return try await getWeatherReadings(for: "London") 274 | } 275 | 276 | group.addTask { 277 | try await getWeatherReadings(for: "Rome") 278 | } 279 | 280 | group.addTask { 281 | try await getWeatherReadings(for: "San Francisco") 282 | } 283 | 284 | // Uncomment for error 285 | // group.addTask {try await getWeatherReadings(for: "Not Here")} 286 | 287 | for city in cities { 288 | // all task are same and only need to cancle one time 289 | group.addTask { 290 | try await getWeatherReadings(for: city) 291 | } 292 | } 293 | 294 | print("\n\nStart for loop") 295 | for try await city in group { // because they all return the same type 296 | print("This is the temp \(String(describing: city.first))") 297 | } 298 | print("End for loop \n\n") 299 | 300 | 301 | // Convert our array of arrays into a single array of doubles 302 | let allValues = try await group.reduce([], +) 303 | 304 | // Calculate the mean average of all our doubles 305 | let average = allValues.reduce(0, +) / Double(allValues.count) 306 | return "Overall average temperature is \(average)" 307 | 308 | } 309 | 310 | print("Done! \(result)") 311 | } catch { print("Error calculating data. With error: \(error)", error) } 312 | } 313 | 314 | func GroupDemo() { 315 | Task { 316 | print("Start Task Weather:", await printAllWeatherReadings()) 317 | } 318 | } 319 | //GroupDemo() // waitDoneBeforeNext() 320 | /*: 321 | In that instance, each of the calls to `async()` is identical apart from the location string being passed in, so you can use something like `for location in ["London", "Rome", "San Francisco"] {` to call `async()` in a loop. 322 | 323 | Task groups have a `cancelAll()` method that cancels any tasks inside the group, but using `async()` afterwards will continue to add work to the group. As an alternative, you can use `asyncUnlessCancelled()` to skip adding work if the group has been cancelled – check its returned Boolean to see whether the work was added successfully or not. 324 |  */ 325 | 326 | // MARK: From Nick 327 | let urlStrings = [ 328 | "https://picsum.photos/300", 329 | "https://picsum.photos/300", 330 | "https://picsum.photos/300", 331 | "https://picsum.photos/300", 332 | "https://picsum.photos/300", 333 | ] 334 | 335 | private func fetchImage(urlString: String) async throws -> UIImage { 336 | guard let url = URL(string: urlString) else { 337 | throw URLError(.badURL) 338 | } 339 | 340 | do { 341 | let (data, _) = try await URLSession.shared.data(from: url, delegate: nil) 342 | if let image = UIImage(data: data) { 343 | return image 344 | } else { 345 | throw URLError(.badURL) 346 | } 347 | } catch { 348 | throw error 349 | } 350 | } 351 | 352 | struct ContentView: View { 353 | let columns = [GridItem(.flexible()), GridItem(.flexible())] 354 | @State var images: [UIImage] = [] 355 | var body: some View { 356 | 357 | ScrollView { 358 | LazyVGrid(columns: columns) { 359 | ForEach(images, id: \.self) { image in 360 | Image(uiImage: image) 361 | .resizable() 362 | .scaledToFit() 363 | .frame(height: 150) 364 | } 365 | 366 | }.onAppear { 367 | Task { 368 | images.reserveCapacity(urlStrings.count) 369 | try await withThrowingTaskGroup(of: UIImage?.self) { group in 370 | for urlString in urlStrings { 371 | 372 | // all task are same and only need to cancle one time 373 | group.addTask { 374 | // Might not get all the images "try?" 375 | try? await fetchImage(urlString: urlString) // not all images might show up becuase "try?" 376 | } 377 | } 378 | 379 | // Async For Loop!!! 380 | for try await image in group { 381 | if let image = image { // only add images we recieve 382 | images.append(image) 383 | } 384 | } 385 | } 386 | } 387 | 388 | } 389 | } 390 | } 391 | } 392 | 393 | PlaygroundPage.current 394 | .setLiveView(ContentView()) 395 | 396 | 397 | 398 | 399 | // lets review task priority 400 | /*Task { 401 | print(Thread.current) 402 | print(Task.currentPriority) 403 | }*/ 404 | 405 | 406 | func ShowTasks() { 407 | Task(priority: .high) { 408 | try? await Task.sleep(nanoseconds: 2_000_000_000) 409 | await Task.yield() 410 | print("high : \(Thread.current) : \(Task.currentPriority)") 411 | } 412 | Task(priority: .userInitiated) { 413 | print("userInitiated : \(Thread.current) : \(Task.currentPriority)") 414 | } 415 | Task(priority: .medium) { 416 | print("medium : \(Thread.current) : \(Task.currentPriority)") 417 | } 418 | Task(priority: .low) { 419 | print("low : \(Thread.current) : \(Task.currentPriority)") 420 | } 421 | Task(priority: .utility) { 422 | print("utility : \(Thread.current) : \(Task.currentPriority)") 423 | } 424 | Task(priority: .background) { 425 | print("background : \(Thread.current) : \(Task.currentPriority)") 426 | } 427 | 428 | 429 | //Both have the same priority -- 430 | Task(priority: .low) { 431 | print("low : \(Thread.current) : \(Task.currentPriority)") 432 | 433 | 434 | Task { // .detached 435 | print("try detached : \(Thread.current) : \(Task.currentPriority)") 436 | } 437 | } 438 | } 439 | 440 | /*: 441 | [< Previous](@previous)           [Home](Introduction)           [Next >](@next) 442 | */ 443 | -------------------------------------------------------------------------------- /MulitSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 813E1BD726C9A9100086A2FE /* MulitSwift.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC626C9A90E0086A2FE /* MulitSwift.xcdatamodeld */; }; 11 | 813E1BD826C9A9100086A2FE /* MulitSwift.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC626C9A90E0086A2FE /* MulitSwift.xcdatamodeld */; }; 12 | 813E1BD926C9A9100086A2FE /* MulitSwiftApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC826C9A90E0086A2FE /* MulitSwiftApp.swift */; }; 13 | 813E1BDA26C9A9100086A2FE /* MulitSwiftApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC826C9A90E0086A2FE /* MulitSwiftApp.swift */; }; 14 | 813E1BDB26C9A9100086A2FE /* DemoListViewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC926C9A90E0086A2FE /* DemoListViewUI.swift */; }; 15 | 813E1BDC26C9A9100086A2FE /* DemoListViewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BC926C9A90E0086A2FE /* DemoListViewUI.swift */; }; 16 | 813E1BDD26C9A9100086A2FE /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BCA26C9A90E0086A2FE /* Persistence.swift */; }; 17 | 813E1BDE26C9A9100086A2FE /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813E1BCA26C9A90E0086A2FE /* Persistence.swift */; }; 18 | 813E1BDF26C9A9100086A2FE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 813E1BCB26C9A9100086A2FE /* Assets.xcassets */; }; 19 | 813E1BE026C9A9100086A2FE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 813E1BCB26C9A9100086A2FE /* Assets.xcassets */; }; 20 | 81BB382B26C9B3D4006CBE91 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81BB382A26C9B3D4006CBE91 /* ImageService.swift */; }; 21 | 81BB382C26C9B3D4006CBE91 /* ImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81BB382A26C9B3D4006CBE91 /* ImageService.swift */; }; 22 | 81BB382E26C9B67A006CBE91 /* ContenvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81BB382D26C9B67A006CBE91 /* ContenvView.swift */; }; 23 | 81BB382F26C9B67A006CBE91 /* ContenvView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81BB382D26C9B67A006CBE91 /* ContenvView.swift */; }; 24 | DE1E692228B2C9780044B7FA /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1E692128B2C9780044B7FA /* ViewModel.swift */; }; 25 | DE1E692328B2C9780044B7FA /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1E692128B2C9780044B7FA /* ViewModel.swift */; }; 26 | DE1E692528B2DB780044B7FA /* ImageViewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1E692428B2DB780044B7FA /* ImageViewUI.swift */; }; 27 | DE1E692628B2DB780044B7FA /* ImageViewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1E692428B2DB780044B7FA /* ImageViewUI.swift */; }; 28 | DE9CF94D28B1B5170011D367 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = DE9CF94C28B1B5170011D367 /* AsyncAlgorithms */; }; 29 | DE9CF94F28B1B52A0011D367 /* AsyncAlgorithms in Frameworks */ = {isa = PBXBuildFile; productRef = DE9CF94E28B1B52A0011D367 /* AsyncAlgorithms */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 813E1BC726C9A90E0086A2FE /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = ""; }; 34 | 813E1BC826C9A90E0086A2FE /* MulitSwiftApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MulitSwiftApp.swift; sourceTree = ""; }; 35 | 813E1BC926C9A90E0086A2FE /* DemoListViewUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoListViewUI.swift; sourceTree = ""; }; 36 | 813E1BCA26C9A90E0086A2FE /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; 37 | 813E1BCB26C9A9100086A2FE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | 813E1BD026C9A9100086A2FE /* MulitSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MulitSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 813E1BD626C9A9100086A2FE /* MulitSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MulitSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 81BB382726C9A93F006CBE91 /* Whats-New-In-Swift-5-7.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = "Whats-New-In-Swift-5-7.playground"; sourceTree = ""; }; 41 | 81BB382A26C9B3D4006CBE91 /* ImageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageService.swift; sourceTree = ""; }; 42 | 81BB382D26C9B67A006CBE91 /* ContenvView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContenvView.swift; sourceTree = ""; }; 43 | DE1E692128B2C9780044B7FA /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 44 | DE1E692428B2DB780044B7FA /* ImageViewUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewUI.swift; sourceTree = ""; }; 45 | DE9CF95028B1BCCD0011D367 /* MulitSwift--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "MulitSwift--iOS--Info.plist"; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 813E1BCD26C9A9100086A2FE /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | DE9CF94D28B1B5170011D367 /* AsyncAlgorithms in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 813E1BD326C9A9100086A2FE /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | DE9CF94F28B1B52A0011D367 /* AsyncAlgorithms in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 813E1BC026C9A90E0086A2FE = { 69 | isa = PBXGroup; 70 | children = ( 71 | DE9CF95028B1BCCD0011D367 /* MulitSwift--iOS--Info.plist */, 72 | 81BB382726C9A93F006CBE91 /* Whats-New-In-Swift-5-7.playground */, 73 | 813E1BC526C9A90E0086A2FE /* Shared */, 74 | 813E1BD126C9A9100086A2FE /* Products */, 75 | DE9CF93B28B1B2480011D367 /* Frameworks */, 76 | ); 77 | sourceTree = ""; 78 | }; 79 | 813E1BC526C9A90E0086A2FE /* Shared */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 813E1BC826C9A90E0086A2FE /* MulitSwiftApp.swift */, 83 | 81BB382D26C9B67A006CBE91 /* ContenvView.swift */, 84 | 813E1BC926C9A90E0086A2FE /* DemoListViewUI.swift */, 85 | 813E1BCA26C9A90E0086A2FE /* Persistence.swift */, 86 | DE1E692128B2C9780044B7FA /* ViewModel.swift */, 87 | DE1E692428B2DB780044B7FA /* ImageViewUI.swift */, 88 | 81BB382A26C9B3D4006CBE91 /* ImageService.swift */, 89 | 813E1BCB26C9A9100086A2FE /* Assets.xcassets */, 90 | 813E1BC626C9A90E0086A2FE /* MulitSwift.xcdatamodeld */, 91 | ); 92 | path = Shared; 93 | sourceTree = ""; 94 | }; 95 | 813E1BD126C9A9100086A2FE /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 813E1BD026C9A9100086A2FE /* MulitSwift.app */, 99 | 813E1BD626C9A9100086A2FE /* MulitSwift.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | DE9CF93B28B1B2480011D367 /* Frameworks */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | ); 108 | name = Frameworks; 109 | sourceTree = ""; 110 | }; 111 | /* End PBXGroup section */ 112 | 113 | /* Begin PBXNativeTarget section */ 114 | 813E1BCF26C9A9100086A2FE /* MulitSwift (iOS) */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = 813E1BE326C9A9100086A2FE /* Build configuration list for PBXNativeTarget "MulitSwift (iOS)" */; 117 | buildPhases = ( 118 | 813E1BCC26C9A9100086A2FE /* Sources */, 119 | 813E1BCD26C9A9100086A2FE /* Frameworks */, 120 | 813E1BCE26C9A9100086A2FE /* Resources */, 121 | ); 122 | buildRules = ( 123 | ); 124 | dependencies = ( 125 | ); 126 | name = "MulitSwift (iOS)"; 127 | packageProductDependencies = ( 128 | DE9CF94C28B1B5170011D367 /* AsyncAlgorithms */, 129 | ); 130 | productName = "MulitSwift (iOS)"; 131 | productReference = 813E1BD026C9A9100086A2FE /* MulitSwift.app */; 132 | productType = "com.apple.product-type.application"; 133 | }; 134 | 813E1BD526C9A9100086A2FE /* MulitSwift (macOS) */ = { 135 | isa = PBXNativeTarget; 136 | buildConfigurationList = 813E1BE626C9A9100086A2FE /* Build configuration list for PBXNativeTarget "MulitSwift (macOS)" */; 137 | buildPhases = ( 138 | 813E1BD226C9A9100086A2FE /* Sources */, 139 | 813E1BD326C9A9100086A2FE /* Frameworks */, 140 | 813E1BD426C9A9100086A2FE /* Resources */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | ); 146 | name = "MulitSwift (macOS)"; 147 | packageProductDependencies = ( 148 | DE9CF94E28B1B52A0011D367 /* AsyncAlgorithms */, 149 | ); 150 | productName = "MulitSwift (macOS)"; 151 | productReference = 813E1BD626C9A9100086A2FE /* MulitSwift.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 813E1BC126C9A90E0086A2FE /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | BuildIndependentTargetsInParallel = 1; 161 | LastSwiftUpdateCheck = 1300; 162 | LastUpgradeCheck = 1300; 163 | TargetAttributes = { 164 | 813E1BCF26C9A9100086A2FE = { 165 | CreatedOnToolsVersion = 13.0; 166 | }; 167 | 813E1BD526C9A9100086A2FE = { 168 | CreatedOnToolsVersion = 13.0; 169 | }; 170 | }; 171 | }; 172 | buildConfigurationList = 813E1BC426C9A90E0086A2FE /* Build configuration list for PBXProject "MulitSwift" */; 173 | compatibilityVersion = "Xcode 13.0"; 174 | developmentRegion = en; 175 | hasScannedForEncodings = 0; 176 | knownRegions = ( 177 | en, 178 | Base, 179 | ); 180 | mainGroup = 813E1BC026C9A90E0086A2FE; 181 | packageReferences = ( 182 | DE9CF93828B1B0AF0011D367 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */, 183 | ); 184 | productRefGroup = 813E1BD126C9A9100086A2FE /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 813E1BCF26C9A9100086A2FE /* MulitSwift (iOS) */, 189 | 813E1BD526C9A9100086A2FE /* MulitSwift (macOS) */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | 813E1BCE26C9A9100086A2FE /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | 813E1BDF26C9A9100086A2FE /* Assets.xcassets in Resources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | 813E1BD426C9A9100086A2FE /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 813E1BE026C9A9100086A2FE /* Assets.xcassets in Resources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 813E1BCC26C9A9100086A2FE /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | DE1E692528B2DB780044B7FA /* ImageViewUI.swift in Sources */, 219 | 81BB382B26C9B3D4006CBE91 /* ImageService.swift in Sources */, 220 | 813E1BD726C9A9100086A2FE /* MulitSwift.xcdatamodeld in Sources */, 221 | 81BB382E26C9B67A006CBE91 /* ContenvView.swift in Sources */, 222 | 813E1BDD26C9A9100086A2FE /* Persistence.swift in Sources */, 223 | 813E1BD926C9A9100086A2FE /* MulitSwiftApp.swift in Sources */, 224 | 813E1BDB26C9A9100086A2FE /* DemoListViewUI.swift in Sources */, 225 | DE1E692228B2C9780044B7FA /* ViewModel.swift in Sources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | 813E1BD226C9A9100086A2FE /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | DE1E692628B2DB780044B7FA /* ImageViewUI.swift in Sources */, 234 | 81BB382C26C9B3D4006CBE91 /* ImageService.swift in Sources */, 235 | 813E1BD826C9A9100086A2FE /* MulitSwift.xcdatamodeld in Sources */, 236 | 81BB382F26C9B67A006CBE91 /* ContenvView.swift in Sources */, 237 | 813E1BDE26C9A9100086A2FE /* Persistence.swift in Sources */, 238 | 813E1BDA26C9A9100086A2FE /* MulitSwiftApp.swift in Sources */, 239 | 813E1BDC26C9A9100086A2FE /* DemoListViewUI.swift in Sources */, 240 | DE1E692328B2C9780044B7FA /* ViewModel.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin XCBuildConfiguration section */ 247 | 813E1BE126C9A9100086A2FE /* Debug */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | CLANG_ANALYZER_NONNULL = YES; 252 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 253 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 254 | CLANG_CXX_LIBRARY = "libc++"; 255 | CLANG_ENABLE_MODULES = YES; 256 | CLANG_ENABLE_OBJC_ARC = YES; 257 | CLANG_ENABLE_OBJC_WEAK = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 275 | CLANG_WARN_STRICT_PROTOTYPES = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 278 | CLANG_WARN_UNREACHABLE_CODE = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu11; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 299 | MTL_FAST_MATH = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | }; 304 | name = Debug; 305 | }; 306 | 813E1BE226C9A9100086A2FE /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ANALYZER_NONNULL = YES; 311 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_ENABLE_OBJC_WEAK = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu11; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | MTL_FAST_MATH = YES; 353 | SWIFT_COMPILATION_MODE = wholemodule; 354 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 355 | }; 356 | name = Release; 357 | }; 358 | 813E1BE426C9A9100086A2FE /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 363 | CODE_SIGN_STYLE = Automatic; 364 | CURRENT_PROJECT_VERSION = 1; 365 | DEVELOPMENT_TEAM = 3F6FBJA4J7; 366 | ENABLE_PREVIEWS = YES; 367 | GENERATE_INFOPLIST_FILE = YES; 368 | INFOPLIST_FILE = "MulitSwift--iOS--Info.plist"; 369 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 370 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 371 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 372 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 373 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 374 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 375 | LD_RUNPATH_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "@executable_path/Frameworks", 378 | ); 379 | MARKETING_VERSION = 1.0; 380 | PRODUCT_BUNDLE_IDENTIFIER = com.ylabz.MulitSwift; 381 | PRODUCT_NAME = MulitSwift; 382 | SDKROOT = iphoneos; 383 | SWIFT_EMIT_LOC_STRINGS = YES; 384 | SWIFT_VERSION = 5.0; 385 | TARGETED_DEVICE_FAMILY = "1,2"; 386 | }; 387 | name = Debug; 388 | }; 389 | 813E1BE526C9A9100086A2FE /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 393 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 394 | CODE_SIGN_STYLE = Automatic; 395 | CURRENT_PROJECT_VERSION = 1; 396 | DEVELOPMENT_TEAM = 3F6FBJA4J7; 397 | ENABLE_PREVIEWS = YES; 398 | GENERATE_INFOPLIST_FILE = YES; 399 | INFOPLIST_FILE = "MulitSwift--iOS--Info.plist"; 400 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 401 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 402 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 403 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 404 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 405 | IPHONEOS_DEPLOYMENT_TARGET = 15.0; 406 | LD_RUNPATH_SEARCH_PATHS = ( 407 | "$(inherited)", 408 | "@executable_path/Frameworks", 409 | ); 410 | MARKETING_VERSION = 1.0; 411 | PRODUCT_BUNDLE_IDENTIFIER = com.ylabz.MulitSwift; 412 | PRODUCT_NAME = MulitSwift; 413 | SDKROOT = iphoneos; 414 | SWIFT_EMIT_LOC_STRINGS = YES; 415 | SWIFT_VERSION = 5.0; 416 | TARGETED_DEVICE_FAMILY = "1,2"; 417 | VALIDATE_PRODUCT = YES; 418 | }; 419 | name = Release; 420 | }; 421 | 813E1BE726C9A9100086A2FE /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 425 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 426 | CODE_SIGN_STYLE = Automatic; 427 | COMBINE_HIDPI_IMAGES = YES; 428 | CURRENT_PROJECT_VERSION = 1; 429 | DEVELOPMENT_TEAM = 3F6FBJA4J7; 430 | ENABLE_APP_SANDBOX = YES; 431 | ENABLE_HARDENED_RUNTIME = YES; 432 | ENABLE_PREVIEWS = YES; 433 | ENABLE_USER_SELECTED_FILES = readonly; 434 | GENERATE_INFOPLIST_FILE = YES; 435 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 436 | LD_RUNPATH_SEARCH_PATHS = ( 437 | "$(inherited)", 438 | "@executable_path/../Frameworks", 439 | ); 440 | MACOSX_DEPLOYMENT_TARGET = 12.0; 441 | MARKETING_VERSION = 1.0; 442 | PRODUCT_BUNDLE_IDENTIFIER = com.ylabz.MulitSwift; 443 | PRODUCT_NAME = MulitSwift; 444 | SDKROOT = macosx; 445 | SWIFT_EMIT_LOC_STRINGS = YES; 446 | SWIFT_VERSION = 5.0; 447 | }; 448 | name = Debug; 449 | }; 450 | 813E1BE826C9A9100086A2FE /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 454 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 455 | CODE_SIGN_STYLE = Automatic; 456 | COMBINE_HIDPI_IMAGES = YES; 457 | CURRENT_PROJECT_VERSION = 1; 458 | DEVELOPMENT_TEAM = 3F6FBJA4J7; 459 | ENABLE_APP_SANDBOX = YES; 460 | ENABLE_HARDENED_RUNTIME = YES; 461 | ENABLE_PREVIEWS = YES; 462 | ENABLE_USER_SELECTED_FILES = readonly; 463 | GENERATE_INFOPLIST_FILE = YES; 464 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 465 | LD_RUNPATH_SEARCH_PATHS = ( 466 | "$(inherited)", 467 | "@executable_path/../Frameworks", 468 | ); 469 | MACOSX_DEPLOYMENT_TARGET = 12.0; 470 | MARKETING_VERSION = 1.0; 471 | PRODUCT_BUNDLE_IDENTIFIER = com.ylabz.MulitSwift; 472 | PRODUCT_NAME = MulitSwift; 473 | SDKROOT = macosx; 474 | SWIFT_EMIT_LOC_STRINGS = YES; 475 | SWIFT_VERSION = 5.0; 476 | }; 477 | name = Release; 478 | }; 479 | /* End XCBuildConfiguration section */ 480 | 481 | /* Begin XCConfigurationList section */ 482 | 813E1BC426C9A90E0086A2FE /* Build configuration list for PBXProject "MulitSwift" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 813E1BE126C9A9100086A2FE /* Debug */, 486 | 813E1BE226C9A9100086A2FE /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | 813E1BE326C9A9100086A2FE /* Build configuration list for PBXNativeTarget "MulitSwift (iOS)" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | 813E1BE426C9A9100086A2FE /* Debug */, 495 | 813E1BE526C9A9100086A2FE /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | 813E1BE626C9A9100086A2FE /* Build configuration list for PBXNativeTarget "MulitSwift (macOS)" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | 813E1BE726C9A9100086A2FE /* Debug */, 504 | 813E1BE826C9A9100086A2FE /* Release */, 505 | ); 506 | defaultConfigurationIsVisible = 0; 507 | defaultConfigurationName = Release; 508 | }; 509 | /* End XCConfigurationList section */ 510 | 511 | /* Begin XCRemoteSwiftPackageReference section */ 512 | DE9CF93828B1B0AF0011D367 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */ = { 513 | isa = XCRemoteSwiftPackageReference; 514 | repositoryURL = "https://github.com/apple/swift-async-algorithms"; 515 | requirement = { 516 | kind = upToNextMajorVersion; 517 | minimumVersion = 0.0.3; 518 | }; 519 | }; 520 | /* End XCRemoteSwiftPackageReference section */ 521 | 522 | /* Begin XCSwiftPackageProductDependency section */ 523 | DE9CF94C28B1B5170011D367 /* AsyncAlgorithms */ = { 524 | isa = XCSwiftPackageProductDependency; 525 | package = DE9CF93828B1B0AF0011D367 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */; 526 | productName = AsyncAlgorithms; 527 | }; 528 | DE9CF94E28B1B52A0011D367 /* AsyncAlgorithms */ = { 529 | isa = XCSwiftPackageProductDependency; 530 | package = DE9CF93828B1B0AF0011D367 /* XCRemoteSwiftPackageReference "swift-async-algorithms" */; 531 | productName = AsyncAlgorithms; 532 | }; 533 | /* End XCSwiftPackageProductDependency section */ 534 | 535 | /* Begin XCVersionGroup section */ 536 | 813E1BC626C9A90E0086A2FE /* MulitSwift.xcdatamodeld */ = { 537 | isa = XCVersionGroup; 538 | children = ( 539 | 813E1BC726C9A90E0086A2FE /* Shared.xcdatamodel */, 540 | ); 541 | currentVersion = 813E1BC726C9A90E0086A2FE /* Shared.xcdatamodel */; 542 | path = MulitSwift.xcdatamodeld; 543 | sourceTree = ""; 544 | versionGroupType = wrapper.xcdatamodel; 545 | }; 546 | /* End XCVersionGroup section */ 547 | }; 548 | rootObject = 813E1BC126C9A90E0086A2FE /* Project object */; 549 | } 550 | --------------------------------------------------------------------------------