Swift: Init with _

I remember when I first watched the Advanced Swift WWDC14 talk, my first thought after “Whoa! Whoa! What are they doing?!!! I’m not ready for this!” was “why would you even want to do any of this?!!!” The code seemed so unreadable.

One of the first things they introduced was using _ as a parameter name in the init function. For example, let’s say you have a class called called myColor, which is initialized with red, green, and blue float values. You can use the _ to skip adding a parameter name in the init method.

So instead of this:

You can choose to skip the parameter names by using the _ like this:

To me, using the _ way seems very unreadable – I have to remember the order of rgb. So I’ve been largely ignoring the _ until I’ve started noticing a pattern in other people’s Swift code…

The json-swift library has a great use case for using the _ in the init. Here is some of the code taken from the JSON.swift file:

// Alias to make using a JSON structure for a single value more natural.
public typealias JSONValue = JSON

public enum JSON : Equatable, Printable {
    
    /// All of the possible values representable by JSON.
    
    case JSONString(Swift.String)
    case JSONNumber(Double)
    case JSONObject([String : JSONValue])
    case JSONArray([JSONValue])
    case JSONBool(Bool)
    case JSONNull
    
    // This case is only supported for bridging NS* types because of the AnyObject requirement.
    // This should NOT be used externally.
    case _Invalid
    
    public init(_ value: Bool?) {
        if let bool = value {
            self = .JSONBool(bool)
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ value: Double?) {
        if let number = value {
            self = .JSONNumber(number)
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ value: Int?) {
        if let number = value {
            self = .JSONNumber(Double(number))
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ value: String?) {
        if let string = value {
            self = .JSONString(string)
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ value: [JSONValue]?) {
        if let array = value {
            self = .JSONArray(array)
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ value: [String : JSONValue]?) {
        if let dict = value {
            self = .JSONObject(dict)
        }
        else {
            self = .JSONNull
        }
    }
    
    public init(_ bytes: [Byte], encoding: Encodings = Encodings.base64) {
        let data = NSData(bytes: bytes, length: bytes.count)
        
        switch encoding {
        case .base64:
            let encoded = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding76CharacterLineLength)
            self = .JSONString("\(encoding.toRaw())\(encoded)")
        }
    }
    
    public init(_ rawValue: AnyObject?) {
        if let value : AnyObject = rawValue {
            switch value {
            case let array as NSArray:
                var newArray = [JSONValue]()
                for item : AnyObject in array {
                    newArray += JSONValue(item)
                }
                self = .JSONArray(newArray)
                
            case let dict as NSDictionary:
                var newDict = [String : JSONValue]()
                for (k : AnyObject, v : AnyObject) in dict {
                    if let key = k as? String {
                        newDict[key] = JSONValue(v)
                    }
                    else {
                        assert(false, "Invalid key type; expected String")
                        self = ._Invalid
                        return
                    }
                }
                self = .JSONObject(newDict)
                
            case let string as NSString:
                self = .JSONString(string)
                
            case let number as NSNumber:
                if number.objCType == "c" {
                    self = .JSONBool(number.boolValue)
                }
                else {
                    self = .JSONNumber(number.doubleValue)
                }
                
            case let null as NSNull:
                self = .JSONNull
                
            default:
                assert(false, "This location should never be reached")
                self = ._Invalid
            }
        }
        else {
            self = .JSONNull
        }
    }
}

By using the _ in every single init method, the JSON enum completely abstracts what the JSON value needs to be, which is exactly what is needed in this use case – after all, we actually don’t know what JSON the server will return! All of these and more work:
JSONValue Abstract
I also put a breakpoint into every init method, to see how it works, and when you call JSONValue(5), for example, only the correct init – init(_ value: Int?) – gets called, which is pretty amazing.

Here is another example where using the _ in init works (taken from the Swift Book):
Celcius Struct

So while I initially dismissed the _ as parameters in init methods, I’m coming around to it! Seems like structs and enums are really great use-cases to start with. Looking forward to seeing what other cool Swift programming patterns emerge that I’m surprised by!

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Kristofer Doman

    That’s actually really neat. Thanks for sharing 🙂

  • Pingback: Swift around the web | Swift for Flash Developers Blog()

  • hh

    MyColor(0,0,0) is unreadable because you have to remember the order of R G B? 🙂

    • yes! MyColor(0,0,0) is really confusing compared to MyColor(R:23, G:84, B:34)

      • rgb

        🙂
        I have never encountered RBG, GRB, GBR, BRG or BGR for colors though…

        • kitten

          Just throwing a couple of examples out there, but FFMpeg for a while only supported BGRA as a transparent format, OpenGL & DirectX both have multiple colour orders (and packing for that matter) – DIB bitmaps can be stored in BGR order. There are many places where implicit convention is something that can get code in to trouble simply because convention can change with industry.

          Byte order aside, for printing purposes, MyColour(1,0,0,0) could be non-transparent pure red, but could equally be pure cyan (as in CMYK). Making the definition explicit saves problems down the line.

          That said, in your own code where you are using your own conventions & where it is never touched by another living soul, implicit is fine – as long as your own ideas of convention never change.

    • but this is perfect for Classes with one/flexible arguments. IsALeapYear(2014) or even AreaOfSquare(4) AreaOfARectangle(10,5). Both practice problems I have encountered.

  • Meghan Lee

    this is helpful 🙂 thanks