├── SwiftSenpai-Swift-Concurrency
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Icon-40.png
│ │ ├── Icon-58.png
│ │ ├── Icon-60.png
│ │ ├── Icon-80.png
│ │ ├── Icon-87.png
│ │ ├── Icon-1024.png
│ │ ├── Icon-120.png
│ │ ├── Icon-121.png
│ │ ├── Icon-180.png
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Info.plist
├── Prevent Thread Explosion
│ ├── HeavyWork.swift
│ └── ThreadExplosionViewController.swift
├── Prevent Data Races
│ ├── ActorViewController.swift
│ ├── DataRaceAsyncAwaitViewController.swift
│ └── DataRaceDispatchQueueViewController.swift
├── Async Stream Progress
│ ├── AsyncStreamProgressViewController.swift
│ └── FileDownloader.swift
├── AppDelegate.swift
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── SceneDelegate.swift
├── Sendable Demo
│ ├── NonSendableDemoViewController.swift
│ └── SendableDemoViewController.swift
├── Understanding Task Group
│ └── UnderstandTaskGroupViewController.swift
├── Making Network Requests
│ ├── AlbumsFetcher.swift
│ └── FetchAlbumsViewController.swift
├── Task Group Error Handling
│ └── TaskGroupErrorHandlingViewController.swift
└── Actor Reentrancy Problem
│ └── ReentrancyViewController.swift
├── SwiftSenpai-Swift-Concurrency.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ └── SwiftSenpai-Swift-Concurrency.xcscheme
└── project.pbxproj
├── LICENSE
├── README.md
└── .gitignore
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-58.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-60.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-80.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-87.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-120.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-121.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-121.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeeKahSeng/SwiftSenpai-Swift-Concurrency/HEAD/SwiftSenpai-Swift-Concurrency/Assets.xcassets/AppIcon.appiconset/Icon-180.png
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/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 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 | UISceneStoryboardFile
19 | Main
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Prevent Thread Explosion/HeavyWork.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeavyWork.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 06/11/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | final class HeavyWork {
11 |
12 | static func runUserInitiatedTask(seconds: UInt32) {
13 | Task(priority: .userInitiated) {
14 | print("🥸 userInitiated: \(Date())")
15 | sleep(seconds)
16 | }
17 | }
18 |
19 | static func runUtilityTask(seconds: UInt32) {
20 | Task(priority: .utility) {
21 | print("☕️ utility: \(Date())")
22 | sleep(seconds)
23 | }
24 | }
25 |
26 | static func runBackgroundTask(seconds: UInt32) {
27 | Task(priority: .background) {
28 | print("⬇️ background: \(Date())")
29 | sleep(seconds)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Lee Kah Seng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftSenpai-Swift-Concurrency
2 | - Sample project for article '[Making Network Requests with Async/await in Swift](https://swiftsenpai.com/swift/async-await-network-requests/)'.
3 | - Sample project for article '[Preventing Data Races Using Actors in Swift](https://swiftsenpai.com/swift/actor-prevent-data-race/)'.
4 | - Sample project for article '[The Actor Reentrancy Problem in Swift](https://swiftsenpai.com/swift/actor-reentrancy-problem/)'.
5 | - Sample project for article '[How Sendable Can Help in Preventing Data Races](https://swiftsenpai.com/swift/sendable-prevent-data-races/)'.
6 | - Sample project for article '[Understanding Swift Task Groups With Examples](https://swiftsenpai.com/swift/understanding-task-groups/)'.
7 | - Sample project for article '[How to Handle Errors in Swift Task Groups](https://swiftsenpai.com/swift/task-groups-error-handling/)'.
8 | - Sample project for article '[How Does Swift Concurrency Prevent Thread Explosion](https://swiftsenpai.com/swift/swift-concurrency-prevent-thread-explosion/)'.
9 | - Sample project for article '[A Deep Dive Into the Swift Concurrency Bottleneck](https://swiftsenpai.com/swift/swift-concurrency-bottleneck/)'.
10 | - Sample project for article '[How to Create Callback-like Behavior Using AsyncStream in Swift](https://swiftsenpai.com/swift/asyncstream-callback/)'.
11 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Prevent Data Races/ActorViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActorViewController.swift
3 | // ActorViewController
4 | //
5 | // Created by Kah Seng Lee on 30/08/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class ActorViewController: UIViewController {
11 |
12 | // MARK: Counter Actor
13 | actor Counter {
14 |
15 | private(set) var count = 0
16 |
17 | func addCount() {
18 | count += 1
19 | }
20 | }
21 |
22 | // MARK: Implementation
23 | @IBOutlet weak var statusLabel: UILabel!
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | }
28 |
29 |
30 | @IBAction func startButtonTapped(_ sender: Any) {
31 |
32 | let totalCount = 1000
33 | let counter = Counter()
34 |
35 | // Create a parent task
36 | Task {
37 |
38 | // Create a task group
39 | await withTaskGroup(of: Void.self, body: { taskGroup in
40 |
41 | for _ in 0.. Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Async Stream Progress/FileDownloader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileDownloader.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 15/07/2023.
6 | //
7 |
8 | import Foundation
9 |
10 | struct File {
11 |
12 | let name: String
13 |
14 | func performDownload() async {
15 |
16 | // Sleep for a random amount of time to emulate the wait required to download a file
17 | let downloadTime = Double.random(in: 0.03...0.5)
18 | try? await Task.sleep(for: .seconds(downloadTime))
19 | }
20 | }
21 |
22 | final class FileDownloader {
23 |
24 | static func download(_ files: [File]) -> AsyncStream {
25 |
26 | // Init AsyncStream with element type = `String`
27 | let stream = AsyncStream(String.self) { continuation in
28 |
29 | Task {
30 | for file in files {
31 |
32 | // Download the file
33 | await file.performDownload()
34 |
35 | // Yield the element (filename) when download is completed
36 | continuation.yield(file.name)
37 | }
38 |
39 | // All files are downloaded
40 | // Call the continuation’s finish() method when there are no further elements to produce
41 | continuation.finish()
42 | }
43 | }
44 |
45 | return stream
46 | }
47 |
48 |
49 | static func download(_ files: [File], completion: (String) -> Void) {
50 |
51 | // Download each file and trigger completion handler
52 | // ...
53 | // ...
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Prevent Thread Explosion/ThreadExplosionViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThreadExplosionViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 06/11/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class ThreadExplosionViewController: UIViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | }
15 |
16 | /// Test 1: Creating Tasks with Same Priority Level
17 | @IBAction func test1Tapped(_ sender: Any) {
18 |
19 | for _ in 1...150 {
20 | HeavyWork.runUserInitiatedTask(seconds: 3)
21 | }
22 | }
23 |
24 | /// Test 2: Creating Tasks from High to Low Priority Level All at Once
25 | @IBAction func test2Tapped(_ sender: Any) {
26 |
27 | for _ in 1...30 {
28 | HeavyWork.runUserInitiatedTask(seconds: 3)
29 | }
30 |
31 | for _ in 1...30 {
32 | HeavyWork.runUtilityTask(seconds: 3)
33 | }
34 |
35 | for _ in 1...30 {
36 | HeavyWork.runBackgroundTask(seconds: 3)
37 | }
38 | }
39 |
40 | /// Test 3: Creating Tasks from Low to High Priority Level All at Once
41 | @IBAction func test3Tapped(_ sender: Any) {
42 |
43 | for _ in 1...30 {
44 | HeavyWork.runBackgroundTask(seconds: 3)
45 | }
46 |
47 | for _ in 1...30 {
48 | HeavyWork.runUtilityTask(seconds: 3)
49 | }
50 |
51 | for _ in 1...30 {
52 | HeavyWork.runUserInitiatedTask(seconds: 3)
53 | }
54 | }
55 |
56 | /// Test 4: Creating Tasks from Low to High Priority Level with Break in Between
57 | @IBAction func test4Tapped(_ sender: Any) {
58 |
59 | for _ in 1...30 {
60 | HeavyWork.runBackgroundTask(seconds: 3)
61 | }
62 |
63 | sleep(3)
64 | print("⏰ 1st break...")
65 |
66 | for _ in 1...30 {
67 | HeavyWork.runUtilityTask(seconds: 3)
68 | }
69 |
70 | sleep(3)
71 | print("⏰ 2nd break...")
72 |
73 | for _ in 1...30 {
74 | HeavyWork.runUserInitiatedTask(seconds: 3)
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 13/08/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | guard let _ = (scene as? UIWindowScene) else { return }
20 | }
21 |
22 | func sceneDidDisconnect(_ scene: UIScene) {
23 | // Called as the scene is being released by the system.
24 | // This occurs shortly after the scene enters the background, or when its session is discarded.
25 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
27 | }
28 |
29 | func sceneDidBecomeActive(_ scene: UIScene) {
30 | // Called when the scene has moved from an inactive state to an active state.
31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
32 | }
33 |
34 | func sceneWillResignActive(_ scene: UIScene) {
35 | // Called when the scene will move from an active state to an inactive state.
36 | // This may occur due to temporary interruptions (ex. an incoming phone call).
37 | }
38 |
39 | func sceneWillEnterForeground(_ scene: UIScene) {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 | func sceneDidEnterBackground(_ scene: UIScene) {
45 | // Called as the scene transitions from the foreground to the background.
46 | // Use this method to save data, release shared resources, and store enough scene-specific state information
47 | // to restore the scene back to its current state.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Sendable Demo/NonSendableDemoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NonSendableDemoViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 03/10/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class NonSendableDemoViewController: UIViewController {
11 |
12 | class Article {
13 |
14 | let title: String
15 | var likeCount = 0
16 |
17 | init(title: String) {
18 | self.title = title
19 | }
20 | }
21 |
22 | actor ArticleManager {
23 |
24 | private let articles = [
25 | Article(title: "Swift Senpai Article 01"),
26 | Article(title: "Swift Senpai Article 02"),
27 | Article(title: "Swift Senpai Article 03"),
28 | ]
29 |
30 | /// Increase like count by 1
31 | func like(_ articleTitle: String) {
32 |
33 | guard let article = getArticle(with: articleTitle) else {
34 | return
35 | }
36 |
37 | article.likeCount += 1
38 | }
39 |
40 | /// Get article based on article title
41 | func getArticle(with articleTitle: String) -> Article? {
42 | return articles.filter({ $0.title == articleTitle }).first
43 | }
44 | }
45 |
46 |
47 | let manager = ArticleManager()
48 |
49 | override func viewDidLoad() {
50 | super.viewDidLoad()
51 | }
52 |
53 | @IBAction func nonSendableExampleButtonTapped(_ sender: Any) {
54 |
55 | let articleTitle = "Swift Senpai Article 01"
56 |
57 | // Create a parent task
58 | Task {
59 |
60 | // Create a task group
61 | await withTaskGroup(of: Void.self, body: { taskGroup in
62 |
63 | // Create 3000 child tasks to like
64 | for _ in 0..<3000 {
65 | taskGroup.addTask {
66 | await self.manager.like(articleTitle)
67 | }
68 | }
69 |
70 | // Create 1000 child tasks to dislike
71 | for _ in 0..<1000 {
72 | taskGroup.addTask {
73 | await self.dislike(articleTitle)
74 | }
75 | }
76 | })
77 |
78 | print("👍🏻 Like count: \(await manager.getArticle(with: articleTitle)!.likeCount)")
79 | }
80 | }
81 |
82 | /// Access article outside of the actor and reduces its like count by 1
83 | func dislike(_ articleTitle: String) async {
84 |
85 | guard let article = await manager.getArticle(with: articleTitle) else {
86 | return
87 | }
88 |
89 | // Reduce like count
90 | article.likeCount -= 1
91 | }
92 |
93 | }
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Understanding Task Group/UnderstandTaskGroupViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnderstandTaskGroupViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 12/10/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class UnderstandTaskGroupViewController: UIViewController {
11 |
12 | struct SlowDivideOperation {
13 |
14 | let name: String
15 | let a: Double
16 | let b: Double
17 | let sleepDuration: UInt64
18 |
19 | func execute() async -> Double {
20 |
21 | // Sleep for x seconds
22 | try? await Task.sleep(nanoseconds: sleepDuration * 1_000_000_000)
23 |
24 | let value = a / b
25 | return value
26 | }
27 | }
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 |
32 | // Do any additional setup after loading the view.
33 | }
34 |
35 | @IBAction func taskGroupButtonTapped(_ sender: Any) {
36 |
37 | let operations = [
38 | SlowDivideOperation(name: "operation-0", a: 5, b: 1, sleepDuration: 5),
39 | SlowDivideOperation(name: "operation-1", a: 14, b: 7, sleepDuration: 1),
40 | SlowDivideOperation(name: "operation-2", a: 8, b: 2, sleepDuration: 3),
41 | ]
42 |
43 | Task {
44 |
45 | print("Task start : \(Date())")
46 |
47 | let allResults = await withTaskGroup(of: (String, Double).self,
48 | returning: [String: Double].self,
49 | body: { taskGroup in
50 |
51 | // Loop through operations array
52 | for operation in operations {
53 |
54 | // Add child task to task group
55 | taskGroup.addTask {
56 |
57 | // Execute slow operation
58 | let value = await operation.execute()
59 |
60 | // Return child task result
61 | return (operation.name, value)
62 | }
63 |
64 | }
65 |
66 | // Collect results of all child task in a dictionary
67 | var childTaskResults = [String: Double]()
68 | for await result in taskGroup {
69 | // Set operation name as key and operation result as value
70 | childTaskResults[result.0] = result.1
71 | }
72 |
73 | // All child tasks finish running, thus return task group result
74 | return childTaskResults
75 | })
76 |
77 | print("Task end : \(Date())")
78 | print("allResults : \(allResults)")
79 |
80 | }
81 |
82 | }
83 | }
84 |
85 |
86 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Sendable Demo/SendableDemoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SendableDemoViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 03/10/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class SendableDemoViewController: UIViewController {
11 |
12 | struct Article: Sendable, Hashable {
13 |
14 | let title: String
15 | var likeCount = 0
16 |
17 | init(title: String) {
18 | self.title = title
19 | }
20 |
21 | static func == (lhs: Article, rhs: Article) -> Bool {
22 | return lhs.title == rhs.title
23 | }
24 |
25 | func hash(into hasher: inout Hasher) {
26 | hasher.combine(title)
27 | }
28 | }
29 |
30 | actor ArticleManager {
31 |
32 | private var articles: Set = [
33 | Article(title: "Swift Senpai Article 01"),
34 | Article(title: "Swift Senpai Article 02"),
35 | Article(title: "Swift Senpai Article 03"),
36 | ]
37 |
38 | /// Increase like count by 1
39 | func like(_ articleTitle: String) {
40 |
41 | guard var article = getArticle(with: articleTitle) else {
42 | return
43 | }
44 |
45 | article.likeCount += 1
46 |
47 | // Update array after increased like count
48 | updateArticle(article)
49 | }
50 |
51 | /// Get article based on article title
52 | func getArticle(with articleTitle: String) -> Article? {
53 | return articles.filter({ $0.title == articleTitle }).first
54 | }
55 |
56 | /// Update articles in Article Manager
57 | func updateArticle(_ article: Article) {
58 | articles.update(with: article)
59 | }
60 | }
61 |
62 |
63 | override func viewDidLoad() {
64 | super.viewDidLoad()
65 | }
66 |
67 | let manager = ArticleManager()
68 | @IBAction func sendableExampleButtonTapped(_ sender: Any) {
69 |
70 | let articleTitle = "Swift Senpai Article 01"
71 |
72 | // Create a parent task
73 | Task {
74 |
75 | // Create a task group
76 | await withTaskGroup(of: Void.self, body: { taskGroup in
77 |
78 | // Create 3000 child tasks to like
79 | for _ in 0..<3000 {
80 | taskGroup.addTask {
81 | await self.manager.like(articleTitle)
82 | }
83 | }
84 |
85 | // Create 1000 child tasks to dislike
86 | for _ in 0..<1000 {
87 | taskGroup.addTask {
88 | await self.dislike(articleTitle)
89 | }
90 | }
91 | })
92 |
93 | print("👍🏻 Like count: \(await manager.getArticle(with: articleTitle)!.likeCount)")
94 | }
95 | }
96 |
97 | /// Access article outside of the actor and reduces its like count by 1
98 | func dislike(_ articleTitle: String) async {
99 |
100 | guard var article = await manager.getArticle(with: articleTitle) else {
101 | return
102 | }
103 |
104 | // Reduce like count
105 | article.likeCount -= 1
106 |
107 | await manager.updateArticle(article)
108 | }
109 |
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Making Network Requests /AlbumsFetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlbumsFetcher.swift
3 | // AlbumsFetcher
4 | //
5 | // Created by Kah Seng Lee on 14/08/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | struct ITunesResult: Codable {
11 | let results: [Album]
12 | }
13 |
14 | struct Album: Codable, Hashable {
15 | let collectionId: Int
16 | let collectionName: String
17 | let collectionPrice: Double
18 | }
19 |
20 | struct AlbumsFetcher {
21 |
22 | enum AlbumsFetcherError: Error {
23 | case invalidURL
24 | case missingData
25 | }
26 |
27 | /// Make network request using closure-based `URLSession` API
28 | static func fetchAlbums(completion: @escaping (Result<[Album], Error>) -> Void) {
29 |
30 | // Create URL
31 | guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
32 | completion(.failure(AlbumsFetcherError.invalidURL))
33 | return
34 | }
35 |
36 | // Create URL session data task
37 | URLSession.shared.dataTask(with: url) { data, _, error in
38 |
39 | if let error = error {
40 | completion(.failure(error))
41 | return
42 | }
43 |
44 | guard let data = data else {
45 | completion(.failure(AlbumsFetcherError.missingData))
46 | return
47 | }
48 |
49 | do {
50 | // Parse the JSON data
51 | let iTunesResult = try JSONDecoder().decode(ITunesResult.self, from: data)
52 | completion(.success(iTunesResult.results))
53 | } catch {
54 | completion(.failure(error))
55 | }
56 |
57 | }.resume()
58 |
59 |
60 | }
61 |
62 |
63 | /// Make network request using checked continuation
64 | static func fetchAlbumWithContinuation() async throws -> [Album] {
65 |
66 | // Bridge between synchronous and asynchronous code using continuation
67 | let albums: [Album] = try await withCheckedThrowingContinuation({ continuation in
68 |
69 | // Async task execute the `fetchAlbums(completion:)` function
70 | fetchAlbums { result in
71 |
72 | switch result {
73 | case .success(let albums):
74 | // Resume with fetched albums
75 | continuation.resume(returning: albums)
76 |
77 | case .failure(let error):
78 | // Resume with error
79 | continuation.resume(throwing: error)
80 | }
81 | }
82 | })
83 |
84 | return albums
85 | }
86 |
87 |
88 | /// Make network request using async `URLSession` API
89 | static func fetchAlbumWithAsyncURLSession() async throws -> [Album] {
90 |
91 | guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=album") else {
92 | throw AlbumsFetcherError.invalidURL
93 | }
94 |
95 | // Use the async variant of URLSession to fetch data
96 | let (data, _) = try await URLSession.shared.data(from: url)
97 |
98 | // Parse the JSON data
99 | let iTunesResult = try JSONDecoder().decode(ITunesResult.self, from: data)
100 | return iTunesResult.results
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency.xcodeproj/xcshareddata/xcschemes/SwiftSenpai-Swift-Concurrency.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
64 |
66 |
72 |
73 |
74 |
75 |
81 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Making Network Requests /FetchAlbumsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchAlbumsViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 13/08/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class FetchAlbumsViewController: UIViewController {
11 |
12 | enum Section {
13 | case main
14 | }
15 |
16 | @IBOutlet weak var collectionView: UICollectionView!
17 |
18 | private var dataSource: UICollectionViewDiffableDataSource!
19 | private var snapshot: NSDiffableDataSourceSnapshot!
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | // Configure collection view
25 | let layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
26 | let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
27 | collectionView.collectionViewLayout = listLayout
28 |
29 | // Configure cell
30 | let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, album) in
31 |
32 | // Setup content configuration
33 | var content = cell.defaultContentConfiguration()
34 | content.text = album.collectionName
35 | content.secondaryText = "USD \(album.collectionPrice)"
36 |
37 | // Assign content configuration to cell
38 | cell.contentConfiguration = content
39 | }
40 |
41 | // Configure data source
42 | dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
43 | (collectionView: UICollectionView, indexPath: IndexPath, identifier: Album) -> UICollectionViewCell? in
44 |
45 | let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
46 | for: indexPath,
47 | item: identifier)
48 |
49 | return cell
50 | }
51 | }
52 |
53 | private func updateCollectionViewSnapshot(_ albums: [Album]) {
54 | var snapshot = NSDiffableDataSourceSnapshot()
55 | snapshot.appendSections([.main])
56 | snapshot.appendItems(albums, toSection: .main)
57 | dataSource.apply(snapshot, animatingDifferences: false)
58 | }
59 |
60 |
61 | @IBAction func closureButtonTapped(_ sender: Any) {
62 |
63 | // Clear the list
64 | updateCollectionViewSnapshot([])
65 |
66 | AlbumsFetcher.fetchAlbums { [unowned self] result in
67 |
68 | switch result {
69 | case .success(let albums):
70 |
71 | // Update UI using main thread
72 | DispatchQueue.main.async {
73 |
74 | // Update collection view content
75 | self.updateCollectionViewSnapshot(albums)
76 | }
77 |
78 | case .failure(let error):
79 | print("Request failed with error: \(error)")
80 | }
81 | }
82 | }
83 |
84 |
85 | @IBAction func continuationButtonTapped(_ sender: Any) {
86 |
87 | // Clear the list
88 | updateCollectionViewSnapshot([])
89 |
90 | // Start an async task
91 | Task {
92 |
93 | do {
94 |
95 | let albums = try await AlbumsFetcher.fetchAlbumWithContinuation()
96 |
97 | // Update collection view content
98 | updateCollectionViewSnapshot(albums)
99 |
100 | } catch {
101 | print("Request failed with error: \(error)")
102 | }
103 |
104 | }
105 | }
106 |
107 | @IBAction func asyncURLSessionButtonTapped(_ sender: Any) {
108 |
109 | // Clear the list
110 | updateCollectionViewSnapshot([])
111 |
112 | // Start an async task
113 | Task {
114 |
115 | do {
116 |
117 | let albums = try await AlbumsFetcher.fetchAlbumWithAsyncURLSession()
118 |
119 | // Update collection view content
120 | updateCollectionViewSnapshot(albums)
121 |
122 | } catch {
123 | print("Request failed with error: \(error)")
124 | }
125 |
126 | }
127 |
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Task Group Error Handling/TaskGroupErrorHandlingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TaskGroupErrorHandlingViewController.swift
3 | // SwiftSenpai-Swift-Concurrency
4 | //
5 | // Created by Kah Seng Lee on 03/11/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class TaskGroupErrorHandlingViewController: UIViewController {
11 |
12 | enum DivideOperationError: Error {
13 | case divideByZero
14 | }
15 |
16 | struct SlowDivideOperation {
17 |
18 | let name: String
19 | let a: Double
20 | let b: Double
21 | let sleepDuration: UInt64
22 |
23 | func execute() async throws -> Double {
24 |
25 | // Sleep for x seconds
26 | try? await Task.sleep(nanoseconds: sleepDuration * 1_000_000_000)
27 |
28 | // Check for cancellation. If task is canceled, throw `CancellationError`.
29 | try Task.checkCancellation()
30 |
31 | // Throw error when divisor is zero
32 | guard b != 0 else {
33 | print("⛔️ \(name) throw error")
34 | throw DivideOperationError.divideByZero
35 | }
36 |
37 | let value = a / b
38 |
39 | print("✅ \(name) completed: \(value)")
40 | return value
41 | }
42 | }
43 |
44 | let operations = [
45 | SlowDivideOperation(name: "operation-0", a: 5, b: 1, sleepDuration: 5),
46 | SlowDivideOperation(name: "operation-1", a: 14, b: 7, sleepDuration: 1),
47 | SlowDivideOperation(name: "operation-2", a: 4, b: 0, sleepDuration: 2),
48 | SlowDivideOperation(name: "operation-3", a: 8, b: 2, sleepDuration: 3),
49 | ]
50 |
51 | override func viewDidLoad() {
52 | super.viewDidLoad()
53 |
54 | // Do any additional setup after loading the view.
55 | }
56 |
57 | @IBAction func throwErrorButtonTapped(_ sender: Any) {
58 |
59 | Task {
60 |
61 | do {
62 | let allResults = try await withThrowingTaskGroup(of: (String, Double).self,
63 | returning: [String: Double].self,
64 | body: { taskGroup in
65 |
66 | // Loop through operations array
67 | for operation in operations {
68 |
69 | // Add child task to task group
70 | taskGroup.addTask {
71 |
72 | // Execute slow operation
73 | let value = try await operation.execute()
74 |
75 | // Return child task result
76 | return (operation.name, value)
77 | }
78 |
79 | }
80 |
81 | // Collect results of all child task in a dictionary
82 | var childTaskResults = [String: Double]()
83 | for try await result in taskGroup {
84 | // Set operation name as key and operation result as value
85 | childTaskResults[result.0] = result.1
86 | }
87 |
88 | // All child tasks finish running, thus return task group result
89 | return childTaskResults
90 |
91 | })
92 |
93 | print("👍🏻 Task group completed with result: \(allResults)")
94 |
95 | } catch {
96 | print("👎🏻 Task group throws error: \(error)")
97 | }
98 | }
99 | }
100 |
101 | @IBAction func returnResultsButtonTapped(_ sender: Any) {
102 |
103 | Task {
104 |
105 | let allResults = await withTaskGroup(of: (String, Double)?.self,
106 | returning: [String: Double].self,
107 | body: { taskGroup in
108 |
109 | // Loop through operations array
110 | for operation in operations {
111 |
112 | // Add child task to task group
113 | taskGroup.addTask {
114 |
115 | // Execute slow operation
116 | guard let value = try? await operation.execute() else {
117 | return nil
118 | }
119 |
120 | // Return child task result
121 | return (operation.name, value)
122 | }
123 |
124 | }
125 |
126 | // Collect results of all child task in a dictionary
127 | var childTaskResults = [String: Double]()
128 | for await result in taskGroup.compactMap({ $0 }) {
129 | // Set operation name as key and operation result as value
130 | childTaskResults[result.0] = result.1
131 | }
132 |
133 | // All child tasks finish running, thus task group result
134 | return childTaskResults
135 |
136 | })
137 |
138 | print("👍🏻 Task group completed with result: \(allResults)")
139 | }
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Actor Reentrancy Problem/ReentrancyViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReentrancyViewController.swift
3 | // ReentrancyViewController
4 | //
5 | // Created by Kah Seng Lee on 11/09/2021.
6 | //
7 |
8 | import UIKit
9 |
10 | class ReentrancyViewController: UIViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | }
16 |
17 | @IBAction func reentrancyProblemExampleButtonTapped(_ sender: Any) {
18 | // Execute example with reentrancy problem
19 | let example = ActorReentrancyProblemExample()
20 | example.execute()
21 | }
22 |
23 | @IBAction func reentrancyHandlingExample1ButtonTapped(_ sender: Any) {
24 | // Execute example with reentrancy handling (synchronous code)
25 | let example = ActorReentrancyHandledExample1()
26 | example.execute()
27 | }
28 |
29 | @IBAction func reentrancyHandlingExample2ButtonTapped(_ sender: Any) {
30 | // Execute example with reentrancy handling (check state)
31 | let example = ActorReentrancyHandledExample2()
32 | example.execute()
33 | }
34 | }
35 |
36 | struct ActorReentrancyProblemExample {
37 |
38 | actor BankAccount {
39 |
40 | private var balance = 1000
41 |
42 | func withdraw(_ amount: Int) async {
43 |
44 | print("🤓 Check balance for withdrawal: \(amount)")
45 |
46 | guard canWithdraw(amount) else {
47 | print("🚫 Not enough balance to withdraw: \(amount)")
48 | return
49 | }
50 |
51 | guard await authorizeTransaction() else {
52 | return
53 | }
54 | print("✅ Transaction authorized: \(amount)")
55 |
56 | balance -= amount
57 |
58 | print("💰 Account balance: \(balance)")
59 | }
60 |
61 | private func canWithdraw(_ amount: Int) -> Bool {
62 | return amount <= balance
63 | }
64 |
65 | private func authorizeTransaction() async -> Bool {
66 |
67 | // Wait for 1 second
68 | try? await Task.sleep(nanoseconds: 1 * 1000000000)
69 |
70 | return true
71 | }
72 |
73 | }
74 |
75 |
76 | func execute() {
77 |
78 | let account = BankAccount()
79 |
80 | Task {
81 | await account.withdraw(800)
82 | }
83 |
84 | Task {
85 | await account.withdraw(500)
86 | }
87 | }
88 |
89 | }
90 |
91 |
92 | struct ActorReentrancyHandledExample1 {
93 |
94 | actor BankAccount {
95 |
96 | private var balance = 1000
97 |
98 | func withdraw(_ amount: Int) async {
99 |
100 | // Perform authorization before check balance
101 | guard await authorizeTransaction() else {
102 | return
103 | }
104 | print("✅ Transaction authorized: \(amount)")
105 |
106 | print("🤓 Check balance for withdrawal: \(amount)")
107 | guard canWithdraw(amount) else {
108 | print("🚫 Not enough balance to withdraw: \(amount)")
109 | return
110 | }
111 |
112 | balance -= amount
113 |
114 | print("💰 Account balance: \(balance)")
115 |
116 | }
117 |
118 | private func canWithdraw(_ amount: Int) -> Bool {
119 | return amount <= balance
120 | }
121 |
122 | private func authorizeTransaction() async -> Bool {
123 |
124 | // Wait for 1 second
125 | try? await Task.sleep(nanoseconds: 1 * 1000000000)
126 |
127 | return true
128 | }
129 |
130 |
131 | }
132 |
133 | func execute() {
134 |
135 | let account = BankAccount()
136 |
137 | Task {
138 | await account.withdraw(800)
139 | }
140 |
141 | Task {
142 | await account.withdraw(500)
143 | }
144 | }
145 |
146 | }
147 |
148 |
149 | struct ActorReentrancyHandledExample2 {
150 |
151 | actor BankAccount {
152 |
153 | private var balance = 1000
154 |
155 | func withdraw(_ amount: Int) async {
156 |
157 | print("🤓 Check balance for withdrawal: \(amount)")
158 | guard canWithdraw(amount) else {
159 | print("🚫 Not enough balance to withdraw: \(amount)")
160 | return
161 | }
162 |
163 | guard await authorizeTransaction() else {
164 | return
165 | }
166 | print("✅ Transaction authorized: \(amount)")
167 |
168 | // Check balance again after the authorization process
169 | guard canWithdraw(amount) else {
170 | print("⛔️ Not enough balance to withdraw: \(amount) (authorized)")
171 | return
172 | }
173 |
174 | balance -= amount
175 |
176 | print("💰 Account balance: \(balance)")
177 |
178 | }
179 |
180 | private func canWithdraw(_ amount: Int) -> Bool {
181 | return amount <= balance
182 | }
183 |
184 | private func authorizeTransaction() async -> Bool {
185 |
186 | // Wait for 1 second
187 | try? await Task.sleep(nanoseconds: 1 * 1000000000)
188 |
189 | return true
190 | }
191 |
192 |
193 | }
194 |
195 | func execute() {
196 |
197 | let account = BankAccount()
198 |
199 | Task {
200 | await account.withdraw(800)
201 | }
202 |
203 | Task {
204 | await account.withdraw(500)
205 | }
206 | }
207 |
208 | }
209 |
210 |
211 |
212 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1804EE282709648600E91CA1 /* SendableDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1804EE272709648600E91CA1 /* SendableDemoViewController.swift */; };
11 | 1804EE2A2709748C00E91CA1 /* NonSendableDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1804EE292709748C00E91CA1 /* NonSendableDemoViewController.swift */; };
12 | 1819F7832A62BDAE00A518B9 /* AsyncStreamProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1819F7822A62BDAE00A518B9 /* AsyncStreamProgressViewController.swift */; };
13 | 1819F7872A62E05600A518B9 /* FileDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1819F7862A62E05600A518B9 /* FileDownloader.swift */; };
14 | 182CC6F326C6994A00D84105 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182CC6F226C6994A00D84105 /* AppDelegate.swift */; };
15 | 182CC6F526C6994A00D84105 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182CC6F426C6994A00D84105 /* SceneDelegate.swift */; };
16 | 182CC6F726C6994A00D84105 /* FetchAlbumsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182CC6F626C6994A00D84105 /* FetchAlbumsViewController.swift */; };
17 | 182CC6FA26C6994A00D84105 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 182CC6F826C6994A00D84105 /* Main.storyboard */; };
18 | 182CC6FC26C6994E00D84105 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 182CC6FB26C6994E00D84105 /* Assets.xcassets */; };
19 | 182CC6FF26C6994E00D84105 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 182CC6FD26C6994E00D84105 /* LaunchScreen.storyboard */; };
20 | 183074FB26EC996A0071FBEF /* ReentrancyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183074FA26EC996A0071FBEF /* ReentrancyViewController.swift */; };
21 | 183571FF271565820061FD22 /* UnderstandTaskGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 183571FE271565820061FD22 /* UnderstandTaskGroupViewController.swift */; };
22 | 184ECFC726C7752500C8EB68 /* AlbumsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184ECFC626C7752500C8EB68 /* AlbumsFetcher.swift */; };
23 | 1862F33F2732AECF00CE9276 /* TaskGroupErrorHandlingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1862F33E2732AECF00CE9276 /* TaskGroupErrorHandlingViewController.swift */; };
24 | 187D599F2917E6A900439677 /* ThreadExplosionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187D599E2917E6A900439677 /* ThreadExplosionViewController.swift */; };
25 | 187D59A12917EE3400439677 /* HeavyWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 187D59A02917EE3400439677 /* HeavyWork.swift */; };
26 | 18BBA1EC26DD170E00C7CB4E /* DataRaceDispatchQueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BBA1EB26DD170E00C7CB4E /* DataRaceDispatchQueueViewController.swift */; };
27 | 18BBA1EE26DD1A4800C7CB4E /* DataRaceAsyncAwaitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BBA1ED26DD1A4800C7CB4E /* DataRaceAsyncAwaitViewController.swift */; };
28 | 18BBA1F026DD27F800C7CB4E /* ActorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18BBA1EF26DD27F800C7CB4E /* ActorViewController.swift */; };
29 | /* End PBXBuildFile section */
30 |
31 | /* Begin PBXContainerItemProxy section */
32 | 182CC70626C6994E00D84105 /* PBXContainerItemProxy */ = {
33 | isa = PBXContainerItemProxy;
34 | containerPortal = 182CC6E726C6994A00D84105 /* Project object */;
35 | proxyType = 1;
36 | remoteGlobalIDString = 182CC6EE26C6994A00D84105;
37 | remoteInfo = "SwiftSenpai-Swift-Concurrency";
38 | };
39 | 182CC71026C6994E00D84105 /* PBXContainerItemProxy */ = {
40 | isa = PBXContainerItemProxy;
41 | containerPortal = 182CC6E726C6994A00D84105 /* Project object */;
42 | proxyType = 1;
43 | remoteGlobalIDString = 182CC6EE26C6994A00D84105;
44 | remoteInfo = "SwiftSenpai-Swift-Concurrency";
45 | };
46 | /* End PBXContainerItemProxy section */
47 |
48 | /* Begin PBXFileReference section */
49 | 1804EE272709648600E91CA1 /* SendableDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendableDemoViewController.swift; sourceTree = ""; };
50 | 1804EE292709748C00E91CA1 /* NonSendableDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonSendableDemoViewController.swift; sourceTree = ""; };
51 | 1819F7822A62BDAE00A518B9 /* AsyncStreamProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStreamProgressViewController.swift; sourceTree = ""; };
52 | 1819F7862A62E05600A518B9 /* FileDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloader.swift; sourceTree = ""; };
53 | 182CC6EF26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SwiftSenpai-Swift-Concurrency.app"; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 182CC6F226C6994A00D84105 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
55 | 182CC6F426C6994A00D84105 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
56 | 182CC6F626C6994A00D84105 /* FetchAlbumsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAlbumsViewController.swift; sourceTree = ""; };
57 | 182CC6F926C6994A00D84105 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
58 | 182CC6FB26C6994E00D84105 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
59 | 182CC6FE26C6994E00D84105 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
60 | 182CC70026C6994E00D84105 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
61 | 182CC70526C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftSenpai-Swift-ConcurrencyTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
62 | 182CC70F26C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftSenpai-Swift-ConcurrencyUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 183074FA26EC996A0071FBEF /* ReentrancyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReentrancyViewController.swift; sourceTree = ""; };
64 | 183571FE271565820061FD22 /* UnderstandTaskGroupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderstandTaskGroupViewController.swift; sourceTree = ""; };
65 | 184ECFC626C7752500C8EB68 /* AlbumsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumsFetcher.swift; sourceTree = ""; };
66 | 1862F33E2732AECF00CE9276 /* TaskGroupErrorHandlingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskGroupErrorHandlingViewController.swift; sourceTree = ""; };
67 | 187D599E2917E6A900439677 /* ThreadExplosionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadExplosionViewController.swift; sourceTree = ""; };
68 | 187D59A02917EE3400439677 /* HeavyWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeavyWork.swift; sourceTree = ""; };
69 | 18BBA1EB26DD170E00C7CB4E /* DataRaceDispatchQueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataRaceDispatchQueueViewController.swift; sourceTree = ""; };
70 | 18BBA1ED26DD1A4800C7CB4E /* DataRaceAsyncAwaitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataRaceAsyncAwaitViewController.swift; sourceTree = ""; };
71 | 18BBA1EF26DD27F800C7CB4E /* ActorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActorViewController.swift; sourceTree = ""; };
72 | /* End PBXFileReference section */
73 |
74 | /* Begin PBXFrameworksBuildPhase section */
75 | 182CC6EC26C6994A00D84105 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | 182CC70226C6994E00D84105 /* Frameworks */ = {
83 | isa = PBXFrameworksBuildPhase;
84 | buildActionMask = 2147483647;
85 | files = (
86 | );
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | 182CC70C26C6994E00D84105 /* Frameworks */ = {
90 | isa = PBXFrameworksBuildPhase;
91 | buildActionMask = 2147483647;
92 | files = (
93 | );
94 | runOnlyForDeploymentPostprocessing = 0;
95 | };
96 | /* End PBXFrameworksBuildPhase section */
97 |
98 | /* Begin PBXGroup section */
99 | 1804EE262709642600E91CA1 /* Sendable Demo */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 1804EE292709748C00E91CA1 /* NonSendableDemoViewController.swift */,
103 | 1804EE272709648600E91CA1 /* SendableDemoViewController.swift */,
104 | );
105 | path = "Sendable Demo";
106 | sourceTree = "";
107 | };
108 | 1819F7812A62BBF600A518B9 /* Async Stream Progress */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 1819F7822A62BDAE00A518B9 /* AsyncStreamProgressViewController.swift */,
112 | 1819F7862A62E05600A518B9 /* FileDownloader.swift */,
113 | );
114 | path = "Async Stream Progress";
115 | sourceTree = "";
116 | };
117 | 182CC6E626C6994A00D84105 = {
118 | isa = PBXGroup;
119 | children = (
120 | 182CC6F126C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */,
121 | 182CC6F026C6994A00D84105 /* Products */,
122 | );
123 | sourceTree = "";
124 | };
125 | 182CC6F026C6994A00D84105 /* Products */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 182CC6EF26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency.app */,
129 | 182CC70526C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyTests.xctest */,
130 | 182CC70F26C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyUITests.xctest */,
131 | );
132 | name = Products;
133 | sourceTree = "";
134 | };
135 | 182CC6F126C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */ = {
136 | isa = PBXGroup;
137 | children = (
138 | 18BBA1E726DD03C100C7CB4E /* Making Network Requests */,
139 | 18BBA1E826DD040E00C7CB4E /* Prevent Data Races */,
140 | 183074F926EC98B00071FBEF /* Actor Reentrancy Problem */,
141 | 1804EE262709642600E91CA1 /* Sendable Demo */,
142 | 183571FD271565580061FD22 /* Understanding Task Group */,
143 | 1862F33D2732AE9A00CE9276 /* Task Group Error Handling */,
144 | 187D599D2917E66C00439677 /* Prevent Thread Explosion */,
145 | 1819F7812A62BBF600A518B9 /* Async Stream Progress */,
146 | 182CC6F226C6994A00D84105 /* AppDelegate.swift */,
147 | 182CC6F426C6994A00D84105 /* SceneDelegate.swift */,
148 | 182CC6F826C6994A00D84105 /* Main.storyboard */,
149 | 182CC6FB26C6994E00D84105 /* Assets.xcassets */,
150 | 182CC6FD26C6994E00D84105 /* LaunchScreen.storyboard */,
151 | 182CC70026C6994E00D84105 /* Info.plist */,
152 | );
153 | path = "SwiftSenpai-Swift-Concurrency";
154 | sourceTree = "";
155 | };
156 | 183074F926EC98B00071FBEF /* Actor Reentrancy Problem */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 183074FA26EC996A0071FBEF /* ReentrancyViewController.swift */,
160 | );
161 | path = "Actor Reentrancy Problem";
162 | sourceTree = "";
163 | };
164 | 183571FD271565580061FD22 /* Understanding Task Group */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 183571FE271565820061FD22 /* UnderstandTaskGroupViewController.swift */,
168 | );
169 | path = "Understanding Task Group";
170 | sourceTree = "";
171 | };
172 | 1862F33D2732AE9A00CE9276 /* Task Group Error Handling */ = {
173 | isa = PBXGroup;
174 | children = (
175 | 1862F33E2732AECF00CE9276 /* TaskGroupErrorHandlingViewController.swift */,
176 | );
177 | path = "Task Group Error Handling";
178 | sourceTree = "";
179 | };
180 | 187D599D2917E66C00439677 /* Prevent Thread Explosion */ = {
181 | isa = PBXGroup;
182 | children = (
183 | 187D599E2917E6A900439677 /* ThreadExplosionViewController.swift */,
184 | 187D59A02917EE3400439677 /* HeavyWork.swift */,
185 | );
186 | path = "Prevent Thread Explosion";
187 | sourceTree = "";
188 | };
189 | 18BBA1E726DD03C100C7CB4E /* Making Network Requests */ = {
190 | isa = PBXGroup;
191 | children = (
192 | 182CC6F626C6994A00D84105 /* FetchAlbumsViewController.swift */,
193 | 184ECFC626C7752500C8EB68 /* AlbumsFetcher.swift */,
194 | );
195 | path = "Making Network Requests ";
196 | sourceTree = "";
197 | };
198 | 18BBA1E826DD040E00C7CB4E /* Prevent Data Races */ = {
199 | isa = PBXGroup;
200 | children = (
201 | 18BBA1EB26DD170E00C7CB4E /* DataRaceDispatchQueueViewController.swift */,
202 | 18BBA1ED26DD1A4800C7CB4E /* DataRaceAsyncAwaitViewController.swift */,
203 | 18BBA1EF26DD27F800C7CB4E /* ActorViewController.swift */,
204 | );
205 | path = "Prevent Data Races";
206 | sourceTree = "";
207 | };
208 | /* End PBXGroup section */
209 |
210 | /* Begin PBXNativeTarget section */
211 | 182CC6EE26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */ = {
212 | isa = PBXNativeTarget;
213 | buildConfigurationList = 182CC71726C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-Concurrency" */;
214 | buildPhases = (
215 | 182CC6EB26C6994A00D84105 /* Sources */,
216 | 182CC6EC26C6994A00D84105 /* Frameworks */,
217 | 182CC6ED26C6994A00D84105 /* Resources */,
218 | );
219 | buildRules = (
220 | );
221 | dependencies = (
222 | );
223 | name = "SwiftSenpai-Swift-Concurrency";
224 | productName = "SwiftSenpai-Swift-Concurrency";
225 | productReference = 182CC6EF26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency.app */;
226 | productType = "com.apple.product-type.application";
227 | };
228 | 182CC70426C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyTests */ = {
229 | isa = PBXNativeTarget;
230 | buildConfigurationList = 182CC71A26C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-ConcurrencyTests" */;
231 | buildPhases = (
232 | 182CC70126C6994E00D84105 /* Sources */,
233 | 182CC70226C6994E00D84105 /* Frameworks */,
234 | 182CC70326C6994E00D84105 /* Resources */,
235 | );
236 | buildRules = (
237 | );
238 | dependencies = (
239 | 182CC70726C6994E00D84105 /* PBXTargetDependency */,
240 | );
241 | name = "SwiftSenpai-Swift-ConcurrencyTests";
242 | productName = "SwiftSenpai-Swift-ConcurrencyTests";
243 | productReference = 182CC70526C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyTests.xctest */;
244 | productType = "com.apple.product-type.bundle.unit-test";
245 | };
246 | 182CC70E26C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyUITests */ = {
247 | isa = PBXNativeTarget;
248 | buildConfigurationList = 182CC71D26C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-ConcurrencyUITests" */;
249 | buildPhases = (
250 | 182CC70B26C6994E00D84105 /* Sources */,
251 | 182CC70C26C6994E00D84105 /* Frameworks */,
252 | 182CC70D26C6994E00D84105 /* Resources */,
253 | );
254 | buildRules = (
255 | );
256 | dependencies = (
257 | 182CC71126C6994E00D84105 /* PBXTargetDependency */,
258 | );
259 | name = "SwiftSenpai-Swift-ConcurrencyUITests";
260 | productName = "SwiftSenpai-Swift-ConcurrencyUITests";
261 | productReference = 182CC70F26C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyUITests.xctest */;
262 | productType = "com.apple.product-type.bundle.ui-testing";
263 | };
264 | /* End PBXNativeTarget section */
265 |
266 | /* Begin PBXProject section */
267 | 182CC6E726C6994A00D84105 /* Project object */ = {
268 | isa = PBXProject;
269 | attributes = {
270 | BuildIndependentTargetsInParallel = 1;
271 | LastSwiftUpdateCheck = 1300;
272 | LastUpgradeCheck = 1300;
273 | TargetAttributes = {
274 | 182CC6EE26C6994A00D84105 = {
275 | CreatedOnToolsVersion = 13.0;
276 | };
277 | 182CC70426C6994E00D84105 = {
278 | CreatedOnToolsVersion = 13.0;
279 | TestTargetID = 182CC6EE26C6994A00D84105;
280 | };
281 | 182CC70E26C6994E00D84105 = {
282 | CreatedOnToolsVersion = 13.0;
283 | TestTargetID = 182CC6EE26C6994A00D84105;
284 | };
285 | };
286 | };
287 | buildConfigurationList = 182CC6EA26C6994A00D84105 /* Build configuration list for PBXProject "SwiftSenpai-Swift-Concurrency" */;
288 | compatibilityVersion = "Xcode 13.0";
289 | developmentRegion = en;
290 | hasScannedForEncodings = 0;
291 | knownRegions = (
292 | en,
293 | Base,
294 | );
295 | mainGroup = 182CC6E626C6994A00D84105;
296 | productRefGroup = 182CC6F026C6994A00D84105 /* Products */;
297 | projectDirPath = "";
298 | projectRoot = "";
299 | targets = (
300 | 182CC6EE26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */,
301 | 182CC70426C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyTests */,
302 | 182CC70E26C6994E00D84105 /* SwiftSenpai-Swift-ConcurrencyUITests */,
303 | );
304 | };
305 | /* End PBXProject section */
306 |
307 | /* Begin PBXResourcesBuildPhase section */
308 | 182CC6ED26C6994A00D84105 /* Resources */ = {
309 | isa = PBXResourcesBuildPhase;
310 | buildActionMask = 2147483647;
311 | files = (
312 | 182CC6FF26C6994E00D84105 /* LaunchScreen.storyboard in Resources */,
313 | 182CC6FC26C6994E00D84105 /* Assets.xcassets in Resources */,
314 | 182CC6FA26C6994A00D84105 /* Main.storyboard in Resources */,
315 | );
316 | runOnlyForDeploymentPostprocessing = 0;
317 | };
318 | 182CC70326C6994E00D84105 /* Resources */ = {
319 | isa = PBXResourcesBuildPhase;
320 | buildActionMask = 2147483647;
321 | files = (
322 | );
323 | runOnlyForDeploymentPostprocessing = 0;
324 | };
325 | 182CC70D26C6994E00D84105 /* Resources */ = {
326 | isa = PBXResourcesBuildPhase;
327 | buildActionMask = 2147483647;
328 | files = (
329 | );
330 | runOnlyForDeploymentPostprocessing = 0;
331 | };
332 | /* End PBXResourcesBuildPhase section */
333 |
334 | /* Begin PBXSourcesBuildPhase section */
335 | 182CC6EB26C6994A00D84105 /* Sources */ = {
336 | isa = PBXSourcesBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | 187D59A12917EE3400439677 /* HeavyWork.swift in Sources */,
340 | 182CC6F726C6994A00D84105 /* FetchAlbumsViewController.swift in Sources */,
341 | 18BBA1EE26DD1A4800C7CB4E /* DataRaceAsyncAwaitViewController.swift in Sources */,
342 | 182CC6F326C6994A00D84105 /* AppDelegate.swift in Sources */,
343 | 1862F33F2732AECF00CE9276 /* TaskGroupErrorHandlingViewController.swift in Sources */,
344 | 187D599F2917E6A900439677 /* ThreadExplosionViewController.swift in Sources */,
345 | 183074FB26EC996A0071FBEF /* ReentrancyViewController.swift in Sources */,
346 | 182CC6F526C6994A00D84105 /* SceneDelegate.swift in Sources */,
347 | 1804EE2A2709748C00E91CA1 /* NonSendableDemoViewController.swift in Sources */,
348 | 1804EE282709648600E91CA1 /* SendableDemoViewController.swift in Sources */,
349 | 1819F7832A62BDAE00A518B9 /* AsyncStreamProgressViewController.swift in Sources */,
350 | 18BBA1F026DD27F800C7CB4E /* ActorViewController.swift in Sources */,
351 | 183571FF271565820061FD22 /* UnderstandTaskGroupViewController.swift in Sources */,
352 | 184ECFC726C7752500C8EB68 /* AlbumsFetcher.swift in Sources */,
353 | 1819F7872A62E05600A518B9 /* FileDownloader.swift in Sources */,
354 | 18BBA1EC26DD170E00C7CB4E /* DataRaceDispatchQueueViewController.swift in Sources */,
355 | );
356 | runOnlyForDeploymentPostprocessing = 0;
357 | };
358 | 182CC70126C6994E00D84105 /* Sources */ = {
359 | isa = PBXSourcesBuildPhase;
360 | buildActionMask = 2147483647;
361 | files = (
362 | );
363 | runOnlyForDeploymentPostprocessing = 0;
364 | };
365 | 182CC70B26C6994E00D84105 /* Sources */ = {
366 | isa = PBXSourcesBuildPhase;
367 | buildActionMask = 2147483647;
368 | files = (
369 | );
370 | runOnlyForDeploymentPostprocessing = 0;
371 | };
372 | /* End PBXSourcesBuildPhase section */
373 |
374 | /* Begin PBXTargetDependency section */
375 | 182CC70726C6994E00D84105 /* PBXTargetDependency */ = {
376 | isa = PBXTargetDependency;
377 | target = 182CC6EE26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */;
378 | targetProxy = 182CC70626C6994E00D84105 /* PBXContainerItemProxy */;
379 | };
380 | 182CC71126C6994E00D84105 /* PBXTargetDependency */ = {
381 | isa = PBXTargetDependency;
382 | target = 182CC6EE26C6994A00D84105 /* SwiftSenpai-Swift-Concurrency */;
383 | targetProxy = 182CC71026C6994E00D84105 /* PBXContainerItemProxy */;
384 | };
385 | /* End PBXTargetDependency section */
386 |
387 | /* Begin PBXVariantGroup section */
388 | 182CC6F826C6994A00D84105 /* Main.storyboard */ = {
389 | isa = PBXVariantGroup;
390 | children = (
391 | 182CC6F926C6994A00D84105 /* Base */,
392 | );
393 | name = Main.storyboard;
394 | sourceTree = "";
395 | };
396 | 182CC6FD26C6994E00D84105 /* LaunchScreen.storyboard */ = {
397 | isa = PBXVariantGroup;
398 | children = (
399 | 182CC6FE26C6994E00D84105 /* Base */,
400 | );
401 | name = LaunchScreen.storyboard;
402 | sourceTree = "";
403 | };
404 | /* End PBXVariantGroup section */
405 |
406 | /* Begin XCBuildConfiguration section */
407 | 182CC71526C6994E00D84105 /* Debug */ = {
408 | isa = XCBuildConfiguration;
409 | buildSettings = {
410 | ALWAYS_SEARCH_USER_PATHS = NO;
411 | CLANG_ANALYZER_NONNULL = YES;
412 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
414 | CLANG_CXX_LIBRARY = "libc++";
415 | CLANG_ENABLE_MODULES = YES;
416 | CLANG_ENABLE_OBJC_ARC = YES;
417 | CLANG_ENABLE_OBJC_WEAK = YES;
418 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
419 | CLANG_WARN_BOOL_CONVERSION = YES;
420 | CLANG_WARN_COMMA = YES;
421 | CLANG_WARN_CONSTANT_CONVERSION = YES;
422 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
423 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
424 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
425 | CLANG_WARN_EMPTY_BODY = YES;
426 | CLANG_WARN_ENUM_CONVERSION = YES;
427 | CLANG_WARN_INFINITE_RECURSION = YES;
428 | CLANG_WARN_INT_CONVERSION = YES;
429 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
430 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
431 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
433 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
434 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
435 | CLANG_WARN_STRICT_PROTOTYPES = YES;
436 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
437 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
438 | CLANG_WARN_UNREACHABLE_CODE = YES;
439 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
440 | COPY_PHASE_STRIP = NO;
441 | DEBUG_INFORMATION_FORMAT = dwarf;
442 | ENABLE_STRICT_OBJC_MSGSEND = YES;
443 | ENABLE_TESTABILITY = YES;
444 | GCC_C_LANGUAGE_STANDARD = gnu11;
445 | GCC_DYNAMIC_NO_PIC = NO;
446 | GCC_NO_COMMON_BLOCKS = YES;
447 | GCC_OPTIMIZATION_LEVEL = 0;
448 | GCC_PREPROCESSOR_DEFINITIONS = (
449 | "DEBUG=1",
450 | "$(inherited)",
451 | );
452 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
454 | GCC_WARN_UNDECLARED_SELECTOR = YES;
455 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
456 | GCC_WARN_UNUSED_FUNCTION = YES;
457 | GCC_WARN_UNUSED_VARIABLE = YES;
458 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
459 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
460 | MTL_FAST_MATH = YES;
461 | ONLY_ACTIVE_ARCH = YES;
462 | SDKROOT = iphoneos;
463 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
464 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
465 | };
466 | name = Debug;
467 | };
468 | 182CC71626C6994E00D84105 /* Release */ = {
469 | isa = XCBuildConfiguration;
470 | buildSettings = {
471 | ALWAYS_SEARCH_USER_PATHS = NO;
472 | CLANG_ANALYZER_NONNULL = YES;
473 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
474 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
475 | CLANG_CXX_LIBRARY = "libc++";
476 | CLANG_ENABLE_MODULES = YES;
477 | CLANG_ENABLE_OBJC_ARC = YES;
478 | CLANG_ENABLE_OBJC_WEAK = YES;
479 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
480 | CLANG_WARN_BOOL_CONVERSION = YES;
481 | CLANG_WARN_COMMA = YES;
482 | CLANG_WARN_CONSTANT_CONVERSION = YES;
483 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
484 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
485 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
486 | CLANG_WARN_EMPTY_BODY = YES;
487 | CLANG_WARN_ENUM_CONVERSION = YES;
488 | CLANG_WARN_INFINITE_RECURSION = YES;
489 | CLANG_WARN_INT_CONVERSION = YES;
490 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
491 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
492 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
493 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
494 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
495 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
496 | CLANG_WARN_STRICT_PROTOTYPES = YES;
497 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
498 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
499 | CLANG_WARN_UNREACHABLE_CODE = YES;
500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
501 | COPY_PHASE_STRIP = NO;
502 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
503 | ENABLE_NS_ASSERTIONS = NO;
504 | ENABLE_STRICT_OBJC_MSGSEND = YES;
505 | GCC_C_LANGUAGE_STANDARD = gnu11;
506 | GCC_NO_COMMON_BLOCKS = YES;
507 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
508 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
509 | GCC_WARN_UNDECLARED_SELECTOR = YES;
510 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
511 | GCC_WARN_UNUSED_FUNCTION = YES;
512 | GCC_WARN_UNUSED_VARIABLE = YES;
513 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
514 | MTL_ENABLE_DEBUG_INFO = NO;
515 | MTL_FAST_MATH = YES;
516 | SDKROOT = iphoneos;
517 | SWIFT_COMPILATION_MODE = wholemodule;
518 | SWIFT_OPTIMIZATION_LEVEL = "-O";
519 | VALIDATE_PRODUCT = YES;
520 | };
521 | name = Release;
522 | };
523 | 182CC71826C6994E00D84105 /* Debug */ = {
524 | isa = XCBuildConfiguration;
525 | buildSettings = {
526 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
527 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
528 | CODE_SIGN_IDENTITY = "iPhone Developer";
529 | CODE_SIGN_STYLE = Manual;
530 | CURRENT_PROJECT_VERSION = 1;
531 | DEVELOPMENT_TEAM = QVEMCYHXHL;
532 | GENERATE_INFOPLIST_FILE = YES;
533 | INFOPLIST_FILE = "SwiftSenpai-Swift-Concurrency/Info.plist";
534 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
535 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
536 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
537 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
538 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
539 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
540 | LD_RUNPATH_SEARCH_PATHS = (
541 | "$(inherited)",
542 | "@executable_path/Frameworks",
543 | );
544 | MARKETING_VERSION = 1.0;
545 | PRODUCT_BUNDLE_IDENTIFIER = "com.LeeKahSeng.SwiftSenpai-Swift-Concurrency";
546 | PRODUCT_NAME = "$(TARGET_NAME)";
547 | PROVISIONING_PROFILE_SPECIFIER = "yeos_lee dev profile";
548 | SWIFT_EMIT_LOC_STRINGS = YES;
549 | SWIFT_VERSION = 5.0;
550 | TARGETED_DEVICE_FAMILY = 1;
551 | };
552 | name = Debug;
553 | };
554 | 182CC71926C6994E00D84105 /* Release */ = {
555 | isa = XCBuildConfiguration;
556 | buildSettings = {
557 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
558 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
559 | CODE_SIGN_IDENTITY = "iPhone Developer";
560 | CODE_SIGN_STYLE = Manual;
561 | CURRENT_PROJECT_VERSION = 1;
562 | DEVELOPMENT_TEAM = QVEMCYHXHL;
563 | GENERATE_INFOPLIST_FILE = YES;
564 | INFOPLIST_FILE = "SwiftSenpai-Swift-Concurrency/Info.plist";
565 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
566 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
567 | INFOPLIST_KEY_UIMainStoryboardFile = Main;
568 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
569 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
570 | IPHONEOS_DEPLOYMENT_TARGET = 16.4;
571 | LD_RUNPATH_SEARCH_PATHS = (
572 | "$(inherited)",
573 | "@executable_path/Frameworks",
574 | );
575 | MARKETING_VERSION = 1.0;
576 | PRODUCT_BUNDLE_IDENTIFIER = "com.LeeKahSeng.SwiftSenpai-Swift-Concurrency";
577 | PRODUCT_NAME = "$(TARGET_NAME)";
578 | PROVISIONING_PROFILE_SPECIFIER = "yeos_lee dev profile";
579 | SWIFT_EMIT_LOC_STRINGS = YES;
580 | SWIFT_VERSION = 5.0;
581 | TARGETED_DEVICE_FAMILY = 1;
582 | };
583 | name = Release;
584 | };
585 | 182CC71B26C6994E00D84105 /* Debug */ = {
586 | isa = XCBuildConfiguration;
587 | buildSettings = {
588 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
589 | BUNDLE_LOADER = "$(TEST_HOST)";
590 | CODE_SIGN_STYLE = Automatic;
591 | CURRENT_PROJECT_VERSION = 1;
592 | DEVELOPMENT_TEAM = SM42V25455;
593 | GENERATE_INFOPLIST_FILE = YES;
594 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
595 | LD_RUNPATH_SEARCH_PATHS = (
596 | "$(inherited)",
597 | "@executable_path/Frameworks",
598 | "@loader_path/Frameworks",
599 | );
600 | MARKETING_VERSION = 1.0;
601 | PRODUCT_BUNDLE_IDENTIFIER = "com.leekahseng.SwiftSenpai-Swift-ConcurrencyTests";
602 | PRODUCT_NAME = "$(TARGET_NAME)";
603 | SWIFT_EMIT_LOC_STRINGS = NO;
604 | SWIFT_VERSION = 5.0;
605 | TARGETED_DEVICE_FAMILY = "1,2";
606 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSenpai-Swift-Concurrency.app/SwiftSenpai-Swift-Concurrency";
607 | };
608 | name = Debug;
609 | };
610 | 182CC71C26C6994E00D84105 /* Release */ = {
611 | isa = XCBuildConfiguration;
612 | buildSettings = {
613 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
614 | BUNDLE_LOADER = "$(TEST_HOST)";
615 | CODE_SIGN_STYLE = Automatic;
616 | CURRENT_PROJECT_VERSION = 1;
617 | DEVELOPMENT_TEAM = SM42V25455;
618 | GENERATE_INFOPLIST_FILE = YES;
619 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
620 | LD_RUNPATH_SEARCH_PATHS = (
621 | "$(inherited)",
622 | "@executable_path/Frameworks",
623 | "@loader_path/Frameworks",
624 | );
625 | MARKETING_VERSION = 1.0;
626 | PRODUCT_BUNDLE_IDENTIFIER = "com.leekahseng.SwiftSenpai-Swift-ConcurrencyTests";
627 | PRODUCT_NAME = "$(TARGET_NAME)";
628 | SWIFT_EMIT_LOC_STRINGS = NO;
629 | SWIFT_VERSION = 5.0;
630 | TARGETED_DEVICE_FAMILY = "1,2";
631 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSenpai-Swift-Concurrency.app/SwiftSenpai-Swift-Concurrency";
632 | };
633 | name = Release;
634 | };
635 | 182CC71E26C6994E00D84105 /* Debug */ = {
636 | isa = XCBuildConfiguration;
637 | buildSettings = {
638 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
639 | CODE_SIGN_STYLE = Automatic;
640 | CURRENT_PROJECT_VERSION = 1;
641 | DEVELOPMENT_TEAM = SM42V25455;
642 | GENERATE_INFOPLIST_FILE = YES;
643 | LD_RUNPATH_SEARCH_PATHS = (
644 | "$(inherited)",
645 | "@executable_path/Frameworks",
646 | "@loader_path/Frameworks",
647 | );
648 | MARKETING_VERSION = 1.0;
649 | PRODUCT_BUNDLE_IDENTIFIER = "com.leekahseng.SwiftSenpai-Swift-ConcurrencyUITests";
650 | PRODUCT_NAME = "$(TARGET_NAME)";
651 | SWIFT_EMIT_LOC_STRINGS = NO;
652 | SWIFT_VERSION = 5.0;
653 | TARGETED_DEVICE_FAMILY = "1,2";
654 | TEST_TARGET_NAME = "SwiftSenpai-Swift-Concurrency";
655 | };
656 | name = Debug;
657 | };
658 | 182CC71F26C6994E00D84105 /* Release */ = {
659 | isa = XCBuildConfiguration;
660 | buildSettings = {
661 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
662 | CODE_SIGN_STYLE = Automatic;
663 | CURRENT_PROJECT_VERSION = 1;
664 | DEVELOPMENT_TEAM = SM42V25455;
665 | GENERATE_INFOPLIST_FILE = YES;
666 | LD_RUNPATH_SEARCH_PATHS = (
667 | "$(inherited)",
668 | "@executable_path/Frameworks",
669 | "@loader_path/Frameworks",
670 | );
671 | MARKETING_VERSION = 1.0;
672 | PRODUCT_BUNDLE_IDENTIFIER = "com.leekahseng.SwiftSenpai-Swift-ConcurrencyUITests";
673 | PRODUCT_NAME = "$(TARGET_NAME)";
674 | SWIFT_EMIT_LOC_STRINGS = NO;
675 | SWIFT_VERSION = 5.0;
676 | TARGETED_DEVICE_FAMILY = "1,2";
677 | TEST_TARGET_NAME = "SwiftSenpai-Swift-Concurrency";
678 | };
679 | name = Release;
680 | };
681 | /* End XCBuildConfiguration section */
682 |
683 | /* Begin XCConfigurationList section */
684 | 182CC6EA26C6994A00D84105 /* Build configuration list for PBXProject "SwiftSenpai-Swift-Concurrency" */ = {
685 | isa = XCConfigurationList;
686 | buildConfigurations = (
687 | 182CC71526C6994E00D84105 /* Debug */,
688 | 182CC71626C6994E00D84105 /* Release */,
689 | );
690 | defaultConfigurationIsVisible = 0;
691 | defaultConfigurationName = Release;
692 | };
693 | 182CC71726C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-Concurrency" */ = {
694 | isa = XCConfigurationList;
695 | buildConfigurations = (
696 | 182CC71826C6994E00D84105 /* Debug */,
697 | 182CC71926C6994E00D84105 /* Release */,
698 | );
699 | defaultConfigurationIsVisible = 0;
700 | defaultConfigurationName = Release;
701 | };
702 | 182CC71A26C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-ConcurrencyTests" */ = {
703 | isa = XCConfigurationList;
704 | buildConfigurations = (
705 | 182CC71B26C6994E00D84105 /* Debug */,
706 | 182CC71C26C6994E00D84105 /* Release */,
707 | );
708 | defaultConfigurationIsVisible = 0;
709 | defaultConfigurationName = Release;
710 | };
711 | 182CC71D26C6994E00D84105 /* Build configuration list for PBXNativeTarget "SwiftSenpai-Swift-ConcurrencyUITests" */ = {
712 | isa = XCConfigurationList;
713 | buildConfigurations = (
714 | 182CC71E26C6994E00D84105 /* Debug */,
715 | 182CC71F26C6994E00D84105 /* Release */,
716 | );
717 | defaultConfigurationIsVisible = 0;
718 | defaultConfigurationName = Release;
719 | };
720 | /* End XCConfigurationList section */
721 | };
722 | rootObject = 182CC6E726C6994A00D84105 /* Project object */;
723 | }
724 |
--------------------------------------------------------------------------------
/SwiftSenpai-Swift-Concurrency/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
254 |
262 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
312 |
324 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
430 |
438 |
446 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
500 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
619 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
663 |
671 |
679 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
828 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
870 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
912 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
956 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
997 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
--------------------------------------------------------------------------------