Upgrade to Pro — share decks privately, control downloads, hide ads and more …

async/awaitやactorでiOSアプリ開発がどう変わるか Before & Afterの具体例で学ぶ

3c031541ed3d92869414857dfef853de?s=47 Yuta Koshizawa
September 19, 2021

async/awaitやactorでiOSアプリ開発がどう変わるか Before & Afterの具体例で学ぶ

今秋リリース予定のSwift 5.5には、async/awaitやactorなどの並行処理関連の新機能が多く含まれます。これによって、iOSアプリ開発における非同期処理や並行処理のコードは劇的に変化します。本トークでは、iOSアプリ開発でよく見かけるようなケースを取り上げ、Before & Afterのコードを示し、何がどのように変わるのかを説明します。

async/awaitやactorは使いこなせばとても便利ですが、新しい概念を初めから抽象的に学ぶのは大変です。しかし、それらが解決しようとしている問題自体はiOSアプリ開発者にとって馴染み深いものです。async/awaitやactorで書くコードが既存コードのどのような処理に対応するのか、既存コードにはどんな問題があり、async/awaitやactorはそれをどう解決してくれるのか、具体的なケースで学べば入口のハードルはぐっと下がります。

Swiftの並行処理関連の新機能は、async/await、async let、Task、CheckedContinuation、actor、Sendable、MainActorなど多岐に渡ります。それらを一つ一つ取り上げ、具体例を題材に、iOSアプリ開発のコードがどのように改善されるのかを説明します。

https://fortee.jp/iosdc-japan-2021/speaker/proposal/view/19c076c3-18cb-4d04-9e9d-59cc02220538

3c031541ed3d92869414857dfef853de?s=128

Yuta Koshizawa

September 19, 2021
Tweet

Transcript

  1. async/await΍actorͰ iOSΞϓϦ։ൃ͕Ͳ͏มΘΔ͔ Before & A)erͷ۩ମྫͰֶͿ Yuta Koshizawa @koher

  2. Swi$ 5.5

  3. Concurrency ʢฒߦॲཧʣ

  4. iOS ΞϓϦ։ൃΛܶతʹม͑Δ

  5. Proposal: 12 ຊ WWDC: 9 ຊ

  6. ۩ମతͳࣄྫ ↓ ந৅తͳ֓೦

  7. iOS ΞϓϦ։ൃͱ͍͏۩ମྫ

  8. Before & A)er ͷ۩ମྫͰֶͿ

  9. 21 ݸͷέʔε

  10. Concurrency ! async / await ! Structured Concurrency ! actor

  11. ! async / await

  12. Case 1: ඇಉظؔ਺ͷར༻ ΤϥʔϋϯυϦϯά͕ͳ͍৔߹

  13. Before func downloadData(from url: URL, completion: @escaping (Data) -> Void)

  14. Before func downloadData(from url: URL, completion: @escaping (Data) -> Void)

    downloadData(from: url) { data in // data Λ࢖͏ॲཧ }
  15. async / await0296 0296 h'ps:/ /github.com/apple/swi9-evolu<on/blob/main/proposals/0296-async-await.md

  16. Before func downloadData(from url: URL, completion: @escaping (Data) -> Void)

    downloadData(from: url) { data in // data Λ࢖͏ॲཧ }
  17. A"er func downloadData(from url: URL) async -> Data let data

    = await downloadData(from: url) // data Λ࢖͏ॲཧ
  18. A ͱ B ͕ಉ͡εϨουͰ࣮ߦ͞ΕΔͱ͸ݶΒͳ͍ func downloadData(from url: URL) async ->

    Data print("A") let data = await downloadData(from: url) print("B")
  19. A ͱ B ͕ಉ͡εϨουͰ࣮ߦ͞ΕΔͱ͸ݶΒͳ͍ func downloadData(from url: URL, completion: @escaping

    (Data) -> Void) print("A") downloadData(from: url) { data in print("B") }
  20. Case 3: ඇಉظؔ਺ͷར༻ ΤϥʔϋϯυϦϯά͕͋Δ৔߹

  21. Case 1 (Before) func downloadData(from url: URL, completion: @escaping (Data)

    -> Void) downloadData(from: url) { data in // data Λ࢖͏ॲཧ }
  22. Before func downloadData(from url: URL, completion: @escaping (Result<Data, Error>) ->

    Void) downloadData(from: url) { result in do { let data = try result.get() // data Λ࢖͏ॲཧ } catch { // ΤϥʔϋϯυϦϯά } }
  23. A"er func downloadData(from url: URL) async throws -> Data do

    { let data = try await downloadData(from: url) // data Λ࢖͏ॲཧ } catch { // ΤϥʔϋϯυϦϯά }
  24. Case 4: ඇಉظؔ਺ͷ࣮૷ ΤϥʔϋϯυϦϯά͕͋Δ৔߹

  25. ྫ: User Λද͢ JSON Λऔಘͯ͠σίʔυ

  26. Before func fetchUser(for id: User.ID, completion: @escaping (Result<User, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! }
  27. Before func fetchUser(for id: User.ID, completion: @escaping (Result<User, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { result in } }
  28. Before func fetchUser(for id: User.ID, completion: @escaping (Result<User, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { result in let data = try result.get() let user = try JSONDecoder().decode(User.self, from: data) completion(.success(user)) } }
  29. Before func fetchUser(for id: User.ID, completion: @escaping (Result<User, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { result in do { let data = try result.get() let user = try JSONDecoder().decode(User.self, from: data) completion(.success(user)) } catch { completion(.failure(error)) } } }
  30. Before func fetchUser(for id: User.ID, completion: @escaping (Result<User, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { result in do { let data = try result.get() let user = try JSONDecoder().decode(User.self, from: data) completion(.success(user)) } catch { completion(.failure(error)) } } }
  31. A"er func fetchUser(for id: User.ID) async throws -> User {

    let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! let data = try await downloadData(from: url) let user = try JSONDecoder().decode(User.self, from: data) return user }
  32. Case 5: ඇಉظؔ਺ͷ࿈݁

  33. ྫ: ϢʔβʔΞΠίϯͷμ΢ϯϩʔυ ʢͨͩ͠ɺΞΠίϯͷ URL ͸Ϣʔβʔͷ JSON ͷதʹهࡌʣ

  34. Ϣʔβʔͷ JSON ͷμ΢ϯϩʔυ ↓ ΞΠίϯͷμ΢ϯϩʔυ

  35. Before func fetchUserIcon(for id: User.ID, completion: @escaping (Result<Data, Error>) ->

    Void) { let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! downloadData(from: url) { data in do { let data = try data.get() let user = try JSONDecoder().decode(User.self, from: data) downloadData(from: user.iconURL) { icon in do { let icon = try icon.get() completion(.success(icon)) } catch { completion(.failure(error)) } } } catch { completion(.failure(error)) } } }
  36. A"er func fetchUserIcon(for id: User.ID) async throws -> Data {

    let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! let data = try await downloadData(from: url) let user = try JSONDecoder().decode(User.self, from: data) let icon = try await downloadData(from: user.iconURL) return icon }
  37. Case 6: ίʔϧόοΫ͔Β async ΁ͷม׵

  38. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { }
  39. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { downloadData(from: url) { result in do { let data = try result.get() } catch { } } }
  40. Con$nua$onSE-0300 SE-0300 h(ps:/ /github.com/apple/swi:-evolu<on/blob/main/proposals/0300-con<nua<on.md

  41. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { downloadData(from: url) { result in do { let data = try result.get() } catch { } } }
  42. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { try await withCheckedThrowingContinuation { continuation in downloadData(from: url) { result in do { let data = try result.get() continuation.resume(returning: data) } catch { continuation.resume(throwing: error) } } } }
  43. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { try await withCheckedThrowingContinuation { continuation in downloadData(from: url) { result in do { let data = try result.get() continuation.resume(returning: data) } catch { continuation.resume(throwing: error) } } } }
  44. // ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result<Data, Error>)

    -> Void) // async func downloadData(from url: URL) async throws -> Data { try await withCheckedThrowingContinuation { continuation in downloadData(from: url) { result in continuation.resume(with: result) } } }
  45. ! Structured Concurrency

  46. Case 7: ඇಉظॲཧͷ։࢝

  47. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺ΂ͳ͍ func foo() async { ... }

    func bar() async { await foo() // ✅ }
  48. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺ΂ͳ͍ func foo() async { ... }

    func bar() { await foo() // ⛔ }
  49. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺ΂ͳ͍ func foo() async { ... }

    func bar() async { await foo() } func baz() async { await bar() } ...
  50. ඇಉظॲཧͷ։࢝ func main() { // ??? }

  51. ඇಉظॲཧͷ։࢝ func main() { Task { await foo() } }

  52. ඇಉظॲཧͷ։࢝ func main() { Task(operation: { await foo() }) }

  53. ඇಉظॲཧͷ։࢝ func main() { Task { await foo() } }

    ͢΂ͯͷ async ؔ਺͸ Task ্Ͱ࣮ߦ͞ΕΔɻ
  54. iOS ΞϓϦ։ൃͰͲͷΑ͏ʹ࢖ΘΕΔ͔ • viewDidAppear • View Controller ͕දࣔ͞ΕͨΒ fetchUser Ͱ

    User Λऔಘ ͯ͠දࣔ • @IBAction • μ΢ϯϩʔυϘλϯ͕ԡ͞ΕͨΒ downloadData Ͱμ΢ϯ ϩʔυ • ...
  55. A"er extension UserViewController { override func viewDidAppear(_ animated: Bool) {

    super.viewDidAppear(animated) ɹ } }
  56. A"er extension UserViewController { override func viewDidAppear(_ animated: Bool) {

    super.viewDidAppear(animated) do { let user = try await fetchUser(for: userID) self.user = user } catch { // ΤϥʔϋϯυϦϯά } } }
  57. A"er extension UserViewController { override func viewDidAppear(_ animated: Bool) {

    super.viewDidAppear(animated) Task { do { let user = try await fetchUser(for: userID) self.user = user } catch { // ΤϥʔϋϯυϦϯά } } } }
  58. Case 9: ฒߦॲཧ ݻఆݸ਺ͷ৔߹

  59. ྫ: େখͷΞΠίϯΛಉ࣌ʹμ΢ϯϩʔυ

  60. None
  61. A"er func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... return icons }
  62. ͜ΕͰ͸ฒߦʹͳΒͳ͍ func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... let smallIcon = try await downloadData(from: smallURL) let largeIcon = try await downloadData(from: largeURL) let icons = (small: smallIcon, large: largeIcon) return icons }
  63. async let BindingSE-0317 SE-0317 h*ps:/ /github.com/apple/swi<-evolu>on/blob/main/proposals/0317-async-let.md

  64. ͜ΕͰ͸ฒߦʹͳΒͳ͍ func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... let smallIcon = try await downloadData(from: smallURL) let largeIcon = try await downloadData(from: largeURL) let icons = (small: smallIcon, large: largeIcon) return icons }
  65. A"er func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... async let smallIcon = downloadData(from: smallURL) async let largeIcon = downloadData(from: largeURL) let icons = try await (small: smallIcon, large: largeIcon) return icons }
  66. A"er func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... async let smallIcon = downloadData(from: smallURL) async let largeIcon = downloadData(from: largeURL) let icons = try await (small: smallIcon, large: largeIcon) return icons } async let ͸ඞͣείʔϓ಺Ͱ await ͞Εͳ͚Ε͹ͳΒͳ͍ɻ
  67. Structured ConcurrencySE-0304 SE-0304 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0304-structured-concurrency.md

  68. Structured Programming ʢߏ଄Խϓϩάϥϛϯάʣ

  69. ߏ଄Խϓϩάϥϛϯά for value in values { if value.isValid { ...

    } else { ... } }
  70. A"er func fetchUserIcons(for id: User.ID) async throws -> (small: Data,

    large: Data) { let smallURL: URL = ... let largeURL: URL = ... async let smallIcon = downloadData(from: smallURL) async let largeIcon = downloadData(from: largeURL) let icons = try await (small: smallIcon, large: largeIcon) return icons } async let ͸ඞͣείʔϓ಺Ͱ await ͞Εͳ͚Ε͹ͳΒͳ͍ɻ
  71. async let ͱ Task Task { async let a =

    foo() async let b = bar() print(await a + b) }
  72. None
  73. None
  74. Case 11: ඇಉظॲཧͷΩϟϯηϧ ඇಉظ API ͷར༻ଆ

  75. Before func downloadData( from url: URL, cancellation: @escaping () ->

    Void, completion: @escaping (Result<Data, Error>) -> Void ) -> DownloadCanceller
  76. Before extension ViewController { @IBAction func downloadButtonPressed(_ sender: UIButton) {

    ɹ ɹ ɹ } }
  77. Before extension ViewController { @IBAction func downloadButtonPressed(_ sender: UIButton) {

    canceller = downloadData(from: url, cancellation: { // Ωϟϯηϧ࣌ͷॲཧ }, completion: { data in do { let data = try data.get() // data Λ࢖͏ॲཧ } catch { // ΤϥʔϋϯυϦϯά } }) } }
  78. Before extension ViewController { @IBAction func cancelButtonPressed(_ sender: UIButton) {

    canceller?.cancel() canceller = nil } }
  79. A"er func downloadData(from url: URL) async throws -> Data

  80. A"er extension ViewController { @IBAction func downloadButtonPressed(_ sender: UIButton) {

    Task { } } }
  81. A"er extension ViewController { @IBAction func downloadButtonPressed(_ sender: UIButton) {

    task = Task { } } }
  82. A"er extension ViewController { @IBAction func cancelButtonPressed(_ sender: UIButton) {

    task?.cancel() task = nil } }
  83. None
  84. A"er extension ViewController { @IBAction func downloadButtonPressed(_ sender: UIButton) {

    task = Task { do { let data = try await downloadData(from: url) // data Λ࢖͏ॲཧ } catch { if Task.isCancelled { // Ωϟϯηϧ࣌ͷॲཧ } else { // ΤϥʔϋϯυϦϯά } } } } }
  85. ! actor

  86. Case 14: ڞ༗͞Εͨঢ়ଶͷมߋ

  87. ڞ༗͞Εͨঢ়ଶΛಉ࣌ʹಡΈॻ͖ ↓ σʔλڝ߹

  88. σʔλڝ߹ͷྫ final class Counter { private var count: Int =

    0 func increment() -> Int { count += 1 return count } }
  89. σʔλڝ߹ͷྫ let counter: Counter = .init() DispatchQueue.global().async { print(counter.increment()) //

    ? } DispatchQueue.global().async { print(counter.increment()) // ? }
  90. σʔλڝ߹ͷྫ let counter: Counter = .init() DispatchQueue.global().async { print(counter.increment()) //

    1 } DispatchQueue.global().async { print(counter.increment()) // 2 }
  91. σʔλڝ߹ͷྫ let counter: Counter = .init() DispatchQueue.global().async { print(counter.increment()) //

    2 } DispatchQueue.global().async { print(counter.increment()) // 1 }
  92. σʔλڝ߹ͷྫ let counter: Counter = .init() DispatchQueue.global().async { print(counter.increment()) //

    2 } DispatchQueue.global().async { print(counter.increment()) // 2 }
  93. σʔλڝ߹ͷྫ final class Counter { private var count: Int =

    0 func increment() -> Int { count += 1 return count } }
  94. σʔλڝ߹Λ๷͙खஈ • ϩοΫ • γϦΞϧΩϡʔ

  95. Before final class Counter { private let queue: DispatchQueue =

    .init(label: UUID().uuidString) private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } }
  96. Before let counter: Counter = .init() counter.increment { count in

    print(count) // 1 or 2 } counter.increment { count in print(count) // 2 or 1 }
  97. actorSE-0306 Ͱղܾ SE-0306 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0306-actors.md

  98. A"er actor Counter { private var count: Int = 0

    func increment() -> Int { count += 1 return count } }
  99. σʔλڝ߹Λى͜͢ Counter final class Counter { private var count: Int

    = 0 func increment() -> Int { count += 1 return count } }
  100. A"er actor Counter { private var count: Int = 0

    func increment() -> Int { count += 1 return count } }
  101. A"er actor Counter { private var count: Int = 0

    func increment() async -> Int }
  102. A"er let counter: Counter = .init() Task.detached { print(await counter.increment())

    // 1 or 2 } Task.detached { print(await counter.increment()) // 2 or 1 }
  103. Case 15: ڞ༗͞Εͨঢ়ଶͷมߋ Πϯελϯε಺Ͱͷϝιουݺͼग़͠

  104. Counter ͷத͔Β Counter ͷϝιουΛ࢖͏ final class Counter { ... func

    increment(completion: @escaping (Int) -> Void) func incrementTwice(completion: @escaping (Int) -> Void) { } }
  105. Counter ͷத͔Β Counter ͷϝιουΛ࢖͏ final class Counter { ... func

    increment(completion: @escaping (Int) -> Void) func incrementTwice(completion: @escaping (Int) -> Void) { increment { [self] _ in increment { count in completion(count) } } } }
  106. ڝ߹ঢ়ଶͷྫ let counter: Counter = .init() counter.incrementTwice { count in

    print(count) // ? } counter.incrementTwice { count in print(count) // ? }
  107. ڝ߹ঢ়ଶͷྫ let counter: Counter = .init() counter.incrementTwice { count in

    print(count) // 2 } counter.incrementTwice { count in print(count) // 4 }
  108. ڝ߹ঢ়ଶͷྫ let counter: Counter = .init() counter.incrementTwice { count in

    print(count) // 3 } counter.incrementTwice { count in print(count) // 4 }
  109. ڝ߹ঢ়ଶͷྫ final class Counter { ... func increment(completion: @escaping (Int)

    -> Void) func incrementTwice(completion: @escaping (Int) -> Void) { increment { [self] _ in increment { count in completion(count) } } } }
  110. Before final class Counter { ... private func _increment() ->

    Int { count += 1 return count } func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in _increment() completion(count) } } ... }
  111. Before final class Counter { ... func incrementTwice(completion: @escaping (Int)

    -> Void) { queue.async { [self] in _increment() _increment() completion(count) } } }
  112. A"er actor Counter { ... @discardableResult func increment() -> Int

    { count += 1 return count } func incrementTwice() -> Int { increment() increment() return count } }
  113. Case 16: ڞ༗͞Εͨঢ়ଶͷมߋ ge#er

  114. Case 14 (A*er) actor Counter { private var count: Int

    = 0 @discardableResult func increment() -> Int { count += 1 return count } }
  115. A"er actor Counter { var count: Int = 0 @discardableResult

    func increment() -> Int { count += 1 return count } }
  116. Effec%ul Read-only ϓϩύςΟSE-0310 class Foo { var value : Int

    { get { ... } } } SE-0310 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0310-effecAul-readonly-proper=es.md
  117. Effec%ul Read-only ϓϩύςΟSE-0310 class Foo { var value : Int

    { get async throws { ... } } } SE-0310 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0310-effecAul-readonly-proper=es.md
  118. A"er actor Counter { var count: Int { get async

    { ... } } ... }
  119. A"er print(await counter.count)

  120. Case 18: ڞ༗͞Εͨঢ়ଶͷมߋ ඇಉظॲཧ݁Ռͷ൓ө

  121. ObservableObject ͷಋೖ final class UserViewController: UIViewController { private let state:

    UserViewState ... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) state.loadUser() } }
  122. ObservableObject ͷಋೖ final class UserViewController: UIViewController { ... override func

    viewDidLoad() { ... state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in // state Λ View ʹ൓ө͢Δॲཧ } .store(in: &cancellables) } ... }
  123. A"er actor UserViewState: ObservableObject { let userID: User.ID @Published var

    user: User? ... func loadUser() async { do { user = try await fetchUser(for: userID) } catch { // ΤϥʔϋϯυϦϯά } } }
  124. A"er final class UserViewController: UIViewController { ... override func viewDidAppear(_

    animated: Bool) { super.viewDidAppear(animated) state.loadUser() // } }
  125. A"er final class UserViewController: UIViewController { ... override func viewDidAppear(_

    animated: Bool) { super.viewDidAppear(animated) Task { await state.loadUser() // } } }
  126. A"er state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in

    // state Λ View ʹ൓ө͢Δॲཧ } .store(in: &cancellables)
  127. A"er state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in

    // state Λ View ʹ൓ө͢Δॲཧ let user = await state.user nameLabel.text = user?.name } .store(in: &cancellables)
  128. A"er state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in

    // state Λ View ʹ൓ө͢Δॲཧ Task { let user = await state.user nameLabel.text = user?.name } } .store(in: &cancellables)
  129. Case 19: ΞΫλʔڥքΛӽ͑Δ

  130. let user = await state.user

  131. struct User { let id: ID var name: String var

    age: Int ... }
  132. final class User { let id: ID var name: String

    var age: Int ... }
  133. let user = await state.user // user?.age += 1 //

    actor ͷ֎͔Βมߋ
  134. final class User { let id: ID let name: String

    let age: Int ... }
  135. let user = await state.user // user?.age += 1 //

  136. Sendable ϓϩτίϧSE-0302 SE-0302 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0302-concurrent-value-and-concurrent- closures.md

  137. struct User { let id: ID var name: String var

    age: Int ... }
  138. struct User: Sendable { let id: ID var name: String

    var age: Int ... }
  139. final class User: Sendable { let id: ID var name:

    String var age: Int ... } ⛔ var ϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  140. final class User: Sendable { let id: ID let name:

    String let age: Int ... } ✅ ΠϛϡʔλϒϧͳͷͰ OK
  141. final class User: Sendable { let id: ID let name:

    NSString let age: Int ... } ⛔ Sendable Ͱͳ͍ܕͷϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  142. final class User: Sendable { let id: ID let firstName:

    String let familyName: String let age: Int var name: String { "\(firstName) \(familyName)" } ... } ✅ Computed Property Λ͍࣋ͬͯͯ΋ΠϛϡʔλϒϧͳͷͰ OK
  143. final class User: Sendable { let id: ID let firstName:

    String let familyName: String let age: Int private var _name: String? // var name: String { if _name == nil { _name = "\(firstName) \(familyName)" } return _name! } ... }
  144. final class User: @unchecked Sendable { let id: ID let

    firstName: String let familyName: String let age: Int private var _name: String? // var name: String { if _name == nil { _name = "\(firstName) \(familyName)" } return _name! } ... }
  145. ϓϩτίϧʹద߹ͤ͞ΒΕͳ͍৔߹͸ʁ

  146. actor Foo { func bar(_ f: (String) -> Int) {

    // // ... } }
  147. actor Foo { func bar(_ f: @Sendable (String) -> Int)

    { // // ... } } Ϋϩʔδϟ͸ϓϩτίϧʹద߹Ͱ͖ͳ͍ͷͰ @Sendable Λ࢖͏
  148. Case 20: ڞ༗͞Εͨঢ়ଶͷมߋ ϝΠϯεϨου্Ͱͷॲཧ

  149. UserViewState ͷΩϡʔ͕ DispatchQueue.main ͩͱ͏Ε͍͠

  150. UserViewState ͷΩϡʔ͕ DispatchQueue.main final class UserViewState: ObservableObject { private let

    queue: DispatchQueue = .main ... }
  151. Case 18 (A*er) // queue ͸Πϯελϯε͝ͱʹࣗಈͰ࡞ΒΕΔ actor UserViewState: ObservableObject {

    ... }
  152. Global ActorSE-0316 Ͱܕ΍ ΠϯελϯεΛӽ͑ͨ Actor Λ࡞Δ SE-0316 h*ps:/ /github.com/apple/swi<-evolu>on/blob/main/proposals/0316-global-actors.md

  153. A"er @MainActorɹ final class UserViewState: ObservableObject { ... }

  154. UIViewController h/ps:/ /developer.apple.com/documenta;on/uikit/uiviewcontroller

  155. Case 18 (A*er) override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated)

    Task { await state.loadUser() } }
  156. A"er override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) state.loadUser() }

  157. Case 18 (A*er) state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self]

    _ in // state Λ View ʹ൓ө͢Δॲཧ Task { let user = await state.user nameLabel.text = user?.name } } .store(in: &cancellables)
  158. A"er state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in

    // state Λ View ʹ൓ө͢Δॲཧ nameLabel.text = state.user?.name } .store(in: &cancellables)
  159. @MainActor ͸ @StateObject ʹ΋ඞਢ struct UserView: View { @StateObject private

    var state: UserViewState ... var body: some View { VStack { if let user = state.user { Text(user.name) } ... } .onAppear { state.loadUser() } } }
  160. ʲ࿕ใʳSwi$ Concurrency ͸ iOS 13 Ҏ߱Ͱ ͋Ε͹ར༻Մೳʂ#39051 #39051 h)ps:/ /github.com/apple/swi;/pull/39051

  161. ·ͱΊ • Concurrency ͰίʔυΛॻ͘ͷָ͕ʹͳΔ • ͔͠΋ɺΑΓ҆શʹʂ • ࠓ݄͔Β࢖͑Δʂʂʂ

  162. h"ps:/ /zenn.dev/koher/ar1cles/swi6- concurrency-cheatsheet