iOS Flashcards

1
Q

Difference between frame and bounds

A

bounds of a UIView:
the rectangle expressed as a location (x,y) and size (width, height) relative to its own coordinate system.

frame of a UIView:
the rectangle expressed as a location (x,y) and size (width, height) relative to the superview it is contained within.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Benefits of Guard

A
  • avoids pyramid of Doom (lots of annoying if let statements nested inside each other moving further and further to the right
  • provides an early exit out of the function using break or return
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Any vs. AnyObject

A

Any can represent an instance of any type at all including function types and optional types.

AnyObject can represent an instance of any class type.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Swift Automatic Reference Counting (ARC)

A
  • tracks and manages app’s memory usage
  • most of the time, memory management “just works”
  • reference counting only applies to class and not to structures and enumerations because they are value types, not reference types
  • keeps track of how many strong references properties, constants and variables make to each class instance
  • increases/decreases reference count accordingly
  • deallocates memory when reference count drops to zero
  • does not increase/decrease reference count of value types
  • by default, all references are strong references
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: "John Appleseed")
// Prints "John Appleseed is being initialized"
reference2 = reference1
reference3 = reference1
reference1 = nil
reference2 = nil
reference3 = nil
// Prints "John Appleseed is being deinitialized"
------------------------------------------------------------------------------------
-a strong reference cycle occurs when two class instances hold a strong reference to each other such that each instance keeps the other alive. The class instances never get to a point where they have zero references. Strong references are resolved by defining some of the relationships between classes as weak or unowned references instead of strong references.

-an example of how a strong reference cycle can be created by accident:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

-the strong reference cycle is created below:

john!.apartment = unit4A
unit4A!.tenant = john

-neither deinitializer is called below:

john = nil
unit4A = nil
————————————————————————————
-two ways to resolve strong reference cycles: weak references and unowned references

  • a weak reference is a reference that does not keep a strong hold on the instance it refers to and so does not stop ARC from disposing of the referred instance. Use a weak reference when the other instance has a shorter lifetime. A weak reference is indicated by placing the weak keyword before a property or variable declaration. A weak reference is automatically set to nil when the instance it refers to is deallocated. Therefore they are always declared as variables of an optional type.
  • the Apartment type’s tenant property is declared as a weak reference:
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
// Prints "John Appleseed is being deinitialized"
unit4A = nil
// Prints "Apartment 4A is being deinitialized"
------------------------------------------------------------------------------------
-an unowned reference does not keep a strong hold on the instance it refers to, but it is used when the other instance has the same or longer lifetime. An unowned reference is indicated by placing the unknown keyword before a property or variable declaration. An unowned reference is always expected to have a value, and ARC never sets its value to nil. Therefore unowned references are defined using non-optional types.
  • use an unowned instance only when sure that the reference always refers to an instance that has not been deallocated.
  • in the following example, the Customer has an optional card property. The CreditCard has an unowned customer property because it will always be associated with a customer, it will not outlive the Customer it refers to, and it always has a customer instance associated with it when the it is created.
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
  • unsafe unowned references disable runtime safety checks and is indicated by writing unowned (unsafe). Trying to access an unsafe unowned reference after the instance it refers to is deallocated is an unsafe operation.
  • there is a third scenario in which both properties should always have a value, and neither property should ever be nil once initialization is complete. One class can have an unowned property, and the other class can have an implicitly unwrapped optional property.

-every country must always have a capital city, and every city must belong to a country:

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}
class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// Prints "Canada's capital city is called Ottawa"
------------------------------------------------------------------------------------
-a strong reference cycle can occur if a closure is assigned to a property of a class instance, and the body of that closure captures the instance. The capture might occur with something like self.someProperty or self.someMethod(). This happens because closures are reference types.

-asHTML holds a strong reference to its closure, and the closure holds a strong reference to the HTMLElement instance:

class HTMLElement {

let name: String
let text: String?
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "\(text)\(self.name)>"
        } else {
            return ""
        }
    }
init(name: String, text: String? = nil) {
    self.name = name
    self.text = text
}

deinit {
    print("\(name) is being deinitialized")
} }
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"

-the HTMLElement instance is not deallocated:

paragraph = nil

  • a closure capture list defines the rules to use when capturing one or more reference cycles within a closure’s body. Each captured reference is declared to be a weak or unowned reference rather than a strong reference.

-each item in a capture list is a pairing of the weak or unowned keyword with a reference to a class instance or a variable defined with some value

lazy var someClosure = {
    [unowned self, weak delegate = self.delegate]
    (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}

-if a closure does not specify a parameter list or return type because they can be inferred from context, place the capture list at the very start of the closure followed by the in keyword:

lazy var someClosure = {
    [unowned self, weak delegate = self.delegate] in
    // closure body goes here
}

-define a capture as an unowned reference when the closure and the instance it captures will always refer to each other and will always be deallocated at the same time

class HTMLElement {

let name: String
let text: String?
    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "\(text)\(self.name)>"
        } else {
            return ""
        }
    }
init(name: String, text: String? = nil) {
    self.name = name
    self.text = text
}

deinit {
    print("\(name) is being deinitialized")
} }

-define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type and automatically become nil when the instance they reference is deallocated.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

xib

A
  • file extension associated with Interface Builder files

- graphics software used to test, develop and design the UI interfaces of different software products.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

bundle id

A
  • uniquely defines every iOS application

- specified in Xcode.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Notification vs. Delegate

A

delegate:
- use when one object needs to send information to only one object
- an object can have only one delegate.

notification:
use when one object needs to send information to more than one object.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Core Data

A
  • framework that is used to manage model layer objects
  • has the ability to persist object graphs to a persistent store
  • data is organized into relational entity-attribute model
  • automatic support for storing objects
  • automatic validation of property values
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Swift Protocols

A
  • a type that defines a blueprint of methods, properties and other requirements that suit a particular task or functionality
  • can be adopted by a class, structure or enumeration to provide an actual implementation of those requirements
  • a type that satisfies the requirements of a protocol is said to conform to that protocol
  • a protocol doesn’t implement any functionality itself, but it defines the functionality
  • a protocol can be extended to implement some of the requirements or to implement additional functionality that conforming types can take advantage of
  • definition:
protocol SomeProtocol {
    // protocol definition goes here
}

-adopting protocols:

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // structure definition goes here
}

-list superclass before any protocols:

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
————————————————————————————
Property Requirements

  • a protocol can require any conforming type to provide an instance property or type property with a particular name and type. It also specifies whether each property must be gettable or gettable and settable.
  • property requirements are always declared as variable properties prefixed with the var keyword:

protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}

-always prefix type property requirements with the static keyword when they are defined in a protocol:

protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}

-protocol with a single instance property requirement:

protocol FullyNamed {
var fullName: String { get }
}

-structure that adopts and conforms to the FullyNamed protocol:

struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: “John Appleseed”)
// john.fullName is “John Appleseed”
————————————————————————————
Method Requirements

-protocols can require specific instance methods and type methods to be implemented by conforming types. They are written without curly braces or a method body. Variadic parameters are allowed. Default values cannot be specified within a protocol’s definition. Type method requirements are prefixed with the static keyword when they are defined in a protocol.

protocol SomeProtocol {
    static func someTypeMethod()
}
------------------------------------------------------------------------------------
Mutating Method Requirements

-for instance methods on value types (structures and enumerations), the mutating keyword is placed before the method’s func keyword to indicate that the method is allowed to modify the instance it belongs to and any properties of that instance

protocol Togglable {
mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on
------------------------------------------------------------------------------------
Initializer Requirements

-protocols can require specific initializers to be implemented by conforming types. They are written without curly braces or an initializer body.

protocol SomeProtocol {
init(someParameter: Int)
}
————————————————————————————
Class Implementations of Protocol Initializer Requirements

-a conforming class can implement a protocol initializer as either a designated initializer or a convenience initializer. The initializer implementation must be marked with the required modifier. Classes marked as final do not need to use the required modifier since they cannot be subclassed:

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}

-if a subclass overrides a designated initializer from a superclass and also implements a matching initializer requirement from a protocol, mark the initializer implementation with the required and override modifiers:

protocol SomeProtocol {
init()
}

class SomeSuperClass {
    init() {
        // initializer implementation goes here
    }
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
    // "required" from SomeProtocol conformance; "override" from SomeSuperClass
    required override init() {
        // initializer implementation goes here
    }
}
------------------------------------------------------------------------------------
Failable Initializer Requirements

Protocols as Types

  • protocols can be used as fully-fledged types. Using a protocol as a type is sometimes called an existential type.
  • a protocol can be used:
    • -as a parameter type or return type in a function, method, or initializer
    • -as the type of a constant, variable, or property
    • -as the type of items in an array, dictionary, or other container

-example:

class Dice {
    let sides: Int
    let generator: RandomNumberGenerator
    init(sides: Int, generator: RandomNumberGenerator) {
        self.sides = sides
        self.generator = generator
    }
    func roll() -> Int {
        return Int(generator.random() * Double(sides)) + 1
    }
}

-create a six-sided dice with a LinearCongruentialGenerator instance:

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
    print("Random dice roll is \(d6.roll())")
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4
------------------------------------------------------------------------------------
Delegation

-a design pattern that enables a class or structure to hand off (delegate) some of its responsibilities to an instance of another type. The conforming type is known as a delegate. Delegates are declared as weak references to prevent strong reference cycles. Marking a protocol as class-only (inherits from AnyObject) lets a class declare that its delegate must use a weak reference.

protocol DiceGame {
    var dice: Dice { get }
    func play()
}
protocol DiceGameDelegate: AnyObject {
    func gameDidStart(_ game: DiceGame)
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
    func gameDidEnd(_ game: DiceGame)
}
------------------------------------------------------------------------------------
-a version of Snakes and Ladders that uses a Dice instance, adopts the DiceGame protocol and notifies a DiceGameDelegate about its progress:
class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
    var board: [Int]
    init() {
        board = Array(repeating: 0, count: finalSquare + 1)
        board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    }
    weak var delegate: DiceGameDelegate?
    func play() {
        square = 0
        delegate?.gameDidStart(self)
        gameLoop: while square != finalSquare {
            let diceRoll = dice.roll()
            delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
            switch square + diceRoll {
            case finalSquare:
                break gameLoop
            case let newSquare where newSquare > finalSquare:
                continue gameLoop
            default:
                square += diceRoll
                square += board[square]
            }
        }
        delegate?.gameDidEnd(self)
    }
}

-DiceGameTracker adopts the DiceGameDelegate protocol:

class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(_ game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            print("Started a new game of Snakes and Ladders")
        }
        print("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        numberOfTurns += 1
        print("Rolled a \(diceRoll)")
    }
    func gameDidEnd(_ game: DiceGame) {
        print("The game lasted for \(numberOfTurns) turns")
    }
}

-using DiceGameTracker:

let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns
------------------------------------------------------------------------------------
Adding Protocol Conformance with an Extension

  • an existing type can be extended without access to the source code for the existing type
  • TextRepresentable can be implemented by any type that has a way to be represented as text:

protocol TextRepresentable {
var textualDescription: String { get }
}

-the Dice class can be extended to adopt and conform to TextRepresentable:

extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

  • a generic type can conditionally conform to a protocol
  • Array instances conform to the TextRepresentable protocol whenever they store elements of a type that conforms to TextRepresentable:
extension Array: TextRepresentable where Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}
let myDice = [d6, d12]
print(myDice.textualDescription)
// Prints "[A 6-sided dice, A 12-sided dice]"

-a type conforms to a protocol but has not stated that it adopts that protocol, it can adopt the protocol with an empty extension. Type must always explicitly declare their adoption of the protocol:

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}
------------------------------------------------------------------------------------
-a protocol can be used as the type stored in a collection

let things: [TextRepresentable] = [game, d12, simonTheHamster]

for thing in things {
    print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon
------------------------------------------------------------------------------------
-a protocol can inherit one or more protocols and can add further requirements on top of the requirements it inherits

protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}

extension SnakesAndLadders: PrettyTextRepresentable {
    var prettyTextualDescription: String {
        var output = textualDescription + ":\n"
        for index in 1...finalSquare {
            switch board[index] {
            case let ladder where ladder > 0:
                output += "▲ "
            case let snake where snake < 0:
                output += "▼ "
            default:
                output += "○ "
            }
        }
        return output
    }
}
------------------------------------------------------------------------------------
-protocol adoption can be limited to class types (and not structures and enumerations) by adding the AnyObject protocol to a protocol's inheritance list. Use a class-only protocol when the behavior defined by that protocol's requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
    // class-only protocol definition goes here
}
------------------------------------------------------------------------------------
-multiple protocols can be combined into a single requirement with protocol composition. Protocol compositions behave as if a temporary local protocol that has the combined requirements of all the protocols in the composition was defined. A protocol composition can also include one class type that can be used to specify a required superclass. Protocol compositions have the form SomeProtocol &amp; Another Protocol.

-birthdayPerson conforms to both protocols:

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named &amp; Aged) {
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
------------------------------------------------------------------------------------
-use the is and as operators to check for protocol conformance and to cast to a specific protocol

  • the is operator returns true if an instance conforms to a protocol and returns false if it doesn’t.
  • the as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance doesn’t conform to that protocol.
  • the as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast doesn’t succeed.

protocol HasArea {
var area: Double { get }
}

class Circle: HasArea {
    let pi = 3.1415927
    var radius: Double
    var area: Double { return pi * radius * radius }
    init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
    var area: Double
    init(area: Double) { self.area = area }
}

-HasArea protocol:

protocol HasArea {
var area: Double { get }
}

-classes that conform to the HasArea protocol:

class Circle: HasArea {
    let pi = 3.1415927
    var radius: Double
    var area: Double { return pi * radius * radius }
    init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
    var area: Double
    init(area: Double) { self.area = area }

-class that does not conform to the HasArea protocol:

class Animal {
    var legs: Int
    init(legs: Int) { self.legs = legs }
}

-array that stores values of type AnyObject:

let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]

-iterate array and check for HasArea protocol conformance:

for object in objects {
    if let objectWithArea = object as? HasArea {
        print("Area is \(objectWithArea.area)")
    } else {
        print("Something that doesn't have an area")
    }
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area
------------------------------------------------------------------------------------
-optional protocol requirements can be defined. They do not have to be implemented by types conforming to the protocol. Optional requirements are prefixed by the optional modifier as part of the protocol's definition. Optional requirements allow writing code that interoperates with Objective-C. A method or property used in an optional requirement type automatically becomes an optional. 

@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}

-Counter class has an optional dataSource property of type CounterDataSource?

class Counter {
    var count = 0
    var dataSource: CounterDataSource?
    func increment() {
        if let amount = dataSource?.increment?(forCount: count) {
            count += amount
        } else if let amount = dataSource?.fixedIncrement {
            count += amount
        }
    }
}
------------------------------------------------------------------------------------
-protocols can be extended to provide method, initializer, subscript and computed property implementations to conforming types.

-RandomNumberGenerator is extended to provide a randomBool() method.

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

-all conforming types automatically gain this method implementation.

let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"
------------------------------------------------------------------------------------
-protocol extensions can add implementations to conforming types but cannot make a protocol extend or inherit from another protocol
------------------------------------------------------------------------------------
-protocol extensions can provide a default implementation to any method or computed property requirement of that protocol

extension PrettyTextRepresentable {
var prettyTextualDescription: String {
return textualDescription
}
}
————————————————————————————
-protocol extensions can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. These constraints are written after the name of the protocol being extended by writing a generic where clause.

extension Collection where Element: Equatable {
    func allEqual() -> Bool {
        for element in self {
            if element != self.first {
                return false
            }
        }
        return true
    }
}

let equalNumbers = [100, 100, 100, 100, 100]
let differentNumbers = [100, 100, 200, 100, 200]

print(equalNumbers.allEqual())
// Prints "true"
print(differentNumbers.allEqual())
// Prints "false"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

public vs. open

A
open access: 
can subclassed by modules they are defined in, modules that import the module in which the class is defined and class members as well. 
public: 
in Swift 3, classes declared public can only be subclassed in the module they are defined in.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

app execution states

A

Active:
app is running in the foreground and is receiving events (normal mode)

Inactive:
app is running in the foreground but is currently not receiving events (it might be executing other code)

Background:

  • app is in the background but it can execute code
  • app might be on the way to being suspended, might stay here for a while, or might be launched directly to background.

Suspended:

  • app is in the background but is not executing code (system does this automatically)
  • the system might purge it if a low-memory condition occurs.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

bounding box

A

smallest measure (area or volume) within a given set of points

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

MVC

A

Models:

  • responsible for the domain data or a data access layer which manipulates the data
  • think of ‘Person’ or ‘PersonDataProvider’ classes.

Views :

  • responsible for the presentation layer (GUI)
  • for iOS environment think of everything starting with ‘UI’ prefix.

Controller:

  • the glue or the mediator between the Model and the View
  • in general responsible for altering the Model by reacting to the user’s actions performed on the View and updating the View with changes from the Model.
  • in iOS, the UIViewController contains the View and the Controller
  • it updates the Model, and the Model notifies the UIViewController of changes
  • can make the UIViewController too complex and hard to test
  • MVC = Massive View Controller.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

MVVM

A

Models:

  • hold application data
  • usually structs or simple classes
  • responsible for the domain data or a data access layer which manipulates the data
  • think of ‘Person’ or ‘PersonDataProvider’ classes.

Views:

  • display visual elements and controls on the screen
  • typically subclasses of UIView
  • responsible for the presentation layer (GUI)
  • for iOS environment think of everything starting with ‘UI’ prefix.

ViewModel:

  • transform model information into values that can be displayed on a view
  • usually classes
  • provides a set of interfaces which represent UI components in the View
  • the interfaces and UI components are connected by binding.
  • should not contain UIKit code
          View Controller       <
                    ^                        |
                     |                        |
                    V                       | Model <=> View Model <=> View
  • use when you need to transform models into another representation for a view
  • in iOS, the UIViewController initiates, lays out and presents UI components, and it binds UI components with the ViewModel
  • the ViewModel does controller logic and provides interfaces to the View
  • the ViewModel can get complex.
Example:
model:
public struct BreachModel {
    var title: String
}
view:
public class BreachView: UIView {
public let titleLabel: UILabel

public override init(frame: CGRect) {
    let titleFrame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
    titleLabel = UILabel(frame: titleFrame)
    titleLabel.textAlignment = .center
    super.init(frame: frame)
    addSubview(titleLabel)
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
    titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
} }
viewModel:
class BreachViewModel {
    // MARK: - Initialization
    init(model: [BreachModel]? = nil) {
        if let inputModel = model {
            breaches = inputModel
        }
    }
var breaches = [BreachModel]()

public func configure(_ view: BreachView) {
    view.titleLabel.text = breaches.first?.title
} }
extension BreachViewModel {
    func fetchBreaches(completion: @escaping (Result) -> Void) {
        completion(.success(breaches))
    }
}
viewController:
class BreachesViewController: UIViewController {
    // the view model is setup with simple
    var breachesViewModel = BreachViewModel(model: [BreachModel(title: "000webhost")])
    override func viewDidLoad() {
        super.viewDidLoad()
        let breachView = BreachView(frame: self.view.frame)
        breachesViewModel.configure(breachView)
        self.view.addSubview(breachView)
        breachView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            breachView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            breachView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            breachView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            breachView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            ])
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Realm

A

open source database framework that is implemented from scratch, has zero object copy store and is fast

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Swift Advantages

A
  • compiled
  • statically typed

optional types:
–make apps crash-resistant

  • built-in error handling
  • tuples and multiple return values
  • generics
  • fast iteration over range or collection
  • structs that support methods, extensions and protocols
  • functional programming patterns (map, filter)
  • native error handling (try/catch/throw)
  • closures unified with function pointers
  • much faster than other languages-LLVM compiler

-type safety:
variables, constants and functions have their types declared in advance

-strongly typed:
types are checked at compile time
(Java is also strongly typed)

-type inference:
type is inferred from the initial value given

-type constraint:
specifies that a generic type must inherit from a specific class or conform to a particular protocol

-supports pattern matching

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Swift Generics

A
  • enables the writing of flexible, reusable functions and types that can work with any type, subject to requirements that can be defined
  • can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
  • T is a placeholder type that will be replaced at compile time
  • the generic function’s name is followed by the placeholder type name (T) inside angle brackets (). The brackets tell Swift that T is a placeholder type name within the function definition, so Swift doesn’t look for an actual type T.
  • Array and Dictionary are generic collections
func swapTwoValues(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
------------------------------------------------------------------------------------
-type parameters specify and name a placeholder type and are written immediately after the function's name between a pair of matching angle brackets. More than one type parameter can be provided by writing multiple parameter names within the angle brackets, separated by commas.

-type parameters can have descriptive names such as Key and Value, but it’s traditional to use single letters such as T, U and V when there is not a meaningful relationship between them.

  • always use upper camel case names for type parameters to indicate they are placeholders for a type, not a value
  • generic types can be defined. They are custom classes, structures and enumerations that can work with any type.

-generic stack example using an Element type parameter:

struct Stack {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

-create a stack of strings:

var stackOfStrings = Stack()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// the stack now contains 4 strings

-when a generic type is extended, a type parameter is not provided as part of the definition. The type parameter list from the original type definition is available within the body of the extension, and the original type parameter names are used. Extensions of a generic type can also include requirements that instances of the extended type must satisfy in order to gain the new functionality.

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}
------------------------------------------------------------------------------------
-type constraints can be enforced on types that can be used with generic functions and generic types. Type constraints can be defined. Type constraints are specified by placing a single class or protocol constraint after a type parameter's name, separated by a colon, as part of the type parameter list.

-adding the type constraint Equatable so types must be able to be compared with the equal to (==) operator

func findIndex(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
------------------------------------------------------------------------------------
-an associated type gives a placeholder name to a type that is used as part of the protocol. 

-an associated name enables the Container protocol to refer to the type of values it stores.

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
    // original IntStack implementation
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // conformance to the Container protocol
    typealias Item = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}
------------------------------------------------------------------------------------
-an existing type can be extended to add conformance to a protocol.

-type constraints can be added to an associated type in a protocol to require that conforming types satisfy those constraints.

protocol Container {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
------------------------------------------------------------------------------------
-a protocol can appear as part of its own requirements
protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}
extension Stack: SuffixableContainer {
    func suffix(_ size: Int) -> Stack {
        var result = Stack()
        for index in (count-size)..()
stackOfInts.append(10)
stackOfInts.append(20)
stackOfInts.append(30)
let suffix = stackOfInts.suffix(2)
// suffix contains 20 and 30
------------------------------------------------------------------------------------
-a generic where clause enables defining requirements for associated types

-a function which checks to see if two Container instances contain the same items in the same order. The two containers do not have to be the same type, but they musts contain the same type of items.

func allItemsMatch
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.Item == C2.Item, C1.Item: Equatable {
        // Check that both containers contain the same number of items.
        if someContainer.count != anotherContainer.count {
            return false
        }
        // Check each pair of items to see if they're equivalent.
        for i in 0..()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")

var arrayOfStrings = [“uno”, “dos”, “tres”]

if allItemsMatch(stackOfStrings, arrayOfStrings) {
    print("All items match.")
} else {
    print("Not all items match.")
}
// Prints "All items match."
------------------------------------------------------------------------------------
-a generic where clause can be used as part of an extension. In the example, the Stack definition does not require its items to be equatable, so using the == operator could result in a compile-time error. Using the generic where clause to add a new requirement prevents this problem.
extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}
------------------------------------------------------------------------------------
-a generic where clause can be used with extensions to a protocol

-adding a startsWith(_:) method:

extension Container where Item: Equatable {
    func startsWith(_ item: Item) -> Bool {
        return count >= 1 &amp;&amp; self[0] == item
    }
}

-require Item to be a specific type:

extension Container where Item == Double {
    func average() -> Double {
        var sum = 0.0
        for index in 0.. Item { get }
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
    func makeIterator() -> Iterator
}
------------------------------------------------------------------------------------
-a generic where clause can add a constraint to an inherited associated type:

protocol ComparableContainer: Container where Item: Comparable { }

-a generic where class can be added to generic subscripts. The placeholder type name is written inside the angle brackets, and the generic where clause is written right before the open curly brace of the subscript body. In the example, the value passed for the indices parameter is a sequence of integers.

extension Container {
    subscript(indices: Indices) -> [Item]
        where Indices.Iterator.Element == Int {
            var result = [Item]()
            for index in indices {
                result.append(self[index])
            }
            return result
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

lazy in Swift

A
  • variables that are created using a specified function called only when that variable is first requested
  • if never requested, the function is never run, so it saves processing time.

lazy var players = String

rules:

  • can’t use lazy with let
  • can’t use with computed properties because a computed property returns the value every time it’s accessed after executing the code inside the computation block
  • can use lazy only with members of struct and class
  • initialized atomically and so is not thread safe.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

3 ways to pass data between view controllers

A

-segue (prepareForSegue method (forward))

  • Delegate (backward):
  • -delegate protocol
  • -delegator that delegates the tasks
  • -delegate object that implements the delegate protocol and does the actual work

-setting variable directly (forward)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Readers-Writers problem

A

multiple threads are reading at the same time when there should only be one thread writing

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Swift pattern matching techniques

A

tuple patterns:
used to match values of corresponding tuple types

type-casting patterns:
allow you to cast or match types

wildcard patterns:
match and ignore any kind and type of value

optional patterns:
used to match optional values

enumeration case patterns:
match cases of existing enumeration types

expression patterns:
allow you to compare a given value against a given expression

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

var vs let

A

var refers to a variable that can be changed

let refers to a constant that cannot be changed once set

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

GCD

A

Grand Central Dispatch

  • low-level API for managing concurrent operations
  • uses thread pool pattern
  • improves app’s responsiveness by deferring computationally expensive tasks and running them in the background

-provides easier concurrency model than locks and threads and helps to avoid concurrency bugs

3 queues:
main:
highest priority, runs on the main thread and is a serial queue. All UI updates should be done on the main thread.

DispatchQueue.main.async {
    // do any UI work here
}

global:
- concurrent queues that are shared by the whole system
- four such queues with different priorities or qualities of service:
- -high
- -default
- -low
- -background (lowest priority and is throttled in any i/o activity to minimize negative system impact)

DispatchQueue.global(qos: .background).async {
    // do any heavy operation here
}

custom:
- queues that you create which can be serial or concurrent
- requests in these queues actually end up in one of the global queues

let concurrentQueue = DispatchQueue(label: “concurrentQueue”, qos: .background, attributes: .concurrent)
let serialQueue = DispatchQueue(label: “serialQueue”, qos: .background)
———————————————————————————–
Quality of Service classes:
-User-interactive: represents tasks that must complete immediately in order to provide a good user experience. Should run on main thread.
-User-initiated: represents tasks a user initiates from the UI. Use them when the user is waiting for immediate results and for tasks required to continue user interaction.
-Utility: represents long-running tasks, typically with a user-visible progress indicator.
-Background: represents tasks the user is not directly aware of. Use for tasks that don’t require user interaction and aren’t time-sensitive.
————————————————————————————
Two ways to execute tasks:
DispatchQueue.sync:
returns control to the caller after the task completes

DispatchQueue.async:
returns immediately, ordering the task to start but not waiting for it to complete
————————————————————————————
Example: using queues together to download an image and display it in an imageView:

DispatchQueue.global(qos: .background).async {
    let image = downloadImageFromServer()
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

autolayout

A

dynamically calculates the size and position of views based on constraints

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Swift collection types

A

Array
Dictionary
Set

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

? in Swift

A
  • used during the declaration of a property and tells the compiler that the property is optional
  • may or may not hold a value.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

?? in Swift

A

nil coalescing operator: either unwraps an optional value or provides a default value
a != nil ? a! : b

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

viewDidLoad vs. viewDidAppear

A

viewDidLoad
-called when the view is loaded. Use when data is fairly static and not likely to change.

viewDidAppear

  • called every time the view is presented on the device
  • use when data changes regularly.

In both cases, the data should be loaded synchronously on a background thread to avoid blocking the UI.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

how to fix a memory leak

A

-use XCode debug memory graph to detect simple retain cycles. It displays all objects currently alive. Purple exclamation mark indicate related leaked objects.

  1. in the box at top projectName > device, click Edit scheme
  2. select Malloc Scribble which will fill freed memory with a predefined value to make it more obvious when memory is leaked
  3. select Logging>Malloc Stack>Live Allocations Only which allows Xcode to build an allocation backtrace
  4. make the debug console visible
  5. run the app
  6. click the Memory Debugger button that looks like three joined circles in a >.
  7. this will display the heap contents with purple exclamation marks beside the items in the debug navigator
    - ———————————————————————————
    - use Allocations instrument which tracks all objects the app allocates over the course of its run
    - ———————————————————————————
    - use Leaks instrument to check all memory and figure out the leaked objects
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q

ways to persist data

A
  • in-memory arrays, dictionaries and sets (doesn’t need to be persisted)
  • UserDefaults: good for small amounts of data
  • Keychain (simple key/value): sensitive, secure data
  • file/disk storage (write to/from disk): file system folders: Documents, Library, tmp
  • Core Data, Realm (frameworks to simplify work with databases): Apple’s solution, not a database, is an object graph, complex
  • SQLite (relational database): large amounts of data with relationships
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q

HTTP networking

A

Alamofire and AFNetworking are the most popular solutions

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q

Cocoa

A
  • application framework for macOS development.

- combination of Appkit Framework and Foundation Framework.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
33
Q

app bundle

A
  • a file directory that combines related resources in one place and contains the related resources the application requires for successful execution
  • Xcode packages the app as a bundle
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
34
Q

Cocoa Touch

A
  • application framework for iPhone and iPad

- includes Foundation Framework and UIKit Framework

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
35
Q

plist

A
  • represents Property Lists, a key-value store for persisting data values in iPhone apps
  • basically an XML file
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
36
Q

IPA

A
  • iOS App Store Package

- has .ipa extension which represents an iPhone application archive file.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
37
Q

how to improve battery life during app execution

A
  • app is notified whenever it’s transferred between foreground and background
  • knowing the background functions helps to extend battery life.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
38
Q

framework used to construct app’s UI

A

UIKit framework:

provides Views, Drawing Model, Controls, Event Handling and Windows specifically designed for a touch screen interface

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
39
Q

singleton pattern

A
  • design pattern that ensures that only one instance exists for a given class and that there’s a global access point to that instance
  • usually uses lazy loading to create the single instance when it’s needed the first time.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
40
Q

facade pattern

A
  • design pattern that provides a single interface to a complex subsystem
  • instead of exposing the user to a set of classes and their APIs, it only exposes one simple unified API.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
41
Q

decorator pattern

A
  • design pattern that dynamically adds behaviors and responsibilities to an object without modifying its code.
  • alternative to subclassing where a class’s behavior is modified by wrapping it with another object.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
42
Q

adapter pattern

A
  • design pattern that allows classes with incompatible interfaces to work together
  • wraps itself around an object and exposes a standard interface to interact with that object.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
43
Q

observer pattern

A

design pattern where one object notifies other objects of any state changes

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
44
Q

memento pattern

A
  • design pattern that saves your stuff somewhere
  • later on, this externalized state can be restored without violating encapsulation; that is, private data remains private
  • implementations include Archiving, Serialization and State Restoration.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
45
Q

OAuth

A
  • an authorization protocol concerned with authorization of third-party applications that can be used to access user data without identifying the user or exposing its credentials
  • has two libraries namely, OAuth2 and OAuthSwift
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
46
Q

IBAction and IBOutlet

A

IBOutlet:

  • variable that can be set when a XIB (interface builder object) is loaded
  • tells Xcode that you can connect to the property from the Interface Builder
  • resolves to void

IBAction:

  • function that the objects loaded by the XIB can call
  • indicates that the method is an action that you can connect to from your storyboard in Interface Builder
  • resolves to nothing
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
47
Q

Main Thread Checker

A
  • new tool launched with Xcode 9 which detects the invalid use of Apple’s frameworks like AppKit, UIKit, etc., that are supposed to be used from main thread but are accidentally used in the background thread
  • the effect of the invalid usage can result in missed visual defects, UI updates, crashes, and data corruption.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
48
Q

how to avoid retain cycles when using closures in Swift

A

closures in Swift can cause retain cycles because they have a strong reference to the object that uses them.

strong reference to aClosure which also captures self strongly:

class SomeObject {
    var aClosure = { 
        self.doSomething()
    }
...
}

aClosure captures variables as weak or unowned:

class SomeObject {
    var aClosure = { [unowned self, weak delegate = self.delegate] in
        self.doSomething()
        delegate?.doSomethingElse()
    }
...
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
49
Q

reuse identifier in UITableViewCell constructor

A
  • tells UITableView which cells may be reused within the table, effectively grouping together rows in a UITableView that differ only in content but have similar layouts
  • improves scroll performance by alleviating the need to create new views while scrolling
  • instead the cell is reused whenever dequeueReusableCellWithIdentifier: is called.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
50
Q
var a1 = [1, 2, 3 ,4 5]
var a2 = a1
a2.append(6)
var x = a1.count

What is the value of x?

A
  • in Swift, arrays are implemented as structs, making them value types rather than reference types (i.e., classes).
  • when a value type is assigned to a variable as an argument to a function or method, a copy is created and assigned or passed
  • as a result, the value of “x” or the count of array “a1” remains equal to 5 while the count of array “a2” is equal to 6, appending the integer “6” onto a copy of the array “a1.” The size of a1 is 5, and the size of a2 is 6. Therefore, x = 5.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
51
Q

what to do if your app is prone to crashing

A
  • This classic interview question is designed to see how well your prospective programmer can solve problems
  • what you’re looking for is a general methodology for isolating a bug, and their ability to troubleshoot issues like sudden crashes or freezing
  • in general, when something goes wrong within an app, a standard approach might look something like this:
  • Determine the iOS version and make or model of the device.
  • Gather enough information to reproduce the issue.
  • Acquire device logs, if possible.
  • Once you have an idea as to the nature of the issue, acquire tooling or create a unit test and begin debugging.

-a great answer would include all of the above, with specific examples of debugging tools like Buglife or ViewMonitor, and a firm grasp of software debugging theory
—knowledge on what to do with compile time errors, run-time errors, and logical errors
-the one answer you don’t want to hear is the haphazard approach
—visually scanning through hundreds of lines of code until the error is found

-when it comes to debugging software, a methodical approach is must.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
52
Q

4 most important Objective-C data types

A

NSString: Represents a string.
CGfloat: Represents a floating point value.
NSInteger: Represents an integer.
BOOL: Represents a boolean.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
53
Q

framework used to construct UI

A
  • The UIKit framework is used to develop application’s user interface
  • The UIKit framework provides event handling, drawing model, windows, views, and controls, specifically designed for a touch screen interface.
  • The UIKit framework (UIKit.framework) provides the crucial infrastructure needed to construct and manage iOS apps.

This framework provides:

  • window and view architecture to manage an app’s user interface
  • event handling infrastructure to respond to user input
  • an app model to drive the main run loop and interact with the system
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
54
Q

difference between retain and assign

A

Assign creates a reference from one object to another without increasing the source’s retain count.

if (_variable != object) {
[_variable release];
_variable = nil;
_variable = object;
}
———————————————————————————–
Retain creates a reference from one object to another and increases the retain count of the source object.

if (_variable != object) {
    [_variable release];
    _variable = nil; 
    _variable = [object retain]; 
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
55
Q

files in an app bundle

A
MyApp.app
-MyApp (executable, name=app name)
-MyAppIcon.png 
-MySearchIcon.png
-Info.plist (configuration information)
-Default.png
-MainWindow.nib (default interface objects to load at launch time)
-Settings.bundle (special plug-in containing app specific preferences to add to Settings)
-MySettingsIcon.png
-iTunesArtwork
-en.lproj
MyImage.png
-fr.lproj
MyImage.png.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
56
Q

force unwrap

A
  • adding a ! after an Optional value to automatically unwrap it without having to check whether it is nil or not.
  • This is dangerous. Never use it for IBOutlets.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
57
Q

NSManagedObject

A

instance which implements the basic behavior required of a Core Data model object.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
58
Q

ways to debug an app

A

NSLog and print functions can be used for output into console.

Breakpoints can also be used together with the Debug bar and Variables view as an alternative.

Other tools such as Instruments and Crash Logs instead of the two above.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
59
Q

considerations when showing images downloaded from a server

A
  • only download the image when the cell is scrolled into view (when cellForRowAtIndexPath is called)
  • download the image asynchronously on a background thread so as not to block the UI
  • when the image has downloaded for a cell, check if the cell is still in view, or if it has been reused by another piece of data
  • if the cell has been reused, discard the image
  • else, switch back to the main thread to change the image on the cell
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
60
Q

weak vs. strong

A

strong:
- the reference count will be increased, and the reference to it will be maintained through the life of the object.
- ———————————————————————————–
weak:
- the object is pointed to, but the reference count is not increased
- often used when creating a parent-child relationship
- the parent has a strong reference to the child, but the child only has a weak reference to the parent.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
61
Q

copy vs. retain

A

copy:
-when an object is copied, it does not share the same version of the object passed
-it is a duplicate of the object created with duplicated values.
————————————————————————————
retain:
-increases retain count by one
-when the retain count of an object reaches zero, it will be deallocated and released from memory.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
62
Q

Spot the bug:

class ViewController: UIViewController {
    @IBOutlet var alert: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        let frame: CGRect = CGRect(x: 100, y: 100, width: 100, height: 50)
        self.alert = UILabel(frame: frame)
        self.alert.text = "Please wait..."
        self.view.addSubview(self.alert)
    }
    DispatchQueue.global(qos: .default).async {
        sleep(10)
        self.alert.text = "Waiting over"
    }
}
A

All UI updates must be performed on the main thread.

DispatchQueue.global(qos: .default).async {
    sleep(10)
    DispatchQueue.main.async {
        self.alert.text = "Waiting over"
    }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
63
Q

Swift variables

A

var mutableDouble:Double = 1.0
mutableDouble = 2.0

let constantDouble:Double = 1.0
// constantDouble = 2.0 // error

var mutableInferredDouble = 1.0

var optionalDouble:Double? = nil
optionalDouble = 1.0

if let definiteDouble = optionalDouble {
 definiteDouble
}
------------------------------------------------------------------------------------
Variable types:

Int 1, 2, 500, 10000

Float
Double 1.5, 3.14, 578.234

Bool true, false

String “Kermit”, “Gonzo”, “Ms. Piggy”

ClassName UIView, UIButton, etc

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
64
Q

Swift control flow

A

Conditional Statements

var condition = true
if condition {
} else {
}
------------------------------------------------------------------------------------
Switch statements in Swift do not fall through the bottom of each case and into the next one by default.
var val = 5
switch val {
case 1:
    "foo"
case 2:
    "bar"
default:
    "baz"
}
------------------------------------------------------------------------------------
Switch Interval Matching
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// Prints "There are dozens of moons orbiting Saturn."
------------------------------------------------------------------------------------
Switch Tuples
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) is at the origin")
case (_, 0):
    print("\(somePoint) is on the x-axis")
case (0, _):
    print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
default:
    print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
------------------------------------------------------------------------------------
Switch Value Bindings
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
------------------------------------------------------------------------------------
Switch Where
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
// Prints "(1, -1) is on the line x == -y"
------------------------------------------------------------------------------------
Switch Compound Cases
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
------------------------------------------------------------------------------------
For Loops
for var i=1; i<=10; ++i {
    // do something using i as it goes from 1 to 10
}
for index in 1…10 {
    // do something using index as it goes from 1 to 10
    println("index is \(index)")
}
for index in 1 ..< 10 {
    // do something using index as it goes from 1 to 9
    println("index is \(index)")
}
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

If you don’t need each value from a sequence, you can ignore the values by using an underscore in place of a variable name.

var abcAges = [“Alice” : 24, “Bob” : 27, “Carol” : 29]

for (name, age) in abcAges {
println(“(name) is (age) years old.”)
}

var i = 1
while i <= 10 {
    println ("\(i)")
    i++
}
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// render the tick mark every 3 hours (3, 6, 9, 12)
}
————————————————————————————
While Loops

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0
while square < finalSquare {
    // roll the dice
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // move by the rolled amount
    square += diceRoll
    if square < board.count {
        // if we're still on the board, move up or down for a snake or a ladder
        square += board[square]
    }
}
print("Game over!")
repeat {
    // move up or down for a snake or ladder
    square += board[square]
    // roll the dice
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // move by the rolled amount
    square += diceRoll
} while square < finalSquare
print("Game over!")
------------------------------------------------------------------------------------
Control Transfer Statements

-continue (stop looping and start again at the beginning of the next loop iteration)
-break (ends execution of a control statement immediately)
-fallthrough (causes code execution to move directly t the statements inside the next case)
-return
-throw
————————————————————————————
Labelled Statements

Early Exit

A guard statement requires that a condition must be true for the code after the guard statement to be executed.

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }
print("Hello \(name)!")
    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }
print("I hope the weather is nice in \(location).") }

greet(person: [“name”: “John”])
// Prints “Hello John!”
// Prints “I hope the weather is nice near you.”
greet(person: [“name”: “Jane”, “location”: “Cupertino”])
// Prints “Hello Jane!”
// Prints “I hope the weather is nice in Cupertino.”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
65
Q

Swift array

A

Syntax:
[type]

these declarations are equivalent:

let someArray: Array = [“Alex”, “Brian”, “Dave”]
let someArray: [String] = [“Alex”, “Brian”, “Dave”]

-multidimensional arrays can be created by nesting pairs of square brackets

three-dimensional array:
var array3D: [[[Int]]] = [ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ]

array3D[0] refers to [[1, 2], [3, 4]], array3D[0][1] refers to [3, 4], and array3D[0][1][1] refers to the value 4.

  • declaring, appending, iterating, accessing:
var person1 = "Ray"
var person2 = "Brian"
var array:[String] = [person1, person2]
array.append("Waldo")
for person in array {
    print("person: \(person)")
}
var waldo = array[2]
------------------------------------------------------------------------------------
-bridged to Foundation's NSArray class
------------------------------------------------------------------------------------
-can create an Array with a default value:

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
————————————————————————————
-can create an Array by adding two Arrays together:

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
————————————————————————————
-methods:

.count
.isEmpty
.append(_:)
.insert(_:at:)
.remove(at:)
.removeLast()
------------------------------------------------------------------------------------
-can change a range of values:

shoppingList[4…6] = [“Bananas”, “Apples”]
// shoppingList now contains 6 items
————————————————————————————
-iterate over entire set of values:

for item in shoppingList {
    print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
------------------------------------------------------------------------------------
-iterate over values and indices:
for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
------------------------------------------------------------------------------------
-map method: transforms source array into an array of another type whose values are obtained by executing the closure passed as a parameter to each element of the array.

struct Planet {
let name: String
let distanceFromSun: Double
}

let planets = [
    Planet(name: "Mercury", distanceFromSun: 0.387),
    Planet(name: "Venus", distanceFromSun: 0.722),
    Planet(name: "Earth", distanceFromSun: 1.0),
    Planet(name: "Mars", distanceFromSun: 1.52),
    Planet(name: "Jupiter", distanceFromSun: 5.20),
    Planet(name: "Saturn", distanceFromSun: 9.58),
    Planet(name: "Uranus", distanceFromSun: 19.2),
    Planet(name: "Neptune", distanceFromSun: 30.1)
]

let result1 = planets.map { $0.name }

result1 is an array of strings, containing the list of the planet names

  • reduce method: returns a single value obtained by recursively applying the closure to each element of the array.

let result2 = planets.reduce(0) { $0 + $1.distanceFromSun }

result2 is a double, calculated as the sum of the distance of all planets

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
66
Q

Swift dictionary

A

Syntax:
[key type : value type]

these declarations are equivalent:

let someDictionary: [String: Int] = [“Alex”: 31, “Paul”: 39]
let someDictionary: Dictionary = [“Alex”: 31, “Paul”: 39]

  • key type must conform to Swift Hashable protocol
  • stores associations between keys of the same type and values of the same type in a collection with no defined ordering
  • bridged to Foundations’s NSDictionary class
  • dictionary key must conform to the Hashable protocol
  • type is Dictionary
  • type shorthand (preferred) is [Key: Value]
  • create empty dictionary:

var namesOfIntegers = Int: String
// namesOfIntegers is an empty [Int: String] dictionary
————————————————————————————
-create a Dictionary with a Dictionary literal:

var airports: [String: String] = [“YYZ”: “Toronto Pearson”, “DUB”: “Dublin”]

  • declaring, accessing, adding:
var dict:[String: String] = ["Frog":
 "Kermit", "Pig": "Ms. Piggy",
 "Weirdo": "Gonzo" ]
dict["Weirdo"] = "Felipe"
dict["Frog"] = nil // delete frog
for (type, muppet) in dict {
     print("type: \(type), muppet:\(muppet)")
}
------------------------------------------------------------------------------------
-methods:

.count
.isEmpty
.updateValue(-:forKey:)
.removeValue(forKey:)
————————————————————————————
-retrieve a iterable collection of keys or values:

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ
for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
67
Q

Swift closures

A
  • self-contained blocks of functionality that can be passed around and used in your code
  • are reference types
  • can be written without a name by surrounding the code with {} braces
  • if closure type is known, can omit the type of its parameters or return type or both
  • single statement closures implicitly return the value of their only statement
  • can refer to parameters by number instead of by name
  • global functions are closures that have a name and do not capture any values
  • nested functions are closures that have a name and can capture values from their enclosing function
  • closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context
  • can be defined inline right where you want to use them
  • can be stored as properties and local variables and can be passed as arguments to functions
  • similar to blocks in C and Objective-C and lambdas in other programming languages.
  • called closures because they can capture and store references to any constants and variables from the context in which they are defined (closing over those constants and variables).
Syntax:
{ (parameters) -> return type in
    statements
}
-------------------------------------------------------------------------------------------
Fuller version of syntax:

let closureName:(parameter types) -> return type =
{
(parameter name:parameter type) -> return type in

}

  • name of the closure: let closureName
  • closure type syntax: (parameter types) -> return type

-closure expression syntax:
{ (parameter name:parameter type) -> return type in}
It repeats the closure type and includes names for the parameters
——————————————————————————————-
Real world use: completion handlers

let imageView = UIImageView()

HTTP.request(“http://imgur.com/kittens”, completionHandler: { data in
imageView.image = data
})

Define an image view, start the networking request, and provide a completion handler. The completion handler is executed when the lengthy task is completed.

Initialize an object with a closure

let bobView = { () -> UIView in
 let view = UIView()
 view.backgroundColor = .black
 return view
}()
-------------------------------------------------------------------------------------------
Example:
let simpleClosure:(String) -> (String) = { name in
    let greeting = "Hello, World! " + "Program"
    return greeting
}
let result = simpleClosure("Hello, World")
print(result)

Output: Hello, World! Program

name: parameter
in: keyword that separates the parameter from the statements
- ———————————————————————————–
- trailing closure is written after the function call’s parentheses even though it is the function’s final argument

reversedNames = names.sorted() { $0 > $1 }

  • a closure can capture constants and variables from the surrounding context in which it is defined. The incrementer() function captures runningTotal and amount.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
------------------------------------------------------------------------------------
-if a closure is assigned a property of a class instance, and the closure captures that instance by referring to the instance or its members, a strong reference cycle between the close and the instance is created.
------------------------------------------------------------------------------------
-a closure escapes a function when the closure is passed as an argument to the function but is called after the function returns. The closure needs to refer to self explicitly.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}
class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
    }
}
------------------------------------------------------------------------------------
-an autoclosure is a closure that is automatically created to wrap an expression passed as an argument to a function. It takes no arguments and returns the value of the expression wrapped inside of it. Autoclosures enable delayed execution.
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
------------------------------------------------------------------------------------
Another autoclosure
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
------------------------------------------------------------------------------------
-an autoclosure that is allowed to escape

// customersInLine is [“Barry”, “Daniella”]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
68
Q

Objective-C class header (.h)

A

import “AnyHeaderFile.h”

@interface ClassName : SuperClass

// define public properties
// define public methods

@end

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
69
Q

Objective-C class implementation (.m)

A

import “YourClassName.h”

@interface ClassName ()
// define private properties
// define private methods
@end
@implementation ClassName {
// define private instance variables
}

// implement methods

@end

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
70
Q

Objective-C defining methods

A

(type) doIt;
- (type)doItWithA:(type)a;
- (type)doItWithA:(type)a
b: (type)b;

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
71
Q

Objective-C implementing methods

A
  • (type)doItWithA:(type)a
    b:(type)b {
    // Do something with a and b…
    return retVal;
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
72
Q

Objective-C creating an object

A

ClassName * myObject =

[[ClassName alloc] init];

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
73
Q

Objective-C calling a method

A

[myObject doIt];
[myObject doItWithA:a];
[myObject doItWithA:a b:b];

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
74
Q

Objective-C declaring variables

A

type myVariable;

Variable types:

int 1, 2, 500, 10000

float
double 1.5, 3.14, 578.234

BOOL YES, NO

ClassName * NSString *, NSArray *, etc.

id Can hold reference to any object

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
75
Q

Objective-C defining properties

A

@property (attribute1, attribute2)
type propertyName;

strong Adds reference to keep object alive
weak Object can disappear, become nil
assign Normal assign, no reference
copy Make copy on assign
nonatomic Make not threadsafe, increase perf
readwrite Create getter & setter (default)
readonly Create just getter

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
76
Q

Objective-C using properties

A

[myObject setPropertyName:a];
myObject.propertyName = a; // alt

a = [myObject propertyName];
a = myObject.propertyName; // alt
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
77
Q

Objective-C what is a property?

A

1) Automatically defines a private instance variable:
type _propertyName;

2) Automatically creates a getter and setter:
- (type)propertyName;
- (void)setPropertyName:(type)name;

-using _propertyName uses the private instance
variable directly
-using self.propertyName uses
the getter/setter.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
78
Q

Objective-C custom initializer example

A
- (id)initWithParam:(type)param {
    if ((self = [super init])) {
        _propertyName = param;
    }
    return self;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
79
Q

Objective-C NSString examples

A
NSString *personOne = @"Ray";
NSString *personTwo = @"Shawn";
NSString *combinedString =
    [NSString stringWithFormat:
    @"%@: Hello, %@!",
    personOne, personTwo];
NSLog(@"%@", combinedString);
NSString *tipString = @"24.99";
float tipFloat = [tipString floatValue];
80
Q

Objective-C NSArray examples

A
NSMutableArray *array =
    [@[person1, person2] mutableCopy];
[array addObject:@"Waldo"];
NSLog(@"%d items!", [array count]);
for (NSString *person in array) {
    NSLog(@"Person: %@", person);
}
NSString *waldo = array[2];
81
Q

Objective-C control statements

A

If Else
if (condition) {
statement(s) if the condition is true;
}
else {
statement(s) if the condition is not true;
}
————————————————————————————
For
for (counter; condition; update counter) {
statement(s) to execute while the condition is true;
}
————————————————————————————
For In
for (Type newVariable in expression ) {
statement(s);
}

or

Type existingVariable ;
for (existingVariable in expression) {
statement(s);
}

Expression is an object that conforms to the NSFastEnumeration protocol.
An NSArray and NSSet enumeration is over content.
An NSDictionary enumeration is over keys.
An NSManagedObjectModel enumeration is over entities.
————————————————————————————
While
while (condition) {
statement(s) to execute while the condition is true
}
————————————————————————————
Do While
do {
statement(s) to execute while the condition is true
} while (condition);
————————————————————————————
Jump statement:
// stop execution and return to calling function
return;

// leave a loop
break; 
// skip the rest of loop and start the next iteration
continue;   

// absolute jump tp another point in the program. Don’t use it.
goto labelName;

labelName:

// terminate program with an exit code
exit();
82
Q

Objective-C defining interface

A
@interface MyClass : NSObject 
{
    int integer;
    id object;
}
- (void)doSomethingWithThisFloat: (float)i;
@end
83
Q

Objective-C implementing interface

A
#import "MyClass.h"
@implementation MyClass
- (void)doSomethingWithThisFloat: (float)i
{
    ...
}
@end
84
Q

Multithreading options

A

Grand Central Dispatch

  • abstracts away the low-level details of multithreading
  • only have to think about the tasks you want to perform
  • add tasks to serial or concurrent queues
  • can add tasks to groups and run code after all tasks within the group complete

NSOperation and NSOperationQueue

  • provide you with a higher-level API than GCD
  • introduced in iOS 4 and implemented with GCD under the hood
  • use this API over GCD unless performing a simple unit of work on a specific queue.
  • provide you with powerful functionality such as cancellation and dependencies

performSelectorInBackground

  • to perform a simple unit of work on a new thread, NSObject provides you with performSelectorInBackground(_:withObject:).
  • can run a function (with an argument) on a background thread
85
Q

Swift 5.0 features

A

ABI stability

  • (Application Binary Interfaces) how Swift interacts with other libraries
  • Swift lives within each app, not iOS, so each app bundles Swift dynamic libraries
  • If Swift becomes ABI stable, it can live within iOS. Apps can be smaller.
86
Q

Swift control statements

A

Continue:
ends program execution of current iteration of a loop statement

Break:
ends program execution of a loop, if statement or a switch statement.

Fallthrough:
causes program execution to continue from one case in a switch statement to the next case

Return:
causes program execution to return to the calling function or method

87
Q

Swift Optional chaining

A
  • process of querying and calling properties, methods and subscripts on an optional that might currently be nil
  • multiple queries can be chained together
  • if any link in the chain is nil, then the entire chain fails
  • alternative to forced unwrapping

class Person {
var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
------------------------------------------------------------------------------------
More complex example:
class Residence {
    var rooms = [Room]()
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?
}
class Room {
    let name: String
    init(name: String) { self.name = name }
}
class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\(buildingNumber) \(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}
------------------------------------------------------------------------------------
Accessing Properties through Optional Chaining

-createAddress is not called

func createAddress() -> Address {
    print("Function was called.")
    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"
return someAddress } john.residence?.address = createAddress()

Calling Methods through Optional Chaining

func printNumberOfRooms() {
    print("The number of rooms is \(numberOfRooms)")
}
if john.residence?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}
// Prints "It was not possible to print the number of rooms."

Accessing Subscripts through Optional Chaining

  • place the question mark before the subscript’s brackets
  • try to retrieve the name of the first room
if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}
// Prints "Unable to retrieve the first room name."

  • try to set a new value
    john. residence?[0] = Room(name: “Bathroom”)

Accessing Subscripts of Optional Type

-place a question mark after the subscript’s closing bracket to chain on its optional return value

var testScores = [“Dave”: [86, 82, 84], “Bev”: [79, 94, 81]]
testScores[“Dave”]?[0] = 91
testScores[“Bev”]?[0] += 1
testScores[“Brian”]?[0] = 72
// the “Dave” array is now [91, 82, 84] and the “Bev” array is now [80, 94, 81]
————————————————————————————
Linking Multiple Levels of Chaining

-multiple levels of optional chaining do not add more levels of optionality to the returned value

if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
    print("John's street name is \(johnsStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."
------------------------------------------------------------------------------------
Chaining on Methods with Optional Return Values

-can use optional chaining to call a method that returns a value of optional type and to chain on that method’s return value if needed

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."

If further optional chaining on the method’s return value is desired, place the optional chaining question mark after the method’s parentheses. In the example below, optional chaining is done on the buildIdentifier() method’s return value, not on the buildingIdentifier() method itself.

if let beginsWithThe =
    john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
    if beginsWithThe {
        print("John's building identifier begins with \"The\".")
    } else {
        print("John's building identifier does not begin with \"The\".")
    }
}
// Prints "John's building identifier begins with "The"."
88
Q

Swift Delegation

A

delegation:
- design pattern where one object in a program acts on behalf of another
- The delegating object keeps a reference to the other object (the delegate) and sends it a message when appropriate.

Example:

  • UIViewController manages a UICollectionView
  • UIViewController adopts the UICollectionViewDelegate protocol
  • to conform to the protocol, UIViewController implements collectionView(_ collectionViewL UICollectionView, didSelectItemAt indexPath: IndexPath)
  • this enables UIViewController to get informed about taps on UICollectionViewCell objects
  • UICollectionView has delegated the responsibility of handling taps on UICollectionViewCell objects to UIViewController.
89
Q

Structure of iOS app

A
  • Models (Data Models and Entities)
  • Views (custom views)
  • Controllers (View Controllers)
  • Application (application related file, app delegates, storyboards)
  • Resources (resources like Images, custom fonts, audio files with each in different subfolders)
  • Services (services/singletons for API calls etc)
  • Library (classes that don’t fall under MVC)
90
Q

Objective-C Category

A
  • parts of a class separated out to simplify the class
  • makes it easy to add new functions to a class after its creation
  • declare with @interface

Syntax:
@interface ClassName (CategoryName)

@end

Example:
@interface NSString(MyAdditions)
+(NSString *)getCopyRightString;
@end

@implementation NSString(MyAdditions)

+(NSString *)getCopyRightString {
return @”Copyright TutorialsPoint.com 2013”;
}

@end

int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *copyrightString = [NSString getCopyRightString];
NSLog(@”Accessing Category: %@”,copyrightString);

[pool drain];
return 0;
}

91
Q

Swift Extension

A
  • similar to Category
  • adds functionality to existing class, structure, enumeration or protocol type
Syntax:
extension SomeType {
    // new functionality to add to SomeType goes here
}
92
Q

Notifications

A

-provide timely and important information anytime whether the device is locked or in use

-local notifications:
app configures notification details locally and passes details to the system which then handles the delivery of the notification when the app is not in the foreground

-remote notifications:
remote server pushes data to user devices via the Apple Push Notification Service

-frameworks to handle notifications:
User Notifications Framework:
manages both local and remote notifications

User Notifications UI Framework:
customizes the appearance of the system’s notification interface

93
Q

memory leaks

A
  • a portion of memory that is occupied forever and never used again
  • garbage that takes up space and causes problems
  • introduce unwanted side effects and crashes
  • most often caused by retain cycles
  • when an object has a strong association to another object, it is retaining it
  • two ways to resolve strong reference cycles:
  • –weak references: variable does not take ownership of an object and can be nil
  • –unowned references: does not keep a strong hold on referring instance but can’t be nil

How to eliminate memory leaks:
-don’t create them

  • use Swift Lint
    • -detects leaks at runtime and make them visible
    –use LifetimeTracker if you know how many instances of an object must be alive at a time.
  • profile the app frequently with the memory analysis tools in XCode
    • -Instruments

-unit test leaks with SpecLeaks, a pod that lets you create tests for leaks.

94
Q

Key-Value Observing (KVO)

A
  • Cocoa programming pattern used to notify objects about changes to properties of other objects.
  • Can only be used with classes that inherit from NSObject.
  1. Annotate a property for Key-Value Observing
  2. Define an Observer
  3. Associate the Observer with the Property to Observe
  4. Respond to a Property Change
95
Q

Class vs. Struct

A

classes are reference types

structs are value types

96
Q

Delegates vs. Closures

A

Delegates:
-have a strong retain cycle (need to define protocol as class and to define the delegate property as weak)
-more code than closures
-implementing many other delegate protocols is messy
-passes a reference
-need to mock delegates for testing
————————————————————————————
Closures:
-need to define self with unowned or weak
-shorter
-passes a function
-code is decoupled
-easier testing

97
Q

Swift functions

A
  • declare with “func”
  • every function has a type consisting of its parameter types and return type
  • use “->” to separate the parameter names and types from the return type
  • by default, functions use their parameter names as labels for their arguments. Write a custom argument label before the parameter name, or write “_” to use no argument label
  • use a tuple to return multiple values

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {

}
————————————————————————————
-can return optional tuple return types

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
------------------------------------------------------------------------------------
Use optional binding to check if the functions returns an actual tuple or nil.
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "min is -6 and max is 109"
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
  • any function with a body containing just one return line can omit the return.
  • each function parameter has both an argument label and a parameter name
  • an argument label for a parameter may be omitted by writing an underscore instead of an explicit argument label for that parameter
  • can define a default value for a parameter
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // If you omit the second argument when calling this function, then
    // the value of parameterWithDefault is 12 inside the function body.
}
------------------------------------------------------------------------------------
-variadic parameters accept zero or more values of a specified type. The values are made available within the function's body as an array. A function may have at most one variadic parameter.

func arithmeticMean(_ numbers: Double…) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
————————————————————————————
-in-out parameters enable a function to modify a parameter value and have those changes persist after the function call has ended. An in-out parameter must be a variable, it cannot have default values, and it cannot be variadic. An ampersand is placed before a variable’s name when it is passed as an argument to an in-out parameter.

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&amp;someInt, &amp;anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

-can define a constant or variable to be of a function type

var mathFunction: (Int, Int) -> Int = addTwoInts

print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"
------------------------------------------------------------------------------------
-a function type can be used as a parameter type for another function
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"
------------------------------------------------------------------------------------
-a function type can be used as the return type of another function
func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}
------------------------------------------------------------------------------------
-functions can be nested. Nested functions have access to variables declared in the outer function.
  • functions can return other functions
  • functions can take another function as an argument
  • functions are a special case of closures
98
Q

Swift class

A
class MyClass : OptionalSuperClass,
OptionalProtocol1, OptionalProtocol2 {
    var myProperty:String
    var myOptionalProperty:String?
    // More properties...
    // Only need override if subclassing
    override init() {
        myProperty = "Foo"
    }
    // More methods...
}

-create a class with “class” followed by a name

-create an instance of a class:
var shape = Shape()

-create an initializer:
init(name : String) {
self.name = name
}

  • initializer for subclass:
  • –sets value of properties that subclass declares
  • –calls superclass’s initializer
  • –changes values of properties defined by superclass
  • –does any other setup work that uses methods, getters or setters
  • use deinit to create a deinitializer if you need to perform some cleanup before the object is deallocated
  • the override keyword is used for methods that override the superclass’s implementation
  • properties can have a getter and a setter
  • use willSet and didSet to supply code that is run before and after setting a new value. The code is run any time the value changes outside of an initializer.
  • can write ? before operations like methods, properties and subscripting. If the value before the ? is nil, everything afterwards is ignored, and value of the whole expression is nil.
  let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
  let sideLength = optionalSquare?.sideLength
99
Q

Swift struct

A
  • value type
  • has methods and initializers like classes, but structs are always copied when they are passed
  • use when you need to encapsulate simple data values and when any properties stored by the struct are value types. Example: a point in a 3D coordinate system with x, y and z properties each with type Double.
struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
100
Q

Swift Operators

A

arithmetic operators in Swift do not overflow by default. Overflow operators begin with an ampersand (&)

BitwiseShiftPrecedence
« Bitwise left shift
» Bitwise right shift

Shifting an integer’s bit the the left by one position doubles its value. Shifting it to the right halves its value.

MultiplicationPrecedence
*	Multiply
/	Divide
%	Remainder
&amp;*	Multiply, ignoring overflow
&amp;/	Divide, ignoring overflow
&amp;%	Remainder, ignoring overflow
&amp;	Bitwise AND
AdditionPrecedence
\+	Add
-	Subtract
&amp;+	Add with overflow
&amp;-	Subtract with overflow
|	Bitwise OR
^	Bitwise XOR

RangeFormationPrecedence
..< Half-open range (contains first value but not final value)
… Closed range (contains all values)

CastingPrecedence
is Type check
as Type cast

NilCoalescingPrecedence
?? nil Coalescing

ComparisonPrecedence
<	Less than
<=	Less than or equal
>	Greater than
>=	Greater than or equal
==	Equal
!=	Not equal
===	Identical
!==	Not identical
~=	Pattern match

LogicalConjunctionPrecedence
& Logical AND

LogicalDisjunctionPrecedence
|| Logical OR

DefaultPrecedence

AssignmentPrecedence
=	Assign
*=	Multiply and assign
/=	Divide and assign
%=	Remainder and assign
\+=	Add and assign
-=	Subtract and assign
<<=	Left bit shift and assign
>>=	Right bit shift and assign
&amp;=	Bitwise AND and assign
^=	Bitwise XOR and assign
|=	Bitwise OR and assign
&amp;&amp;=	Logical AND and assign
||=	Logical OR and assign
Prefix
\+   Unary plus
-    Unary minus
!    Logical NOT
~   Bitwise NOT

Postfix
… open-ended range

Ternary
?: question ? answer1 : answer2

Can create custom operators:

  • declare precedencegroup
  • declare operator
  • implement top-level function
  • consider providing a mutating variant as well

-classes and structures can overload existing operators by providing their own implementations

struct Vector2D {
    var x = 0.0, y = 0.0
}
extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

-implementing a prefix operator:

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

-compound assignment operators combine assigment (=) with another operation.

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}
  • equivalence operators
  • implement:

extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
}

-ask Swift to synthesize an implementation:

struct Vector3D: Equatable {
    var x = 0.0, y = 0.0, z = 0.0
}
let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
    print("These two vectors are also equivalent.")
}
// Prints "These two vectors are also equivalent."

-new operators are declared at a global level using the operator keyword marked with the prefix, infix or postfix modifiers:

prefix operator +++

-a custom infix operator that is not explicitly placed into a precedence group is given a default precedence group with a precedence immediately higher than the precedence of the ternary conditional operator

infix operator +-: AdditionPrecedence
extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)
101
Q

Swift reserved words

A
class	
break	
as	
associativity
deinit	
case	
dynamicType	
convenience
enum	
continue	
false	
dynamic
extension	
default	
is	
didSet
func 
do	
nil	
final
import	
else	
self	
get
init	
fallthrough	
Self	
nfix
internal	
for	
super	
inout
let	
if	
true	
lazy
operator	
in	
\_\_COLUMN\_\_	
left
private	
return	
\_\_FILE\_\_	
mutating
protocol	
switch	
\_\_FUNCTION\_\_	
none
public	
where	
\_\_LINE\_\_	
nonmutating
static	
while		
optional
struct			
override
subscript			
postfix
typealias			
precedence
var			
prefix
Protocol
required
right
set
Type
unowned
weak
102
Q

Swift value and reference types

A

value type:
-each instance keeps a unique copy of its data, usually a struct, enum or tuple

  • use when comparing instance data with == makes sense
  • use when you want copies to have independent state
  • use when the data will be used in code across multiple threads

reference type:
-instances share a single copy of the data

  • type is usually defined as a class
  • use when comparing instance identity with === makes sense
  • use when you want to create shared, mutable state
103
Q

Swift named types and compound types

A

named types:
-can be given a particular name when it’s defined

  • include classes, structures, enumerations and protocols
  • data types that are considered primitive in other languages are actually named types

compound types:
-don’t have names

  • defined in Swift language itself
  • two types, function and tuple
104
Q

Swift type annotation

A

begin with a colon and end with a type

let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }
105
Q

Swift type identifier

A

refers to either a named type or a type alias of a named or compound type

-case 1: type identifier directly refers to named type

Int refers to the named type Int

-case 2: type identifier refers to type alias of named or compound type

typealias Point = (Int, Int)
let origin: Point = (0, 0)

-case 3: type identifier uses dot syntax to refer to named types declared in other modules or nested within other types

var someValue: ExampleModule.MyType

106
Q

Swift tuple type

A
  • comma-separated list of types enclosed in parentheses
  • used to arrange multiple values in a single compound value
  • can be used as a return type of a function to enable the function to return a single tuple containing multiple values
  • can name the elements of a tuple type and use those names to refer to the values of the individual elements
var point = (0, 0)
point.0 = 10
point.1 = 15
point // (10, 15)
107
Q

Swift function type

A

represents the type of a function, method or closure and consists of a parameter and return type separated by an arrow

(parameter type) -> return type

  • parameter type is a comma-separated list of types
  • return type can be a tuple type
  • a parameter of the function type () -> T (where T is any type) can apply the autoclosure attribute to implicitly create a closure at its call sites
  • can have a variadic parameter (a base type name followed immediately by …). A variadic parameter is treated as an array.
  • an inout parameter is specified with the inout keyword. An inout parameter persists changes made to the parameter. Variadic parameters and return types cannot be marked with the inout keyword.
  • if a function type has only one parameter and the parameter’s type is a tuple type, the tuple type must be parenthesized when writing the function’s type.
  • argument names in functions and methods are not part of the corresponding function type
  • function types that throw an error must be marked with the throws keyword
  • function types that can rethrow an error must be marked with the rethrows keyword
108
Q

Swift optional type

A

A type that represents either a wrapped value or nil, the absence of a value.

Syntax:
?

these declarations are equivalent:

var optionalInteger: Int?
var optionalInteger: Optional

  • named type is Optional
  • Optional is an enumeration with two values, none and some (Wrapped), which are used to represent values that may or may not be present
enum Optional {
    case none
    case some(Wrapped)
}
  • if an instance of an optional type contains a value, the value can be accessed with ! operator.
  • using the ! operator to unwrap an optional that has a value of nil results in a runtime error
  • optional chaining and optional binding can be used to conditionally perform an operation on an optional expression. If the value is nil, no operation is performed, and therefore no runtime error is produced.
  • an implicitly unwrapped optional is safely assumed to always have a value after the value is first set. Therefore it doesn’t need to be checked and unwrapped every time it is accessed. You mark it with ! instead of ?.
109
Q

Swift error handling

A
  • used to respond to and recover from error conditions your program may encounter during execution
  • errors are represented by values of types that conform to the Error protocol
enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

-throw an error

Handling Errors

Four ways to handle errors in Swift:

  • propagate the error from a function to the code that calls the function
  • handle the error using a do-catch statement
  • handle the error as an optional value
  • assert that the error will not occur

Propagating Errors Using Throwing Functions

-a throwing function is a function marked with throws

func canThrowErrors() throws -> String

func cannotThrowErrors() -> String

struct Item {
var price: Int
var count: Int
}

class VendingMachine {
var inventory = [
“Candy Bar”: Item(price: 12, count: 7),
“Chips”: Item(price: 10, count: 4),
“Pretzels”: Item(price: 7, count: 11)
]
var coinsDeposited = 0

    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }
    guard item.count > 0 else {
        throw VendingMachineError.outOfStock
    }

    guard item.price <= coinsDeposited else {
        throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
    }

    coinsDeposited -= item.price
        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem
    print("Dispensing \(name)")
} }

-buyFavoriteSnack() will propagate to the point where it is called:

let favoriteSnacks = [
“Alice”: “Chips”,
“Bob”: “Licorice”,
“Eve”: “Pretzels”,
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? “Candy Bar”
try vendingMachine.vend(itemNamed: snackName)
}
————————————————————————————
-throwing initializers can also propagate errors:

struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
————————————————————————————
Handling Errors using Do-Catch

-use a do-catch statement to handle errors by running a block of code

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
    print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
    print("Unexpected error: \(error).")
}
// Prints "Insufficient funds. Please insert an additional 2 coins."

-the catch clauses do not have to handle every possible error that can be thrown. If none of the catch clauses handle the error, the error propagates to the surrounding scope. The propagated error must be handled by some surrounding scope, or a runtime error will occur.

func nourish(with item: String) throws {
    do {
        try vendingMachine.vend(itemNamed: item)
    } catch is VendingMachineError {
        print("Invalid selection, out of stock, or not enough money.")
    }
}
do {
    try nourish(with: "Beet-Flavored Chips")
} catch {
    print("Unexpected non-vending-machine-related error: \(error)")
}
// Prints "Invalid selection, out of stock, or not enough money."
------------------------------------------------------------------------------------
Converting Errors to Optional Values

  • use try? to handle an error by converting it to an optional value
  • if someThrowingFunction() throws an error, the value of x and y is nil
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}
------------------------------------------------------------------------------------
-the following code uses separate approaches to fetch data or returns nil if all of the approaches fail:

func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
————————————————————————————
Disabling Error Propagation

-disable error propagation by writing try! before the expression and wrap the call in a runtime assertion that no error will be thrown. If an error is thrown, a runtime error results.

Specifying Cleanup Actions

-use a defer statement to execute a set of statements just before code execution leaves the current block of code. This enables any cleanup that should be performed regardless of how execution leaves the current block of code. Execution is deferred until the current scope is exited. Deferred statements may not contain any code that would transfer control. Deferred actions are executed in the reverse order they are written in the source code. Defer statements can be used even when no error handling code is involved.

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // Work with the file.
        }
        // close(file) is called here, at the end of the scope.
    }
}
110
Q

Swift assertions and preconditions

A
  • checks that happen at runtime
  • used to make sure an essential condition is satisfied before executing any further code
  • if the Boolean condition in the assertion or precondition evaluates to true, code execution continues. If the condition evaluates to false, the current state of the program is invalid, code execution ends, and the app is terminated.
  • are a useful form of documentation within the code
  • cause app to terminate more predictably if an invalid state occurs and helps make the problem easier to debug
  • assertions are checked only in debug builds
  • preconditions are checked in both debug and production builds
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.

-use a precondition when a condition has the potential to be false but must definitely be true for the code to continue execution.

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
111
Q

Swift protocol composition type

A

-a type that conforms to each protocol in a list of specified protocols or a type that is a subclass of a given class and conforms to each protocol in a list of specified protocols

Protocol 1 & Protocol 2

-allows you to specify a value whose type conforms to the requirements of multiple protocols without explicitly defining a new, named protocol that inherits from each protocol you want the type to conform to.

112
Q

Swift metatype type

A

refers to the type of any type, including class types, structure types, enumeration types and protocol types

class SomeBaseClass {
    class func printClassName() {
        print("SomeBaseClass")
    }
}
class SomeSubClass: SomeBaseClass {
    override class func printClassName() {
        print("SomeSubClass")
    }
}
let someInstance: SomeBaseClass = SomeSubClass()
// The compile-time type of someInstance is SomeBaseClass,
// and the runtime type of someInstance is SomeSubClass
type(of: someInstance).printClassName()
// Prints "SomeSubClass"

-use an initializer expression to construct an instance of a type from that type’s metatype value. For class instances, the initializer that is called must be marked with the required keyword, or the entire class marked with the final keyword.

class AnotherSubClass: SomeBaseClass {
    let string: String
    required init(string: String) {
        self.string = string
    }
    override class func printClassName() {
        print("AnotherSubClass")
    }
}
let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: "some string")
113
Q

Swift type inheritance clause

A
  • used to specify which class a named type inherits from and which protocols a named type conforms to
  • begins with a colon followed by a list of type identifiers.
114
Q

Swift type inference

A

allows omission of the type or part of the type of many variables and expressions in your code

-operates at the level of a single expression or statement

var x: Int = 0
can be written as:
x = 0

let e = 2.71828 // The type of e is inferred to be Double.
let eFloat: Float = 2.71828 // The type of eFloat is Float.
115
Q

Swift properties

A
  • associate values with a particular class, structure or enumeration
  • stored properties store constant and variable values as part of an instance
    • -stored properties are provided only by classes and structures
    • -can be either variable stored properties (var keyword) or constant stored properties (let keyword).
    • -a lazy stored property is a property whose initial value is not calculated until the first time it is used
    • -use the lazy modifier before its declaration
    • -a lazy property must be declared as a variable with the var keyword
    • -lazy properties could be initialized more than once in a multithreading situation
    • -a stored property does not have a corresponding instance variable, and its backing store is not accessed directly
    • -the properties of an instance of structure assigned to a constant cannot be changed even if the properties were declared as variables. This is because structures are value types.
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
------------------------------------------------------------------------------------
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property
  • this is not true for classes which are reference types. If you assign an instance of a reference type to a constant, you can still change that instance’s variable properties.
  • computed properties are provided by classes, structures and enumerations.
    • -do not actually store a value
    • -must be declared as variable properties with the var keyword
    • -provide a getter and an optional setter
    • -get is accessed through dot syntax
    • -if a setter does not provide a name for the new value to be set, a default name of newValue is used
    • -a computed property with a getter but no setter is a read-only computed property. Its declaration can be simplified by removing the get keyword and its braces.
  • property observers monitor changes in a property’s value.
    • -can respond to them with custom actions
    • -can be added to stored properties you define yourself (not lazy ones) and to properties a subclass inherits from its superclass
    • -called every time a property’s value is set
    • -can define either or both willSet (called just before the value is stored) and didSet (called immediately after the new value is stored)
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
------------------------------------------------------------------------------------
-global variables are defined outside of any function, method, closure or type context
  • global constants and variables are always computed lazily
  • local variables are defined within a function, method or closure context
  • local constants and variables are never computed lazily
  • instance properties belong to an instance of a particular type
  • computed variables can be defined in either a global or a local scope
  • type properties belong to the type itself
    • -only one copy of these properties exists no matter how many instances are created
    • -written as part of the type’s definition within the type’s outer curly braces
    • -stored type properties can be variables or constants and must always have a default value. They are lazily initialized on the first access. They are guaranteed to be initialized only once. They do not need the lazy modifier.
    • -computed type properties are always declared as variable properties
    • -defined with the static keyword
struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}

–queried and set with dot syntax (but on the type, not on an instance of the type)

print(SomeStructure.storedTypeProperty)
// Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"
------------------------------------------------------------------------------------
-property wrappers add a layer of separation between code that manages how a property is stored and the code that defines a property
@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init() { self.number = 0 }
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

Usage:

struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints “12”
————————————————————————————
-can have an initializer for a property wrapper to support setting an initial value or other customization

@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int

var wrappedValue: Int {
    get { return number }
    set { number = min(newValue, maximum) }
}

init() {
    maximum = 12
    number = 0
}
init(wrappedValue: Int) {
    maximum = 12
    number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
    self.maximum = maximum
    number = min(wrappedValue, maximum)
} } ------------------------------------------------------------------------------------ -property wrappers can define a projected value. The name of the projected value is the same as the wrapped value except it begin with a $ sign. 
@propertyWrapper
struct SmallNumber {
    private var number: Int
    var projectedValue: Bool
    init() {
        self.number = 0
        self.projectedValue = false
    }
    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }
}
struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"
someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"
116
Q

Swift methods

A

-functions that are associated with a particular type

  • can be defined by classes, structure and enumerations
  • instance methods belong to instances of a particular class, structure or enumeration
    • -support the functionality of those instances
    • -have exactly the same syntax as functions
    • -written within open and closing braces of the type it belongs to
    • -has implicit access to all other instance methods and properties of that type
    • -can be called only on a specific instance of the the type it belongs to
    • -called with same dot syntax as properties
class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}
let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0

–self property is exactly equivalent to the instance itself. Don’t usually need it but do need it if a parameter name for an instance has the same name as a property of that instance.

  func increment() {
      self.count += 1
  }

–value types can be modified within instance methods by using the mutating keyword before the func keyword for that method. However, you cannot call a mutating method on a constant of structure type because its properties cannot be changed. Mutating methods can assign a new instance to the implicit self property.

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"
------------------------------------------------------------------------------------
-type methods are called on an instance of a particular type. You can also define methods that are called on the type itself.
  • -indicated by the static keyword before the method’s func keyword
  • -called with dot syntax like instance methods, but they are called on the type, not on an instance of that type
class SomeClass {
    class func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

–self refers to the type itself

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1
static func unlock(_ level: Int) {
    if level > highestUnlockedLevel { highestUnlockedLevel = level }
}

static func isUnlocked(_ level: Int) -> Bool {
    return level <= highestUnlockedLevel
}
    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

LevelTracker is used with the Player class:

class Player {
    var tracker = LevelTracker()
    let playerName: String
    func complete(level: Int) {
        LevelTracker.unlock(level + 1)
        tracker.advance(to: level + 1)
    }
    init(name: String) {
        playerName = name
    }
}

Create a new player:

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"

Create a second player:

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("player is now on level 6")
} else {
    print("level 6 has not yet been unlocked")
}
// Prints "level 6 has not yet been unlocked"
117
Q

Swift inheritance

A
  • a class can inherit methods, properties and other characteristics from another class
  • the inheriting class is known as a subclass, and the class it inherits from is its superclass
  • classes can call and access methods, properties and subscripts belonging to their superclass and can provide their own overriding versions of those methods, properties and subscripts
  • classes can add property observers to inherited properties in order to be notified when the value of a property changes. Property observers cannot be added to inherited constant stored properties or inherited read-only computed properties because they cannot be set.
  • any class that does not inherit from another class is known as a base class
  • classes do not inherit from a universal base class. Classes defined without specifying a superclass automatically become base classes.

Vehicle base class:

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // do nothing - an arbitrary vehicle doesn't necessarily make a noise
    }
}
------------------------------------------------------------------------------------
-subclassing is the act of basing a new class on an existing class. Write the subclass name before the superclass name separated by a colon.
class Bicycle: Vehicle {
    var hasBasket = false
}

Can modify the inherited currentSpeed property:

bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
------------------------------------------------------------------------------------
-subclasses can be subclassed
class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}
------------------------------------------------------------------------------------
-subclasses can provide their own custom implementations of an instance method, type method, instance property, type property or subscript that it would otherwise inherit from a superclass. This is known as overriding. Prefix the overriding definition with the override keyword. Can use the existing superclass implementation as part of the override by using the super prefix.
class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}
------------------------------------------------------------------------------------
-an inherited instance or type property can be overridden. Custom getters and setters can override any inherited property. If a setter is provided as part of a property override, a getter must also be provided. If the getter doesn't modify the value, the inherited value can be passed through with the super keyword.
class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

-property overriding can be used to add property observers to an inherited property. Note that both an overriding setter and an overriding property observer cannot be provided for the same property. Changes may be observed within the custom setter.

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}
------------------------------------------------------------------------------------
-preventing a method, property or subscript from being overridden is done by marking it as final. This is done by writing the final modifier before the method, property or subscript's introducer keyword. An entire class can be marked as final by writing the final modifier before the class keyword in its class definition. Attempting to override something marked as final results in a compile-time error.
118
Q

Swift initialization

A
  • the process of preparing an instance of a class, structure or enumeration for use
  • involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use
  • initializers are like special methods that can be called to create a new instance of a particular type. They do not return a value.
struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"

-default property values are initial values assigned to a property when it is defined

struct Fahrenheit {
var temperature = 32.0
}
————————————————————————————
-initialization can be customized with initialization parameters.

struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
------------------------------------------------------------------------------------
-argument labels must be used in an initializer if they are defined
struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}
------------------------------------------------------------------------------------
-can use an underscore if you do not want to use an argument label.
struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
    init(_ celsius: Double) {
        temperatureInCelsius = celsius
    }
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0

-can have properties of optional type. Properties of optional type are automatically initialized with a value of nil.

class SurveyQuestion {
    var text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
------------------------------------------------------------------------------------
-a constant property can be set at any point during initialization as long as it it set to a definite value by the time initialization finishes.
class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
------------------------------------------------------------------------------------
-Swift provides a default initializer for any structure or class that provide default values for all of its properties and does not provide at least one initializer itself
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()
------------------------------------------------------------------------------------
-structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers. The structure receives a memberwise initializer even if it has stored properties that do not have default values.
struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
------------------------------------------------------------------------------------
-can omit values for any properties that have default values:
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// Prints "0.0 2.0"
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// Prints "0.0 0.0"

-initializer delegation is the process of calling other initializers to perform part of an instance’s initialization

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}
------------------------------------------------------------------------------------
-all of a class's stored properties must be assigned an initial value during initialization

-designated initializers are the primary initializers for a class. They fully initialize all properties introduced by that class and call an appropriate superclass initializer to continue the initialization process up the superclass chain. Every class must have at least one.

init(parameters) {
statements
}
————————————————————————————
-convenience initializers are secondary, supporting initializers for a class. They can call a designated initializer from the same class, or they can create an instance of that class for a specific use case or input value type. They are optional. Use the convenience keyword before the init keyword separated by a space.

convenience init(parameters) {
    statements
}
------------------------------------------------------------------------------------
Initializer Delegation for Class Types

-rules for delegation calls between initializers:

Rule 1
A designated initializer must call a designated initializer from its immediate superclass

Rule 2
A convenience initializer must call another initializer from the same class

Rule 3
A convenience initializer must ultimately call a designated initializer

A simple way to remember this is:
-Designated initializers must always delegate up
-Convenience initializers must always delegate across
————————————————————————————
Two-Phase Initialization

  • class initialization is a two-phase process
    • -in the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use. This two-stage process makes initialization safe while still giving complete flexibility to each class in a class hierarchy.
  • Swift’s compiler performs four safety-checks:
    • -Safety check 1: a designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer
    • -Safety check 2: a designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property
    • –Safety check 3: a convenience initializer must delegate to another initializer before assigning a value to any property
    ## –Safety check 4: an initializer cannot call any instance methods, read the values of any instance properties or refer to self as a value until after the first phase of initialization is completePhase 1
-A designated or convenience initializer is called on a class.
Memory for a new instance of that class is allocated. The memory is not yet initialized.
  • A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
  • The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
  • This continues up the class inheritance chain until the top of the chain is reached.

Phase 2

  • Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
  • Finally, any convenience initializers in the chain have the option to customize the instance and to work with self.

Initializer Inheritance and Overriding

  • subclasses do not inherit their superclass initializers by default
  • a subclass initializer that matches a superclass designated initializer is effectively an override, and the override modifier must be written before the subclass’s initializer definition
  • a subclass initializer that matches a superclass convenience initializer can never be called directly by the subclass. It is not an override.
class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
}
class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}
class Hoverboard: Vehicle {
    var color: String
    init(color: String) {
        self.color = color
        // super.init() implicitly called here
    }
    override var description: String {
        return "\(super.description) in a beautiful \(color)"
    }
}
------------------------------------------------------------------------------------
-superclass initializers are automatically inherited if:  

  • -Rule 1: your subclass doesn’t define any designated initializers
  • -Rule 2: your subclass provides an implementation of all its superclass initializers either by inheriting them per Rule 1 or by providing a custom implementation as part of its definition
class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}
class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}
class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}
var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
    print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘
------------------------------------------------------------------------------------
-failable initializers cope with initialization conditions that can fail. Place a ? after the init keyword (init?). It creates an optional value of the type it initializes. You write "return nil" within a failable initializer to indicate a point at which initialization failure can be triggered.
struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
    print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
------------------------------------------------------------------------------------
-a failable initializer can be used to select an appropriate enumeration case
enum TemperatureUnit {
    case kelvin, celsius, fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .kelvin
        case "C":
            self = .celsius
        case "F":
            self = .fahrenheit
        default:
            return nil
        }
    }
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
------------------------------------------------------------------------------------
-enumerations with raw values automatically receive a failable initializer, init?(rawValue:)

  • a failable initializer can delegate across to another failable initializer from the same class, structure or enumeration. A subclass failable initializer can delegate up to a superclass failable initializer.
  • a failable initialize can also delegate to a nonfailable initializer
  • a superclass failable initializer can be overridden in a subclass. It can be overridden with a subclass nonfailable initializer.

Required Initializers

-required initializers indicate that every subclass of the class must implement that initializer. Use the required modifier before the init keyword. The required modifier must also be used before every subclass implementation.

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
————————————————————————————
Setting a Default Property Value with a Closure or Function

-a closure or global function can be used to provide a customized default value for a stored property

struct Chessboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...8 {
            for j in 1...8 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
    func squareIsBlackAt(row: Int, column: Int) -> Bool {
        return boardColors[(row * 8) + column]
    }
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"
119
Q

Testing an app

A

XCTest:

  • framework for unit and UI tests
  • tightly integrated into XCode
120
Q

Weak self in closures

A
  • closure is getting a weak reference to self
  • prevents retain cycle
  • also this won’t crash if the self doesn’t exist anymore
let myClosure = { [weak self] in 
  self?.doStuff()
}
121
Q

Operation and OperationQueue

A
  • enable concurrent performance
  • built on top of GCD
Operation:
-abstract class that represents a logical unit of work

OperationQueue:
-prioritized FIFO queue
————————————————————————————
base class for Operation:

class DAOperation: Operation {

    private var _executing = false {
        willSet {
            willChangeValue(forKey: "isExecuting")
        }
        didSet {
            didChangeValue(forKey: "isExecuting")
        }
    }
override var isExecuting: Bool {
    return _executing
}
    private var _finished = false {
        willSet {
            willChangeValue(forKey: "isFinished")
        }
    didSet {
        didChangeValue(forKey: "isFinished")
    }
}

override var isFinished: Bool {
    return _finished
}
    func executing(_ executing: Bool) {
        _executing = executing
    }
    func finish(_ finished: Bool) {
        _finished = finished
    }
}
------------------------------------------------------------------------------------
networking operation:

class GetDataOperation: DAOperation {

private let urlString: String
private let provider: NetworkingProvider

var responseData: Data?

init(withURLString urlString: String, andNetworkingProvider provider: NetworkingProvider = AFNetworkConnector()) {
    self.urlString = urlString
    self.provider = provider
}
    override func main() {
        guard isCancelled == false else {
            finish(true)
            return
        }
        executing(true)
        provider.restCall(urlString: urlString) { (data) in
            self.responseData = data
            self.executing(false)
            self.finish(true)
        }
    }
}
------------------------------------------------------------------------------------
create operation queue:

create operations and add to the queue:

let networkingOperation = GetDataOperation(withURLString: urlString, andNetworkingProvider: networkingProvider)
let parsingOperation = ParseDataOperation(withFactory: moviesFactory)

operationQueue.addOperations([networkingOperation, parsingOperation], waitUntilFinished: false)

122
Q

optional binding

A

conditionally bind the wrapped value of an Optional instance to a new variable

Use one of the optional binding control structures, including if let, guard let, and switch.

if let starPath = imagePaths["star"] {
    print("The star image is at '\(starPath)'")
} else {
    print("Couldn't find the star image")
}
123
Q

nil-coalescing operator

A

can be used to supply a default value in case the Optional instance is nil

let defaultImagePath = "/images/default.png"
let heartPath = imagePaths["heart"] ?? defaultImagePath
print(heartPath)
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath
print(shapePath)
124
Q

unconditional unwrapping

A

use when certain that an instance of Optional contains a value

let number = Int("42")!
print(number)
125
Q

Swift String and Character

A
  • bridged with Foundation’s NSString class. Foundation extends String to expose NSString methods.
  • are value types
  • Unicode-compliant
  • can iterate over the Character values for a String

for character in “Dog!” {
print(character)
}

-create a multiline string literal by enclosing the charactes with three double quotation marks.

let quotation = “””
The White Rabbit put on his spectacles. “Where shall I begin,
please your Majesty?” he asked.

“Begin at the beginning,” the King said gravely, “and go on
till you come to the end; then stop.”
“””
-String literals can include special characters:
-escaped special characters \0 (null character)
\ (backslash)
\t (horizontal tab)
\n (line feed)
\r (carriage return)
" (double quotation mark)
' (single quotation mark)
-arbitrary Unicode scalar value, written as \u{n}, where n is a 1–8 digit hexadecimal number
————————————————————————————
-Character represents a single extended grapheme cluster

-count Character values in a string with the count property

-access and modify a string with indices:
index((before:)
index(after:)
index(_:offsetBy:)

let greeting = “Guten Tag!”
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

-insert:

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"

-remove:

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..

126
Q

Swift Set

A
  • stores distinct values of the same type in a collection with no defined ordering
  • bridged to Foundation’s NSSet class
  • type must be hashable in order to be stored in a set. String, Int, Double and Bool are hashable by default.
  • create an empty Set with initializer syntax:
var letters = Set()
print("letters is of type Set with \(letters.count) items.")
// Prints "letters is of type Set with 0 items."

-create a set with an Array literal:

var favoriteGenres: Set = [“Rock”, “Classical”, “Hip hop”]
// favoriteGenres has been initialized with three initial items
————————————————————————————
-methods:

.count
.isEmpty
.insert(_:)
.remove(_:)
.removeAll()
.contains(_:)
------------------------------------------------------------------------------------
-iterate over values:
for genre in favoriteGenres {
    print("\(genre)")
}
// Classical
// Jazz
// Hip hop
------------------------------------------------------------------------------------
-iterate over values in a specific order:
for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz
------------------------------------------------------------------------------------
-fundamental operations:
.union(_:)
.intersection(_:)
.symmetricDifference(_:)
.subtracting(_:)
------------------------------------------------------------------------------------
-membership and equality:
== 
.isSubset(of:)
.isSuperset(of:)
.isStrictSubset(of:)
.isDisjoint(with:)
127
Q

API Availability

A

Swift has built-in support for checking API availability. The compiler uses information in the SDK to verify that all the APIs used in the code are available on the deployment target specified by the project. Use an availability condition in an if or guard statement.

The last argument, the asterisk, is required.

if #available(platform name version, …, *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}

if #available(iOS 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}
128
Q

Swift Enumeration

A
  • defines a common type for a group of related values and enables working with those values in a type-safe way
  • enumeration values don’t have an integer value by default and are values in their own right
  • can have methods associated with them
  • by default, Swift assigns raw types starting at 0 and incrementing by 1. Can explicitly provide values and can use strings or floating-point numbers.
  • use to describe a state

Examples:

enum CollisionType: Int {
    case player = 1
    case enemy = 2
}
var type = CollisionType.player
enum CompassPoint {
    case north
    case south
    case east
    case west
}

enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

Usage:
var directionToHead = CompassPoint.west
directionToHead = .east

Matching enumeration values with a Switch statement (must consider all cases or use default case):

directionToHead = .south
switch directionToHead {
case .north:
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
}
// Prints "Watch out for penguins"
------------------------------------------------------------------------------------
Get collection of all enumeration's cases:
enum Beverage: CaseIterable {
    case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
for beverage in Beverage.allCases {
    print(beverage)
}
// coffee
// tea
// juice
------------------------------------------------------------------------------------
Associated values: values can be associated with the individual cases
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

Usage:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode(“ABCDEFGHIJKLMNOP”)

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
------------------------------------------------------------------------------------
Raw values:
enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}
------------------------------------------------------------------------------------
Implicitly assigned raw values:

enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

enum CompassPoint: String {
case north, south, east, west
}

Use rawValue to convert an enum value to its raw value:
var rawValue: Int = enumVar.rawValue
————————————————————————————
Recursive enumerations have another instance of the enumeration as the associated value for one or more of the enumerations cases. An enumeration case is indicated as recursive by the indirect keyword.

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}
129
Q

Swift Structures and Classes

A
  • general purpose, flexible constructs that become the building blocks of a program’s code
  • defined in a single file
  • both structures and classes can:
    • define properties to store values
    • define methods to provide functionality
    • define subscripts to provide access to their values using subscript syntax
    • define initializers to set up their initial state
    • be extended to expand their functionality beyond a default implementation
    • conform to protocols to provide standard functionality of a certain kind
  • additional capabilities of a class:
    • inheritance enables one class to inherit the characteristics of another
    • type casting enables checking and interpreting the type of a class instance at runtime
    • deinitializers enable an instance of a class to free up resources
    • reference counting allows more than one reference to a class instance
  • structures are preferred because they are less complex
  • definition defines a new Swift type. Use CamelCase for type names, and use lowercase for properties and methods:
struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

-creating instances:

let someResolution = Resolution()
let someVideoMode = VideoMode()

-properties are accessed with dot syntax

print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"

-memberwise initializers for Structure types (not for classes):

let vga = Resolution(width: 640, height: 480)

  • Structures and enumerations are value types. The variable cinema is set to the current value of hd. The width of cinema is modified. The width of hd is unchanged. cinema and hd are two separate instances.
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
------------------------------------------------------------------------------------
-Classes are reference types. tenEighty and alsoTenEighty refer to the same instance. tenEighty and alsoTenEighty are constants, but their properties can be changed. The reference to the VideoMode is the constant.
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"

-identity operators check whether two constants or variables refer to the same single instance.

=== Identical to
!== Not identical to

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
130
Q

Swift Subscripts

A
  • shortcuts for accessing the member elements of a collection, list or sequence
  • used to set and retrieve values by index without needing separate methods for setting and retrieval
  • can define multiple subscripts for a single type
  • not limited to a single dimension
  • can be defined with multiple input parameters
subscript(index: Int) -> Int {
    get {
        // Return an appropriate subscript value here.
    }
    set(newValue) {
        // Perform a suitable setting action here.
    }
}

Can remove the get keyword and its braces:

subscript(index: Int) -> Int {
    // Return an appropriate subscript value here.
}

-can implement subscripts in most appropriate way for the particular class or structure’s functionality. For example, dictionary uses subscript assignment to add a key and a value.

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
  • can take any number of input parameters, can take a varying number of parameters, can provide default values for their parameters, but cannot use in-out parameters.
  • can have subscript overloading
struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 &amp;&amp; row < rows &amp;&amp; column >= 0 &amp;&amp; column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}
------------------------------------------------------------------------------------
Construct a new Matrix instance:

var matrix = Matrix(rows: 2, columns: 2)

Set values in the matrix:

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

Convenience method to check that the row and column are within the bounds of the matrix:

func indexIsValid(row: Int, column: Int) -> Bool {
    return row >= 0 &amp;&amp; row < rows &amp;&amp; column >= 0 &amp;&amp; column < columns
}
  • instance subscripts are called on an instance of a particular type
  • type subscripts are called on the type itself.
    • -type subscripts are indicated by the static keyword before the subscript keyword
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}
let mars = Planet[4]
print(mars)
131
Q

Swift Deinitialization

A
  • a deinitializer is called immediately before a class instance is deallocated. Deinitializers are written with the deinit keyword. They are only available on class types. There can be at most one deinitializer per class.
  • Swift automatically deallocates instances through ARC, but sometimes additional cleanup needs to be performed
  • deinitializers are called automatically. Superclass deinitializers are called automatically at the end of a subclass deinitializer implementation.
class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}
class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
    }
    deinit {
        Bank.receive(coins: coinsInPurse)
    }
}
var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins"
print("There are now \(Bank.coinsInBank) coins left in the bank")
// Prints "There are now 9900 coins left in the bank"
playerOne!.win(coins: 2_000)
print("PlayerOne won 2000 coins &amp; now has \(playerOne!.coinsInPurse) coins")
// Prints "PlayerOne won 2000 coins &amp; now has 2100 coins"
print("The bank now only has \(Bank.coinsInBank) coins left")
// Prints "The bank now only has 7900 coins left"
playerOne = nil
print("PlayerOne has left the game")
// Prints "PlayerOne has left the game"
print("The bank now has \(Bank.coinsInBank) coins")
// Prints "The bank now has 10000 coins"
132
Q

Swift Type Casting

A
  • a way to check the type of an instance or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy
  • implemented with is and as operators
  • can also be used to check whether a type conforms to a protocol

Defining a Class Hierarchy for Type Casting

// base class
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

// subclasses

class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}
class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}

// array containing two Movie instances and three Song instances

let library = [
Movie(name: “Casablanca”, director: “Michael Curtiz”),
Song(name: “Blue Suede Shoes”, artist: “Elvis Presley”),
Movie(name: “Citizen Kane”, director: “Orson Welles”),
Song(name: “The One And Only”, artist: “Chesney Hawkes”),
Song(name: “Never Gonna Give You Up”, artist: “Rick Astley”)
]
// the type of “library” is inferred to be [MediaItem]

-use the type check operator (is) to check whether an instance is of a certain subclass type

var movieCount = 0
var songCount = 0
for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}

print(“Media library contains (movieCount) movies and (songCount) songs”)
// Prints “Media library contains 2 movies and 3 songs”
————————————————————————————
Downcasting

  • a constant or variable of a certain class type may actually refer to an instance of a subclass. An attempt can be made to downcast it to the subclass type with the typecast operator (as? or as!).
  • conditional form, as?, returns an optional value of the downcast type
  • forced form, as!, attempts the downcast and force-unwraps the result
  • using the conditional form:
for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
————————————————————————————
Type Casting for Any and AnyObject

  • Swift provides two special types for working with nonspecific types
  • Any can represent an instance of any type at all including function types
  • AnyObject can represent an instance of any class type
  • only use Any and AnyObject when you explicitly need the behavior and capabilities they provide
  • using Any to work with a mix of different types:

var things = Any

things. append(0)
things. append(0.0)
things. append(42)
things. append(3.14159)
things. append(“hello”)
things. append((3.0, 5.0))
things. append(Movie(name: “Ghostbusters”, director: “Ivan Reitman”))
things. append({ (name: String) -> String in “Hello, (name)” })

-using as to discover the specific types:

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael
133
Q

Swift Nested Types

A

-supporting enumerations, classes and structures nested within the definition of the type they support

struct BlackjackCard {

    // nested Suit enumeration
    enum Suit: Character {
        case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
    }
    // nested Rank enumeration
    enum Rank: Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
        struct Values {
            let first: Int, second: Int?
        }
        var values: Values {
            switch self {
            case .ace:
                return Values(first: 1, second: 11)
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }
// BlackjackCard properties and methods
let rank: Rank, suit: Suit
var description: String {
    var output = "suit is \(suit.rawValue),"
    output += " value is \(rank.values.first)"
    if let second = rank.values.second {
        output += " or \(second)"
    }
    return output
} }
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// Prints "theAceOfSpades: suit is ♠, value is 1 or 11"

-refer to nested types by prefixing its name with the name of the type it is nested within:

let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// heartsSymbol is "♡"
134
Q

Swift Extensions

A
  • add new functionality to an existing class, structure, enumeration or protocol type
  • can extend types without access to the original source code (known as retroactive modeling)
  • similar to categories in Objective-C, but extensions do not have names
  • extensions in Swift can:
    • -add computed instance properties and computed type properties
    • -define instance methods and type methods
    • -provide new initializers
    • -define subscripts
    • -define and use new nested types
    • -an existing type conform to a protocol

Extension Syntax

-declare with the extensions keyword:

extension SomeType {
    // new functionality to add to SomeType goes here
}

-can make a type adopt protocols:

extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
————————————————————————————
Computed Properties

-adding five computed instance properties to Double to provide support for working with distance units:

extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

-these are read-only computed properties, so the get keyword is not needed:

let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// Prints "A marathon is 42195.0 meters long"

Initializers

  • extensions can add initializers to existing types
  • extensions can new convenience initializers to a class, but they cannot add new designated initializers or deinitializers to a class
  • define a custom Rect structure:
struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

-create new Rect instances:

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
   size: Size(width: 5.0, height: 5.0))

-extend Rect to provide an additional initializer that takes a specific center point and size:

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect’s origin is (2.5, 2.5) and its size is (3.0, 3.0)
————————————————————————————
Methods

-extensions can add new instance methods and type methods to existing types

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0.. Int {
        var decimalBase = 1
        for _ in 0.. 0:
            return .positive
        default:
            return .negative
        }
    }
}

Usage:

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print("- ", terminator: "")
        case .zero:
            print("0 ", terminator: "")
        case .positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + "
135
Q

Swift Opaque Types

A
  • a function or method with an opaque return type hides its return value’s type information
  • opaque types solve the problem about exposing detailed information caused by needing to state the full return type. Generic types let the code that calls a function pick the type for that function’s parameters and return value in a way that is abstracted away from the function implementation.
  • makeTrapezoid returns a value of some given type that conforms to the Shape protocol without specifying any concrete type:
struct Square: Shape {
    var size: Int
    func draw() -> String {
        let line = String(repeating: "*", count: size)
        let result = Array(repeating: line, count: size)
        return result.joined(separator: "\n")
    }
}
func makeTrapezoid() -> some Shape {
    let top = Triangle(size: 2)
    let middle = Square(size: 2)
    let bottom = FlippedShape(shape: top)
    let trapezoid = JoinedShape(
        top: top,
        bottom: JoinedShape(top: middle, bottom: bottom)
    )
    return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *

-opaque return types can be combined with generics:

func flip(_ shape: T) -> some Shape {
    return FlippedShape(shape: shape)
}
func join(_ top: T, _ bottom: U) -> some Shape {
    JoinedShape(top: top, bottom: bottom)
}
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
  • if a function with an opaque return type returns from multiple places, all of the possible return values must have the same type
  • an opaque type refers to one specific type, and a protocol type can refer to any type that conforms to the protocol
  • using a protocol type as the return type for a function gives the flexibility to return any type that conforms to the protocol, but the cost is that some operations aren’t possible on the returned values (like the == operator). Another problem is nesting. A function can return a value of a certain type, and it can take an argument that conforms to that type protocol, but the value of a protocol does not conform to that protocol.
  • opaque types preserve the identity of the underlying type. The example returns a container but declines to specify the container’s type. The type is inferred to be Int.
func makeOpaqueContainer(item: T) -> some Container {
    return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int"
136
Q

Swift Memory Safety

A
  • conflicting access to memory can occur when different parts of code are trying to access the same location in memory at the same time
  • problems can occur on a single thread
  • memory access conflicts occur if all the following conditions are met:
    • -at least one access is a write access
    • -they access the same location in memory
    • -their durations overlap
  • an access is instantaneous if it is not possible for the other code to run after that access starts but before it ends. Most memory access is instantaneous.
  • long-term accesses span the execution of other code.
  • overlap occurs when it is possible for other code to run after a long-term access begins but before it ends. It primarily occurs in code that uses in-out parameters in functions or methods or mutating methods of a structure.
  • a function has long-term write access to all of its in-out parameters

var stepSize = 1

func increment(_ number: inout Int) {
    number += stepSize
}
increment(&amp;stepSize)
// Error: conflicting accesses to stepSize

-a solution:

// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&amp;copyOfStepSize)
// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2

-passing a single variable as the argument for multiple in-out parameters of the same function produces a conflict:

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&amp;playerOneScore, &amp;playerTwoScore)  // OK
balance(&amp;playerOneScore, &amp;playerOneScore)
// Error: conflicting accesses to playerOneScore
  • operators also can have long-term access to their in-out parameters
  • a mutating method on structure has write access to self for the duration of the method call

extension Player {
mutating func shareHealth(with teammate: inout Player) {
balance(&teammate.health, &health)
}
}

var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &amp;maria)  // OK
oscar.shareHealth(with: &amp;oscar)
// Error: conflicting accesses to oscar

-mutating any piece of a structure, tuple or enumeration mutates the whole value

var playerInformation = (health: 10, energy: 20)
balance(&amp;playerInformation.health, &amp;playerInformation.energy)
// Error: conflicting access to properties of playerInformation
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&amp;holly.health, &amp;holly.energy)  // Error

-overlapping access to properties of a structure is safe if the following conditions apply:

  • -only accessing stored properties of an instance, not computed properties or class properties
  • -the structure is the value of a local variable, not a global variable
  • -the structure is either not captured by any closures, or it is captured only by nonescaping closures
137
Q

Swift Access Control

A
  • restricts access to parts of code from code in other source files and modules
  • specific access levels can be assigned to individual types (classes, structures and enumerations) as well as to properties, methods, initializers and subscripts belonging to those types–
  • protocols and global constants, variables and functions can be restricted to a certain context
  • Swift provides default access levels for typical scenarios
  • a module is a single unit of code distribution. It can be imported with the import keyword, and it is a separate build target.
  • a source file is a single Swift source code file within a module
  • five access levels:
    • -open access is the highest (least restrictive) access level. Entities can be used within any source file from their defining module and also in a source file from another module that imports the defining module. Open access differs from public access by allowing code outside the module to subclass and override.
    • -public access enables entities to be used within any source file from their defining module and also in a source file from another module that imports the defining module.
    • -internal access enables entities to be used within any source file from their defining module but not outside of that module.
    • -file-private access restricts the use of an entity to its own defining source file.
    • -private access restricts the use of an entity to the enclosing declaration and to extensions of that declaration that are in the same file.
  • no entity can be defined in terms of another entity that has a lower (more restrictive) access level
  • all entities (with a few specific exceptions) have a default access level of internal if explicit access is not specified
  • single-target apps have internal access by default but might want to have some part marked as private or file private to hide their implementation details from other code within the app’s module
  • frameworks should have the public-facing interface marked as open or public. This is the API for the framework. Any internal implementation details can use the default access level of internal or be marked as private or file private.

-access control syntax:
public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}

  • specify an explicit access level for a custom type at the point of its definition
  • a public type defaults to having internal members
public class SomePublicClass {                  // explicitly public class
    public var somePublicProperty = 0            // explicitly public class member
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}
class SomeInternalClass {                       // implicitly internal class
    var someInternalProperty = 0                 // implicitly internal class member
    fileprivate func someFilePrivateMethod() {}  // explicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}
fileprivate class SomeFilePrivateClass {        // explicitly file-private class
    func someFilePrivateMethod() {}              // implicitly file-private class member
    private func somePrivateMethod() {}          // explicitly private class member
}
private class SomePrivateClass {                // explicitly private class
    func somePrivateMethod() {}                  // implicitly private class member
}

  • the access level for a tuple type is the most restrictive access level of all types used in that tuple. A tuple type’s access level is determined automatically and cannot be specified explicitly.
  • the access level for a function type is calculated as the more restrictive access of the function’s parameter types and return type. The access level must be specified explicitly as part of the function’s definition if the function’s calculated access level does not match the contextual default.
  • one class is defined as internal, and the other is defined as private, so the function must be marked private because the default access level of internal is not valid.
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // function implementation goes here
}

  • the individual cases of an enumeration automatically receive the same access level as the enumeration they belong to. A different access level cannot be specified for individual enumeration cases.
  • the types used for any raw values or associated values in an enumeration definition must have an access level as least as high as the enumeration’s access level
  • the access level of a nested type is the same as its containing type unless the containing type is public. Nested types defined within a public type have an automatic access level of internal. If a nested type within a public type needs to be publicly available, explicitly declare the nested type as public.
  • any class that can be accessed in the current access context and that is defined in the same module as the subclass may be subclassed. Any open class defined in a different module may be subclassed. A subclass cannot have a higher level that its superclass. For classes that are defined in the same module, any class member that is visible in a certain access context may be overridden. For classes that are defined in another module, open class members may be overridden.
  • an override can make an inherited class member more accessible than its superclass version
public class A {
    fileprivate func someMethod() {}
}
internal class B: A {
    override internal func someMethod() {}
}

-a subclass member can call a superclass member that has lower access permissions as long as the call to the superclass’s member takes place within an allowed access level context.

public class A {
    fileprivate func someMethod() {}
}
internal class B: A {
    override internal func someMethod() {
        super.someMethod()
    }
}

-a constant, variable or property cannot be more public than
its type

  • getters and setters for constants. variables, properties and subscripts automatically receive the same access level as the constant, variable, property or subscript they belong to
  • a setter may be given a lower access level than its corresponding getter. Assign a lower access level by writing fileprivate(set), private(set), or internal(set) before the var or subscript introducer.
struct TrackedString {
    private(set) var numberOfEdits = 0
    var value: String = "" {
        didSet {
            numberOfEdits += 1
        }
    }
}

  • a custom initializers can be assigned an access level less than or equal to the type it initializes. A required initializer must have the same access level as the class it belongs to.
  • a default initializer has the same access level as the type it initializes unless that type is defined as public. If the type is defined as public, the default initializer is considered internal. If a public type is wanted to be initializable with a no-argument initializer, a public no-argument initializer must be explicitly provided.
  • the default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. If any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal. If a public structure type is wanted to be initializable with a memberwise initializer, a public memberwise initializer must be explicitly provided.
  • if an explicit access level is wanted for a protocol type, assign it as the point the protocol is defined. The access level of each requirement is automatically set to the same access level as the protocol. A protocol requirement cannot be set to a different access level than the protocol it supports. If a protocol is defined as public, the protocol’s requirements require a public access for those requirements when they are implemented.
  • a protocol can have at most the same access level as the protocol it inherits from
  • a type can conform to a protocol with a lower access level than the type itself. The context in which a type conforms to a particular protocol is the minimum of the type’s access level and the protocol’s access level.
  • a class, structure or enumeration can be extended in any access context in which the class, structure or enumeration is available.
  • extensions that are in the same file as the class, structure or enumeration that they extend behave as if the code in the extension had been written as part of the original type’s declaration.
  • the access level for a generic type or generic function is the minimum of the access level of the generic type or function itself and the access level of any type constants on its type parameters
  • any type aliases are treated as distinct types for the purposes of access control. A type alias can have an access level less than or equal to the access level of the type it aliases.
138
Q

Swift UITableViewController: displaying cells

A
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)
    print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)")
    cell.textLabel?.text = contacts[indexPath.row][0]
    return cell
} 
  • when a cell scrolls off the screen, it’s queued for reuse
  • the cell is registered with a reuse identifier (done automatically with a xib file by Storyboard)
  • if there are no cells that can be reused, iOS automatically creates them
139
Q

iOS App Launch Sequence

A
  1. The app is launched, either explicitly by the user or implicitly by the system.
  2. The Xcode-provided main function calls UIKit’s UIApplicationMain(::::) function.
  3. The UIApplicationMain(::::) function creates the UIApplication object and your app delegate.
  4. UIKit loads the app’s default interface from the main storyboard or nib file.
  5. UIKit calls the app delegate’s application(_:willFinishLaunchingWithOptions:) method.
  6. UIKit performs state restoration, which calls additional methods of your app delegate and view controllers.
  7. UIKit calls the app delegate’s application(_:didFinishLaunchingWithOptions:) method .

When initialization is complete, the system uses either the scene delegates or app delegate to display the UI and manage the life cycle for the app.

140
Q

GCD and race conditions

A

Concurrent queues can execute one or more tasks in parallel

DispatchQueue(label: “com.theswiftdev.queues.concurrent”, attributes: .concurrent)

queue methods:

  • sync: blocks
  • async: returns immediately

Don’t call sync on the main queue! It will cause a deadlock and a crash.

Use barriers to ensure thread safety. If a task arrives at the queue, nothing else will be executed until the barrier task has completed. In the following example, async barriers are used for writes, and sync blocks are used for reads.

let q = DispatchQueue(label: “com.theswiftdev.queues.concurrent”, attributes: .concurrent)

q.async(flags: .barrier) {
  // writes
}
q.sync() {
  // reads
}
141
Q

Swift Bug: memory

A
class Master {
    lazy var detail: Detail = Detail(master: self)
init() {
    println("Master init")
}

deinit {
    println("Master deinit")
} }
class Detail {
    var master: Master
init(master: Master) {
    println("Detail init")
    self.master = master
}

deinit {
    println("Detail deinit")
} }
func createMaster() {
    var master: Master = Master()
    var detail = master.detail
}

createMaster()

Master and Detail have strong references to each other so neither will be deallocated. This can cause memory leaks. It can be fixed by making a weak reference to Master in the Detail class.

class Detail {
    unowned var master: Master
    ...
}
142
Q

Swift Bug: struct

A
struct IntStack {
  var items = [Int]()
  func add(x: Int) {
    items.append(x) // Compile time error here.
  }
}

This results in a compile-time error because structs are value types, and the properties of a value type can’t be modified from within its instance methods. This can be fixed by declaring the instance method as mutating.

mutating func add(x: Int) {
items.append(x)
}

143
Q

Swift Coding question: Array map() function

A
let d = ["john": 23, "james": 24, "vincent": 34, "louis": 29]
let x = d.sort{ $0.1 < $1.1 }.map{ $0.0 }

What is the type of x? What is its value?

x is of type [String], and its value is [“john”, “james”, “louis”, “vincent”]

144
Q

Swift Higher-Order Functions

A

map: performs an operation on all the elements of a collection and returns a new collection with the results of the operation on the original elements

mapValues: map function that keeps the original keys

reduce: produce one value from the values of all elements in a collection
sorted: sort the collection’s data
filter: filter the elements of a collection based on a condition and produce a new one containing only those elements that satisfy the condition

145
Q

Swift Optional Unwrapping

A
guard statement: safe
guard let a = x else {
  return
}

forced unwrapping: using “!” operator, unsafe
let a: String = x!

optional binding: safe
if let a = x {
  print("x was successfully unwrapped and is = \(a)")
}

optional pattern: safe
if case let a? = x {
print(a)
}

nil coalescing operator: safe
let a = x ?? ""
implicitly unwrapped variable declaration: unsafe in many cases
var a = x!

optional chaining: safe
(execute everything after the ? if everything before the ? has a value)
let a = x?.count

146
Q

Swift nil and .none

A

Optional.none is an enumeration case representing the absence of a value

nil is a literal representing the absence of a value

They are equivalent
nil === .none

nil is the recommended convention

147
Q

Swift let vs. Objective-C const

A

const is a variable initialized at compile with a value or expression that must be resolved at compile time

let is a constant determined at runtime that may be initialized with a static or a dynamic expression. Its value may only be assigned once.

148
Q

Swift class func vs. static function

A

a class function is overridable

a static function is not overridable. It’s the same as class final.

149
Q

Swift Coding Question: Can you add a stored property to a type with an extension?

A

Swift doesn’t allow extensions to contain stored properties. That would require extra storage which the extension can’t manage. Extensions can add new computed properties.

150
Q

Swift Coding Question: Can you define a custom ^^ power operator?

A

precedencegroup ExponentialPrecedence {
higherThan: MultiplicationPrecedence
associativity: right
}

infix operator ^^: ExponentialPrecedence

func ^^ (base: Int, exponent: Int) -> Int {
    let l = Double(base)
    let r = Double(exponent)
    let p = pow(l, r)
    return Int(p)
}

let fivesquared = 5 ^^ 2

151
Q

How does UIKit render views on the screen?

A
  • UIViews are arranged in a hierarchy
  • each view has a backing CALayer which contains a backing store which is a pixel bitmap
  • when a view needs to be rendered, the system checks if the view’s layer has a up-to-date image of the view
  • drawRect renders the image by calling display() on the view’s layer
  • the layer sets up its backing store
  • the layer sets up a Core Graphics context
  • the layer’s backing store will be rendered onto the display

if a UIImageView is used, the layer doesn’t have a backing store. It uses a CGImageRef as its contents, and that image’s bits will be drawn on the display

152
Q

How does UIKit animation work?

A

Core Animation:

  • Apple framework
  • configure animation parameters and attach to the view’s layer
Example:
let animation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.cornerRadius))
animation.fromValue = 0
animation.toValue = 16
animation.duration = 1
animation.autoreverses = true
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)

UIView.animate:
-call a UIView animation method and specify how long it should run along with some other parameters
-set up an animation block with final values of properties to be animated
-can only animate a single property. Create a group to animate multiple properties.

Example:

UIView.animate(withDuration: 1) {
self.squareView.layer.cornerRadius = 16
}

153
Q

UIViewController lifecycle

A

viewDidLoad()
is called when a view controller’s content view, i.e. the view property, is created and loaded from a storyboard or XIB. It’s the primary touch point for customizing a view controller with code.

viewWillAppear()
is called just before a view controller’s content view is added to the app’s view hierarchy. Differently said, it’s called just before the view controller appears. This function is suitable for (small) operations that need to happen before a view controller is presented on screen.

viewDidAppear()
is called just after a view controller’s content view is added to the app’s view hierarchy. This function is best used for operations that need to start as soon as a view controller is shown on screen, such fetching data.

viewWillDisappear()
is called just before a view controller’s content view is removed from the app’s view hierarchy. Differently said, it’s called just before the view controller disappears. This function is suitable for cleaning up a view controller before it disappears, such as hiding the on-screen keyboard or triggering a save or commit operation.

viewDidDisappear()
is called just after a view controller’s content view is removed from the app’s view hierarchy. This function is best used for teardown operations, such as removing memory-intensive data that can be recreated later.

154
Q

How to add a view as a subview and make it adaptive to size changes?

A
  • create the subview as a subclass of UIView
  • in the parent view controller, in viewDidAppear(), add the subview
 let mySubView = SubView(with: randomStrings.firstString.rawValue)
  view.addSubview(mySubView)

-remove subview

mySubView.view.removeFromSuperview()

155
Q

How is concurrency handled?

A

Thread:

  • low-level constructs
  • developer responsible for creation and management
  • need to leverage synchronization mechanisms
  • complex and risky

GCD:

  • handles thread creation and management
  • 3 queues:
    • -Main dispatch queue (serial, pre-defined)
    • -Global queues (concurrent, pre-defined)
    • -private queues (serial or concurrent, user-defined)
  • good for one-off tasks

OperationQueue:

  • Cocoa high level abstraction on GCD
  • object-oriented
  • wrapper around DispatchQueue
  • enables grouping of tasks
  • Operation and OperationQueue contain observable properties
  • operations can be paused, resumed or cancelled
  • can specify maximum number of queued operations that can run simultaneously
156
Q

ways to specify layout of elements in UIView

A

XIB:
pros:
-reusability (good for when same layout is shared among multiple classes)
-performance is good (lazily loaded)
-views can be broken into modules which can each be implemented with its own NIB file (easier development, testing and debugging)

cons:

  • not good for dynamic content where the layout changes significantly based on content
  • not good for complicated transitions that could be simplified with Storyboarding

Storyboard:
pros:
-good if not too large
-good for multiple interconnected view controllers
-eliminate boilerplate code for managing view controllers (push, pop, present, dismiss)
-good for single table view controller
-performance is good
-simplifies prototyping of user interfaces and flow

cons:

  • not good for complicated or dynamic code
  • not good if view is already implemented with view or code
  • difficult to manage if it gets too large
  • poor portability: a view controller is dependent upon the rest of the storyboard
  • doesn’t handle data flow
157
Q

Managed Object Context

A
  • represents a single object space or scratch pad in a Core Data Application.
  • an instance of NSManagedObjectContext
  • group of related model objects which represent an internally consistent view of one or more persistent stores
  • changes to managed objects are held in memory in the associated context until that context is save to one or more persistent stores
  • an object is unique to a particular context
  • central object in Core Data stack
  • used to create and fetch managed objects and to undo and redo operations

Concurrency
-assumes default owner is the thread or queue that allocated it

Subclassing
-do not subclass NSManagedObjectContext
-adding own logic might have unforeseen consequences

158
Q

what long-running tasks can iOS support in the background

A
  • location services
  • BLE
  • audio playback
  • fetch latest content from a server
159
Q

concurrency anti-pattern

A

let queue = DispatchQueue(label: “com.theswiftdev.queues.serial”)

queue.sync {
    // do some sync work
    queue.sync {
        // this won't be executed -> deadlock!
    }
}
//What you are trying to do here is to launch the main thread synchronously from a background thread before it exits. This is a logical error.
//https://stackoverflow.com/questions/49258413/dispatchqueue-crashing-with-main-sync-in-swift?rq=1
DispatchQueue.global(qos: .utility).sync {
    // do some background task
    DispatchQueue.main.sync {
        // app will crash
    }
}
160
Q

third-party library or SDK

A
  • can speed up development
  • should have a long history, be current, well-supported and exceed required functionality
  • impacts app’s maintainability
161
Q

workflow to create an iOS app

A
  1. Come up with an idea
  2. Create wireframes. I use Sketch.
  3. Create mockups. I use Marvel.
  4. Demo
  5. Code
  6. Test, refine
  7. Publish
162
Q

CocoaPods

A

dependency manager for third-party libraries.

  1. In your Xcode project, create a Podfile
  2. Open the Podfile in an editor and add the pod(s)
  3. In Terminal, do “pod install”
  4. Open the project’s .xcworkspace file with Xcode.

You might have to do a “pod update” sometime, and sometimes you have to find the right combinations of versions of the pods.

163
Q

testing practices

A
  • Write XCTest unit test cases (ideally TDD).
  • In a professional environment, work with QA to provide test builds and fix bugs.
  • In a personal environment, have people test the app with test builds.
164
Q

how to support other languages

A

In Xcode Project Info Settings, add languages. Change the localizable strings texts into the appropriate languages.

165
Q

Instruments

A

Instruments is an Xcode tool that has many tools to improve app performance.

Select Product->Profile to bring up the Instruments panel.

166
Q

Storing user credentials

A
  1. check for the user’s credentials in the iOS keychain.
  2. if current credentials exist for the user, log them in directly.
  3. if not, prompt the users for their user name and password, and then try to log them in.
  4. save their credentials after the login is successful.
167
Q

iOS Extension

A
  • extends custom functionality and content beyond the app and makes it available to users while they’re interacting with other apps or the system
  • enables a specific task

Extension points & functionality:

Action (iOS and macOS; UI and non-UI variants)
Manipulate or view content originating in a host app.

Audio Unit (iOS and macOS; UI and non-UI variants)
Generates an audio stream to send to a host app, or modifies an audio stream from a host app and sends it back to the host app.

Broadcast UI (iOS and tvOS)

Broadcast Upload (iOS and tvOS)

Call Directory (iOS)
Identify and block incoming callers by their phone number. To learn more, see CallKit Framework Reference.
Content Blocker (iOS and macOS)
Indicate to WebKit that your content-blocking app has updated its rules.
(This app extension has no user interface.)
Custom Keyboard (iOS)
Replace the iOS system keyboard with a custom keyboard for use in all apps.
Document Provider (iOS; UI and non-UI variants)
Provide access to and manage a repository of files.

iMessage (iOS)
Interact with the Messages app. To learn more, see Messages.

Intents (iOS)
Handle tasks related to supporting Siri integration with your app. To learn more, see SiriKit Programming Guide.

Intents UI (iOS)
Customize the Siri or Maps interface after handling a task related to supporting Siri integration with your app. To learn more, see SiriKit Programming Guide.

Notification Content (iOS)

Notification Service (iOS)

Photo Editing (iOS and macOS)
Edit a photo or video within the Photos app.

Share (iOS and macOS)
Post to a sharing website or share content with others.

Smart Card Token (macOS)

Spotlight Index (iOS)
Index content within your app while it isn’t running. To learn more, see Index App Content.
Sticker Pack (iOS)
Provide a set of stickers that users can use within the Messages app. To learn more, see Messages.

Today (iOS and macOS)
Get a quick update or perform a quick task in the Today view of Notification Center.
(A Today extension is called a widget.)

168
Q

SpriteKit

A
  • a general-purpose framework for drawing shapes, particles, text, images, and video in two dimensions.
  • leverages Metal to achieve high-performance rendering, while offering a simple programming interface to make it easy to create games and other graphics-intensive apps.
  • can quickly add life to your visual elements and gracefully transition between screens.
169
Q

SceneKit

A
  • combines a high-performance rendering engine with a descriptive API for import, manipulation, and rendering of 3D assets
  • requires only descriptions of the scene’s contents and the actions or animations it is to perform.
170
Q

Metal

A
  • framework communicates directly with the GPUs on a device to render advanced 3D graphics and perform data-parallel computing
  • use cases: games that render sophisticated 3D environments, video processing apps, data-crunching apps
171
Q

Responder chain

A
  • UIKit-generated linked list of UIResponder objects
  • foundation for everything regarding events in iOS
  • examples of UIResponders: UIView, UIViewController, UIWindow, UIApplication and UIApplicationDelegate
  1. System event (UIEvent) is detected
  2. UIKit determines the first responder capable of of handling the event and sends the event to the selected one
  3. UIResponder subclasses can register themselves as capable of handling specific UIEvent types
172
Q

Keychain Services

A
  • API that gives a secure way to store sensitive information in an encrypted database
  • painful to use. Wrappers exist.
  • two main parts:
  • —encrypted database (SecKeychain class)
  • —items inserted into the database (SecKeychainItem class)
173
Q

Importance of caching and compression

A

-important because app should load quickly

  • compress images
  • compress resources
  • minify resources
  • leverage browser caching
174
Q

MVC question

A

App downloads data and displays it immediately. According to MVC, where is the best place to perform the data download?

The data should be downloaded in the view controller.

It could be done in a separate controller class in order to avoid the massive view controller problem.

175
Q

design patterns iOS uses

A
  • Singleton: init() method
  • Chain of Responsibility: UIResponder
  • Facade: NSPersistentContainer encapsulates the Core Data stack in an app
  • Observer: KVO, NotificationCenter
176
Q

UIScrollView implementation

A
  • a view defines its own coordinate system
  • a view’s bounds describe the view’s location in its owns coordinate system
  • changing the bounds coordinates make it look as if the view has moved
  • the frame rectangle describe the view’s location and size in its superview’s coordinate system
  • adjusting the bound’s origin is equivalent to moving the transparent film such that another part of it becomes visible through the view
177
Q

iOS UI assets

A

-image assets are stored in the Assets.xcassets catalog

178
Q

device status bar

A

the 20-pixel-high strip at the top of the window that shows the carrier name and signal strength, network status, current time, and battery strength

179
Q

navigation bar

A

appears at the top of an app screen, below the status bar, and enables navigation through a series of hierarchical screens

180
Q

tab bar

A
  • appears at the bottom of an app screen and provides the ability to quickly switch between different sections of an app.
  • is translucent, may have a background tint, maintains the same height in all screen orientations, and is hidden when a keyboard is displayed
181
Q

toolbar

A
  • appears at the bottom of an app screen and contains buttons for performing actions relevant to the current view or content within it
  • is translucent, may have a background tint, and often hides when people are unlikely to need it
  • is hidden when a keyboard is onscreen
182
Q

UITableView

A

view that presents data using rows arranged in a single column

183
Q

UICollectionView

A

object that manages an ordered collection of data items and presents them using customizable layouts

184
Q

UISplitViewController

A

container view controller that implements a master-detail interface

185
Q

UIPickerView

A
  • view that uses a spinning-wheel or slot-machine metaphor to show one or more sets of values
  • uses: choosing options between colors, font types, dates, times
186
Q

UILabel vs. UITextField vs UITextView

A

UILabel:
use when some text is needed, but don’t need the user to enter or edit the text

UITextField:
use when the user needs to enter a single line of text

UITextView:
use when the user needs to enter more than one line of text

187
Q

UISegmentedControl

A
  • horizontal control made of multiple segments, each segment functioning as a discrete button
  • often used to display different views
  • in Maps, for example, a segmented control lets you switch between Map, Transit, and Satellite views
188
Q

modal view

A
  • child view that disables the main window, but keeps it visible behind it
  • users must interact with the modal window before they can return to the parent application
189
Q

app icon

A
  • unique image for a mobile app
  • what user sees in the App Store

Normal app icon sizes:
iPhone:
180px × 180px (60pt × 60pt @3x)
120px × 120px (60pt × 60pt @2x)

Small app icon sizes:

Keep the background solid. Transparency doesn’t work well because you’ll lose control over what your app looks like on each individual user’s phone.

No photos or screenshots. App icons are too small for people to actually make out intricate details.

Do not round corners. Apple will apply a rounded corners mask for you. So when designing keep your icon’s corners square rather than rounded.

190
Q

provisioning profile

A
  • a collection of digital entities that uniquely ties developers and devices to an authorized iPhone Development Team and enables a device to be used for testing
  • include signing certificates, device identifiers, and a bundle ID
191
Q

App ID

A
  • a unique identifier that iOS uses to allow an application to connect to the Apple Push Notification service, share keychain data between applications, and to communicate with external hardware accessories to be paired to the iOS application
  • different from the name of the app
192
Q

development certificate

A

lets an individual install and run an app on a device

193
Q

development provisioning profile

A
  • links the signing certificate and App ID so that apps can be signed to install and launch on iOS devices
  • select team members who should have access, and select devices for the provisioning profile
194
Q

prepare app for distribution

A
  1. Create an iOS distribution provisioning profile and distribution certificate
  2. Create an App Store Connect record
  3. Archive and upload the app using Xcode
  4. Configure the app’s metadata and further details in its App Store Connect record
  5. Submit the app for review
  6. Check on the status of the app
195
Q

SwiftUI

A
  • user interface toolkit
  • enables designing apps in a declarative way
  • cross-platform
  • can use both SwiftUI and UIKit in the same app. Use UIHostingController.
  • only supports iOS 13 and Xcode 11
  • doesn’t need Interface Builder (replaced by Canvas)
  • always produces a valid layout. No autolayout problems.