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

ADDC 2019 - Daniel Steinberg - Bringing SwiftUI...

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. * I have worked with SwiftUI for three weeks *

    The syntax will change * SwiftUI is how we will code for Apple platforms
  2. 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) }
  3. 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) }
  4. 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) }
  5. 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) }
  6. 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) }
  7. 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) }
  8. 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) }
  9. struct ContentView: View { var body: some View { VStack

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  34. 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
  35. 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
  36. struct ValueDisplay: View { @Binding var value: Int var body:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    { Text("Increase") } } } struct ValueDisplay: View { var body: some View { Text("\(value)") } }
  60. The example has: •Components: Text, TextField, Slider, Buttons, Drawing •Containers:

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

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

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

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

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

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

    { MoodEntryView() .tabItemLabel(Text("Face")) .tag(0) HistoryView() .tabItemLabel(Text("History")) .tag(1) } } }
  67. 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) } }
  68. 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) } }
  69. 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) } } }
  70. 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) } } }
  71. 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) } } }
  72. 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) } } }
  73. 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) } } }
  74. 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) } } }
  75. 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) } } }
  76. 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) } } }
  77. 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) } } }
  78. 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]) }
  79. 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]) }
  80. 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]) }
  81. 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]) }
  82. 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]) }
  83. 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]) }
  84. 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]) }
  85. View Requires •Image •Text (title) •Text (time) •Text (date) Model

    Provides •Mood (enum) •String (title) •Date (timestamp)
  86. 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) } }
  87. 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) } }
  88. 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) } }
  89. 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) } }
  90. 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) } }
  91. 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) } }
  92. extension History { func add(mood: Mood, title: String) { moodRecords.append(MoodRecord(mood:

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

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

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

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

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

    mood, title: title)) sendUpdate() } func clear() { moodRecords.removeAll() sendUpdate() } }
  98. struct ContentView: View { @State var value: Int = 0

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

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

    var body: some View { VStack { Text("\(value)") .padding() Button(action: {self.value += 1}) { Text("Increase") } } } }
  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 ContentView: View { @State var value: Int = 0

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

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

    var body: some View { VStack { ValueDisplay(value: $value) .padding() IncreaseButton(value: $value) } } }
  107. public class History: BindableObject { lazy var moodRecords = initializeMoodRecords()

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

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

    let didChange = PassthroughSubject<History, Never>() }