slidenumbers: false


# [fit] Codify the Pattern

## [fit] *How Swift lets us eschew convention*

^ This talk will focus on type design at the lowest level.

^ A lot of these concepts will translate to other languages.

^ Swift has a number of features that allow us to ensure a hardened model, especially when compared with Objective-C.

---

# Codify the Pattern

Conventions get embeded in team culture and the app

_Possibly_ documented:
* readme
* wiki
* Confluence

Swift makes it easy to codify conventions

^ What do I mean when I say convention and pattern?

^ Generally any practice which is documented (or not!), but which is actually possible to break in code.

^ An example of this might be a network request returning a string and the API author saying it's always a numerical value.

---

# [fit] Swift is opinionated

^ Since inception, Swift is far more opinionated than Objective-C.

^ Many things that were a mistake in Objective-C are compilation errors in Swift.

---

# Objective-C Conventions vs Swift

**Objective-C Convention**    | **Swift**
------------------------------|------------------------------------
Initialization                | `convenience` & `designated`
Mutable / Immutable types     | `var` / `let`
nil, null, NSNull, -1         | Optional
Properties -title, -setTitle: | var title { get {} set {} }

^ Initialization in Swift follows a gauranteed approach. Convenience initializers **must** call the designated initialiser of self.

^ var and let encode the mutability of types, whereas Objective-C had no such concept. Mutable types came with the Cocoa *framework* which added mutable variants of *some* common types.

^ In many languages, nothingness has multiple definitions. In Swift we have one such mechanism, Optional and Swift's strong typing means we can't accidentally get confused about whether something is nil. **We will know!**

^ Objective-C didn't start with properties, they were defined by a conventional duo of methods, this convention was used for properties.

^ **So if Swift is opinionated, maybe our code should be opinionated.**

---

> Maybe our code should be opionionated
> -- Me, just now

^ So much code seems to be generalised to deal with multiple differnet scenarios, but almost always we either want to make the same assumption about it accross our app **or** these are actually two different types.

^ I've seen assumptions made by views, view controllers or view models, but fundamentally in all these cases it's usually spreading this burden among our apps.

---

# Rectangle Type

```swift
struct Rectangle {

    let identifier: String

    var x: Double
    var y: Double
    var width: Double
    var height: Double

    var red: Double
    var green: Double
    var blue: Double
    var alpha: Double
}
```

^ A fairly harmless type, it's got nine properties.

^ What could be wrong with this? :)

---

# [fit] Atomic Types

^ Atomic types describe properties of a type that all want to change as one.

---

# Atomic Types

Separation of properties that are unlikely to change as one

^ It's unlikely that someone would chnage the red and y values at the same time.

Group properties that change together

^ It's feasible that someone could change the x and y component at the same time by dragging the rectangle around the canvas.

^ If the model was saved for every property change, when the user drags to a new position, causing the x value to change, causing a save, then the y value to changes, causing a second save. We'd rather have those save together.

^ This is the notion of "**atomicness**".

---

# Creating Atomic Types

```swift
struct Position {
    let x: Double
    let y: Double
}

struct Size {
    let width: Double
    let height: Double
}

struct Color {
    let red: Double
    let green: Double
    let blue: Double
    let alpha: Double
}
```

^ Here we separate our values into three distinct types. A position, size and colour. All three of which could change independently of one another.

---

# Creating Atomic Types

```swift
struct Rectangle {
    let identifier: String
    var position: Position
    var size: Size
    var color: Color
}
```

Changes to each property are atomic

^ A property can change as one.

Separation of concerns

^ A property can change as one.

Easier to reason about

^ Easy to think about Positions, Sizes and Colors as distinct things.

These new types can be extended

^ New logic can be added to these specific types.

---

# [fit] Type Specialization

^ Type specialization is what I call creating a type around a primitive where you aren't neccessarily adding new functionality, but rather wrapping it to **remove** functionality.

---

# Type Specialization

```swift
struct Rectangle {
    let identifier: String
    var frame: Frame
    var color: Color
}
```

Identifier is a string so it can be used like a string

```swift
var identifier = rectangle.identifier
let start = identifier.index(after: identifier.startIndex)
let end = identifier.index(before: identifier.endIndex)
identifier.replaceSubrange(start..<end, with: "")
```

^ An identifier isn't valid if you strip the first and last characters from it.

^ This line of thinking can lead to a code smell called “primitive obsession”.

^ Also it's not gauranteed to be unique!

---

# Type Specialization

```swift
struct Identifier {

    private let value: String

    init(_ value: String) {
        self.value = value
    }
}
```

^ We create a type where the sole purpose is to wrap the string.

^ The true value is kept private so actually nothing else can get the value and play with it.

---

# Type Specialization

```swift
struct Identifier: Equatable {

    private let value: String

    init(_ value: String) {
        self.value = value
    }
}
```

Provide Equatable conformance to allow external use to determine equality rather than allow access to the value inside.

^ Often I find that anything that wants access to this property is doing something which actually belongs in this model type itself.

^ For instance equating two identifiers.

---

# Type Specialization

```swift
struct Identifier: Equatable {

    private let value: String

    init(_ value: String = NSUUID().uuidString) {
        self.value = value
    }
}
```

Provide a standard way to generate new identifiers.

---

# Type Specialization

```swift
struct Rectangle {

    let identifier = Identifier()

    var frame: Frame
    var color: Color
}
```

^ Our Rectangle type can now use the identifier creation.

---

# [fit] Constricting types

^ If type specialization creates types that have fewer abilities, then this is about constricting their values.

---

# Constricting types

Developer is confused.

Developer attempts to create color.

```swift
rectangle.color = Color(red: 100, green: 50, blue: 0, alpha: 100)
```

^ Clearly the developer knows what they want to do.

^ In this case they probably want to represent 100% red, 50% green, 0% blue with a 100% alpha value – a lovely bright **orange**!

A fundamental misundertanding has occured.

---

# Validate with Fatal Error

```swift
struct Color {

    init(red: Double, green: Double, blue: Double, alpha: Double) {

        guard
            0 <= red, red <= 1,
            0 <= green, green <= 1,
            0 <= blue, blue <= 1,
            0 <= alpha, alpha <= 1
        else {
            fatalError()
        }

        ...
    }
}
```

^ We could solve this problem by causing a fatalError that way the coder would know about it.

^ In this case it won't fly because the user could have entered these values. Crashing out because a user typed in a wrong number is frowned upon these days.

---

# Validate Using Minimum and Maximum Values

```swift
struct Color {

    init(red: Double, green: Double, blue: Double, alpha: Double) {

        switch red {
			case  ...0 : self.red = 0
			case 1...  : self.red = 1
			default    : self.red = red
		}

        switch green {
			case  ...0 : self.green = 0
			case 1...  : self.green = 1
			default    : self.green = green
		}

        ...
    }
}
```

^ Some code would have us validate that values are between zero and one.

^ It's a lot of code to read to validate each value.

---

# Validate Using Specialized Type

```swift
struct Percentage {

    fileprivate let value: Double
    init(_ value: Double) {

        switch value {
            case  ...0 : self.value = 0
            case 1...  : self.value = 1
            default    : self.value = value
        }
    }
}
```

^ Create a specialised type to deal with this.

^ Adding validation to the input.

---

# Validate Using Specialized Type

```swift
struct Color {
    let red: Percentage
    let green: Percentage
    let blue: Percentage
    let alpha: Percentage
}
```

Each color component now *must* be between zero and one.

Parameters define themselves as percentages.

Unfamiliar developers can look up the Percentage logic.

^
* This is amazing!
* All our values are strongly defined.
* Each color component must be between zero and one.
* Also, as a side effect, the parameters now define themselves:
(A coder coming to this type will either know what a Percentage is or know to delve deeper and not make the assumptions they may have made if it were just a Double.)

---

# Validate Using Specialized Type

```swift
rectangle.color = Color(red: Percentage(2),
                        green: Percentage(0.5),
                        blue: Percentage(-1),
                        alpha: Percentage(100))
```

Components are validated before Color is created.

^ But isn't this a little ugly for Swift?

---

# Expressible By Float/Integer Literal

```swift
extension Percentage: ExpressibleByFloatLiteral {

	init(floatLiteral value: Double) {
		self.init(value)
	}
}

extension Percentage: ExpressibleByIntegerLiteral {

	init(integerLiteral value: Int) {
		self.init(Double(value))
	}
}
```

^ These allow float and int **literals** to be used when creating values.

---

# Expressible By Float/Integer Literal

```swift
rectangle.color = Color(red: 2, green: 0.5, blue: -1, alpha: 100)

// Creates Color(red: 1, green: 0.5, blue: 0, alpha: 1)
```

Using integer and float *literal* values

Values from elsewhere still need to call Percentage initialiser

Makes for easier to read tests

---

# Improving Size

```swift
struct Size {
    let width: Positive
    let height: Positive
}
```

This definition makes it clear what values are accepted

User of this type can inspect to verify their aassumptions of the logic of Positive

---

# Improving Size

```swift
struct Positive {

    fileprivate let value: Double
    init(_ value: Double) {

        switch value {
            case  ...0 : self.value = 0
            default    : self.value = value
        }
    }
}
```

^ And yes by positive, I mean not-negative.

---

# [fit] Preventing Invalid States

^ We've now talked about creating types that have fewer abilities, constricting the values they can take.

^ Here we will look briefly at removing invalid states.

---

# Adding Gradient

```swift
struct Gradient {
	let start: Color
	let end: Color
}
```

^ An example gradient which is probably a little more trivial than we'd actually like. But will suit us for demonstration purposes.

We've made sure Gradient is:

* Atomic

^ A wholesale change in gradient is possible.

* A specialized type

^ It's not a tuple!

* Can't represent an illegal state

^ It must have start and end color.

---

# Add Gradient to Rectangle

```swift
struct Rectangle {
    let identifier = Identifier()
    var position: Position
    var size: Size
    var fill: Color?
    var gradient: Gradient?
}
```

What happens if `fill` and `gradient` are both set?

What happens if they are both nil?

---

# Preventing Invalid States

```swift
enum Style {
	case fill(color: Color)
	case linear(gradient: Gradient)
	case radial(gradient: Gradient)
}
```

^ In this case we want either one or the other option to be set. In fact we can envision wanting to use gradient for linear and radial gradients.

```swift
struct Rectangle {
    let identifier = Identifier()
    var position: Position
    var size: Size
    var style: Style
}
```

^ The style *must* be set. It must either be a fill, a linear or radial gradient.

---

# Drawing a rectangle

```swift
switch rectangle.style {
case .fill(let color): // Fill with color
case .linear(let gradient): // Fill linear gradient
case .radial(let gradient): // Fill radial gradient
}
```

When we come to draw the rectangle, our logic for drawing is pre-defined.

^ There's no second guessing what is needed.

---

# [fit] Validating User Input

^ Let's see how these ideas could be used and extended to validate user input.

---

```swift
extension Percentage {

	init?(_ string: String) {

		guard let double = Double(string) else {
			return nil
		}

		self.init(double)
	}
}
```

Optional initialiser prevents an invalid Percentage being created

But what was the issue?

^ Say we want to let the user provide colors, we can validate their input for each color component.

^ The text field would give us a string.

---

```swift
extension Percentage {

	enum Error: Swift.Error {
		case invalidCharacter, unknown
	}

	init(_ string: String) throws {

		let validSet = CharacterSet(charactersIn: "-.0123456789")
		let set = CharacterSet(charactersIn: string)
		guard set.isSubset(of: validSet) else {
			throw Error.invalidCharacter
		}

		guard let double = Double(string) else {
			throw Error.unknown
		}

		self.init(double)
	}
}
```

^ We can use a throwing initializer to provide more feedback for the caller when invalid values are given.

---

The following gives a valid Percentage

```swift
do {
	let percentage = try Percentage("4")
} catch {
	print(error)
}
```

---

The following throws an invalid character error

```swift
do {
	let percentage = try Percentage("A")
} catch {
	print(error) // "invalidCharacter"
}
```

---

The following throws an unknown error

```swift
do {
	let percentage = try Percentage("0.0.0")
} catch {
	print(error) // "unknown"
}
```

---

# [fit] Restricting APIs

---

# Incorrect Initializers

Types can be extended and custom initializers added:

```swift
extension Percentage {

	init(badValue: Double) {
		self.value = badValue
	}
}
```

^ So in an app, someone could just bypass our checks and just create instances for themselves.

---

# Use modules to prevent misuse

>If an initializer is declared in a different module from a struct, it must use self.init(…) or self = … before returning or accessing self
> -- SE-0189, Restrict Cross-module Struct Initializers

This prevents custom initializers in another module creating invalid instances.

^ You get a warning for directly setting properties before calling an exposed initialiser in Swift 4.1 and above.

^ This will be an error from Swift 5.

---

# [fit] Final Thoughts

---

# Final Thoughts

Raises questions about **edge cases** upfront

^ Over the last two years, I've had everyone from UI and UX designers to business analysts and product owners sit down and work out "edge cases".

^ What happens if that value comes down as nil in the JSON? Should the whole request fail? Should we default to a value? Or should it really just be nil?

Most business logic happens in the model

^ You can add extensions to model types that can easily be tested providing a stronger base to work from.

Actually simpler to change incorrect assumptions

^ Because you have custom types for things, any refactoring that happens becomes easier because you own those domain types.

Can split out JSON -> model types conversion to another module

^ This allows the data types to never know about the JSON data and its structure. Also provides **one** single place where assumptions are made and coded.



---

# **Daniel Tull**
## *@danielctull*
