Archive for January, 2017

Forced Unwrapping URLs

Let me start with a little background. Laptopmini wanted to know why this code wasn’t compiling. Admittedly, Swift’s error messages leave room for improvement. The tl;dr was: he needed to swap the 2nd and 3rd lines of code. This allows both properties to be initialized before the code refers to self.


I immediately fixated on those exclamation points. “Are there any circumstances that these URLs will fail during creation? And if so, don’t you want to control that crash?” Laptop pointed out that there was no circumstance where his URLs would be malformed, and it was perfectly safe to use the exclamation point.

Despite this, his approach bugged my aesthetics. There’s no reason those URLs should be created in that initializer. I figured there were better ways to deal with this, moving URL creation out of the initializer and controlling crash landings with better error messages.

First, I considered extending URL to offer a safer non-optional initializer. The following snippet offers “controlled descent”, providing valuable feedback for any string that cannot be used to construct a URL.

extension URL {
    /// Non-optional initializer with better fail output
    public init(safeString string: String) {
        guard let instance = URL(string: string) else {
            fatalError("Unconstructable URL: \(string)")
        }
        self = instance
    }
}

This seemed too big a hammer for this problem. Instead, I decided to recommend the same approach with a solution localized to the SocketEndPoint type:

// Returns `URL` with guided fail
public enum SocketEndPoint: String {
    case events = "http://nowhere.com/events"
    case live = "http://nowhere.com/live"
    
    public var url: URL {
        guard let url = URL(string: self.rawValue) else {
            fatalError("Unconstructable URL: \(self.rawValue)")
        }
        return url
    }
}

This approach allows his init code to simplify. Under the updated design, it uses ordinary URLs in the initializer and no exclamation points.

// His initializer
fileprivate init() {
    self.eventSocket = WebSocket(url: SocketEndPoint.events.url))
    self.liveSocket = WebSocket(url: SocketEndPoint.live.url))
    self.eventSocket.delegate = self
    self.liveSocket.delegate = self
}

Long story short:

  • String enumerations are handy but SocketEndPoint wasn’t pulling its weight. Its job is to deliver named URLs. It should do that no matter however the type is implemented.  Don’t let types slack off because there’s an “almost there” solution baked into the language.
  • Keep initializers clean and simple.
  • When weighing a universal solution against a simple local one, sometimes it’s best to think small and fix the problem in the immediate context.
  • I considered normal URL construction (returning an Optional) and creating a WebSocket initializer with implicitly unwrapped optionals (public init(url: URL!)This approach offered no real advantages and lost the ability to report which string was malformed. Also, ugly.

You can see a few of approaches I kicked around at this gist.

Dear Erica: Singletons and Static Property Side Effects

Laptopmini writes, “Can you define a ‘get‘ closure for a singleton’s sharedInstance? I have a web socket manager and I’d like it to call ‘connect()‘ any time its instance is fetched”

A basic Swift singleton looks like this:

public final class Singleton {
    public static let shared = Singleton()
    private init() { }
}

This design creates a class with a single accessible shared instance. The class is marked final, and the initializer is private, ensuring the type cannot be subclassed or instantiated beyond shared.

To introduce side effects, create indirect access to the singleton and add your custom behavior to a getter:

public final class Singleton {
    private static let _shared = Singleton()
    private init() { }
    
    public static var shared: Singleton {
        get {
            print("side effects here")
            connect() // for example
            return _shared
        }
    }
}

This works but it’s a little more complicated than it has to be. As a get-only property, you can omit the get syntax. Just move the custom behavior to the top-level var clause, as in the following modification:

public final class Singleton {
    private static let _shared = Singleton()
    private init() { }
    
    public static var shared: Singleton {
        print("side effects here")
        connect() // for example
        return _shared
    }
}

Quick summary:

  • Use reference types for singletons.
  • Mark the type final, the instance public, and the initializer private.
  • When naming, prefer the Swiftier shared to the more Objective-C sharedInstance.
  • Create a static getter for any side effects.
  • Omit the get syntax for get-only properties.

Have some thoughts about improving this? As always, drop a note in the comments or tweet me and I’ll tweak.

Rob N has a good point here: