Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Swi$ 5.5

Slide 3

Slide 3 text

Concurrency ʢฒߦॲཧʣ

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Proposal: 12 ຊ WWDC: 9 ຊ

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

21 ݸͷέʔε

Slide 10

Slide 10 text

Concurrency ! async / await ! Structured Concurrency ! actor

Slide 11

Slide 11 text

! async / await

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

async / await0296 0296 h'ps:/ /github.com/apple/swi9-evolu

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

A"er func downloadData(from url: URL) async -> Data let data = await downloadData(from: url) // data Λ࢖͏ॲཧ

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

A ͱ B ͕ಉ͡εϨουͰ࣮ߦ͞ΕΔͱ͸ݶΒͳ͍ func downloadData(from url: URL, completion: @escaping (Data) -> Void) print("A") downloadData(from: url) { data in print("B") }

Slide 20

Slide 20 text

Case 3: ඇಉظؔ਺ͷར༻ ΤϥʔϋϯυϦϯά͕͋Δ৔߹

Slide 21

Slide 21 text

Case 1 (Before) func downloadData(from url: URL, completion: @escaping (Data) -> Void) downloadData(from: url) { data in // data Λ࢖͏ॲཧ }

Slide 22

Slide 22 text

Before func downloadData(from url: URL, completion: @escaping (Result) -> Void) downloadData(from: url) { result in do { let data = try result.get() // data Λ࢖͏ॲཧ } catch { // ΤϥʔϋϯυϦϯά } }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Case 4: ඇಉظؔ਺ͷ࣮૷ ΤϥʔϋϯυϦϯά͕͋Δ৔߹

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Before func fetchUser(for id: User.ID, completion: @escaping (Result) -> 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)) } }

Slide 29

Slide 29 text

Before func fetchUser(for id: User.ID, completion: @escaping (Result) -> 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)) } } }

Slide 30

Slide 30 text

Before func fetchUser(for id: User.ID, completion: @escaping (Result) -> 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)) } } }

Slide 31

Slide 31 text

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 }

Slide 32

Slide 32 text

Case 5: ඇಉظؔ਺ͷ࿈݁

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Before func fetchUserIcon(for id: User.ID, completion: @escaping (Result) -> 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)) } } }

Slide 36

Slide 36 text

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 }

Slide 37

Slide 37 text

Case 6: ίʔϧόοΫ͔Β async ΁ͷม׵

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Con$nua$onSE-0300 SE-0300 h(ps:/ /github.com/apple/swi:-evolu

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

// ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result) -> 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) } } } }

Slide 43

Slide 43 text

// ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result) -> 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) } } } }

Slide 44

Slide 44 text

// ίʔϧόοΫ func downloadData(from url: URL, completion: @escaping (Result) -> Void) // async func downloadData(from url: URL) async throws -> Data { try await withCheckedThrowingContinuation { continuation in downloadData(from: url) { result in continuation.resume(with: result) } } }

Slide 45

Slide 45 text

! Structured Concurrency

Slide 46

Slide 46 text

Case 7: ඇಉظॲཧͷ։࢝

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

ඇಉظॲཧͷ։࢝ func main() { // ??? }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

ඇಉظॲཧͷ։࢝ func main() { Task { await foo() } } ͢΂ͯͷ async ؔ਺͸ Task ্Ͱ࣮ߦ͞ΕΔɻ

Slide 54

Slide 54 text

iOS ΞϓϦ։ൃͰͲͷΑ͏ʹ࢖ΘΕΔ͔ • viewDidAppear • View Controller ͕දࣔ͞ΕͨΒ fetchUser Ͱ User Λऔಘ ͯ͠දࣔ • @IBAction • μ΢ϯϩʔυϘλϯ͕ԡ͞ΕͨΒ downloadData Ͱμ΢ϯ ϩʔυ • ...

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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 { // ΤϥʔϋϯυϦϯά } } } }

Slide 58

Slide 58 text

Case 9: ฒߦॲཧ ݻఆݸ਺ͷ৔߹

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

͜ΕͰ͸ฒߦʹͳΒͳ͍ 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 }

Slide 63

Slide 63 text

async let BindingSE-0317 SE-0317 h*ps:/ /github.com/apple/swi<-evolu>on/blob/main/proposals/0317-async-let.md

Slide 64

Slide 64 text

͜ΕͰ͸ฒߦʹͳΒͳ͍ 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 }

Slide 65

Slide 65 text

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 }

Slide 66

Slide 66 text

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 ͞Εͳ͚Ε͹ͳΒͳ͍ɻ

Slide 67

Slide 67 text

Structured ConcurrencySE-0304 SE-0304 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0304-structured-concurrency.md

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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 ͞Εͳ͚Ε͹ͳΒͳ͍ɻ

Slide 71

Slide 71 text

async let ͱ Task Task { async let a = foo() async let b = bar() print(await a + b) }

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

Case 11: ඇಉظॲཧͷΩϟϯηϧ ඇಉظ API ͷར༻ଆ

Slide 75

Slide 75 text

Before func downloadData( from url: URL, cancellation: @escaping () -> Void, completion: @escaping (Result) -> Void ) -> DownloadCanceller

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

Before extension ViewController { @IBAction func cancelButtonPressed(_ sender: UIButton) { canceller?.cancel() canceller = nil } }

Slide 79

Slide 79 text

A"er func downloadData(from url: URL) async throws -> Data

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

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 { // ΤϥʔϋϯυϦϯά } } } } }

Slide 85

Slide 85 text

! actor

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

σʔλڝ߹ͷྫ final class Counter { private var count: Int = 0 func increment() -> Int { count += 1 return count } }

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

σʔλڝ߹ͷྫ final class Counter { private var count: Int = 0 func increment() -> Int { count += 1 return count } }

Slide 94

Slide 94 text

σʔλڝ߹Λ๷͙खஈ • ϩοΫ • γϦΞϧΩϡʔ

Slide 95

Slide 95 text

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) } } }

Slide 96

Slide 96 text

Before let counter: Counter = .init() counter.increment { count in print(count) // 1 or 2 } counter.increment { count in print(count) // 2 or 1 }

Slide 97

Slide 97 text

actorSE-0306 Ͱղܾ SE-0306 h)ps:/ /github.com/apple/swi;-evolu=on/blob/main/proposals/0306-actors.md

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

A"er actor Counter { private var count: Int = 0 func increment() async -> Int }

Slide 102

Slide 102 text

A"er let counter: Counter = .init() Task.detached { print(await counter.increment()) // 1 or 2 } Task.detached { print(await counter.increment()) // 2 or 1 }

Slide 103

Slide 103 text

Case 15: ڞ༗͞Εͨঢ়ଶͷมߋ Πϯελϯε಺Ͱͷϝιουݺͼग़͠

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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) } } } }

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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) } } ... }

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

Case 16: ڞ༗͞Εͨঢ়ଶͷมߋ ge#er

Slide 114

Slide 114 text

Case 14 (A*er) actor Counter { private var count: Int = 0 @discardableResult func increment() -> Int { count += 1 return count } }

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

A"er actor Counter { var count: Int { get async { ... } } ... }

Slide 119

Slide 119 text

A"er print(await counter.count)

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

ObservableObject ͷಋೖ final class UserViewController: UIViewController { ... override func viewDidLoad() { ... state .objectWillChange .receive(on: DispatchQueue.main) .sink { [self] _ in // state Λ View ʹ൓ө͢Δॲཧ } .store(in: &cancellables) } ... }

Slide 123

Slide 123 text

A"er actor UserViewState: ObservableObject { let userID: User.ID @Published var user: User? ... func loadUser() async { do { user = try await fetchUser(for: userID) } catch { // ΤϥʔϋϯυϦϯά } } }

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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)

Slide 128

Slide 128 text

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)

Slide 129

Slide 129 text

Case 19: ΞΫλʔڥքΛӽ͑Δ

Slide 130

Slide 130 text

let user = await state.user

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

let user = await state.user // user?.age += 1 // actor ͷ֎͔Βมߋ

Slide 134

Slide 134 text

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

Slide 135

Slide 135 text

let user = await state.user // user?.age += 1 // ⛔

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

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

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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! } ... }

Slide 144

Slide 144 text

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! } ... }

Slide 145

Slide 145 text

ϓϩτίϧʹద߹ͤ͞ΒΕͳ͍৔߹͸ʁ

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

actor Foo { func bar(_ f: @Sendable (String) -> Int) { // // ... } } Ϋϩʔδϟ͸ϓϩτίϧʹద߹Ͱ͖ͳ͍ͷͰ @Sendable Λ࢖͏

Slide 148

Slide 148 text

Case 20: ڞ༗͞Εͨঢ়ଶͷมߋ ϝΠϯεϨου্Ͱͷॲཧ

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

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

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

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

Slide 155

Slide 155 text

Case 18 (A*er) override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task { await state.loadUser() } }

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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)

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

@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() } } }

Slide 160

Slide 160 text

ʲ࿕ใʳSwi$ Concurrency ͸ iOS 13 Ҏ߱Ͱ ͋Ε͹ར༻Մೳʂ#39051 #39051 h)ps:/ /github.com/apple/swi;/pull/39051

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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