Swift Review, Day 2

On the second Swift language review day of 100 Days of Swift, I can honestly say that I don’t think I really learned anything new, but I did start thinking about a topic that’s probably going to want its own blog post at some point: when to use optional chaining vs when to use explicit unwrapping via if let or guard let vs when to use nil coalescing. They’re all doing pretty much the same thing: safely unwrapping an optional value, and I would guess that most developers learn one technique first and lean on that, so it might be nice to iterate the strong points and natural use cases for each.

Swift Review, Day 1

I thought going back through all of these base features of Swift would be a bore, but it was quite fun. And despite this being the nth time I was going over this stuff, I still learned a few new things.

First, I didn’t realize that if you want an initialize an array of type Any, you can’t depend on type inference. Swift won’t infer an Array type if the initial values don’t have a class, parent class, struct, or protocol in common.

Also, I didn’t realize that you could define and initialize an Array using the syntax below. I guess if you explicitly specify the type in the definition, you don’t need to specify a type in the initialization part of the statement.

var powers: [String] = []
powers.append("Super strength")

This made me wonder if dictionaries in Swift allowed a similar initialization syntax. Turns out they do.

var thingsICanSmell: [String: Bool] = [:]
thingsICanSmell["coffee"] = true
thingsICanSmell["dog poop"] = true
thingsICanSmell["iocane powder"] = false

Optionals

Whew, I’m running a little behind. It’s Tuesday, and I’m just now writing the recap for the work that I did yesterday. Yesterday, I caught up and wrote a recap of the work from Sunday. My goal for the night is to recap yesterday and today’s work, so that I’m back on track for the rest of the week.

Anyways, on to optionals in Swift, day 12 of 100 Days of Swift. Once again a few new things, some small bits that I’d seen before but hadn’t ever thought about, and a few bigger gaps in my knowledge that was great to close up a bit.

First, the easy stuff: I had seen the optional try? syntax before, maybe copy/pasted it from a tutorial or something, but I hadn’t really understood what it does. Turns out it executes some code that might throw an error, returning either a nil value if the code threw, or some actual value if the code didn’t throw an error. If you don’t care about the error that might be thrown, and just want to know if it succeeded or not, this might be a good option. Another simple thing, I didn’t realize you can use guard without let if you’re testing a condition.

A bigger piece of Swift that I’d come across on Day Two but had already completely forgotten was nil coalescing with the ?? operator. It’s kind of a default value for an optional.

Finally, something that I’ve used in Objective-C, optional initializers, showed up in Swift today. Here’s an example that also illustrates almost everything I know about coffee:

enum Roast {
    case Unroasted
    case LightRoast
    case DarkRoast
}

enum Origin {
    case Ethiopia
    case LatinAmerica
}

struct Beans {
    var origin: Origin
    var roast: Roast
    
    mutating func roast(_ roast: Roast) {
        self.roast = roast
    }
}

struct BrewedCoffee {
    var caffeine: Int
    var flavor: String
    init?(beans: Beans) {
        switch beans.roast {
        case .DarkRoast:
            self.caffeine = 10
            self.flavor = "Rich"
        case .LightRoast:
            self.caffeine = 12
            
            switch beans.origin {
            case .Ethiopia:
                self.flavor = "Fruity"
            case .LatinAmerica:
                self.flavor = "Chocolatey"
            }
        case .Unroasted:
            print("You can't brew unroasted beans!")
            return nil
        }
    }
}

var beans = Beans(origin: .Ethiopia, roast: .Unroasted)
beans.roast(.LightRoast)
var cup = BrewedCoffee(beans: beans)
cup?.flavor

Protocols & Extensions

Yesterday I worked through the protocols & extensions section of 100 Days of Swift. I stayed up a little late to finish reading a new book, so today I’m catching up on my writing.

I discovered a few things yesterday only via the tests, which was kinda neat. For example, I didn’t know that a variable with a setter must also have a getter, or that variables in protocols must have { get } or { get set } specifiers.

One thing that was new to me was protocol inheritance:


protocol Westerner {
    var name: String { get set }
    var town: String { get set }
}

protocol Dueler {
    func duel(_ other: Dueler)
}

protocol Cowboy: Westerner {
    func wrangleHorses()
    func driveCattle()
    func rodeo()
}

protocol Farmer: Westerner {
    func plantCrop(_ crop: String)
    func carryWater()
}

protocol Outlaw: Westerner, Dueler {
    func rob(_other: Westerner)
}

protocol Sheriff: Westerner, Dueler {
    func arrest(_ other: Westerner)
}

I am also a little confused about getter property syntax. These two are both read-only properties, the get { } part is optional if there is no setter:

extension Dueler {
    var pistol: String {
        return "Colt 45"
    }
    
    var rifle: String {
        get {
            return "Winchester Model 1873"
        }
    }
}

Classes

Yay, I’m 10% done with 100 Days of Swift! Today was all about classes, and once again I learned a few new things. One thing I’d never explicitly thought about was that the automatic member-wise initializer in Swift structs isn’t available in Swift classes. Another thing that I theoretically knew about, but hadn’t ever used before, was that Swift classes can be marked final to make them un-extendable. I’ve used libraries that mark their classes as final but I’ve never marked any of my own classes that way.

Finally, one thing that was totally new to me was Swift’s deinit keyword in classes. You can use it in the same contexts that you might have used the Objective-C dealloc method:

class DatabaseConnection {
    var hostname: String
    var port: Int
    
    var inputStream: InputStream
    var outputStream: OutputStream
    
    init(hostname: String, port: Int) {
        self.hostname = hostname
        self.port = port
        
        var readStream: Unmanaged<CFReadStream>?
        var writeStream: Unmanaged<CFWriteStream>?

        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
                                           self.hostname as CFString,
                                           UInt32(self.port),
                                           &readStream,
                                           &writeStream)
        
        self.inputStream = readStream!.takeRetainedValue()
        self.outputStream = writeStream!.takeRetainedValue()
    }
    
    deinit {
        self.inputStream.close()
        self.outputStream.close()
    }
}