$30 off During Our Annual Pro Sale. View Details »

Swift Zoomin' #8

Swift Zoomin' #8

Yuta Koshizawa

October 23, 2021
Tweet

More Decks by Yuta Koshizawa

Other Decks in Programming

Transcript

  1. Swi$ Zoomin' #8
    Actor ϋϯζΦϯ

    View Slide

  2. Actor ͱ͸

    View Slide

  3. ɾɾɾͷલʹ

    View Slide

  4. Actor ͸ async/await Λଟ༻͢Δ

    View Slide

  5. async/await ͷ؆୯ͳ෮श

    View Slide

  6. ίʔϧόοΫʹΑΔඇಉظؔ਺
    // એݴ
    func downloadData(from url: URL,
    completion: @escaping (Data) -> Void)
    // ར༻
    downloadData(from: url) { data in
    // data Λ࢖͏ॲཧ
    }

    View Slide

  7. async/await ʹΑΔඇಉظؔ਺
    // એݴ
    func downloadData(from url: URL) async -> Data
    // ར༻
    let data = await downloadData(from: url)
    // data Λ࢖͏ॲཧ

    View Slide

  8. ίʔϧόοΫʹΑΔඇಉظؔ਺
    // એݴ
    func downloadData(from url: URL,
    completion: @escaping (Result) -> Void)
    // ར༻
    downloadData(from: url) { result in
    do {
    let data = try result.get()
    // data Λ࢖͏ॲཧ
    } catch { /* ΤϥʔϋϯυϦϯά */ }
    }

    View Slide

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

    View Slide

  10. ࿅श໰୊༻ϦϙδτϦ
    h"ps:/
    /github.com/koher/swi4-zoomin-8

    View Slide

  11. 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

    View Slide

  12. Q1: ղ౴ྫ

    View Slide

  13. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍
    func foo() async
    func bar() async {
    await foo() //

    OK
    }

    View Slide

  14. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍
    func foo() async
    func bar() {
    await foo() //

    NG
    }

    View Slide

  15. async ؔ਺͸ async ؔ਺ͷதͰ͔͠ݺͼग़ͤͳ͍
    func foo() async
    func bar() async {
    await foo() //

    OK
    }
    func baz() async {
    await bar() //

    OK
    }
    ...

    View Slide

  16. async ؔ਺Λ࠷ॳʹݺͼग़͢ͱ͖͸ʁ

    View Slide

  17. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠
    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    await foo() //

    NG
    }

    View Slide

  18. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠
    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    Task {
    await foo() //

    OK
    }
    }

    View Slide

  19. ಉظؔ਺͔Βͷ async ؔ਺ͷݺͼग़͠
    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    Task(operation: {
    await foo() //

    OK
    })
    }

    View Slide

  20. 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

    View Slide

  21. Q2: UserViewController.viewDidAppear
    UserViewController ͸ User ͷ৘ใΛऔಘͯ͠දࣔ͢Δ View
    Controller Ͱ͢ɻ viewDidAppear ͷதͰ downloadData ؔ਺Λ
    ࢖͍ɺ User ͱΞΠίϯΛऔಘͯ͠දࣔ͠·͢ɻ
    ͜ΕΛɺ async/await Ͱ࣮૷͞Εͨ downloadData ؔ਺Λར
    ༻ͯ͠࠶࣮૷ͯ͠Լ͍͞ɻ

    View Slide

  22. Q2: ղ౴ྫ

    View Slide

  23. Actor ͱ͸

    View Slide

  24. Actor ͸ΫϥεʹࣅͨࢀরܕͰɺσʔλڝ߹
    Λ๷͙ͨΊͷϘΠϥʔϓϨʔτ͕ෆཁʹͳ
    Δɻ
    ͦͷͨΊɺίʔυ͕؆͔ܿͭ҆શʹͳΔɻ

    View Slide

  25. σʔλڝ߹ͱ͸

    View Slide

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

    View Slide

  27. Χ΢ϯλʔͷσʔλڝ߹
    let counter: Counter = .init()
    Task {
    print(counter.increment()) // ?
    }
    Task {
    print(counter.increment()) // ?
    }

    View Slide

  28. Χ΢ϯλʔͷσʔλڝ߹
    let counter: Counter = .init()
    Task {
    print(counter.increment()) // 1
    }
    Task {
    print(counter.increment()) // 2
    }

    View Slide

  29. Χ΢ϯλʔͷσʔλڝ߹
    let counter: Counter = .init()
    Task {
    print(counter.increment()) // 2
    }
    Task {
    print(counter.increment()) // 1
    }

    View Slide

  30. Χ΢ϯλʔͷσʔλڝ߹
    let counter: Counter = .init()
    Task {
    print(counter.increment()) // 2
    }
    Task {
    print(counter.increment()) // 2
    }

    View Slide

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

    View Slide

  32. Χ΢ϯλʔͷσʔλڝ߹
    let counter: Counter = .init()
    Task {
    print(counter.increment()) // 1
    }
    Task {
    print(counter.increment()) // 1
    }

    View Slide

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

    View Slide

  34. σʔλڝ߹΁ͷରԠํ๏
    • ϩοΫ
    • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ

    View Slide

  35. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ
    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)
    }
    }
    }

    View Slide

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

    View Slide

  37. γϦΞϧΩϡʔʹΑΔσʔλڝ߹ͷղফ
    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)
    }
    }
    }

    View Slide

  38. σʔλڝ߹΁ͷରԠํ๏
    • ϩοΫ
    • ʢ DispatchQueue ͷʣγϦΞϧΩϡʔ
    • Actor

    View Slide

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

    View Slide

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

    View Slide

  41. Actor ʹΑΔσʔλڝ߹ͷղফ
    let counter: Counter = .init()
    Task {
    print(await counter.increment())
    }
    Task {
    print(await counter.increment())
    }

    View Slide

  42. Q3: BankAccount.deposit
    ࣍ͷΑ͏ͳ BankAccount ΫϥεΛ࣮૷͠ɺฒߦʹ༬ۚ͢Δ͜ͱ
    Ͱσʔλڝ߹͕ൃੜ͢Δ͜ͱΛ֬ೝͯ͠Լ͍͞ɻͦͷޙɺ
    BankAccount Λ actor ʹมߋ͠ɺσʔλڝ߹͕๷ࢭ͞ΕΔ͜ͱ
    Λ֬ೝͯ͠Լ͍͞ɻ
    final class BankAccount { // ۜߦޱ࠲
    var balance: Int = 0 // ༬ۚ࢒ߴ
    func deposit(_ amount: Int) -> Int // ೖۚʢ໭Γ஋͸࢒ߴʣ
    }

    View Slide

  43. Q3: ղ౴ྫ

    View Slide

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

    View Slide

  45. Q4: BankAccount.getInterest
    BankAccount ʹ getInterest ϝιουΛ࣮૷ͯ͠Լ͍͞ɻ
    getInterest ϝιου͸࣍ͷΑ͏ͳγάωνϟΛ࣋ͪɺҾ਺Ͱ
    ར཰Λड͚औΓɺͦΕΛݩʹརଉΛܭࢉͯ͠༬ۚ࢒ߴΛมಈͤ͞
    ্ͨͰɺ৽͍͠༬ۚ࢒ߴΛฦ͢΋ͷͱ͠·͢ɻͨͩ͠ɺ༬ۚ࢒ߴ
    ͷมಈʹ͸ deposit ϝιουΛར༻͢Δ΋ͷͱ͠·͢ɻ
    func getInterest(with rate: Double) -> Int

    View Slide

  46. Q4: ղ౴ྫ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. Q5: BankAccount.transfer
    BankAccount ʹ࣍ͷೋͭͷϝιουΛ௥Ճͯ͠Լ͍͞ɻ
    • ޱ࠲͔Βग़ۚ͢ΔͨΊͷ withdraw ϝιου
    • ޱ࠲ؒͰૹۚ͢ΔͨΊͷ transfer ϝιου
    // ࢒ߴ͕଍Γͳ͍৔߹͸ΤϥʔΛ throw
    func withdraw(_ amount: Int) throws -> Int // ໭Γ஋͸࢒ߴ
    func transfer(_ amount: Int, to account: BankAccount) async throws

    View Slide

  52. Q5: ղ౴ྫ

    View Slide

  53. Sendable

    View Slide

  54. Sendable 3
    actor ڥքΛ௒͑ͯ΍ΓͱΓͰ͖Δ͜ͱΛද͢ϓϩτίϧ
    3 h$ps:/
    /github.com/apple/swi6-evolu9on/blob/main/proposals/0302-concurrent-value-and-concurrent-
    closures.md

    View Slide

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

    View Slide

  56. actor A {
    let user: User = ...
    }
    let a: A = ...
    Task {
    let user = await a.user
    user.age += 1 //

    }
    Actor ͷΩϡʔͷ֎Ͱ Actor ͷ಺෦ঢ়ଶΛมߋͰ͖ͯ͠·͏

    View Slide

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

    ࢀরܕͰ var ϓϩύςΟΛ࣋ͭͷͰίϯύΠϧΤϥʔ

    View Slide

  58. 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

    View Slide

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

    View Slide

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

    Sendable Ͱͳ͍ϝϯόʔΛ࣋ͭͷͰίϯύΠϧΤϥʔ

    View Slide

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

    ࢀরܕͰ΋ΠϛϡʔλϒϧͳΒ OK

    View Slide

  62. final class User: Sendable {
    let id: ID
    let firstName: String
    let familyName: String
    let age: Int
    var name: String {
    "\(firstName) \(familyName)"
    }
    ...
    }

    Computed Property Λ͍࣋ͬͯͯ΋ΠϛϡʔλϒϧͳͷͰ OK

    View Slide

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

    View Slide

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

    View Slide

  65. actor Counter {
    private var count: Int = 0
    func increment() -> Int {
    count += 1
    return count
    }
    }
    actor ͸҉໧తʹ Sendable ʹద߹

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. final class User {
    var name: String
    ...
    }
    let user: User = ...
    Task {
    let name = user.name //
    }
    @Sendable Ϋϩʔδϟ͸ Sendable ͳ஋͔͠ΩϟϓνϟͰ͖ͳ
    ͍ʢ Task.init ʹ౉͢Ϋϩʔδϟ͸ @Sendable ʣ

    View Slide

  70. var a = 42
    Task {
    print(a) //

    }
    @Sendable Ϋϩʔδϟ͸ม਺ΛΩϟϓνϟͰ͖ͳ͍

    View Slide

  71. var a = 42
    Task { [a] in
    print(a) //

    }
    by-value Ωϟϓνϟ͸Մೳ

    View Slide

  72. View Slide

  73. Actor-isolated Context ͷҾܧ͗

    View Slide

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

    View Slide

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

    View Slide

  76. @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 Ͱͳ͍

    View Slide

  77. Task.initʢ context ΛҾ͖ܧ͙ʣ
    actor A {
    func foo() {
    // A
    Task { // @Sendable
    // A
    }
    }
    }

    View Slide

  78. @_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

    View Slide

  79. Task.initʢ context ΛҾ͖ܧ͙ʣ
    actor A {
    func foo() {
    // A
    Task { // @Sendable
    // A
    }
    }
    }

    View Slide

  80. Task.detachedʢ context ΛҾ͖ܧ͕ͳ͍ʣ
    actor A {
    func foo() {
    // A
    Task.detached { // @Sendable
    // not A
    }
    }
    }

    View Slide

  81. Actor-isolated Context ͷҾܧ͗ͷؔ͢Δ
    swi2-developers-japan Ͱͷ΍ΓͱΓ11
    11 h$ps:/
    /discord.com/channels/291054398077927425/291054454793306112/890858321693769748

    View Slide

  82. UserViewController

    View Slide

  83. View Slide

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

    View Slide

  85. View Controller ʹϩδοΫΛॻ͖ͨ͘ͳ͍

    View Slide

  86. ViewModel తͳΫϥεΛಋೖ

    View Slide

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

    View Slide

  88. Q6: UserViewState
    UserViewState ʹ loadUser ϝιουΛ௥Ճ͠ɺϢʔβʔ͓Α
    ͼϢʔβʔΞΠίϯΛऔಘ͢ΔίʔυΛ UserViewController
    ͔Β෼཭ͯ͠Լ͍͞ɻ UserViewController ʹ͸
    UserViewState ܕͷ state ϓϩύςΟΛ࣋ͨͤɺ state ͷঢ়
    ଶΛ View ʹ൓ө͢ΔΑ͏ʹͯ͠Լ͍͞ɻͳ͓ɺ͜ͷ໰୊͸ Actor
    ʹؔ܎͋Γ·ͤΜɻ
    !
    state.user Ͱ͸ͳ͘ state.$user ͱॻ͘͜ͱͰɺ user Λ
    Publisher ͱͯ͠औಘ͢Δ͜ͱ͕Ͱ͖·͢ɻ

    View Slide

  89. Q6: ղ౴ྫ

    View Slide

  90. loadUser Λฒߦʹݺͼग़ͯ͠΋େৎ෉͔ʁ

    View Slide

  91. loadUser Ͱى͜ΓಘΔσʔλڝ߹
    let data = try await downloadData(from: url)
    let user = try JSONDecoder().decode(User.self, from: data)
    self.user = user // ͕͜͜ಉ࣌ʹ࣮ߦ͞ΕΔͱʁ

    View Slide

  92. UserViewState Λ actor ʹ͍ͨ͠

    View Slide

  93. Q7: actor UserViewState
    UserViewState Λ Actor ʹͯ͠Լ͍͞ɻ

    View Slide

  94. Q7: ղ౴ྫ

    View Slide

  95. Swi$ Concurrency Λ࢖ͬͯ
    Publisher ΛߪಡͰ͖Δ

    View Slide

  96. Publisher.values 5
    for await value in publisher.values {
    ...
    }
    5 h$ps:/
    /developer.apple.com/documenta6on/combine/publisher/values-1dm9r

    View Slide

  97. 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

    View Slide

  98. Q8: Publisher.values
    UserViewState ͷ user ΍ iconImage ͷߪಡΛɺ for await
    Λ࢖͏ܗͰॻ͖׵͑ͯԼ͍͞ɻ

    View Slide

  99. Q8: ղ౴ྫ

    View Slide

  100. await state ͷ await ͕ᓔಃ͍͠
    for await user in await state.$user.values {
    ...
    }

    View Slide

  101. state ͷΩϡʔ͕ϝΠϯΩϡʔͳΒɾɾɾ

    View Slide

  102. ܕΛ௒͑ͯΩϡʔͰอޢ͍ͨ͠

    View Slide

  103. Global Actor 7
    7 h$ps:/
    /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md

    View Slide

  104. MainActor 8
    8 h$ps:/
    /github.com/apple/swi6-evolu9on/blob/main/proposals/0316-global-actors.md#the-main-actor

    View Slide

  105. @MainActor
    @MainActor func foo() { // isolated
    // ϝΠϯεϨου
    }
    @MainActor ͕෇༩͞Εͨؔ਺΍ϝιουɺϓϩύςΟ౳͸
    MainActor ʹ actor-isolated

    View Slide

  106. @MainActor
    @MainActor final class Foo {
    var value: Int = 42 // isolated
    func a() { // isolated
    // ϝΠϯεϨου
    }
    }
    @MainActor ͕෇༩͞ΕͨܕͷϝιουɺϓϩύςΟ౳͸
    MainActor ʹ actor-isolated

    View Slide

  107. Q9: @MainActor
    UserViewState ʹ @MainActor Λ෇༩ͯ͠ɺ
    UserViewController ͱͷ΍ΓͱΓΛ MainActor ಺ͷॲཧͱ
    ͯ͠ѻ͑ΔΑ͏ʹͯ͠Լ͍͞ɻ

    View Slide

  108. Q9: ղ౴ྫ

    View Slide

  109. ObservableObject

    View Slide

  110. UserViewState Λ ObservableObject ʹ
    final class UserViewState: ObservableObject {
    ...
    }

    View Slide

  111. UserViewState Λ ObservableObject ʹ
    • Swi%UI ΁ͷҠߦΛݟਾ͑ͯ
    • Computed Property ͸ @Published ʹͰ͖ͳ͍

    View Slide

  112. UserViewState Λ ObservableObject ʹ
    • user ΍ iconImage Λݸผʹߪಡ͢ΔͷͰ͸ͳ͘ɺ
    objectWillChange9 Λߪಡ͢Δ
    9 h$ps:/
    /developer.apple.com/documenta6on/combine/observableobject/objectwillchange-2oa5v

    View Slide

  113. Q10: ObservableObject
    UserViewState Λ ObservableObject ʹద߹ͤ͞ɺ user,
    iconImage Λݸผʹߪಡ͢ΔͷͰ͸ͳ͘ɺ objectWillChange
    Λߪಡͯ͠ View ʹ൓ө͢Δܗʹमਖ਼ͯ͠Լ͍͞ɻ

    View Slide

  114. Q10: ղ౴ྫ

    View Slide

  115. Q10 Ͱ࣮૷ͨ͠ UserViewState ͸
    Swi(UI Ͱ΋ͦͷ··ར༻Մೳ

    View Slide

  116. Q11: Swi(UI
    UserViewController ͱಉ༷ͷݟͨ໨ɾڍಈΛ࣮ݱ͢Δ
    UserView Λ Swi%UI Ͱ࣮૷ͯ͠Լ͍͞ɻ
    !
    @StateObject Ͱ UserViewState Λอ࣋͠·͢ɻ
    @StateObject private var state: UserViewState

    View Slide

  117. Q11: ղ౴ྫ

    View Slide