Learning Swift — Days 127-131

Strengthening our skills

Day 127 part 2

Xcode Shortcut commands

Navigation Area

  • Cmd-0: hides or shows the Navigator
    • Cmd-1…8: cycles through the different navigators
  • Cmd-Option-0: hides of shows the Inspectors
    • Cmd-Option-1…8: cycles through the different (available) inspectors.
  • Cmt-Shift-Y: hides or shows the Debug area

Editors

  • Cmd-Option-Enter: invokes the Assistant Editor
  • Cmd-Enter: go back to Standard Editor
  • Cmd-Shift-O: opens a specified file inside of the project but it will always open it into the primary editor. So, go to Preferences > Navigation and toggle on the Uses Focused Editor.

Text Editing

  • Cmd-/: comments out code
  • Ctrl-I: re-indent all of the selected code.
  • Use // MARK: - to get jump points into your code.

Using the Debugger

In the (lldb) debugger we can write po (print object) followed by some object we want to inspect.

p just prints things out.

We can put breakpoints to stop execution of our app and then use the step over command to go line by line and check what is happening. The step into allows us to go inside a method.

Xcode Behaviours

When things happen we can ask Xcode to behave in a specific way. This part of Preferences is just full, full of things and options and I will refrain from touching them until more comfortable with them.

What the Xcode Project contains

Build Settings: contains all the settings about the current build, from deployment settings to language to linking etc…

Build Phases: all the source files are in Compile Sources. Libraries we have added to our app will be into the Link Binary With Libraries.

Day 128

Strengthening our Swift knowledge

Floating point CountableRange

Floating point numbers don’t stride by Int, they stride by a floating point value. Luckily, there’s a global function that will create a CountableRange from floating point values:

for i in stride(from: 0.5, through: 15.25, by: 0.3) { }

The return type of stride is CountableRange (actually ClosedCountableRange in this case because it’s through: instead of to:). CountableRange is a generic type.

Tuples

It is a grouping of values. We can use it anywhere we can use a type.

let x: (String, Int, Double) = ("hello", 5, 0.85) // the type of x is a tuple.
let (word, number, value) = x // this names the tuple elements when accessing the tuple

… or the tuple elements can be named when the tuple is declared (this is strongly preferred):

let x: (w: String, i: Int, v: Double) = ("hello", 5, 0.85)

The naming is super-flexible!

We can use tuples to return multiple values from a function or method.

Computed Properties

The value of a property can be computed rather than stored. A typical stored property looks something like this:

var foo: Double

A computed property looks like this:

var foo: Double {
	get {
		// return the calculated value of foo
	}
	set(newValue) {
		// do something based on the fact that foo has changed to newValue
	}
}

We don’t have to implement the set side of it if we don’t want to. The property then becomes “read-only”.

But why compute the value of a property? Lots of times a “property” is “derived” from other state. For example, in Concentration game, we can derive this var easily from looking at the cards:

var indexOfOneAndOnlyFaceUpCard: Int?

In fact, properly keeping this var up-to-date is just plain error-prone. This would be safer:

var indexOfOneAndOnlyFaceUpCard: Int? {
	get {
		// look at all the cards and see if you find only one that's face up
		// if so, return it, else return nil
	}
	set {
		// turn all the cards face down except the card at index newValue
	}
}

Let’s change this into our app:

Go to ViewController.swift:

Create a new computed property like this:

var numberOfPairsOfCards: Int {
        return (cardButtons.count + 1) / 2
}

… and go back to the previous implementation of the game:

lazy var game = Concentration(numberOfPairsOfCards: numberOfPairsOfCards)

Go to Concentration.swift:

Change the stored property indexOfOneAndOnlyFaceUpCard to a computed property like this:

var indexOfOneAndOnlyFaceUpCard: Int? {
    get { // when the property is accessed (read)
        var foundIndex: Int?
        for (index, card) in cards.enumerated() {
            if card.isFaceUp {
                if foundIndex == nil {
                    foundIndex = index
                } else {
                    return nil
                }
            }
        }
        return foundIndex
    }
    set { // when the property is set (gets written to)
        for (index, _) in cards.enumerated() {
            cards[index].isFaceUp = (index == newValue)
        }
    }
}

Inside chooseCard(at:) we can remove the indexOfOneAndOnlyFaceupCard = nil because this is already taken care for in the computed property and, in the else part, we can remove everything before the setting of the property at the end.


Access Control

Protecting our internal implementations

When we work on relatively small projects, any object can pretty much call any function in any other object. When projects start to get large, though, this becomes very dicey. We want to be able to protect the INTERNAL implementation of data structures. We do this by marking which API (methods and properties) we want other code to use with certain keywords.

Swift supports protecting our internal implementations by default, that is that this API is usable by any object in our app or framework.

Then we have:

  • private: only callable from within this object
  • private(set): this property is readable outside this object but not settable.
  • fileprivate: accessible by any code in this source file.
  • public (for frameworks only), this can be used by objects outside my framework
  • open (for frameworks only), this is like public but objects outside my framework can subclass this.

A good strategy is to just mark everything private by default then remove the designation when that API is ready to be used by other code.

How this can affect our Concentration app?


Go to ViewController.swift:

We want the game property to be private because the number of pairs of cards in the game is intimately tied to the UI. Both emojiChoices and emoji need to “suffer” the same fate. All our outlets need to be private as we do not want them to be accessible by other future files. All our action methods need to be private as well so, updateViewFromModel (which is not an action but is a method), touchCard(), newGamePressed(), emoji(for:), newGame all need to be marked private.

I am not sure how much this is necessary as in all Paul’s tutorial I have followed until now there was never the need for this but, ok … This is another Paul’s tutorial from Stanford University so I will just follow and get the most out of it.

Go to Card.swift:

The first three properties need to stay public (that is internal access) while identifierFactory and getUniqueIdentifier() both need to be turned private.

Go to Concentration.swift:

The cards array need to be made private(set) because it has to be readable from other files but we (this file) is responsible for setting it.

In theory the flipCount, score and matchCount ones needed to be private as well but in the homework assignment we needed to refactor this to move them outside of the controller so, here, they need to stay like this.

The indexOfOneAndOnlyFaceUpCard computed property needs to be marked private as well, same as the seenCard set.

The chooseCard(at:) method and the initialiser need to stay public (otherwise no one could play our game) but we can insert two asserts to make our code a bit safer. They are:

assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)): chosen index not in the cards")

… and:

assert(numberOfPairsOfCards > 0, "Concentration.init(\(numberOfPairsOfCards)): you must have at least one pair of cards")

Extensions

Extending existing data structures: we can add methods/properties to a class, struct, enum (even if we don’t have the source).

There are some restrictions:

  • we cannot re-implement methods or properties that are already there (only add new ones).
  • the properties we add can have no storage associated with them (only computed).

This feature is easily abused: it should be used to add clarity to readability, not obfuscation! We should not use it as a substitute for good object-oriented design technique.

It is best used (at least for beginners like me) for very small, well-contained helper functions. It can actually be used well to organise code but requires architectural commitment. When in doubt (for now), we should not do that.


Optionals

This is a completely normal type in Swift. It’s an enumeration.

Enumerations

Enumerations are a variety of data structure that can have only discrete states. They are VALUE TYPE (like struct) so it is copied as it gets passed around.

They can have associated data:

enum FastFoodMenuItem {
	case hamburger(numberOfPatties: Int)
	case fries(size: FryOrderSize)
	case(String, ounces: Int) // the unnamed String is the brand, e.g. "Coke"
	case cookie
}

Note that the drink case has 2 pieces of associated data (one of them “unnamed”). In the example above, FryOrderSize would also probably be an enum, for example…

enum FryOrderSize {
	case large
	case small
}

Assigning a value to a variable or constant of type enum is easy, just use the same of the type along with the case you want, separated by a dot:

let menuItem: FastFoodMenuItem = FastFoodMenuItem.hamburger(patties: 2)
var otherItem: FastFoodMenuItem = FastFoodMenuItem.cookie

Swift can infer the type on one side of the assignment or the other (but not both).

An enum’s state is checked with a switch statement.

var menuItem = FastFoodMenuItem.hamburger(patties: 2)

switch menuItem {
	case .hamburger: print("burger")
	case .fries: print("fries")
	case .drink: print("drink")
	case .cookie: print("cookie")
}

If we do not want to do anything in a given case, we can use break.

We must handle ALL POSSIBLE CASES (although we can default uninteresting cases). Multiple lines are allowed in each case of an enumeration and the code does not fall through to the next case.

Associated data is accessed through a switch statement using let syntax:

var menuItem = FastFoodMenuItem.drink("Coke", ounces: 32)

switch menuItem {
	case .hamburger(let pattyCount): print("a burger with \(pattyCount) patties!")
	case .fries(let size): print("a \(size) order of fries!")
	case .drink(let brand, let ounces): print("a \(ounces)oz \(brand)")
	case .cookie: print("a cookie!")
}

An enumeration can have methods (and computed properties) but no stored properties. In an enum’s own methods, we can test the enum’s state (and get associated data) using self.

We can modify self in an enumeration as long as it is in a var. We can even reassign it inside an enumeration method, but we need the mutating keyword in front of the method as enum is a value type.

Optional

An optional is just an enumeration and it looks like this:

enum Optional<T> { // a generic type, like Array<Element> or Dictionary<Key,Value>
	case none
	case some(<T>) // the some case has associated data of type T
}

This type is so important that it has a lot of special syntax that other types don’t have:

  • the “not set” case has a special keyword: nil
  • the character ? is used to declare an Optional, e.g. var indexOfOneAndOnlyFaceUpCard: Int?
  • the character ! is used to “unwrap” the associated data if an Optional is in the “set” state, e.g. let index = cardButtons.firstIndex(of: button)!
  • the keyword if can also be used to conditionally get the associated data, e.g.: if let index = cardButtons.firstIndex(of: button) { ... }
  • an Optional declared with ! (instead of ?) will implicitly unwrap when accessed
  • we can use ?? to create an expression which “defaults” to a value if an Optional is not set.
  • we can also use ? when accessing an Optional to bail out of an expression midstream. This is called Optional Chaining.
let y = x?.foo()?.bar?.z

// is equivalent to
switch x {
	case .none: y = nil
	case .some(let data1): 
		switch data1.foo() {
			case .none: y = nil
			case .some(let data2):
				switch data2.bar {
					case .none: y = nil
					case .some(let data3): y = data3.z
				}
		}
}

Optionals always start out at nil.

Data Structures

The four essential data structure-building concepts in Swift are classes, structures, enumerations and protocols

Class

  • Supports object-oriented design
  • Single inheritance of both functionality and data (i.e. instance variables)
  • Reference type (classes are stored in the heap and are passed around via pointers)
  • Heap is automatically “kept clean” by Swift (via reference counting, not garbage collection)
  • Examples: ViewController, UIButton, Concentration

Automatic Reference Counting

Reference types (classes) are stored in the heap, but how does the system know when to reclaim the memory for these from the heap? It “counts references” to each of them and when there are zero references, they get tossed. This is done automatically and it is known as “Automatic Reference Counting” and it is NOT garbage collection.

We can influence ARC by how we declare a reference-type var with these keyword: strong, weak, unowned.

  • strong: is “normal” reference counting. As long as anyone, anywhere has a strong pointer to an instance, it will stay in the heap.
  • weak: this means “if no one else is interested in this, then neither am I, set me to nil in that case”. Because it has to be nil-able, weak only applies to optional pointers to reference types. A weak pointer will never keep an object in the heap.
  • unowned: this means “don’t reference count this; crash if I’m wrong”. This is very rarely used. Usually only to break memory cycles between objects.

Structures

  • Value type (structs don’t live in the heap and are passed around by copying them)
  • they have a very efficient “copy on write” system which is automatic in Swift; this copy on write behaviour requires us to mark mutating methods.
  • they have no inheritance of data.
  • their mutability is controlled via let (e.g., we can’t add elements to an Array assigned by let).
  • they support functional programming design
  • examples: Card, Array, Dictionary, String, Character, Int, Double, UInt32

Enum

  • used for variables that have one of a discrete set of values
  • each option for that discrete value can have “associated data” with it
  • the associated data is the only storage that an enum can have (no instance variables)
  • they are a value type (i.e., passed around by copying)
  • they can have methods and computed (only) properties

Day 129

Today I’m attempting my 100 Days of Swift final exam so that may take the whole allotted time for the time. I will keep you posted.

Day 130

After the exam was passed with distinction, it is time to move on for still two days before leaving for a much deserved (and sought after) week of holiday!


In our card game we should now try to change the Concentration class to a struct because we are not really passing it around. The only other edit to be made is to mark the chooseCard(at:) method as mutating.


Let’s dive a bit more now into protocols.

  • It is a type which is a declaration of functionality only
  • it has no data storage of any kind (so it doesn’t make sense to say it’s a value or reference type).
  • essentially provides multiple inheritance (of functionality only, not storage) in Swift.

Protocols are a way to express an API more concisely.

Instead of forcing the caller of an API to pass a specific class, struct, or enum, an API can let callers pass any class/struct/enum that the caller wants but can require that they implement certain methods and/or properties that the API wants.

The API expresses the functions or variables it wants the caller to provide using a protocol. So a protocol is simply a collection of method and property declarations.

What are protocols good for?

They make API more flexible and expressive, they are very good to create blind, structured communication between View and Controller (known as delegation). They favour mandating behaviour (e.g. the keys of a Dictionary must be hashable), or for sharing functionality in disparate types (String, Array, CountableRange, that are all Collections). Finally they allow for multiple inheritance of functionality (not data!).

A protocol is a TYPE

It can be used almost anywhere any other type is used: variables, function parameters etc…

There are three aspects to a protocol:

  1. The protocol declaration (which properties and methods are in the protocol)
  2. A class, struct or enum declaration that makes the claim to implement the protocol
  3. The code in said class, struct or enum (or extension) that implements the protocol.

Optional methods in a protocol

Normally any protocol implementor must implement all the methods/properties in the protocol. However, it is possible to mark some methods in a protocol optional (not to confuse with the type Optional, which is a different thing). Any protocol that has optional methods must be marked @objc and any class that implements an optional protocol must inherit from NSObject. These sorts of protocols are used often in iOS for delegation (more later on this). Except for delegation, a protocol with optional methods is rarely (if ever) used. As we can tell from the @objc designation, it’s mostly for backwards compatibility.

Declaration of the protocol itself:

protocol SomeProtocol : InheritedProtocol1, InheritedProtocol2 {
	var someProperty: Int { get set }
	func aMethod(arg1: Double, anotherArgument: String) -> Some Type
	mutating func changeIt()
	init(arg: Type)
}

Anyone that implements SomeProtocol must also implement InheritedProtocol1 and 2. We must specify whether a property is get only or both get and set. Similarly, any functions that are expected to mutate the receiver should be marked mutating, unless you are going to restrict your protocol to class implements only with class keyword after the column of the declaration. We can even specify that implementers must implement a given initialiser.

How an implementer says “I implement that protocol”

class SomeClass : SuperclassOfSomeClass, SomeProtocol, AnotherProtocol {
	// implementation of SomeClass here
	// which must include all the properties and methods in SomeProtocol & AnotherProtocol
}

Claims of conformance to protocols are listed after the superclass for a class. For structs there would just be no class inheritance.

Any number of protocols can be implemented by a given class, struct or enum. In a class, inits must be marked required (or otherwise a subclass might not conform).

We are allowed to add protocol conformance via an extension.

Using protocols like the type that they are!

protocol Moveable {
	mutating func move(to point: CGPoint)
}

class Card : Moveable {
	func move(to point: CGPoint) { ... }
	func changeOil()
}

struct Shape : Moveable {
	mutating func move(to point: CGPoint) { ... }
	func draw()
}

let prius: Car = Car()
let square: Shape = Shape()

var thingToMove: Moveable = prius // allowed
thingToMove.move(to ...) // good
thingToMove.changeOil() // error! It's not a Car anymore
thingToMove = square // good
let thingsToMove: [Moveable] = [prius, square] 

func slide(slider: Moveable) {
	let positionToSlideTo = ...
	slider.move(to: positionToSlideTo)
}
slide(prius)
slide(square)

func slipAndSlide(x: Slippery & Moveable)
slipAndSlide(prius) // not allowed

Delegation

It is a very important yet simple use of protocols and it’s a way to implement “blind communication” between a View and its Controller.

How it plays out:

  1. A View declares a delegation protocol (i.e. what the View wants the Controller to do for it)
  2. The View’s API has a weak delegate property whose type is that delegation protocol.
  3. The view uses the delegate property to get/do things it can’t own or control on tis own
  4. The Controller declares that it implements the protocol
  5. The Controller sets delegate of the View to itself using the property in #2 above
  6. The Controller implements the protocol (probably it has lots of optional methods in it).

Now the View is hooked up to the Controller, but the View still has no idea what the Controller is, so the View remains generic/reusable.

This mechanism is found throughout iOS, however, it was designed pre-closures in Swift. Closures are sometimes a better option.

Another use of Protocols: being a key in a Dictionary.

To be a key in a Dictionary, you have to be able to be unique. A key in a Dictionary does this by providing an Int that is very probably unique (a hash) and then also by implementing equality testing to see if two keys are, in fact, the same. This is enforced by requiring that a Dictionary’s keys implement the Hashable protocol.

That means that to be Hashable, you also have to implement Equatable. The Equatable protocol looks like this:

protocol Equatable {
	static func ==(lhs: Self, rhs: Self) -> Bool
}

Types that conform to Equatable need to have a type function (note the static) called ==. The arguments to == are both of that same type (i.e. Self of the type is the type itself).

The == operator also happens to look for such a static method to provide its implementation!

Dictionary is then declared like this: Dictionary<Key: Hashable, Value>. This restricts keys to be things that conform to Hashable (there’s no restriction on values).


In our card game let’s go on and make Card be Hashable. In this way we can use it directly as the key into our emoji dictionary. As a bonus, we’ll be able to compare cards directly since they’ll be Equatable. This will even allow us to make identifier be private in Card, which makes a lot of sense.

Go to ViewController.swift:

Change the type of the emoji dictionary to be [Card : String]. Then, in the emoji(for:) method, remove the .identifier call throughout. A bunch of errors will pop up, don’t panic.

Go to Cards.swift:

Simply conform to the Hashable protocol. As of Swift 4, this required the following code to be added:

var hashValue: Int { return identifier }
    
static func ==(lhs: Card, rhs: Card) -> Bool {
    return lhs.identifier == rhs.identifier
}

…but thankfully this is not needed anymore in Swift 5 (or maybe even a little before, maybe 4.2?).

Last change here, make identifier a private var, as we wanted before. This will cause an error in the Concentration class so …

Got to Concentration.swift:

Inside the chooseCard(at:) method take away any mention to the .identifier call. That’s it!

… running this, though, seems to have broken something… as when the cards match they do not disappear anymore… let’s go back and change one thing at a time…

  • making Concentration a class is not disrupting anything. So let’s move on.
  • simply adding Hashable conformance to Card obviously doesn’t break anything.

The issue is clear when we remove, in the chooseCard(at:) method, .identifier from line 55 if cards[matchIndex].identifier == cards[index].identifier. But let’s try something else first…

Even going back to ViewController.swift and changing the dictionary to be of type [Card : String] breaks everything… sometimes a card gets a question mark, sometimes it gets matched to the wrong one…

Somehow I made it work but … I’m not really sure this is how it should be… Inside the Card struct I added that static func == mentioned above and implemented the following code, without which the app doesn’t work as intended:

func hash(into hashed: inout Hasher) {
	hasher.combine(identifier)
}

I really don’t like where this is heading… but … by now it works again… Just to be clear, I have no idea why the app works with this line and why without it doesn’t but … well …

I have now created two different branches, one on which I have the changed part and one where the app is in its previous state… I really don’t want to lose what I have accomplished until now because of these changes…


Advanced use of Protocols

“Multiple inheritance” with protocols:

  • CountableRange implements many protocols, but here are a couple of important ones …
    • SequencemakeIterator (and thus support for-in)
    • Collectionsubscripting (i.e. [], index(offsetBy:), index(of:), etc. …
  • Why do it this way?
    • Because Array, for example, also implements these protocols.
    • So now Apple can create generic code that operates on a Collection and it will work on both!
    • Dictionary is also a Collection, as is Set and String.
    • …and they don’t all just inherit the fact that they implement the methods in Collection, they actually inherit an implementation of many of the methods in Collection

Using extension to provide protocol implementation.

We said that protocol implementation is provided by implementing types (struct, enum, class). However, an extension, can be used to add default implementation to a protocol. Since there’s no storage, said implementation has to be in terms of other API in the protocol (and any API in any protocol that that protocol inherits from, of course).

For example, for the Sequence protocol, you really only need to implement makeIterator. An iterator implements the IteratorProtocol which just has the method next(). If you do, you will automatically get implementations for all these other method in Sequence: contains(), forEach(), joined(separator:), min(), max(), even filter() and map() and many more.

All of these are implemented via an extension to the Sequence protocol. This extension, provided by Apple, uses only Sequence protocol methods in its implementation.

Functional Programming

By combining protocols with generics and extensions (default implementations), we can build code that focuses more on the behaviour of data structures than storage. This approach to development is called “functional programming”.

It is different than “object-oriented programming” (it’s sort of an evolution thereof).

String

A String is made up of Unicodes, but there’s also the concept of a Character. A Character is what a human would perceive to be a single lexical character. This is true even if a single Character is made up of multiple Unicodes.

For example, there is a Unicode which is “apply an accent to the previous character”. But there is also a Unicode which is é(the letter e with an accent on it). So the string café might be 4 or 5 Unicodes. In either case, we perceive it as 4 Characters.

Because of this ambiguity, the index into a String cannot be an Int. Indices into Strings are therefore of a different type … String.Index The simplest ways to get an index are startIndex, endIndex and index(of:). To move to another index, use index(String.Index, offsetBy: Int).

String is also a Collection (in the same sense that an Array is a Collection) or Characters. All the indexing stuff (index(of:), etc.) is part of Collection.

A String is a value type (it’s a struct).

NSAttributedString

It is a String with attributes attached to each character. Conceptually, an object that pairs a String and a Dictionary of attributes for each Character. The Dictionary’s keys are things like “the font” or “the color”, etc. … The Dictionary’s values depend on what the key is (UIFont or UIColor or whatever). Many times (almost always), large ranges of Characters have the same Dictionary. Here’s how we would make the flip could label have orange, outlined text …

let attributes: [NSAttributedString.Key: Any] = [ // note: type cannot be inferred here
	.strokeColor: UIColor.orange,
	.strokeWidth: 5.0 // negative number here would mean fill (positive means outline
]

NSAttributedString is a completely different data structure than String. The “NS” is a clue that it is an “old style” Objective-C class. Thus it is not really like String (for example, it’s a class, not a struct). Since it’s not a value type, you can’t create a mutable NSAttributedString by just using var. To get mutability, you have to use a subclass of it called NSMutableAttributedString.

NSAttributedString was constructed with NSString in mind, not Swift’s String. NSString and String use slightly different encodings but there is some automatic bridging between old Objective-C stuff and Swift types. However, it can be tricky with NSString to String bridging because of varying-length Unicodes. This all doesn’t matter if the entire string has the same attributes or if the NSAttributedString doesn’t contain “wacky” Unicode characters.

Day 131

This is the last day before the holidays so I will try to progress as much as possible. It is incredible how following a tutorial written in Swift 4.0, so less than 2 years ago, makes thing very, very complicated in term of code correctness… Also, as Paul Hudson said, one should finish one course before delving into another, but I was really lost in creating this card game and didn’t have the time to spend hours and hours staring at the screen. I had to learn how to do things… Now, let’s move on…

Function types

Functions get the full treatment of a type. We can declare a variable (or parameter to a method or whatever) to be of type “function”. We will declare it with the types of the functions arguments (and return type) included.

var operation: (Double) -> Double

It is of type “function that takes a Double and returns a Double”.

We can then assign it like any other variable and we can “call” this function using syntax very similar to any function call.

Closures

Often we want to create the function “on the fly” (rather than already-existing). We can do this “in-line” using a closure.

Where do we use closures? Often as arguments to methods. Many times a method wants to know “what to do” and providing a function tells it what to do.

Array has a method called map which takes a function as an argument. It applies that function to each element of the Array to create and return a new Array.

Closures with property initialisation: we can also execute a closure to do initialisation of a property if we want:

var someProperty: Type = {
	// construct the value of someProperty here
	return <theconstructed value>
}()

This is especially useful with lazy property initialisation.

Capturing: closures are regular types, so they can be put in Arrays, Dictionarys, etc. When this happens, they are stored in the heap (i.e. they are reference types). What is more, they “capture” variables they use from the surrounding code into the heap too. Those captured variables need to stay in the heap as long as the closure stays in the heap.

This can create a memory cycle though. We will see this later in the quarter and how to avoid that.

Let’s see how we can improve something in our game card:

Go to Concentration.swift:

Extend the Collection protocol like this:

extension Collection {
    var oneAndOnly: Element? {
        return count == 1 ? first : nil
    }
} 

Then modify the indexOfOneAndOnlyFaceUpCard property to look something like this:

private var indexOfOneAndOnlyFaceUpCard: Int? {
    get {
        return cards.indices.filter { cards[$0].isFaceUp }.oneAndOnly
    }
    set {
        for (index, _) in cards.enumerated() {
            cards[index].isFaceUp = (index == newValue)
        }
    }
}

This does exactly the same thing that it was doing before, just in a much more efficient way! And it looks great as well!

Thrown Errors

In Swift, methods can throw errors. We always know that a method can throw because it will have the keyword throw at the end. We must put calls to functions like this in a do { } block and use the word try to call them.

do {
	try context.save()
} catch let error {
	// error will be something that implements the Error protocol, e.g., NSError
	// usually there are enums that have associated values to get error details
	throw error // this would re-throw the error (only ok if the method we are in throws)
}

Any & AnyObject

Any & AnyObject are special types. They used to be commonly used for compatibility with old Objective-C APIs but not so much anymore since this old Objective-C APIs have been updated.

Variables of type Any can hold something of any type (AnyObject holds classes only). Swift is a strongly typed language, though, so we cannot invoke a method on an Any, we have to convert it into a concrete type first.

We cannot use these two types directly (since we don’t know what type it really is). Instead, we must convert it to another, known type.

Casting

Conversion is done with the as? keyword in Swift. This conversion might not be possible, so the conversion generates an Optional. We can also check to see if something can be converted with the is keyword (which returns a Boolean).

We almost always use as? with if let.

We can cast any type with as? into any other type that makes sense. Mostly this would be casting an object from one of its superclasses down to a subclass. But it could also be used to cast any type to a protocol it implements.

Other Interesting Classes

  • NSObject: base class for all Objective-C classes. Some advanced features will require us to subclass from NSObject.
  • NSNumber: it can represent any kind of number. It is a generic number-holding class (reference type).
  • Date: value type used to find out the date and time right now or to store past or future dates. There are other classes that go with this, Calendar, DateFormatter, DateComponents. If we are displaying a date in our UI, there are localisation ramifications.
  • Data: a value type “bag o’ bits”. Used to save/restore/trasmit raw data throughout the iOS SDK.

Views

A view (i.e. UIView subclass) represents a rectangular area, defines a coordinate space for drawing and for handling touch events.

It is hierarchical: a view has only one superview but it can have zero or more subviews (var subviews: [UIView]. The order in the subviews array matters: those later in the array are on top of those earlier. A view can clip its subviews to its own bounds or not (the default is not to).

There is also UIWindow, which is the UIView at the very, very top of the view hierarchy (it even includes the status bar). Usually there is only one UIWindow in an entire iOS application … iOS is all about views, not windows.

The hierarchy is most often constructed in Xcode graphically but it can be done in code as well.

The top of the (useable) view hierarchy is the Controller’s var view: UIView. This simple property is a very important thing to understand! This view is the one whose bounds will change on rotation, for example and it is likely the one we will programmatically add subview to. All of our MVC’s View’s UIViews will have this view as an ancestor and it is automatically hooked up for us when we create an MVC in Xcode.

Initialising a UIView

As always we should try to avoid an initialiser if possible but having one in UIView is slightly more common than having a UIViewController initialiser. A UIView’s initialiser is different if it comes out of a storyboard.

init(frame: CGRect) // initialiser if the UIView is created in code
init(code: NSCoder) // initialiser if the UIView comes out of a storyboard

If we need an initialiser, we need to implement them both:

func setup() { ... }

override init(frame: CGRect) { // a designated initialiser
	super.init(frame: frame)
	setup() // this might have to be before super.init
}

required init?(coder aDecoder: NSCoder) {
	super.init(coder: aDecoder)
	setup()
}

Another alternative to initialisers in UIView is awakeFromNib(). This is only called if the UIView came out of a storyboard. This is not an initialiser (it’s called immediately after initialisation is complete). All objects that inherit from NSObject in a storyboard are sent this but the order is not guaranteed, so we cannot message any other objects in the storyboard here.

Coordinate System Data Structure

CGFloat

Always use this instead of Double or Float for anything to do with a UIView’s coordinate system. We can convert to/from a Double or Float using initialisers, e.g., let cgf = CGFloat(3.14).

CGPoint

Simply a struct with two CGFloats in it: x and y.

CGSize

Also a struct with two CGFloats in it: width and height.

CGRect

A struct with a CGPoint and a CGSize in it:

struct CGRect {
	var origin: CGPoint
	var size: CGSize
}

View Coordinate System

The origin is in the upper left!

The units are points, not pixels. Pixels are the minimum-sized unit of drawing our device is capable of. Points are the units in the coordinate system. Most of the time there are 2 pixels per point, but it could be only 1 or even 3.

The boundaries of where drawing happens is determined by var bounds: CGRect, which is the view’s internal drawing space’s origin and size. This is the rectangle containing the drawing space in its own coordinate system.

The var center: CGPoint is the center of a UIView in its superview’s coordinate system while the var frame: CGRect is the rect containing a UIView in its superview’s coordinate system.

When would I create my own UIView subclass? If I want to do some custom drawing on screen, or I need to handle touch events in a special way (i.e. different than what a button or slider does naturally).

To draw we just create a UIViewsubclass and override draw(CGRect)like this:

override func draw(_ rect: CGRect)

We can draw outside the rect but it’s never required to do so. The rect is purely an optimisation. It is our UIView’s bounds that describe the entire drawing area (the rect is a subarea).

NEVER call draw(CGRect)!! EVER! Or else! Instead, if we need our view to be redrawn, we need to let the system know that by calling setNeedsDisplay() or setNeedsDisplay(_ rect: CGRect) where rect is the area that needs to be redrawn.


If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).

If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).

You can also support me by buying Paul Hudson’s books from this Affiliate Link.

Anyways, thank you so much for reading!

Till the next one!

Published by Michele Galvagno

Professional Musical Scores Designer and Engraver Graduated Classical Musician (cello) and Teacher Tech Enthusiast and Apprentice iOS / macOS Developer Grafico di Partiture Musicali Professionista Musicista classico diplomato (violoncello) ed insegnante Appassionato di tecnologia ed apprendista Sviluppatore iOS / macOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: