# Some SwiftUI fundamentals

[[PDF version](SwiftUI Fundamentals.pdf) unfortunately with some spacing and page splitting problems]

[Guy Lapalme](mailto:lapalme@iro.umontreal.ca), Université de Montréal, October 2023

> *This document was originally written to help me remember what I consider to be the [SwiftUI](https://developer.apple.com/xcode/swiftui/) fundamentals. Finally, it became the tutorial that I would have like to read before working with SwiftUI. I hope it is as useful  to others as it has been for me writing it.* 

Many documents on the web, notably [Apple Tutorials](https://developer.apple.com/tutorials/swiftui), usually demonstrate SwiftUI features by means of examples without never taking time to explain the underlying ideas. If it can be relatively easy, but not always, to build spectacular demos by following the steps of a tutorial, it is not so straightforward to apply these ideas later in one's own application. There are also many commercial or ad-full web sites and videos that  explain some ideas, but there are usually limited to a few minutes, [even seconds](https://www.hackingwithswift.com/sixty), reading  focused on aspects of the language and system[^1].

A notable exception to this superficial approach was the first edition of [Thinking in Swift](https://www.objc.io/books/thinking-in-swiftui/), which aimed to explain the rationale behind SwiftUI, but even there I found it a bit hard to follow. So I was looking forward for their second edition. Unfortunately, this new edition is even less explicit on the fundamentals than their first one. The authors go to great length to illustrate some aspects, but they skip interesting explanations about the flow of information in SwiftUI that appeared in the first edition. They seem to take for granted that the reader is a seasoned Swift programmer at ease with more advanced features of the language or that they have followed their on-line courses. The main advantage of the second edition is that it explains important changes in the state management that appeared in IOS 17.

[Swift](https://www.swift.org) is a script-like language combining great insights from years of programming language design. I see it as a Python-like script language with static strong typing *à la* Haskell. Its designers have also introduced some *uncommon* features, such as *result builders* or *property wrappers*  that are heavily used in SwiftUI[^2]. This is why I will spend some time explaining these in this document.

I take it for granted that the reader has already some basic notions of the Swift language and some experience working with Xcode.  [Link to the Xcode project of the examples in this document](../Swift-Fundamentals.zip).  At the beginning of some sections of this document, links to the complete source code for that section are also given.

## Main principle: linking states and views

Interactive applications must ensure that the display always reflects the states, *sources of truth* in the Apple of terminology, that define the system. When the user or an external event modifies states, the display must be updated to reflect these changes. Many frameworks have been developed over the years to help build systems for synchronizing the states and display. The most well known being the *Model-View-Controller* approach in which the controller updates the view when notified of model changes, usually by means of callback functions. 

Recently, a new paradigm has appeared which bypasses the controller, so that the system is defined in terms of a view which is updated *automatically* when the model changes. One well-known example being [React](https://react.dev). See [this article, for a comparison between SwiftUI and React](https://blog.maximeheckel.com/posts/swiftui-as-react-developer/).

According to this [Apple introductory document](https://developer.apple.com/documentation/swiftui/declaring-a-custom-view), *you create a lightweight description of your user interface by declaring views in a hierarchy that mirrors the desired layout of your interface. SwiftUI then manages drawing and updating these views in response to events like user input or state changes.* 

[According to Bart Jacobs](https://cocoacasts.com/swiftui-fundamentals-what-is-swiftui), *SwiftUI provides developers with an API to declare or describe what the user interface should look like. SwiftUI inspects the declaration or description of the user interface and converts it to your application's user interface. A view is no longer the result of a sequence of events. A view is a function of state. In other words, the user interface is a derivative of the application's state. If the application's state changes, the user interface automatically updates, eliminating a whole category of bugs.* 

To explain how to achieve these laudable goals, I will start by explaining how to build a view from a hierarchy of other views. When developing in SwiftUI with Xcode, views are defined by Swift code which can be executed on the fly in a preview window, so that it is easy to see how it is laid out. Xcode allows building a SwiftUI view by drag-and-drop of code snippets and patterns, but finally it seems simpler to me to type the code directly. Once the views can be defined, I will describe how to synchronize them with system states.

## View

`View` is a predefined Swift type, a *protocol*, the equivalent to an *interface* in Java. Remember that SwiftUI `View` instances, being `struct`, are **immutable**, so they must be recreated when some of its content changes. SwiftUI provides a whole gamut of predefined views that can be combined to form new custom views.

### Predefined views

- **Controls**: basic interface elements such as `Text`,`Button` or `Label`, but also more sophisticated such as `DatePicker`, `List`, `Table` or `TextEditor`
- **Layout** : organizers such as `HStack`, `VStack`, `ZStack`  or `Spacer`
- **Other** : such as `Circle`, `Divider`, `Group` or `Image`
- **View modifiers**: return a modified version of the view by means of the *dot notation* for different purposes, for example
  - changing its look: `.font()`, `.padding()`, `.border()`
  - changing its behavior: `.onChange{}`, `.onAppear{}`, `.onTapGesture{}`

### Custom views

A new view is defined with a `struct` declaration that must conform to the  `View` protocol which means that there must be a `body` property of the type `some View` of the following  form.

```swift
struct aCustomView: View {
    var body: some View {
      	.. content of the view ..
    }
}
```

### Show two dice 

[[*source code*](Show-two-dice/ContentView.swift)]

We begin with an application that displays views without any interaction. The left part of following figure is an example of custom view (`ImageNameView`) that is used twice within the scaffolding code provided by Xcode with the corresponding preview. It illustrates the following  interesting features:

- line 11: definition of  a custom view with the following components: 
  - `image`: mandatory string parameter, because of `let`, corresponding to the name of a system image (found in the Xcode Symbols library)
  - `name`: optional string parameter, because of `var` with initialization, the text to display
  - `body`: defining how the View is composed of other views: here the `Image` view is stacked vertically on top of a `Text`. These views are slightly modified, the `Image` is made larger and the `Text` set in bold. The whole stack in then added some space (padding) around it and a red border is applied.
- line 24: `ContentView` (this name is conventional, but it should match the one used in `ContentView_Previews` and in the `body` of the app when run on a device) whose `body` defines the view that will be displayed when run on the device. In this case, this is a *Horizontal Stack* of  two calls to the custom view I have previously defined each with a different parameter. This stack is then itself  stacked vertically with a text below with some padding and a blue border.
- line 37: `ContentView_Previews`: (provided by the Xcode scaffolding) defines what will be displayed in the simulated device on the right. In this case, it is merely the `ContentView`. This allows following the how the global view develops like while coding it. This is especially useful when learning how to layout views in SwiftUI.

![ImageNameView](./images/Show-two-dice.jpg)



The syntax used for defining the value of the body of a View is defined by a specialized *result builder* which is a predefined *property wrapper*.  These concepts are interesting in themselves, so I spend some time explaining them independently of SwiftUI.  Views also use *trailing closures*, a notation which might be unfamiliar to programmers in other languages than Swift. I will first present this notation used throughout Swift programs.

## Trailing closure

Swift uses *closure* to designate an anonymous function, often called *lambda* in other programming languages. A closure is a list of expressions and instructions within curly brackets. When parameters are specified, they are indicated at the beginning followed by the keyword `in`  such as in the following example for computing the sum of squares of two integers and returning an integer. A closure containing a single expression does not need a `return` keyword in front.

```swift
{ (a:Int, b:Int)->Int in a*a + b*b }
```

In most contexts, type annotation of parameters and result can be inferred by the Swift compiler, such as in the following expression returning 25

```swift
{a,b in a*a + b*b}(3,4)  // => 25
```

A closure is most often used as a functional parameter for functions like `map` that transforms all elements of an array to create a new one in with elements of the original list modified by the function. For example, the next expression creates a list of corresponding squares of two lists of integers. It uses `zip` to build a list of pairs from corresponding elements of two lists.

```swift
zip([1,2,3],[4,5,6]).map({a,b in a*a + b*b})  // => [17,29,45] 
```

As the closure is the last (and only) functional parameter of `map`, the call can also be written as:

```swift
zip([1,2,3],[4,5,6]).map {
  a,b in a*a + b*b
}  // => [17,29,45] 
```

Here I use several lines to show how it would appear if the content of the function would be more elaborated. The fact that it appears without the enclosing parentheses is called a *trailing closure*. 

There are also some other *goodies* for simplifying closures (not only trailing ones) especially in the case of single expressions. Parameters can be referenced implicitly by position with a dollar sign.  The above expression could thus be written as:

```swift
zip([1,2,3],[4,5,6]).map {$0*$0 + $1*$1}  // => [17,29,45] 
```

 And even better (or worse !), a closure of the form `{$0 op $1}`, where `op` is a binary operator, can be simplified as `op`, but then it must be used as a parameter not a trailing closure. For example,

```swift
zip([1,2,3],[4,5,6]).map(+) // => [5,7,9]
(1...5).reduce(1,*)         // => 125 i.e. 5! "reduce" is often called "foldRight" 
```

## Property wrapper 

[*[source code](PropertyWrapper/main.swift)*]

A property wrapper is a notation that encapsulates read and write access to a value and adds additional behavior to it. A Swift property is the name for accessing a value in an object, in SwiftUI most often a `struct`. A property can be either *stored* (as in most programming languages) or *computed* which means that the value is recomputed at each time its value is needed with a *getter* function or changed with a *setter* function. 

I start with a very simple example of a `struct` for a die whose values must be between 1 and 6.  Internally, an instance of `Die` uses a value between 0 and 5 but this fact is hidden from the user. The *concrete* value used for computation is the private integer `number` and the private function `limit` converts the value to the acceptable range. `Die`  *wraps* an integer to limit its values between 1 and 6 by taking the value minus one modulo 6 and *projects* its current value as a string. The value is accessed and modified by the `get` and `set` function of the computed property `wrappedValue`.  The`projectedValue` has only a `get`. 

This seemingly convoluted terminology for such a simple application will prove to be useful later. Note that any action could be added to the *setter* code here,  such as accessing a database, validating a data or refreshing a view!

```swift
struct Die {
    private var number:Int!// implictely unwrapped optional to allow limit call in init()
    
    init(wrappedValue:Int){
        number = limit(wrappedValue)
    }
    
    var wrappedValue: Int {
        get { number+1 }
        set { number = limit(newValue) }
    }
    
    var projectedValue:String {
        get {["one","two","three","four","five","six"][number]}
    }
    
    private func limit(_ val:Int) -> Int {
        return max(0,(val-1)%6)
    }
}
```

Given this definition, the creation of an instance of this `struct` would be
            `var d = Die(wrappedValue:10)` 

Accessing the constrained values would be `d.wrappedValue`, in this case 4, or `d.projectedValue`, in this case `"four"` . 

This seems a bit cumbersome, but once this `struct` is prefixed with `@propertyWrapper`, the Swift compiler greatly simplifies the access and manipulation because the *wrapped* integer can now be used like any other integer.  So the structure definition would be 

```swift
@propertyWrapper struct Die {.. same as above ..}
```

The declaration and initialization of an integer of this type become
           `@Die var d = 10`  

The ampersand before a property wrapper creates an *attribute* in the Swift terminology, in a way like *annotations* in other programming languages. Now `d` , whose value is 4, can be used like any integer in an expression such as `d*3+d`, the range constraint being applied transparently . The projected value is obtained with `$d` which returns `"four"`. 

So that the compiler knows which variable are *wrapped* and *projected*, **the variable names `wrappedValue` and `projectedValue` must be used in the definition of the `struct`**. As an added bonus, `_v` gives access to the `struct` instance itself, although this is seldom needed.

## Result builder 

[[*source code*](Block/index.html)]

A *[result builder](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/advancedoperators/#Result-Builders)* is a user-defined type attribute that adds syntax for creating nested data, like a list or tree, in a natural, declarative way. The code that uses the result builder can include ordinary Swift syntax, like `if` and `for`, to handle conditional or repeated pieces of data. This notation allows the creation of  Domain Specific Languages (DSL), see [some *spectacular* examples](https://github.com/carson-katri/awesome-result-builders). It explains how the body of a SwiftUI view can be considered as a single expression without spurious parentheses, brackets and commas.

I now give an example of a result builder, independently of SwiftUI. It is a command-line application featuring a *Blocks world* with the following behavior:

- A `Block` is created from a text (a `String`) with, optionally,  a border (a `String` with a single character), a  *width*, and a *height*. If the text contains newlines, all lines are centered in the block as for a SwiftUI `Button`. If a border is specified, it is added around the block and if `height` or `width` are specified, they are used for centering the text vertically or horizontally.

- A `Block` can be printed using the method print(). Here is a first interaction.

  ```swift
  Block("We\nlove\nSwiftUI",".").print()
  ```
  which outputs

  ```
  .........
  .  We   .
  . love  .
  .SwiftUI.
  .........
  ```

- A `Block`  or list of `Block`s can be added to the left, to the right, to the top and to the bottom of another one to create a new `Block` .  It also possible to create  copies of a block either horizontally or vertically. Blocks of different height or width are centered relative to one another.  A border can be added to the final result. 

  Here is an example of chained calls with the result,

  ```swift
  Block("We\nlove\nSwiftUI")
      .add(right:Block("|").repeating(vertical: 4))
      .add(right:[Block("truly"),Block("so"," ")])
      .add(bottom:Block("very much","~",width:10))
      .border("+").print()
  ```

  which outputs

  ```
  +++++++++++++++++++
  +  We   |         +
  + love  |truly so +
  +SwiftUI|         +
  +       |         +
  +  ~~~~~~~~~~~~   +
  +  ~very much ~   +
  +  ~~~~~~~~~~~~   +
  +++++++++++++++++++
  ```

  

Using the result builder explained later this intricate chain of embedded calls can be simplified with the following call that is more intuitive and reminiscent of a SwiftUI view.

```swift
Vert("+") {
    Horiz {
        Block("We\nlove\nSwiftUI")
        Vert {for _ in 0..<4 {Block("|")}}
        Block("truly")
        Block("so"," ")
    }
    Block("very much","~",width:10)
}.print()
```

​	

A `resultBuilder` is a predefined property wrapper for building at compile time elaborated data structures or lists of calls. It can be considered as a kind of structured macro system that separates the various components and combines them into calls to the appropriate data structure.  

The first thing to define is a `resultBuilder` structure to deal with a list of statements, conditionals and loops.  In our case, all functions of the result builder create a list of `Block`s of various forms: variadic parameters in the first case and list of list of blocks in the second and last cases. `buildEither` calls deal with conditional statements and `buildExpression` is applied automatically by the `resultBuilder` interpreter when a single `Block` is encountered. Definitions for `buildEither` (lines 13-18) and `buildArray`  (lines 19-21) are optional, but when they are not defined then no conditional statement, nor loop can be used in the calls.

```swift
@resultBuilder struct BlocksBuilder {
    static func buildBlock(_ components: Block...) -> [Block] {
        components
    }
    // deal also with variadic parameter of list of blocks
    static func buildBlock(_ components: [Block]...) -> [Block] {
        Array(components.joined())
    }
    // transform a single block into a list of blocks
    static func buildExpression(_ expression: Block) -> [Block] {
        [expression]
    }
    static func buildEither(first component: [Block]) -> [Block] {
        component
    }
    static func buildEither(second component: [Block]) -> [Block] {
        component
    }
    static func buildArray(_ components: [[Block]]) -> [Block] {
        Array(components.joined())
    }
}
```

The resulting property wrapper `BlocksBuilder` can then be used to define the following two functions to create an appropriate list of calls to `Block`s. The last parameter is a Swift *trailing closure* that is called to create the list of blocks that are stacked either to the right for `Horiz` or to the bottom for `Vert`. The first parameter is the optional border that is applied. 

```swift
func Horiz(_ border:String="",@BlocksBuilder content:() -> [Block])->Block {
    let blocks = content()
    return blocks[0].add(right:Array(blocks[1...])).border(border)
}

func Vert(_ border:String="",@BlocksBuilder content:() -> [Block]) -> Block {
    let blocks = content()
    return blocks[0].add(bottom:Array(blocks[1...])).border(border)
}
```

With these can now write functions to print blocks of numbers, here $C_k^n$  in a pyramid such as this

```swift
Vert{
    for n in 0...5 {
        Horiz {
            for k in 0...n {
                Block(String(C(n,k)),width:4)
            }
        }
    }
}.print()
```

to create a [Pascal's triangle](https://en.wikipedia.org/wiki/Pascal%27s_triangle)

```
           1
         1   1
       1   2   1
     1   3   3   1
   1   4   6   4   1
 1   5   10  10  5   1
```

 or to print a checkerboard 

```swift
let black = Block("*","*") // create a 3x3 block of 9 "*"
let white = Block(" "," ") // create a 3x3 block of 9 " "
Vert("+"){
    for i in 1..<8 {
        Horiz {
            if i%2 == 0 {
                for _ in 1...4 {black ; white}
            } else {
                for _ in 1...4 {white ; black}
            }
        }
    }
}.print()
```

whose output starts with

```
++++++++++++++++++++++++++
+   ***   ***   ***   ***+
+   ***   ***   ***   ***+
+   ***   ***   ***   ***+
+***   ***   ***   ***   +
+***   ***   ***   ***   +
+***   ***   ***   ***   +
+   ***   ***   ***   ***+
+   ***   ***   ***   ***+
```

## Back to SwiftUI

The `resultBuilder` for creating views in SwiftUI is (appropriately) called `ViewBuilder` which is seldom called directly. It was defined by SwiftUI designers for declaring functions that create the views to perform layout e.g. `VStack`, `HStack`, `List` , etc. The trailing closure explains the peculiar syntax which also relies on the fact that a function definition containing a single expression does not need to add the `return` keyword.

Equipped with this specialized result builder, static views can be built, but the key point is how to link this view with an application model. SwiftUI uses again *property wrappers* which proved to be well adapted for transparently linking view updates with object modifications. Swift being statically typed, SwiftUI designers managed to use types to limit the number of views that need to be recreated when certain variables and objects are changed. 

## State management system

In SwiftUI, a view is bound to some data (or state) as a source of truth, and automatically updates whenever the state changes. Every view has a state, which can be changed during execution and each time the state is changed, the view is **recreated**, remember that a view is immutable. 

As shown above, annotating a variable as a `propertyWrapper` allows executing code at each access and or modification.  SwiftUI uses this feature to ensure that when a state variable is modified, the view is recreated accordingly to reflect the new value. The code that Swift executes on getter and setter of the state property wrapper is hidden from the user.

SwiftUI provides [17 property wrappers](https://www.hackingwithswift.com/quick-start/swiftui/all-swiftui-property-wrappers-explained-and-compared), but I present only the five ones that I consider to be fundamental: `@State`, `@Binding`, `@StateObject`, `@ObservedObject`, `@Environment`. 

I first describe the first two attributes that are used for local modifications of simple values and use them in a small application. The other three will be used in the next application.

-  `@State` is a source of truth for the view and is used when the scope of changes is limited to the current view. The framework allocates a persistent storage for this value type and makes it a dependency, so changes to the state will automatically be reflected in the view. Usually a `@State` annotates a  *private* variable, because it is used by the view internally.
-  `@Binding`  can read and write a value owned by a source of truth, for example, a `@State`. An interesting feature is the fact that the **projected value of a State is a binding** than can be passed in a view hierarchy and change it. The changes will be reflected on any view which uses it as a source of truth.

In iOS 17 and macOS 14, the state management API of SwiftUI has changed while still staying compatible with previous versions. As the basic principles stay the same (specifically `@State` and `@Binding`), we will first describe the *previous* API and [explain later](#changes-for-ios-17) how the same application can be somewhat *simplified* with the new API.

## Roll two dice

[*[source code](Roll-two-dice/ContentView.swift)*]

The following example, a variation on our [first example](#show-two-dice), shows typical use cases for these two property wrappers. It  displays two dice that can be *rolled* by tapping (or clicking) on their view. The global view displays the total of the two dice as shown in the next figure. The left part shows the initial display and the right part a typical display once the user has tapped (or clicked) at least once on each die.

<center><img src="images/DiceView-start.jpg" style="height:150px"/>   <img src="images/DiceView-after.jpg" style="height:150px"/></center> 

A `DieView` defines `number` as a `@State` corresponding to the number of dots on the last draw. Conceptually, this could be a simple integer value, but as **Swift views are immutable**,  `number` must be annotated as a `@State` which performs the appropriate delegation and linking with the view so that when  `number` is changed, the view is recreated appropriately. `number` is initially set to 0 and modified in the code associated with the view modifier `.onTapGesture` in which it is set to a random integer between 1 and 6.  Updating the total is done by subtracting the current value to get the value of the other die before adding the new value. The `Image` and the `Text` displayed in the view depend on the value of number, so each time a new number is regenerated, the `Image` and the `Text` are recreated,


```swift
struct DieView:View {
    @State private var number:Int = 0
    @Binding var total:Int
    var body: some View {
        VStack {
            Image(systemName: number==0 ? "squareshape" 
                                        : ("die.face."+String(number)))
                .imageScale(.large)
            Text(["—","one","two","three","four","five","six"][number])
                 .fontWeight(.bold)
        }.padding()
         .border(.red)
         .onTapGesture {
             let other = total - number
             number = Int.random(in:1...6)
             total = other + number
         }
    }
}
```

The `ContentView` stacks two `DieView`s side by side over a text that displays the `total` of the two dice when it is not 0. `total` is a local `@State` to the `ContentView`, but it needs to be passed to the two `DieView`s to be updated.  For this,  the `Binding` associated with the `State` must be retrieved and sent to each `DieView`. As the `projectedValue` of a `State` is a `Binding` (how convenient!), the binding  `$total` is given to each call to `DieView` and any modification done to total in a `DieView` will be reflected in the `ContentView` to recreate its `Text` with the new value.

```swift
struct ContentView: View {
    @State private var total:Int = 0
    var body: some View {
        return VStack {
            HStack{
                DieView(total: $total)
                DieView(total: $total)
            }
            Text(total == 0 ? "Tap a die" : "Total: \(total)")
        }.padding().border(.blue)
    }
}
```

So conceptually a variable with attribute `@State` is used for storing a local value that is *watched*  by the system to ensure that the display is updated when it is modified. A variable with attribute `@Binding` has similar properties but it is linked to a `@State` variable in another context.  The Swift implementation ensure that the new view (re)creation process is transparent to the user and efficient. This how the *controller*  of the Model-View-Controller is bypassed, thus simplifying the flow of information, because when the state is changed, the view is automatically *changed* to reflect the new state.

## Solitaire Yahtzee

[*[source code](Yahtzee/index.html)*]

To show use cases of other property wrappers available in SwiftUI, I now present an application for playing a solitaire version of the game of [Yahtzee](https://en.wikipedia.org/wiki/Yahtzee).  The objective of the game is to score points by rolling five dice to make certain combinations. The dice are rolled three times in a turn to try to make various scoring combinations. On the second or third turn, the player can *fix* some dice and roll only the others. After the third roll, the player chooses a scoring category to add to the total score. Once a category has been selected in the game, it cannot be used again. The scoring categories have varying point values, some of which are fixed values and others for which the score depends on the value of the dice.  The goal is to score the most points over the 13 turns.

### Description of the Yahtzee interface

<center><img src="images/Yathzee-1.jpg" alt="Yathzee-0" style="width:160px;border:thin solid black;" />    <img src="images/Yathzee-2.jpg" alt="Yathzee-0" style="width:160px;border:thin solid black" />    <img src="images/Yathzee-3.jpg" alt="Yathzee-0" style="width:160px;border:thin solid black" />  <img src="images/Yathzee-4.jpg" alt="Yathzee-0" style="width:160px;border:thin solid black" /></center>

As shown in this figure showing the first turn, the top part is a display of five dice, followed by buttons `Roll` and `C`. Below are shown two sections of possible dice combinations, called categories,  with subtotal scores. When tapped, the `Roll` button assign new values to the dice.

The second part on the left of the figure shows the situation once the user has rolled the dice three times. As the three dice in the middle have been *fixed* by clicking on them, thus changing their color, only the values of the first and last dice have changed. The system displays all possible scores: a *Small straight* (a list of 4 consecutive values here 1,2,3 and 4) worth 30 points or 2 *Threes* worth 6 points, an Ace, a Two or Chance which is the sum of all dice values. 

In the third part of the figure, the user has chosen the *Small straight* by tapping on it and its total is added to *Lower section total* and *Grand total*. Once a category has been selected, it cannot be selected again. The game continues until all 13 categories have been selected. If at a turn no *paying* category occurs,  one must be chosen anyway which results in a 0 score for this turn.

The last part of the figure shows what happens when the user clicks on the `C` button  for changing the color of the selection. This change of color would also affect *fixed* dice.

I do not claim that this user interface is the best one or the most intuitive for this game, but it illustrates the use of SwitfUI complex object attributes in a restricted setting. It involves `class` instances, and not only `structs`,  with methods and properties, these instances being shared between many views. 

I first define the roles of three attributes, although the nuances might seem a bit cryptic at this point, I hope that they will become clearer once they are seen in action.

-  `@ObservedObject` This property wrapper annotates a complex object in which it is the user that is responsible for indicating that a part of the object has been changed by calling the `objectWillChange` method required by the `ObservableObject` protocol.  This seems complex, but thanks to the property wrapper `@Published`  (yet another attribute!), this method is called automatically when the annotated part of the object is changed. 
-  `@StateObject` is a special kind of`@ObservedObject` that should be used within the view in which the object is created to indicate to SwiftUI it is the owner of this object. Other views that reference this object should use `@ObservedObject`.
-  `@EnvironmentObject` This property wrapper also doesn’t create or allocate the object itself. Instead, it provides a mechanism to share values across a hierarchy of views. 

### Organization of the application

The main view (`ContentView` in the usual SwiftUI terminology) is organized as follows from top to bottom

- View with five dice and two buttons
- Upper section with 6 categories
- Total and bonus of upper section
- Lower section with 7 categories
- Lower section total and grand total

This is coded as follows in the body of the main `ContentView`. The other Views will be explained later.

```swift
var body: some View {
    VStack {
        HStack (spacing:3){ // top view with dice and buttons
            DiceView(dice: dice)
            Button (move,action:roll_dice)
                .frame(width: 90)
                .buttonStyle(.bordered)
                .fixedSize()
            Button ("C", action: selectColor.nextColor)
        }
        SectionView(section: upper_section,action: update_totals)
        CategoryView(kind: "Upper section total",score: $upper_total)
        CategoryView(kind: "Upper section bonus",score: $upper_bonus)
        SectionView(section: lower_section,action: update_totals)
        CategoryView(kind: "Lower section total", score: $lower_total)
        CategoryView(kind: "Grand total", score: $grand_total)
        if upper_section.all_selected() && lower_section.all_selected(){
            // must call .init() so that Markdown string interpolation works 
            Button (.init("*Game over: \(grand_total) points*\nClick to restart"),
                    action:restart)
              .font(.title)
              .buttonStyle(.bordered)
       }
    }.environmentObject(selectColor)
}
```

The internal states (sources of truth) for this application are kept in instances of two classes:

- `Dice` whose properties are 
  - an array of 5  `Int`s  with the current value of each die
  - an array of 5  `Bool`s  indicating whether the die at the same index is fixed or not
  - function `roll` which sets a random number between 1 and 6 to all dice that are not fixed
  - function `clear_fixed` which indicates that no die is currently fixed

[[*source code*](Yahtzee/DiceView.swift)]

```swift
class Dice:ObservableObject {
    @Published var dice  = [Int](repeating: 0, count: 5)
    @Published var fixed = [Bool](repeating:false, count:5)
 
    func roll(){...}
    func clear_fixed(){...}
}
```

- `Section` whose properties are
  - an array of `String` for keeping the names of the categories of this section
  - an array of `Int` (having the same length as the names) for the current value of the score associated with a name; this value is initially -1 to show that no score has yet been computed
  - an array of `Bool` (same length as the names) indicated if this category has been selected
  - function `set_scores` for setting the values of the scores
  - function `clear_unselected` for indicating that unselected scores should be reset to -1
  - function `all_selected` for indicating that all its scores have been selected
  - function `total` for computing the total of selected categories

[[*source code*](Yahtzee/SectionView.swift)]

```swift
class Section:ObservableObject {
    var names:[String]
    @Published var scores:[Int] = []
    @Published var selected:[Bool] = []
  	init(_ names:String){...}
  	func init_scores(){...}
    func set_scores(values:[Int]){...}    
    func clear_unselected(){...}
    func all_selected()->Bool {...}
    func total() -> Int {...}
}
```

As properties of instances of these classes will be shared among many views, they cannot be annotated as `@State` with the corresponding `@Binding` which can only be used for *simple* values.  So these classes inherit from the `ObservableObject` protocol. This implies that the programmer must ensure that the `objectWillChange` method is called on any modification of the object. But thanks to the `@Published` attribute, this happens automatically when references are made to this object. So the  `Dice`  class  *publishes* its `dice` and `fixed` properties while the  `Section` class *publishes* its `scores` and `selected` properties that need to be used externally. Note the `names` of the section are not needed outside the `Section`, so this list is not marked as an attribute, but as an *ordinary* local variable to the class instance.

Instances of these classes will be used in specialized views. Here is the `DiceView` which itself uses a view for a single die. [[*source code*](Yahtzee/DiceView.swift)]

```swift
struct DieView:View {
    @Binding var value:Int
    @Binding var fixed:Bool
    @EnvironmentObject var selectColor:SelectColor
    
    var body: some View {
        Image(systemName:value==0 ? "squareshape":("die.face."+String(value)))
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 50, height: 50)
            .background(fixed ? selectColor.color : .clear)
            .onTapGesture {fixed.toggle()}
    }
}

struct DiceView:View {
    @ObservedObject var dice:Dice    
    var body: some View {
        ForEach (dice.dice.indices,id: \.self) {i in
            DieView(value: $dice.dice[i],fixed: $dice.fixed[i])
        }
    }
}
```

The `DiceView` gets a `Dice` object as a parameter and creates a list of `DieView`s. As the `Dice` instance is an `ObservableObject`, it must be marked with the attribute `@ObservedObject`.  In `DieView`, `value` and `fixed` define the state of a die, but it must be used outside, so they are annotated with `@Binding`. In `DiceView`, all `DieView`s are created by passing the binding for each `Die` thus the dollar sign in front. The property `selectColor` will be dealt with later.

Viewing a section composed of categories is as  follows  [[*source code*](Yahtzee/SectionView.swift)]

```swift
struct CategoryView:View {
    var kind:String
    @Binding var score:Int
    var body: some View {
        HStack{
            Text(kind)
            Spacer()
            Text(score<0 ? "—" : String(score))
        }
        .padding(.horizontal)
        .font(.title2)
    }
}

struct SectionView:View {
    @ObservedObject var section:Section
    let action:()->Void // callback on parent...
    @EnvironmentObject var selectColor:SelectColor

    var body: some View {
        VStack{
            ForEach(section.names.indices,id: \.self) {
                i in CategoryView(kind:section.names[i], score: $section.scores[i])
                    .background(section.selected[i] ? selectColor.color : .clear)
                    .TapGesture {
                        if !section.selected[i]{
                            section.selected[i] = true
                            action()
                        }
                    }
            }
        }.padding(.vertical).border(.black)
    }
}

```

A `CategoryView` displays its `kind` (a `String`) with the corresponding score (a dash if is negative). Its value is not modified inside this view, but it can be modified by the parent view (see function `update_totals`, lines 36-46 of next listing), so it is annotated as `@Binding`.  `SectionView` gets a `Section` instance, an `ObservableObject`, as a parameter so its declaration must be annotated with `@ObservedObject`.  Once the selection has been done, the scores must be updated in the main view  by the caller. These actions are wrapped in a closure parameter[^3]. The body of the `SectionView` creates a `CategoryView` for each name of its section. When it is tapped, the category is marked as selected and its background color is changed.

With these views, the application is built as follows [[*source code*](Yahtzee/ContentView.swift)] [[*source code of yahtzeeScores*](Yahtzee/score.swift) ].

```swift
struct ContentView: View {
    @State private var move:String = "Roll"
    @State private var nbRolls:Int = 0
    @StateObject private var dice = Dice()

    @StateObject private var upper_section
        = Section(["Aces", "Twos", "Threes", "Fours", "Fives", "Sixes"])
    @State private var upper_total:Int = 0
    @State private var upper_bonus:Int = 0
    
    @StateObject private var lower_section
        = Section(["3 of a kind", "4 of kind", "Full house",
                   "Small straight", "Large straight", "Yahtzee","Chance"])
    @State private var lower_total = 0
    @State private var grand_total = 0
    
    @StateObject var selectColor = SelectColor()
    
    func roll_dice(){
        nbRolls += 1
        if nbRolls <= 3 {
            dice.roll()
            if nbRolls == 3 {
                show_scores()
                move = "Score"
            }
        }
    }
    
    func show_scores(){
        let (upper,lower) = yathzeeScores(dice: dice.dice)
        upper_section.set_scores(values:upper)
        lower_section.set_scores(values:lower)
    }

   func update_totals(){        
        upper_section.clear_unselected()
        lower_section.clear_unselected()
        upper_total = upper_section.total()
        upper_bonus = upper_total >= 63 ? 35 : 0
        lower_total = lower_section.total()
        grand_total = upper_total + upper_bonus + lower_total
        dice.clear_fixed()
        move = "Roll"
        nbRolls=0
		}

    func restart(){
        upper_section.init_scores()
        lower_section.init_scores()
        update_totals()
    }

    var body: some View {
      ... see the code at start of section ...
    }
 }
```

The status of the system is kept in the following *simple* variables (of *value* type in Swift terminology) annotated as `@State` :

- `move`: label for the `Button`: either `"Roll"` or `"Choose\nscore"` (line 2)
- `nbRolls`: tracks the number of rolls (between 0 and 3) (line 3)
- `upper_total`, `upper_bonus`, `lower_total`, `grand_total`: tracks the various scores at this point of the game (lines 8,9,14,15)

There are three `@StateObject`s, ignoring `selectColor` for the moment:

- `dice`: an instance of `Dice` (line 4)
- `upper_section`, `lower_section`: instances of `Section` with different names. (lines 6,11)

The body of the `View` is vertical stacking of 8 views, the first one being a line with the dice and two buttons. The action associated with the first button tracks the number of rolls and displays the scores at the third. The second button, used to change the color of the selection, will be presented later.

Then are added the upper and lower sections followed by two `CategoryView`s for their partial scores. When all categories are fixed, then a `Text` is added at the bottom.

The code for the `show_scores` and `update_totals` is straightforward is not discussed here as I want to focus on the SwiftUI interface aspects.

### Accessing the global environment

The [Environment](https://developer.apple.com/documentation/swiftui/environment/) is a mechanism used by SwiftUI to propagate values from a *View* to its descendants. It is used implicitly for handling some methods that are available on all view types. When these types of function are called their effect is to change a value that is available not only in the current view but also in all embedded views of this one.  There are about 50 predefined [EnvironmentValues](https://developer.apple.com/documentation/swiftui/environmentvalues)  dealing with global objects (e.g. `locale`, `timeZone`), characteristics of the display (e.g. `colorScheme`), text styles (e.g. `font`, `lineSpacing`), view attributes (e.g. `backgroundStyle`). 

But it is also possible to define custom environment values to transfer information from one view to another without passing them as parameters as this can become cumbersome in complex applications. 

In my simple application, this is probably an *overkill*, but I want to give an example of the use of an environment object. I define a color used for a *fixed* die or for a *selected* category (see the last part of the last figure). This color can be changed by clicking the `C` button. There are five choices of color that are selected in rotation. The environment is an instance of an `ObservableObject` with `@Published` properties and kept in the `ContentView` as a `@StateObject`. The object tracking the current color is follows  [[*source code*](Yahtzee/SelectColor.swift)] 

```swift
class SelectColor:ObservableObject {
    @Published var color:Color = .yellow
    let colorChoices:[Color] = [.yellow,.teal,.pink,.green,.orange]
    var current = 0
    
    func nextColor(){
        current = (current+1)%colorChoices.count
        color = colorChoices[current]
    }
}
```

 For setting the environment, an instance of this class is given as a parameter to the global view modifier `environmentObject`  (see line 24 of [`ContentView`](#organization-of-the-application)). When the user taps on the `C` button, the `nextColor` method of this object is called to change the value of the `@Published` variable `color`. This *new* color is used in the current view for setting the color of the text for the end of game, but also in the `DieView` and `SectionView` which refer to this environment object with the following attribute.

```swift
@EnvironmentObject var selectColor:SelectColor
```

In the code of the views, the color itself is referred to `selectColor.color`. So when the value in the environment object is changed, this value is propagated to all views in the hierarchy. As for global variables, this is useful for making changes to the whole application, but it should be used parsimoniously at it implies recreating many views.

### Changes for iOS 17

[*[source code](Yahtzee_17/index.html)*] *file names are the same as the previous ones, but suffixed by `_17`*

Starting with iOS 17 and macOS 14, SwiftUI provides support for [Observation](https://developer.apple.com/documentation/Observation), a Swift-specific implementation of the observer design pattern implemented by means of macros that modify the abstract syntax tree of the program. The compiler can then track changes in objects and in the environment. This [migration guide](https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro) illustrates the changes on a simple example.

In our case, the following changes are made for IOS 17:

- Variable declarations annotated by `@StateObject/@ObservedObject` are annotated by `@State`
- `@EnvironmentObject` annotations are replaced by `@Environment(`*class_name*`.self)`
- The `@Binding` annotations stay the same
- Class inheriting from `ObservableObject` is now annotated with `@Observable`
- `@Published`  annotations are removed
- The function call `.environmentObject(...)` is replaced by`.environment(...)`

The logic of the application is not changed, the main advantage is that any state is annotated the same independently of the fact that it is a single variable or an object. It is the job of the compiler to detect changes in the object and redraw only the necessary views

## Conclusion

This document has presented small SwiftUI applications featuring some aspects of SwiftUI. After presenting how SwiftUI differs from the usual approach to developing interactive applications, some *unusual* features of Swift such as *trailing closure*, *property wrapper* and *result builder*, were first described as they are used extensively to define views in SwiftUI. State management and the associated bindings being at the core of SwiftUI, they were first illustrated with a single view application. Then more complex state management attributes were presented and put in action in an application using multiple objects and views. 

This document does not deal with many  aspects of SwiftUI such as scene management, animation or navigation lists, but I feel it gives the necessary tools to tackle them, because state management is at the core of SwiftUI. 

# References

- Chris Eidhof and Florian Kugler, *Thinking in SwiftUI, a Transition Guide*, 1st edition, [2nd edition](https://www.objc.io/books/thinking-in-swiftui/) 
  *Very informative, but you should read the first edition (unfortunately not unavailable on the web...)  before tackling the second.*

- Mateus Rodrigues: Understanding SwiftUI : [View](https://dev.to/mtsrodrigues/understanding-swiftui-view-331n) ; [Modifiers](https://dev.to/mtsrodrigues/understanding-swiftui-modifiers-3e50)
  *Very clear and to the point explanation of views and modifiers*

- Aleksandr Gaidukov: [How to Approach Wrappers for Swift Properties](https://www.toptal.com/swift/wrappers-swift-properties)
  *High level and clear explanation of property wrappers*

- Mike Zaslavskiy,   Anthony Williams,   Ryan Zhang: [How the SwiftUI View Lifecycle and Identity work](https://doordash.engineering/2022/05/31/how-the-swiftui-view-lifecycle-and-identity-work/) 
  *Very good explanation of the Swift state management works.*

- Paul Hudson, [What’s the difference between @ObservedObject, @State, and @EnvironmentObject?](https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject#:~:text=Summing%20up%20the%20differences,be%20using%20%40ObservedObject%20for%20it.)
  *Short and to the point introduction to SwiftUI attributes with a good illustration of their differences.*

# Appendix: Useful tricks

Here is a list of personal *non-obvious* techniques that I found useful during my SwiftUI development. 

- To find all predefined controls, system and images in Xcode:

  CMD-Shift-L : show Library or click the `+` in the upper right of the application window 

- To see how the various views are organized add `.border(.red)` to the views

- From the first edition of [Thinking in Swift](https://www.objc.io/books/thinking-in-swiftui/) (p. 10)

  To inspect the underlying type of the body, use the following helper function:

```swift
    extension View {
      func debug() -> Self {
        print(Mirror(reflecting: self).subjectType)
        return self
      } 
    }
```

​	The function is used like this to print out the view’s type when the body gets executed:

​			**`var`** `body:` **some** `View` `{ VStack { */\*... \*/* }.debug()}`

- within the body of a View it is possible to execute some code with the following pattern

  ```swift
  { // some arbritrary code...
    return {
       View definition as usual
    }
  }
  ```

- From the second edition of [Thinking in Swift](https://www.objc.io/books/thinking-in-swiftui/) (p. 45)

​	Insert `print` in a `View` body while ignoring the `Void` return type...

​			`let _ = print("Executing <MyView> body")`  //  print when the body is re-executed
​			`let _ = Self._printChanges()` //  print why the body is re-executed

- *Simulate*  a callback function in a `View` by adding a function parameter such as

  ```swift
  let action:()->Void
  ```

  that can be called from within the view to execute code in the caller context. The view is then created with `theView(action: the function)` or with a trailing closure.

[^1]: My text editor claims that this document is less than a 20-minute read, but it surely does not consider making  sense of the computer code...

[^2]: I often wonder if some of these features were not introduced primarily for the sake of SwiftUI.
[^3]: In principle, updating scores could have been done with a SwiftUI *simultaneous gesture*, but in SwiftUI the gesture of the caller is executed before the one of the callee, a dubious choice... This is not what is needed in my case because the selection must be considered for computing the totals before clearing them. I would have needed to *bubble* in JavaScript parlance, this explains the *callback* *trick*.

