Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Swift Zoomin' #8

Swift Zoomin' #8

Yuta Koshizawa

October 23, 2021
Tweet

More Decks by Yuta Koshizawa

Other Decks in Programming

Transcript

  1. ίʔϧόοΫʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL, completion: @escaping (Data)

    -> Void) // ར༻ downloadData(from: url) { data in // data Λ࢖͏ॲཧ }
  2. async/await ʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL) async ->

    Data // ར༻ let data = await downloadData(from: url) // data Λ࢖͏ॲཧ
  3. ίʔϧόοΫʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL, completion: @escaping (Result<Data,

    Error>) -> Void) // ར༻ downloadData(from: url) { result in do { let data = try result.get() // data Λ࢖͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ } }
  4. async/await ʹΑΔඇಉظؔ਺ // એݴ func downloadData(from url: URL) async throws

    -> Data // ར༻ do { let data = try await downloadData(from: url) // data Λ࢖͏ॲཧ } catch { /* ΤϥʔϋϯυϦϯά */ }
  5. Q1: downloadData ίʔϧόοΫͰ݁ՌΛฦ͢ downloadData ؔ਺͕ Download.swi, ʹ࣮૷͞Ε͍ͯ·͢ɻ async/await Λར༻͠ ͯɺ໭Γ஋Ͱ݁ՌΛฦ͢

    downloadData ؔ਺Λ࣮૷ͯ͠Լ͞ ͍ɻ ! iOS 15 ͔Β URLSession ʹ௥Ճ͞Εͨ࣍ͷϝιου͕໾ʹཱ ͪ·͢ɻ1 func data(from url: URL) async throws -> (Data, URLResponse) 1 h$ps:/ /developer.apple.com/documenta6on/founda6on/urlsession/3767353-data
  6. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍ func foo() async func bar() async

    { await foo() // ✅ OK } func baz() async { await bar() // ✅ OK } ...
  7. Task.init 2 @discardableResult init( priority: TaskPriority? = nil, operation: @escaping

    @Sendable () async throws -> Success ) 2 h$ps:/ /developer.apple.com/documenta6on/swi9/task/3856790-init
  8. Q2: UserViewController.viewDidAppear UserViewController ͸ User ͷ৘ใΛऔಘͯ͠දࣔ͢Δ View Controller Ͱ͢ɻ viewDidAppear

    ͷதͰ downloadData ؔ਺Λ ࢖͍ɺ User ͱΞΠίϯΛऔಘͯ͠දࣔ͠·͢ɻ ͜ΕΛɺ async/await Ͱ࣮૷͞Εͨ downloadData ؔ਺Λར ༻ͯ͠࠶࣮૷ͯ͠Լ͍͞ɻ
  9. Χ΢ϯλʔͷσʔλڝ߹ final class Counter { private var count: Int =

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

    0 func increment() -> Int { count += 1 Thread.sleep(forTimeInterval: 1.0) return count } }
  11. final class Counter { private var count: Int = 0

    func increment() -> Int { let count = self.count Thread.sleep(forTimeInterval: 1.0) self.count = count + 1 return self.count } }
  12. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ final class Counter { private let queue: DispatchQueue =

    .init(label: "Counter") private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } }
  13. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { counter.increment {

    print($0) } // 1 } Task { counter.increment { print($0) } // 2 }
  14. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ final class Counter { private let queue: DispatchQueue =

    .init(label: "Counter") private var count: Int = 0 func increment(completion: @escaping (Int) -> Void) { queue.async { [self] in count += 1 completion(count) } } }
  15. Actor ʹΑΔσʔλڝ߹ͷղফ final class Counter { private var count: Int

    = 0 func increment() -> Int { count += 1 return count } }
  16. Actor ʹΑΔσʔλڝ߹ͷղফ actor Counter { private var count: Int =

    0 func increment() -> Int { count += 1 return count } }
  17. Actor ʹΑΔσʔλڝ߹ͷղফ let counter: Counter = .init() Task { print(await

    counter.increment()) } Task { print(await counter.increment()) }
  18. Q3: BankAccount.deposit ࣍ͷΑ͏ͳ BankAccount ΫϥεΛ࣮૷͠ɺฒߦʹ༬ۚ͢Δ͜ͱ Ͱσʔλڝ߹͕ൃੜ͢Δ͜ͱΛ֬ೝͯ͠Լ͍͞ɻͦͷޙɺ BankAccount Λ actor ʹมߋ͠ɺσʔλڝ߹͕๷ࢭ͞ΕΔ͜ͱ

    Λ֬ೝͯ͠Լ͍͞ɻ final class BankAccount { // ۜߦޱ࠲ var balance: Int = 0 // ༬ۚ࢒ߴ func deposit(_ amount: Int) -> Int // ೖۚʢ໭Γ஋͸࢒ߴʣ }
  19. Actor ͷಉҰΠϯελϯε಺Ͱͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar() { foo() // ಉظ } } await foo() // ඇಉظ
  20. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    } actor B { func bar(a: A) async { await a.foo() // ඇಉظ } }
  21. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
  22. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { self.foo() // ಉظ await a.foo() // ඇಉظ } }
  23. Actor ͷΠϯελϯεؒͰͷϝιουݺͼग़͠ actor A { func foo() { ... }

    func bar(a: A) async { foo() // ಉظ await a.foo() // ඇಉظ } }
  24. Q5: BankAccount.transfer BankAccount ʹ࣍ͷೋͭͷϝιουΛ௥Ճͯ͠Լ͍͞ɻ • ޱ࠲͔Βग़ۚ͢ΔͨΊͷ withdraw ϝιου • ޱ࠲ؒͰૹۚ͢ΔͨΊͷ

    transfer ϝιου // ࢒ߴ͕଍Γͳ͍৔߹͸ΤϥʔΛ throw func withdraw(_ amount: Int) throws -> Int // ໭Γ஋͸࢒ߴ func transfer(_ amount: Int, to account: BankAccount) async throws
  25. actor A { let user: User = ... } let

    a: A = ... Task { let user = await a.user user.age += 1 // ⛔ } Actor ͷΩϡʔͷ֎Ͱ Actor ͷ಺෦ঢ়ଶΛมߋͰ͖ͯ͠·͏
  26. final class User: Sendable { let id: ID var name:

    String var age: Int ... } ⛔ ࢀরܕͰ var ϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  27. struct User { let id: ID var name: String var

    age: Int ... } public Ͱͳ͍஋ܕ͸҉໧తʹద߹4 4 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0302-concurrent-value-and-concurrent- closures.md#implicit-structenum-conformance-to-sendable
  28. public struct User: Sendable { public let id: ID public

    var name: String public var age: Int ... } public ͩͱ໌ࣔతʹద߹ͤ͞Δඞཁ͋Γ
  29. struct User: Sendable { let id: ID var name: NSString

    var age: Int ... } ⛔ Sendable Ͱͳ͍ϝϯόʔΛ࣋ͭͷͰίϯύΠϧΤϥʔ
  30. final class User: Sendable { let id: ID let name:

    String let age: Int ... } ✅ ࢀরܕͰ΋ΠϛϡʔλϒϧͳΒ OK
  31. final class User: Sendable { let id: ID let firstName:

    String let familyName: String let age: Int var name: String { "\(firstName) \(familyName)" } ... } ✅ Computed Property Λ͍࣋ͬͯͯ΋ΠϛϡʔλϒϧͳͷͰ OK
  32. 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! } ... }
  33. 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! } ... }
  34. actor Counter { private var count: Int = 0 func

    increment() -> Int { count += 1 return count } } actor ͸҉໧తʹ Sendable ʹద߹
  35. actor Foo { func bar(_ f: @Sendable (String) -> Int)

    { // // ... } } Ϋϩʔδϟ͸ϓϩτίϧʹద߹Ͱ͖ͳ͍ͷͰ @Sendable Λ࢖͏
  36. final class User { var name: String ... } let

    user: User = ... Task { let name = user.name // } @Sendable Ϋϩʔδϟ͸ Sendable ͳ஋͔͠ΩϟϓνϟͰ͖ͳ ͍ʢ Task.init ʹ౉͢Ϋϩʔδϟ͸ @Sendable ʣ
  37. var a = 42 Task { print(a) // ⛔ }

    @Sendable Ϋϩʔδϟ͸ม਺ΛΩϟϓνϟͰ͖ͳ͍
  38. var a = 42 Task { [a] in print(a) //

    ✅ } by-value Ωϟϓνϟ͸Մೳ
  39. @nonescaping, non-@Sendable ʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(values:

    [Int]) { // A values.forEach { value in // A } } }
  40. @escaping, non-@Sendableʢ context ΛҾ͖ܧ͙ʣ actor A { func foo(value: Int)

    { // A let square: (Int) -> Int = { // @escaping // A $0 * $0 } print(square(value)) print(square(square(value))) } }
  41. @Sendable ʢ context ΛҾ͖ܧ͕ͳ͍ʣ actor A { func foo() {

    // A let foo: Foo = .init() // Foo: Sendable DispatchQueue.global().async { [foo] in // @Sendable // not A foo.value = 2 } foo.value = 3 print(foo.value) } } ※ͨͩ͠ɺ Swi% 5.5 ݱࡏ͸ @Sendable Ͱͳ͍
  42. @_inheritActorContext extension Task where Failure == Never { ... @discardableResult

    @_alwaysEmitIntoClient public init( priority: TaskPriority? = nil, @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Success ) { ... } } 10 h%ps:/ /github.com/apple/swi7/blob/main/stdlib/public/Concurrency/Task.swi7
  43. Task.detachedʢ context ΛҾ͖ܧ͕ͳ͍ʣ actor A { func foo() { //

    A Task.detached { // @Sendable // not A } } }
  44. override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task { do

    { // User ͷ JSON ͷऔಘ let url: URL = ... let data = try await downloadData(from: url) ... // View ΁ͷ൓ө iconImageView.image = iconImage } catch { // ΤϥʔϋϯυϦϯά print(error) } } }
  45. UserViewState final class UserViewState { let id: User.ID @Published private(set)

    var user: User? @Published private(set) var iconImage: UIImage? init(id: User.ID) { self.id = id } }
  46. Q6: UserViewState UserViewState ʹ loadUser ϝιουΛ௥Ճ͠ɺϢʔβʔ͓Α ͼϢʔβʔΞΠίϯΛऔಘ͢ΔίʔυΛ UserViewController ͔Β෼཭ͯ͠Լ͍͞ɻ UserViewController

    ʹ͸ UserViewState ܕͷ state ϓϩύςΟΛ࣋ͨͤɺ state ͷঢ় ଶΛ View ʹ൓ө͢ΔΑ͏ʹͯ͠Լ͍͞ɻͳ͓ɺ͜ͷ໰୊͸ Actor ʹؔ܎͋Γ·ͤΜɻ ! state.user Ͱ͸ͳ͘ state.$user ͱॻ͘͜ͱͰɺ user Λ Publisher ͱͯ͠औಘ͢Δ͜ͱ͕Ͱ͖·͢ɻ
  47. loadUser Ͱى͜ΓಘΔσʔλڝ߹ let data = try await downloadData(from: url) let

    user = try JSONDecoder().decode(User.self, from: data) self.user = user // ͕͜͜ಉ࣌ʹ࣮ߦ͞ΕΔͱʁ
  48. Publisher.values 5 for await value in publisher.values { ... }

    5 h$ps:/ /developer.apple.com/documenta6on/combine/publisher/values-1dm9r
  49. AsyncSequence 6 protocol AsyncSequence { associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element

    == Element associatedtype Element func makeAsyncIterator() -> AsyncIterator } protocol AsyncIteratorProtocol { associatedtype Element mutating func next() async throws -> Element? } 6 h$ps:/ /github.com/apple/swi6-evolu9on/blob/main/proposals/0298-asyncsequence.md
  50. @MainActor @MainActor func foo() { // isolated // ϝΠϯεϨου }

    @MainActor ͕෇༩͞Εͨؔ਺΍ϝιουɺϓϩύςΟ౳͸ MainActor ʹ actor-isolated
  51. @MainActor @MainActor final class Foo { var value: Int =

    42 // isolated func a() { // isolated // ϝΠϯεϨου } } @MainActor ͕෇༩͞ΕͨܕͷϝιουɺϓϩύςΟ౳͸ MainActor ʹ actor-isolated
  52. UserViewState Λ ObservableObject ʹ • user ΍ iconImage Λݸผʹߪಡ͢ΔͷͰ͸ͳ͘ɺ objectWillChange9

    Λߪಡ͢Δ 9 h$ps:/ /developer.apple.com/documenta6on/combine/observableobject/objectwillchange-2oa5v
  53. Q11: Swi(UI UserViewController ͱಉ༷ͷݟͨ໨ɾڍಈΛ࣮ݱ͢Δ UserView Λ Swi%UI Ͱ࣮૷ͯ͠Լ͍͞ɻ ! @StateObject

    Ͱ UserViewState Λอ࣋͠·͢ɻ @StateObject private var state: UserViewState