Migrating to NSPersistentContainer

Update - 2017/07/12

if let oldUrl = oldUrl {
        let description = NSPersistentStoreDescription(url: oldUrl)
        persistentContainer.persistentStoreDescriptions = [description]
    }
    persistentContainer.loadPersistentStores { (description, error) in
        if let oldUrl = self.oldUrl {
        do {
            let psc = persistentContainer.persistentStoreCoordinator
            let store = psc.persistentStores[0]
            try psc.migratePersistentStore(store, to: url, options: nil, withType: NSSQLiteStoreType)                                                   self.deleteDatabase(url: oldUrl)
            self.cleanDb()
        } catch {
            block(description, error)
            return
        }
    }

Small update - instead of replacing, I use migratePersistentStore instead. This seems to work a little better with the current setup. So I have an oldUrl which is returned if the old store exists. If it's not nil I create the database against this and then migrate it to the correct location under Application Support. The old database still needs to be deleted in a separate step (assuming you no longer need it).


iOS 10 brought some welcome changes to Core Data and I need to migrate an existing database into this. Here’s how I did it.

First off, my existing DB is in the Documents folder. That’s not really good practice and NSPersistentContainer will create one in Application Support. That seems more sensible so I decided to move my Database. The default SQLite backed Core Data store includes SHM and WAL files (Write-Ahead Logging). To ensure you don’t lose data you need to move all three files at the same time. Fortunately, NSPersistentStoreCoordinator has built-in support for moving a database around.

First, I set up the NSPersistentStoreContainer:

persistentContainer = NSPersistentContainer(name: "GameModel")
persistentContainer.loadPersistentStores { (description, error) in
    if let error = error {
        fatalError("Could not create CoreData store: \(error)")
    }
    print(description)
}

This gives me something to work against. NSPersistentStoreContainer will create a NSPersistentStoreCoordinator with an sqlite DB at ApplicationSupport/GameModel.sqlite upon calling loadPersistentStores(completionHandler:). I want to replace that with the old Documents/Core_Data.sqlite. Fortunately, there’s a replace function right there.

let psc = persistentContainer.persistentStoreCoordinator
guard let storeUrl = psc.persistentStores.first?.url else {
    return
}
do {
    try psc.replacePersistentStore(at: storeUrl,
                                   destinationOptions: nil,
                                   withPersistentStoreFrom: oldUrl,
                                   sourceOptions: nil,
                                   ofType: NSSQLiteStoreType)
    persistentContainer.loadPersistentStores(completionHandler: { (description, error) in
        if let error = error {
            fatalError("Could not create CoreData store: \(error)")
        }
        print(description)
    })
} catch {
    print("Could not replace store: \(error)")
}

The newly created GameModel sqlite DB is replaced with the contents of the existing database by the replace call. I then need to call loadPersistentStores again to set up the NSPersistentContainer against the updated DB. Once it’s successful, I can delete the old DB files. There might be a better way, but this worked for me:

private func deleteOld(url: URL) {
        let parent = url.deletingLastPathComponent()
        let name = url.lastPathComponent
        do {
            try FileManager.default.contentsOfDirectory(at: parent, includingPropertiesForKeys: nil, options: [])
                .filter {
                    $0.lastPathComponent.hasPrefix(name)
                }
                .forEach {
                    try FileManager.default.removeItem(at: $0)
                }
        } catch {
            print("Failed to clear old DB: \(error)")
        }
    }

Where the passed URL is the old store URL.

Swift 3 - HTTPURLResponse.allHeaderFields - Case Sensitive

In Obj-C, Swift 2, and RFC 7230, headers are case-insensitive. Changes in Swift 3 have put them into a normal Dictionary which caused some unexpected behaviour when I discovered that the server was sometimes returning headers as Camel-Case, or lower-case. My code had been working fine until I moved from Swift 2.2 to Swift 3, thanks to the documented behaviour of HTTPURLResponse.

There’s a bug logged already for this so, thankfully, it was quick to discover what the underlying issue was.

I had a quick look through proposed solutions but they all involved changing the code to handle this special case. I was loathe to make these alterations since none of them looked particularly clean and, if this got fixed, I wanted something that would be quick to remove. My case was X-Custom-Header occasionally being returned as x-custom-header.

Since I didn’t want to change the existing code if I didn’t have to (at least, not in a way that would take a bunch of work to revert down the road), I started looking at writing an extension for Dictionary and adding support for additional subscripts.

So, since I can’t guarantee the response, here's the full thing I ended up with to cover as many cases as possible:

struct DictionaryKey: Hashable {
        let keys: [String]
        var hashValue: Int {
            get {
                return keys[0].hashValue
            }
        }
    }
    func ==(lhs: DictionaryKey, rhs: DictionaryKey) -> Bool {
        return lhs.keys == rhs.keys
    }

    fileprivate extension Dictionary {
        subscript(key: String) -> Value? {
            get {
                let anyKey = key as! Key
                if let value = self[anyKey] {
                    return value
                }
                if let value = self[key.lowercased() as! Key] {
                    return value
                }
                if let value = self[key.capitalized as! Key] {
                    return value
                }
                for (storedKey, storedValue) in self {
                    if let stringKey = storedKey as? String {
                        if stringKey.caseInsensitiveCompare(key) == .orderedSame {
                            return storedValue
                        }
                    }
                }

                return nil
            }
            set {
                self[key] = newValue
            }
        }

        subscript(key: DictionaryKey) -> Value? {
            get {
                for string in key.keys {
                    if let value = self[string as! Key] {
                        return value
                    }
                }

                return nil
            }
        }
    }

Starting with the extension:

  • Since case is only applicable to Strings, that’s the only case we’re intercepting.
  • The first check just looks to see if we have a match already.
  • If that fails, we try an all lower-case match (X-Custom-Header -> x-custom-header)
  • If that fails, we try a camel-case match (x-custom-header -> X-Custom-Header).
  • As a last ditch effort, we try a case-insensitive match against all the keys. I don’t honestly expect to hit this so it could feasibly be removed but I’ve kept it for completeness.

This covers all use cases but I was curious how it stacked up in terms of performance.

Using dispatch_benchmark to run 100,000 iterations 10 times (extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));) I created a Dictionary of typical header keys and values and got some timings. All tests were run with optimisation set to -Os

let headerFields = ["User-Agent": "Some User Agent",
                    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                    "Accept-Language": "en-us,en;q=0.5",
                    "Accept-Encoding": "gzip,deflate",
                    "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
                    "Keep-Alive": "300",
                    "Connection": "keep-alive",
                    "Cookie": "PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120",
                    "Pragma": "no-cache",
                    "Cache-Control": "no-cache",
                    "X-Custom-Header" : "12345678",
                    "Etag": "87654321"]
let header = "X-Custom-Header"
let t = dispatch_benchmark(100000) {
    let _ = headerFields[header]
}

Against a standard dictionary, where we have a matching key (and this dictionary is case-sensitive): Avg. Runtime: 1257ns

Against a standard dictionary but having to make two checks because the first one was nil: Avg: Runtime: 2412ns

Against my dictionary extension, with a perfect match: Avg .Runtime: 1213ns (re-checked this a few times and it went up and down marginally so I would say it’s basically comparable to a standard dictionary check).

Against my extension with a lower-case name in the headers (the issue I was encountering): Avg:.Runtime: 2511ns - Suprisingly good. I certainly wouldn’t be inclined to duplicate the check with different case given that this is likely to be a one-off check rather than something that’s going to be performance gating

Against my extension with a capitalised name in the headers (x-custom-header -> X-Custom-Header issue): Avg: Runtime: 8928ns - Quite a jump but, in this use case, it’s probably fine

Against my extension with arbitrary case. Avg: Runtime: 22317ns - This is the worst case and it shows in the performance.

Since the capitalisation check turned out to be quite expensive, I wondered if there was an alternative way to handle it. This is due the note in the documentation that content-length would be changed to Content-Length. I didn’t actually see this in testing so it might not be needed, but now I was curious.

I created a Hashable struct which contains an array of Strings. This can be used to get a value from the dictionary thanks to another subscript which just iterates through the array returns on the first value. For two values (second one succeeds) I got: Avg: Runtime: 2430ns - So pretty much what you’d expect. Very close to two dictionary fetches. I don’t need this (yet) but it’s a nice to have.

For my case, I made the extension fileprivate so it’s only available in the file it’s declared in. That worked fine for my particular issue, although that might change in the future. Should the issue be fixed in the future, I can delete the extension and all my current cases will still work fine. If I end up with any DictionaryKeys style stuff in there, I can delete the struct and a quick search/replace will get the rest back in line.

So, the moral of story is that stuff you depend on can break in interesting ways - especially between major versions.