An app that designs shoes.
In order to better understand how SwiftUI works, we will develop a simple tutorial app. We will continue adding features to this app in the next chapters. For now, the first thing we will build is a simple way for users to customize / design sneakers. In order to allow this, we need a preview of the current sneaker, a way to change the colors of the current sneaker, and a way to store the current sneaker.
Here is a small GIF of what we are about to create.
The Model
Lets start with the model as it will help us shape the rest of the application. Currently, our model will only store the colors of the current sneaker. However, future chapters will also add the manufacturers name, shoe name, shoe model, etc. So in order to prepare for that we will have a more general ShoeConfiguration type that will contain a more distinct ShoeColors type. We will first have a look at this ShoeColors type.
struct ShoeColors {
     /// Outline Color
     var outline: Color
     /// Base Color
     var base: Color
     /// Side Color
     var side: Color
     /// Sole Color
     var sole: Color
     /// Back Cage Color
     var cage: Color
}
Our configuration has colors for multiple parts of the shoe. The outline, the base color, the side color, and so on. Note that we're not using UIColor or NSColor or CGColor, or even CIColor; no, there's a new color type in SwiftUI. It has a limited set of default color defintions, but it is sufficient for our use case here. 
The next part is to have a configuration for our shoe. The colors will be just one part of the configuration. A first draft would look something like this:
class ShoeConfiguration {
     
     var shoeColors: ShoeColors 
     
     init() {
         shoeColors = ShoeColors(outline: .black, base: .white, side: .orange, sole: .purple, cage: .gray)
     }
}
We're creating a simple class that acts as the configuration of one shoe. Currently, we're only hosting shoeColors, so there's not really much going on. What we do do, though, is to configure a default shoe in the initializer. 
import SwiftUI
import Combine
class ShoeConfiguration: BindableObject {
     
     struct ShoeColors {
         var outline: Color
         var base: Color
         var side: Color
         var sole: Color
         var cage: Color
     }
     
     var shoeColors: ShoeColors {
         didSet {
             didChange.send(self)
         }
     }
     
     var didChange = PassthroughSubject<ShoeConfiguration, Never>()
     
     init() {
         shoeColors = ShoeColors(outline: .black, base: .white, side: .orange, sole: .purple, cage: .gray)
     }
}
struct ShoeView : View {
     
     @Binding var colors: ShoeConfiguration.ShoeColors
     
     private func colorParts() -> [(name: String, color: Color)] {
         return [
             (\"base\", colors.base),
             (\"side\", colors.side),
             (\"sole\", colors.sole),
             (\"cage\", colors.cage),
             (\"outline\", colors.outline)
         ]
     }
     
     var body: some View {
         ZStack {
             ForEach(colorParts().identified(by: \.name)) { shoePart in
                 Image(shoePart.name).resizable()
                     .renderingMode(.template)
                     .foregroundColor(shoePart.color)
             }
         }
     }
}
extension View {
     func scaledFrame(from geometry: GeometryProxy, scale: CGFloat) -> some View {
         self.frame(width: geometry.size.width * scale, height: geometry.size.height * scale)
     }
}
struct ColorPickerEntry : View {
     var selected: Bool
     var shoeColor: Color
     
     private let outerScale: CGFloat = 0.7
     private let innerScale: CGFloat = 0.5
     
     var body : some View {
         GeometryReader { geometry in
             Group {
                 if self.selected {
                     Circle().fill(self.shoeColor)
                         .overlay(Circle().stroke(Color.black, lineWidth: 2.0))
                         .scaledFrame(from: geometry, scale: self.outerScale)
                 } else {
                     Circle().stroke(Color.gray)
                         .scaledFrame(from: geometry, scale: self.innerScale)
                         .overlay(Circle().fill(self.shoeColor)
                             .scaledFrame(from: geometry, scale: self.innerScale), alignment: .center)
                         .overlay(Circle().stroke(Color.gray)
                             .scaledFrame(from: geometry, scale: self.outerScale))
                 }
             }.frame(width: geometry.size.width, height: geometry.size.height)
         }
     }
}
struct ColorPicker : View {
     @Binding var selectedColor: Color
     var name: String
     var body : some View {
         VStack(alignment: HorizontalAlignment.center, spacing: 0) {
             Text(name).font(.body)
             HStack {
                 ForEach([Color.black, Color.white, Color.orange, Color.purple, Color.gray].identified(by: \.hashValue)) { color in
                     Button(action: {
                         self.selectedColor = color
                     }) {
                         ColorPickerEntry(selected: self.selectedColor.hashValue == color.hashValue, shoeColor: color)
                             .frame(width: 38, height: 38)
                     }
                 }
             }
         }
     }
}
struct ShoeConfigurator : View {
     
     @ObjectBinding var shoeConfiguration = ShoeConfiguration()
     
     var body: some View {
         VStack {
             ShoeView(colors: $shoeConfiguration.shoeColors)
                 .frame(width: 250, height: 114, alignment: .center)
             ColorPicker(selectedColor: $shoeConfiguration.shoeColors.base,
                         name: \"Base\")
             ColorPicker(selectedColor: $shoeConfiguration.shoeColors.cage,
                         name: \"Cage\")
             ColorPicker(selectedColor: $shoeConfiguration.shoeColors.side,
                         name: \"Side\")
             ColorPicker(selectedColor: $shoeConfiguration.shoeColors.sole,
                         name: \"Sole\")
         }
     }
}
struct ContentView : View {
     @State private var selection = 0
     
     var body: some View {
         ShoeConfigurator()
     }
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
     static var previews: some View {
         ContentView()
     }
}
#endif
	