Refactoring NSCoding to Swift

Rewriting an old rusty Objective-C code is fun.
You start with shiny new classes, adding some optimisation and covering the rest with syntactic sugar.
But some parts can get tricky. Migration is one of them.

While fetching a String from UserDefaults shouldn’t be a problem.
And CoreData has a lightweight migration.
With good old NSCoding, things might get tricky.

Let’s say we have an Objective-C Class LCToken that conforms to NSCoding

Which used to be stored in UserDefaults
(storing sensitive data in UserDefaults is bad for your security, kids)

How do we create a swift reincarnation without loosing any of the users data?

Let’s swift it up!

We might want to use Codable protocol in our new class.
Most of the code is auto-generated for us. So the implementation is as simple as adding the protocol conformance.

Persistence with Codable is also safe, and easy to set up


Let’s create a test to see if we can read the Legacy data.

Image for post
Image for post
XCTAssertTrue failed

Unfortunately, NSCoding and Codable are not compatible “out of the box”.


A simple solution to migrate a class from a legacy data is to make it NSCoding conformant.

We will need to subclass NSObject (I know, everything has it’s downsides)
and implement functions required by the protocol.

Now, if data fetching fails, we can try to decode it using NSCoding.

Image for post
Image for post
Still fails

There is one more thing we forgot to set up. Class names.
NSKeyedArchiver uses a classname as one of the keys for encoding.

Swift class names, though, are prefixed with a module name, so leaving it untouched wouldn’t work.

Luckily there is asetClass:forClassName: method, which adds something called a class translation mapping for NSKeyedUnarchiver

NSKeyedUnarchiver.setClass(Token.self, forClassName: "LCToken")

Let’s add it in our code and run the test again.

Image for post
Image for post
It’s green!

It’s Green!

Our users can now update without loosing any of their data.
Migrate safely!


This example is oversimplified to keep focus on a main problem.

Author recommends not to use UserDefaults in UnitTests, but to have a dependency injected storage and mock it in tests.

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store