|
| 1 | +--- |
| 2 | +title: Cookie Clicker with SwiftUI |
| 3 | +--- |
| 4 | + |
| 5 | +This tutorial is an introduction to the world of iOS development using Apple's latest UI framework: [SwiftUI](https://developer.apple.com/xcode/swiftui/) |
| 6 | + |
| 7 | +We'll be building a counter app, where the user will press a button and increment the number on the screen. |
| 8 | + |
| 9 | +## Pre-requisites |
| 10 | + |
| 11 | +In order to work through this tutorial, you will need a Mac with Xcode 11 installed. Xcode 11 is the minimum version that supports SwiftUI. It is useful to have Mac OSX Catalina installed to see live previews in Xcode, but you can still use Mac OS X Mojave. |
| 12 | + |
| 13 | +Download and install Xcode from the App Store. This may take a while — its a big program. If you are at a Codebar ask the coaches before downloading it from the App Store as they frequently have a copy on a USB drive which will save some installation time, but it would be better if you come to the workshop with it installed. |
| 14 | + |
| 15 | +## Creating a Project |
| 16 | + |
| 17 | +### 1. Open Xcode and click Create a new Xcode project |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +### 2. Select Single View Application from the **iOS > Application list**, and click **Next** |
| 22 | + |
| 23 | + |
| 24 | + |
| 25 | +### 3. Fill in the project details: |
| 26 | + |
| 27 | +- Product Name: Clicker |
| 28 | +- Team: None |
| 29 | +- Organization Name: Whatever you want — your name is always a good fill-in |
| 30 | +- Organization Identifier: com.(OrganizationName) |
| 31 | + |
| 32 | +Make sure Swift is selected as the language. |
| 33 | + |
| 34 | +Make sure SwiftUI is selected as the User Interface. |
| 35 | + |
| 36 | +Make sure the three boxes at the bottom are unchecked, we won't be using those options in this tutorial. |
| 37 | + |
| 38 | +Click Next |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +### 4. Select a sensible place to save your project, then hit **Create**. |
| 43 | + |
| 44 | +Take some time to look around the project that's been created with your coach. What files are there? How do you run your app? |
| 45 | + |
| 46 | +If you are running on Mac OS X Catalina, try running the preview to see what the default project provides. |
| 47 | + |
| 48 | +If this is your first time running Xcode, then this may take a few minutes. This is normal, the next time you run the preview it will be much faster. |
| 49 | + |
| 50 | +## Add a Button |
| 51 | + |
| 52 | +### 5. Open up the **Navigation Area** in Xcode, and then the `ContentView.swift` file and its associated Canvas. |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +If you click on the `Resume` button in the top right, and you are running on Mac OS X Catalina you will see a preview of the **Hello World** application that the template has provided for you. |
| 57 | + |
| 58 | +### 6. Edit the Canvas and add a Button |
| 59 | + |
| 60 | +When using SwiftUI the Canvas provides a live view of your code. Editing the Canvas will also update your code! We shall be adding code from the Editor a little later on, but for now just add a button to the canvas. |
| 61 | + |
| 62 | +- Open the Library |
| 63 | +- Make sure you are browsing the Views |
| 64 | +- Search for Button |
| 65 | +- Drag the button under the label |
| 66 | + |
| 67 | +###  |
| 68 | + |
| 69 | +If you look at the Editor, you can see that this has created a `VStack` with both the label and the button. A `VStack` is a built in view that arranges the contained views vertically. |
| 70 | + |
| 71 | +### 7. Run the app on the Simulator or the canvas. |
| 72 | + |
| 73 | +You can either run the app in the simulator or use the live preview in the Canvas to see the visual effect of pressing the button. |
| 74 | + |
| 75 | +Celebrate appropriately! |
| 76 | + |
| 77 | +Try changing the `VStack` to an `HStack` Are the results what you expect? |
| 78 | + |
| 79 | +A `HStack` is a view that arranges its contained views horizontally. |
| 80 | + |
| 81 | +However, it would be even more awesome if our button actually did something. |
| 82 | + |
| 83 | +## Make the Button do something |
| 84 | + |
| 85 | +To do this we are going to need some state in our view. |
| 86 | + |
| 87 | +### 8. Create some state for the view |
| 88 | + |
| 89 | +Create a state variable at the top of the definition of `ContentView`. We make it `private` because it is internal to the view, and we give it a default value of `false` which implicitly makes `showGreeting` a variable that holds a `Bool` (Now is the time to ask your Coach questions). |
| 90 | + |
| 91 | +```swift |
| 92 | +struct ContentView: View { |
| 93 | + @State private var showGreeting = false |
| 94 | + |
| 95 | + var body: some View { |
| 96 | + <...> |
| 97 | +``` |
| 98 | + |
| 99 | +We are going to use this to configure what is displayed on the screen. |
| 100 | + |
| 101 | +### 9. Configure the view to display according to the state |
| 102 | + |
| 103 | +Change the string that is displayed in the `Text` view depending on this state: |
| 104 | + |
| 105 | +```swift |
| 106 | + if showGreeting { |
| 107 | + Text("Hello World") |
| 108 | + } else { |
| 109 | + Text("") |
| 110 | + } |
| 111 | +``` |
| 112 | + |
| 113 | +When `showGreeting` is `true` the label will display the greeting, otherwise it will be empty. |
| 114 | + |
| 115 | +### 10. Use the Button's action to change the state |
| 116 | + |
| 117 | +When you dragged the button onto the canvas it created an empty `action` This is a closure, and when the button is tapped it will perform whatever code is written in this closure. We are going to use this to toggle the `showGreeting` state. |
| 118 | + |
| 119 | +Edit the Button code look like this: |
| 120 | + |
| 121 | +```swift |
| 122 | +Button(action: { self.showGreeting.toggle() }) |
| 123 | +``` |
| 124 | + |
| 125 | +The `ContentView.swift` file should now look like this: |
| 126 | + |
| 127 | +```swift |
| 128 | +import SwiftUI |
| 129 | + |
| 130 | +struct ContentView: View { |
| 131 | + @State private var showGreeting = false |
| 132 | + |
| 133 | + var body: some View { |
| 134 | + VStack { |
| 135 | + if showGreeting { |
| 136 | + Text("Hello World") |
| 137 | + } else { |
| 138 | + Text("") |
| 139 | + } |
| 140 | + Button(action: {self.showGreeting.toggle()}) { |
| 141 | + Text("Button") |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +struct ContentView_Previews: PreviewProvider { |
| 148 | + static var previews: some View { |
| 149 | + ContentView() |
| 150 | + } |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +### Interlude: What is @State? |
| 155 | + |
| 156 | +SwiftUI is a _declarative_ framework. That means that you write the view to show different things according to its state, and actions change the state, and let the framework handle redrawing the views when these change. If you have done any iOS programming with UIKit before this will be very different. We don't update properties and tell the view to redraw; we just update the properties. |
| 157 | + |
| 158 | +We can use normal constants and variables in SwiftUI, but when we use `@State` variables we are telling the framework that these are the values that the view depends on. SwiftUI keeps track of these internally, and when they change the view is redrawn. |
| 159 | + |
| 160 | + |
| 161 | +### 11. Run the app in the Simulator or by using the Canvas's live preview feature |
| 162 | + |
| 163 | +As you tap the button, the greeting appears and disappears! |
| 164 | + |
| 165 | +/giphy Celebrate |
| 166 | + |
| 167 | + |
| 168 | +This is not trivial. You have learned an important part of developing using SwiftUI. You create state, you write your view to respond to the state, and you have actions that change the state. The framework takes care of redrawing the view when the state changes. |
| 169 | + |
| 170 | +If you have done any iOS development before, or even the other CodeBar tutorial, you can see how little code is needed. There are no `ViewController`s. There are no `Storyboards`. You write code and see the results immediately. |
| 171 | + |
| 172 | + |
| 173 | +## Make the Counter |
| 174 | + |
| 175 | +There are two major tasks for making the counter app. Firstly, _the functionality_ - getting the counter to increment on button clicks and showing the new number. Secondly _the UI_, getting an app that looks nice and is easy to use. This tutorial will create the functionality first and then move on to the UI. You may choose to tackle the tasks in the alternate order. Have a brief discussion with your Coach about the pros and cons of each approach. |
| 176 | + |
| 177 | +### 12. Create the Counter functionality |
| 178 | + |
| 179 | +We are a good way towards this, we already have a `Button` with an action, and a `Text` view that displays something. |
| 180 | + |
| 181 | +Change the state variable to hold a number instead of a `Bool` |
| 182 | + |
| 183 | +```swift |
| 184 | +@State private var count = 0 |
| 185 | +``` |
| 186 | + |
| 187 | +Since the state is variable is now a number and not a string, change the `Text` view to display this: |
| 188 | + |
| 189 | +```swift |
| 190 | +Text("\(count)") |
| 191 | +``` |
| 192 | + |
| 193 | +Finally change the `Button`'s action to increment this variable |
| 194 | + |
| 195 | +```swift |
| 196 | +Button(action: { self.count += 1 }) |
| 197 | +``` |
| 198 | + |
| 199 | +The entire file should now look like this |
| 200 | + |
| 201 | +```swift |
| 202 | +import SwiftUI |
| 203 | + |
| 204 | +struct ContentView: View { |
| 205 | + @State private var count = 0 |
| 206 | + |
| 207 | + var body: some View { |
| 208 | + VStack { |
| 209 | + Text("\(count)") |
| 210 | + Button(action: { self.count += 1 }) { |
| 211 | + Text("Button") |
| 212 | + } |
| 213 | + } |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +struct ContentView_Previews: PreviewProvider { |
| 218 | + static var previews: some View { |
| 219 | + ContentView() |
| 220 | + } |
| 221 | +} |
| 222 | +``` |
| 223 | + |
| 224 | +### 13. Run the app in the Simulator on the Canvas live preview to see the counter working |
| 225 | + |
| 226 | + |
| 227 | + |
| 228 | + |
| 229 | +## Make it look better |
| 230 | + |
| 231 | +Having the button and the label so close together isn't the nicest looking UI. Nor is it the easiest to use. And the text for the counter seems a little small. Let's fix these things |
| 232 | + |
| 233 | +### 14. Increase the size of the numbers |
| 234 | + |
| 235 | +In the canvas Cmd + Click on the `Text` view, this brings up an inspector. |
| 236 | + |
| 237 | +Select the `show SwiftUI Inspector...` option. |
| 238 | + |
| 239 | +Choose `Large Title` for the font. |
| 240 | + |
| 241 | + |
| 242 | + |
| 243 | +Notice that in the editor, the modifier has been added to the `Text` view that matches this choice. |
| 244 | + |
| 245 | +Working with your coach, try to apply other sizes and styles to this `Text` view. |
| 246 | + |
| 247 | +### 15. Add some spacing |
| 248 | + |
| 249 | +Let's add some separation between the elements. `VStack`s are quite helpful in laying items out. All that needs to be done is for some spacers to be added between the elements and the it will handle layout. |
| 250 | + |
| 251 | +Add a `Spacer()` above and below the `Text` view. The entire file should now look like this: |
| 252 | + |
| 253 | +```swift |
| 254 | +import SwiftUI |
| 255 | + |
| 256 | +struct ContentView: View { |
| 257 | + @State private var count = 0 |
| 258 | + |
| 259 | + var body: some View { |
| 260 | + VStack { |
| 261 | + Spacer() |
| 262 | + Text("\(count)") |
| 263 | + .font(.largeTitle) |
| 264 | + Spacer() |
| 265 | + Button(action: { self.count += 1 }) { |
| 266 | + Text("Button") |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | +} |
| 271 | + |
| 272 | +struct ContentView_Previews: PreviewProvider { |
| 273 | + static var previews: some View { |
| 274 | + ContentView() |
| 275 | + } |
| 276 | +} |
| 277 | +``` |
| 278 | + |
| 279 | +And the Canvas shows the result: |
| 280 | + |
| 281 | + |
| 282 | + |
| 283 | +## Summary |
| 284 | + |
| 285 | +Congratulations! You've just created your first SwiftUI iOS app. You've learned how to move around Xcode and edit your code. You've added UI elements from the library and configured them. You've seen how to use previews, live previews, and the simulator. You've seen how SwiftUI is declarative. This is a good start! |
| 286 | + |
| 287 | +## Bonus : Run your app on your Apple device |
| 288 | + |
| 289 | +Running your app in the simulator is cool, but you know what's even cooler? Running your app in your Apple devices! Couple years back, you need to have a developer account to run your app in a physical device, but luckily know Apple allows us to run our app using your everyday Apple account. |
| 290 | + |
| 291 | +### Pre-requisites |
| 292 | + |
| 293 | +- 📱 Apple device (the more recent device the better) + cable |
| 294 | +- An Apple account |
| 295 | + |
| 296 | +### A. Add your account in Xcode |
| 297 | + |
| 298 | +- From the menubar, go to `Xcode > Preferences...` |
| 299 | +- Click the `Accounts` tab (the second tab) |
| 300 | +- On bottom left, press the + icon |
| 301 | +- Select `Add Apple ID...` |
| 302 | +- Insert your Apple ID's credentials |
| 303 | + |
| 304 | + |
| 305 | + |
| 306 | +### B. Configure your project's signing |
| 307 | + |
| 308 | +- Go to `Navigator` area, select `Project navigator` tab, select your project name |
| 309 | +- Select your app target in the `Targets` tab |
| 310 | +- In the `Signing` area, select your account as the team name |
| 311 | + |
| 312 | + |
| 313 | + |
| 314 | +### C. Configure your device 📱 |
| 315 | + |
| 316 | +- Plug your Apple device to your development machine (this will trigger Xcode to [processes symbol files from your devices](http://stackoverflow.com/a/19706886/851515), which will take a sometime) |
| 317 | +- Instead of running on simulator, select your device instead |
| 318 | + |
| 319 | +- Press the play button to run the app! |
| 320 | +- Errm... not so fast. We need to allow codesign to access our keychain, press `Always Allow` |
| 321 | + |
| 322 | +- Almost there, you will be prompted to verify your account in your device |
| 323 | + |
| 324 | +- Now on your iOS device, go to `Settings` > `General` > `Profiles & Device Management`, tap your account under `Developer App`, tap `Trust '<your apple account email>'`, and confirm by tapping `Trust` again |
| 325 | + |
| 326 | +- Back at Xcode, press play button to build and run again |
| 327 | +- 🎉 |
| 328 | + |
| 329 | +It's great to see that Apple makes it easy for us to try our app in our actual hands. Happy testing! |
| 330 | + |
| 331 | +------ |
| 332 | + |
| 333 | +## Extension Tasks |
| 334 | + |
| 335 | +If you've finished all of the above why not think about some of the following ideas: |
| 336 | + |
| 337 | +* Disable the button when the counter reaches, say, 10. so the number can't get larger. |
| 338 | + |
| 339 | +* make a new button that decreases the count. Can you turn it into a two player game? |
| 340 | + |
| 341 | +* make the background change after a set amount of clicks? How about creating an array of colours to change every set interval e.g. after 5 taps. |
| 342 | + |
| 343 | +* change your button into a picture button, you could make a cookie clicker? Make the picture change after a set amount of clicks. |
0 commit comments