Swift version: 5.10
Broadly speaking, NSCoding
is the Objective-C way of archiving data and Codable
is the Swift way. However, that doesn’t mean the two can’t work together – with a little work you can save any NSCoding
data right inside Codable
, which is helpful because many Apple types such as UIColor
and UIImage
conform to NSCoding
but not Codable
.
Here’s a simple struct as an example:
struct Person {
var name: String
var favoriteColor: UIColor
}
That stores one Codable
type (the string) and one NSCoding
type (the color), and we’re going to make them all work through Codable
using JSONEncoder
.
This takes four steps:
Person
where we’ll put our Codable
functionality.init(from:)
method that converts raw data back into a UIColor
.encode(to:)
method that converts a UIColor
into raw data, which Codable
can then base-64 encode.Start by adding the extension to Person
:
extension Person: Codable {
}
That will stop your code from compiling because Swift knows UIColor
isn’t compatible with Codable
. So, let’s move on to step two: adding custom coding keys. Put this inside the extension:
enum CodingKeys: String, CodingKey {
case name
case favoriteColor
}
Those are just the same coding keys we’d get by default, but because we’re going to be encoding and decoding things by hand we need to declare them explicitly.
Step three is to create an init(from:)
method that can read raw data and convert it to a UIColor
. This will used NSKeyedUnarchiver
just like regular NSCoding
code. Add this to the extension:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
let colorData = try container.decode(Data.self, forKey: .favoriteColor)
favoriteColor = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(colorData) as? UIColor ?? UIColor.black
}
The last step is to create an encode(to:)
method that does the opposite – it takes a UIColor
and converts it to data using NSKeyedArchiver
. Put this inside the extension:
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
let colorData = try NSKeyedArchiver.archivedData(withRootObject: favoriteColor, requiringSecureCoding: false)
try container.encode(colorData, forKey: .favoriteColor)
}
That’s all the work done – by converting our UIColor
into a Data
, Codable
can take care of the rest.
If you want to try it out, here’s some sample code:
let taylor = Person(name: "Taylor Swift", favoriteColor: .blue)
let encoder = JSONEncoder()
do {
let encoded = try encoder.encode(taylor)
let str = String(decoding: encoded, as: UTF8.self)
print(str)
} catch {
print(error.localizedDescription)
}
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Available from iOS 8.0 – learn more in my book Swift Design Patterns
This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.
Link copied to your pasteboard.