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

ADDC 2019 - Daniel Steinberg - Bringing SwiftUI to your App

ADDC 2019 - Daniel Steinberg - Bringing SwiftUI to your App

What changes in the way you design your app with SwiftUI? Everything and nothing. In this fast-paced talk we’ll look at how to get the most out of Apple’s new Swift-friendly declarative UI framework and the impact it has on how you envision and architect your app.

More Decks by ADDC - App Design & Development Conference

Other Decks in Technology

Transcript

  1. The Changes Coming in SwiftUI ADDC 2019 Barcelona Daniel H

    Steinberg dimsumthinking.com *
  2. * I have worked with SwiftUI for twenty years

  3. * I have worked with SwiftUI for twenty years

  4. * I have worked with SwiftUI for twenty years three

    weeks
  5. * I have worked with SwiftUI for twenty years three

    weeks almost
  6. * I have worked with SwiftUI for three weeks *

    The syntax will change
  7. * I have worked with SwiftUI for three weeks *

    The syntax will change * SwiftUI is how we will code for Apple platforms
  8. The Changes Coming in SwiftUI ADDC 2019 Barcelona Daniel H

    Steinberg dimsumthinking.com
  9. The Old Days

  10. The Old Days very

  11. I'm a label

  12. let label = UILabel(frame: CGRect(x: 0, y: 100, width: 380,

    height: 40))
  13. let label = UILabel(frame: CGRect(x: 0, y: 100, width: 380,

    height: 40))
  14. let label = UILabel(frame: CGRect(x: 0, y: 101, width: 380,

    height: 40))
  15. I'm a label

  16. I'm a label

  17. Conditional Code

  18. I'm a label

  19. I'm a label

  20. Pixels -> Points

  21. I'm a label

  22. I'm a label

  23. I'm a label

  24. I'm a label

  25. Autolayout

  26. Gained flexibility for different size classes

  27. Lost pixel-perfect design

  28. The Old Days

  29. cell.text = "Hello, World!"

  30. cell.textLabel.text = "Hello, World!"

  31. private func setUpLabel() { label.textColor = .white label.textAlignment = .center

    label.text = "0" view.addSubview(label) }
  32. private func setUpLabel() { label.textColor = .white label.textAlignment = .center

    label.text = "0" view.addSubview(label) }
  33. private func setUpLabel() { label.textColor = .white label.textAlignment = .center

    label.text = "0" view.addSubview(label) }
  34. private func setUpLabel() { label.textColor = .white label.textAlignment = .center

    label.text = "0" view.addSubview(label) }
  35. private func setUpLabel() { label.textColor = .white label.textAlignment = .center

    label.text = "0" view.addSubview(label) }
  36. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  37. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  38. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  39. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  40. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  41. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  42. private func setUpButton() { button.setTitle("Increase", for: .normal) button.frame = CGRect(x:

    0, y: 300, width: 380, height: 40) button.addTarget(self, action: #selector(increaseValue), for: .touchUpInside) view.addSubview(button) }
  43. @objc private func increaseValue(){ value += 1 label.text = value.description

    }
  44. @objc private func increaseValue(){ value += 1 label.text = value.description

    }
  45. @objc private func increaseValue(){ value += 1 label.text = value.description

    }
  46. @objc private func increaseValue(){ value += 1 label.text = value.description

    } Classic Controller Code
  47. SwiftUI

  48. None
  49. SwiftUI

  50. struct ContentView: View { var body: some View { Text("0")

    } }
  51. struct ContentView: View { var body: some View { Text("0")

    } }
  52. struct ContentView: View { var body: some View { Text("0")

    } }
  53. struct ContentView: View { var body: some View { return

    Text("0") } }
  54. struct ContentView: View { var body: some View { Text("0")

    } }
  55. None
  56. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  57. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  58. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  59. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } } cell.textLabel.text
  60. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  61. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  62. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  63. struct ContentView: View { var body: some View { VStack

    { Text("0") Button(action: {}) { Text("Increase") } } } }
  64. None
  65. struct ContentView: View { var body: some View { VStack

    { Text("0") .padding() Button(action: {}) { Text("Increase") } } } }
  66. None
  67. struct ContentView: View { var body: some View { VStack

    { Text("0") .padding() Button(action: {}) { Text("Increase") } } } }
  68. @objc private func increaseValue(){ value += 1 label.text = value.description

    } Flashback
  69. struct ContentView: View { var body: some View { VStack

    { Text("0") .padding() Button(action: {}) { Text("Increase") } } } }
  70. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  71. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  72. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  73. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  74. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  75. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  76. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  77. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  78. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  79. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  80. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  81. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  82. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  83. None
  84. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  85. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } } Layout/Content/Behavior
  86. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } } Layout/Content/Behavior
  87. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  88. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } }
  89. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  90. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  91. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  92. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  93. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } } Extract the Text
  94. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  95. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  96. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  97. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  98. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  99. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  100. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  101. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  102. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  103. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  104. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  105. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  106. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  107. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  108. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  109. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  110. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  111. struct IncreaseButton: View { var body: some View { Button()

    { Text("Increase") } } } struct ValueDisplay: View { var body: some View { Text("\(value)") } }
  112. SwiftUI

  113. None
  114. Note to developers

  115. Note to developers Hire a designer

  116. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

    VStack, HStack, ZStack, Geometry •Navigation: Tabs, Nav, Detail View •Data: State, Binding, Object Binding
  117. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

    VStack, HStack, ZStack, Geometry •Navigation: Tabs, Nav, Detail View •Data: State, Binding, Object Binding
  118. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

    VStack, HStack, ZStack, Geometry •Navigation: Tabs, Nav, Detail View •Data: State, Binding, Object Binding
  119. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

    VStack, HStack, ZStack, Geometry •Navigation: Tabs, Nav, Detail View •Data: State, Binding, Object Binding
  120. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

    VStack, HStack, ZStack, Geometry •Navigation: Tabs, Nav, Detail View •Data: State, Binding, Object Binding
  121. struct ContentView: View { var body: some View { TabbedView

    { MoodEntryView() .tabItemLabel(Text("Face")) .tag(0) HistoryView() .tabItemLabel(Text("History")) .tag(1) } } }
  122. struct ContentView: View { var body: some View { TabbedView

    { MoodEntryView() .tabItemLabel(Text("Face")) .tag(0) HistoryView() .tabItemLabel(Text("History")) .tag(1) } } }
  123. None
  124. var body: some View { VStack { FaceView(mood: $mood) FaceSlider(mood:

    $mood) TextField($message, placeholder: Text("What's going on")) Button(action: {history.add(mood: Mood(for: self.mood), title: self.message) self.message = "" self.mood = 2 }){ Text("Record Mood") }.disabled(buttonDisabled) } }
  125. var body: some View { VStack { FaceView(mood: $mood) FaceSlider(mood:

    $mood) TextField($message, placeholder: Text("What's going on")) Button(action: {history.add(mood: Mood(for: self.mood), title: self.message) self.message = "" self.mood = 2 }){ Text("Record Mood") }.disabled(buttonDisabled) } }
  126. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  127. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  128. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  129. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  130. None
  131. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: 15) }.aspectRatio(1, contentMode: .fit) } } }
  132. None
  133. None
  134. None
  135. Use the geometry

  136. Use the geometry Allows us to listen to our designers

  137. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  138. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  139. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  140. None
  141. struct FaceView: View { @Binding var mood: Double var body:

    some View { GeometryReader {geometry in ZStack { Circle().fill(faceColor(mood: self.mood)) Eyes(size: min(for: geometry)) mouth(mood: self.mood, size: min(for: geometry)) .stroke(Color.black, lineWidth: min(for: geometry) / 50 ) }.aspectRatio(1, contentMode: .fit) } } }
  142. None
  143. None
  144. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  145. None
  146. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  147. None
  148. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  149. None
  150. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  151. None
  152. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  153. None
  154. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  155. None
  156. HStack(alignment: .top) { Image(uiImage: record.image) .resizable() .frame(width: 40, height: 40)

    .padding([.leading, .trailing]) Text(record.title) .font(.headline) .lineLimit(3) Spacer() VStack(alignment: .trailing){ Text(record.time) .font(.body) Text(record.date) .font(.footnote) }.padding([.leading, .trailing]) }
  157. None
  158. View Requires •Image •Text (title) •Text (time) •Text (date)

  159. View Requires •Image •Text (title) •Text (time) •Text (date) Model

    Provides •Mood (enum) •String (title) •Date (timestamp)
  160. View Model

  161. struct MoodRecordViewModel { let moodRecord: MoodRecord }

  162. struct MoodRecordViewModel { let moodRecord: MoodRecord }

  163. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  164. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  165. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  166. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  167. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  168. extension MoodRecordViewModel { public var title: String { return moodRecord.title

    } public var image: UIImage { guard let image = UIImage(named: moodRecord.mood.description) else {fatalError()} return image } public var time: String { return timeFormatter.string(from: moodRecord.timeStamp) } public var date: String { return dateFormatter.string(from: moodRecord.timeStamp) } }
  169. Updates

  170. None
  171. history

  172. public class History { lazy var moodRecords = initializeMoodRecords() }

  173. public class History { lazy var moodRecords = initializeMoodRecords() }

  174. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  175. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  176. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  177. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  178. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  179. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  180. Updates

  181. @State

  182. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  183. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  184. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  185. @Binding

  186. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  187. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  188. struct IncreaseButton: View { @Binding var value: Int var body:

    some View { Button(action: increaseValue) { Text("Increase") } } private func increaseValue() { value += 1 } }
  189. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  190. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  191. struct ContentView: View { @State var value: Int = 0

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  192. struct ValueDisplay: View { @Binding var value: Int var body:

    some View { Text("\(value)") } }
  193. history

  194. @ObjectBinding

  195. public class History: BindableObject { lazy var moodRecords = initializeMoodRecords()

    let didChange = PassthroughSubject<History, Never>() }
  196. public class History: BindableObject { lazy var moodRecords = initializeMoodRecords()

    let didChange = PassthroughSubject<History, Never>() }
  197. public class History: BindableObject { lazy var moodRecords = initializeMoodRecords()

    let didChange = PassthroughSubject<History, Never>() }
  198. extension History { func sendUpdate() { updateMoodRecords() didChange.send(self) } private

    func updateMoodRecords() {…} }
  199. extension History { func sendUpdate() { updateMoodRecords() didChange.send(self) } private

    func updateMoodRecords() {…} }
  200. struct HistoryView: View { @ObjectBinding private var moodHistory = history

  201. struct HistoryView: View { @ObjectBinding private var moodHistory = history

  202. history

  203. history

  204. history

  205. SwiftUI

  206. Caution:

  207. Caution: It is early days.

  208. Caution: It is early days. Much will change.

  209. It is our future

  210. The Changes Coming in SwiftUI ADDC 2019 Barcelona Daniel H

    Steinberg dimsumthinking.com
  211. None
  212. None