1

I have a an app in production I'm trying to convert from Swift 2.2 to Swift 3. I have tried the Swift 3 code in both XCode 8.1 and XCode 8.2.

The following Swift 2 code works perfectly:

func saveItemsToCache() {
    NSKeyedArchiver.archiveRootObject(items, toFile: itemsCachePath)
}

func loadItemsFromCache() {
    if let cachedItems = NSKeyedUnarchiver.unarchiveObjectWithFile(itemsCachePath) as? [TruckItem] {
        items = cachedItems
    }
}

var itemsCachePath: String {
    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let fileURL = documentsURL.URLByAppendingPathComponent("Trucks.dat")
    return fileURL.path!
}

But the data isn't being persisted when I use the same code converted for Swift 3:

func saveItemsToCache() {
    print("SAVED TRUCKS:", items)
    NSKeyedArchiver.archiveRootObject(items, toFile: itemsCachePath)
}

func loadItemsFromCache() {
    if let cachedItems = NSKeyedUnarchiver.unarchiveObject(withFile: itemsCachePath) as? [TruckItem] {
        items = cachedItems
        print("LOADED TRUCKS:", items)
    }
}

var itemsCachePath: String {
    let documentsURL = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    let fileURL = documentsURL.appendingPathComponent("Trucks.dat")
    return fileURL.path
}

Example console output:

SAVED TRUCKS: [<TruckTelematics.TruckItem: 0xc852380>, <TruckTelematics.TruckItem: 0x9b23ba0>]

LOADED TRUCKS: []
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
T. Steele
  • 325
  • 3
  • 14

1 Answers1

0

I've recently discovered that the issue wasn't with NSKeyedArchiver at all, but instead with the convenience init?(coder aDecoder: NSCoder) method in my NSObject subclass TruckItem.

In Swift 2.2, you would decode different object properties like this:

let IMEI = aDecoder.decodeObject(forKey: CodingKeys.IMEI) as! String
let active = aDecoder.decodeObject(forKey: CodingKeys.active) as! Bool
let daysInactive = aDecoder.decodeObject(forKey: CodingKeys.daysInactive) as! Int

In Swift 3, instead of using decodeObject() for all property types, it appears there are now some new functions to be aware of. Here are the same object properties decoded in Swift 3:

let IMEI = aDecoder.decodeObject(forKey: CodingKeys.IMEI) as! String
let active = aDecoder.decodeBool(forKey: CodingKeys.active)
let daysInactive = aDecoder.decodeInteger(forKey: CodingKeys.daysInactive)

It took quite some time for me to discover this, hopefully this answer saves other users from similar frustration.

T. Steele
  • 325
  • 3
  • 14