Can't save Bool in Swift

Viewed 1108

I'm new to Swift (background in C++) and I'm trying to do a very simple thing: save a Bool. It works perfectly if I convert the bool to a string that is either "A" or "B" and then convert back but if I save the bool directly with encode and aDecoder the Bool comes back nil every time. Can't find anything about it on the internet.

As you can see below I simply substitute a string for a Bool and it works.

func boolwontsave(aBool:Bool) -> String {
    if aBool {
        return "A"
    }
    return "B"
}

func encode(with aCoder: NSCoder) {

    aCoder.encode(name, forKey: PropertyKey.name)
    aCoder.encode(number, forKey: PropertyKey.number)
    aCoder.encode(boolwontsave(aBool: ispresent), forKey: PropertyKey.present)

}

required convenience init?(coder aDecoder: NSCoder) {

    // The name is required. If we cannot decode a name string, the initializer should fail.
    guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
        os_log("Unable to decode the name for a player object.", log: OSLog.default, type: .debug)
        return nil
    }

    let number = aDecoder.decodeObject(forKey: PropertyKey.number) as? String

    guard let localpresent = aDecoder.decodeObject(forKey: PropertyKey.present) as? String else {
        print("got the nil")
        os_log("Unable to decode the ispresent for a player object.", log: OSLog.default, type: .debug)
        return nil
    }


    // Must call designated initializer.
    self.init(name:name, number:number, present: localpresent == "A")

}

Aren't Bools supposed to save? This seems inelegant.

2 Answers

Use decodeBool(forKey key: String) instead of decodeObject(forKey: String)

DecodeBool decodes and returns a boolean value that was previously encoded with encode(_:forKey:) and associated with the string key.

aCoder.encode(true, forKey: PropertyKey.present)
aCoder.decodeBool(forKey: PropertyKey.present)

It's very easy to save a Bool if you are using the proper API.

In terms of NSCoding a Bool is not an object, there is a decodeBool(forKey method.

required convenience init?(coder aDecoder: NSCoder) {

    // The name is required. If we cannot decode a name string, the initializer should fail.
    guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
        os_log("Unable to decode the name for a player object.", log: OSLog.default, type: .debug)
        return nil
    }

    let number = aDecoder.decodeObject(forKey: PropertyKey.number) as? String
    let localpresent = aDecoder.decodeBool(forKey: PropertyKey.present)

    // Must call designated initializer.
    self.init(name:name, number:number, present: localpresent)

}

func encode(with aCoder: NSCoder) {
    aCoder.encode(name, forKey: PropertyKey.name)
    aCoder.encode(number, forKey: PropertyKey.number)
    aCoder.encode(ispresent, forKey: PropertyKey.present)
}

In Swift 4+ I'd prefer the native Codable protocol over constrained NSCoding.

  • NSCoding requires an class inherited from NSObject.
  • Codable can be used for any struct, class and enum which conforms to the protocol.
Related