├── 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 |
--------------------------------------------------------------------------------