Make class conforming to Codable by default in Swift
Here's a good blog post which includes an answer to your question: source
Scroll down to inheritance and you'll see the following:
Assuming we have the following classes:
class Person : Codable {
var name: String?
}
class Employee : Person {
var employeeID: String?
}
We get the Codable conformance by inheriting from the Person class, but what happens if we try to encode an instance of Employee?
let employee = Employee()
employee.employeeID = "emp123"
employee.name = "Joe"
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(employee)
print(String(data: data, encoding: .utf8)!)
{
"name" : "Joe"
}
This is not the expected result, so we have to add a custom implementation like this:
class Person : Codable {
var name: String?
private enum CodingKeys : String, CodingKey {
case name
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
}
}
Same story for the subclass:
class Employee : Person {
var employeeID: String?
private enum CodingKeys : String, CodingKey {
case employeeID = "emp_id"
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(employeeID, forKey: .employeeID)
}
}
The result would be:
{
"emp_id" : "emp123"
}
Which again is not the expected result, so here we are using inheritance by calling super.
// Employee.swift
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(employeeID, forKey: .employeeID)
}
Which finally gives us what we really wanted from the beginning:
{
"name": "Joe",
"emp_id": "emp123"
}
If you're not happy with the flattened result, there's a tip on how to avoid that too.
All the credits to the guy who wrote the blog post and my thanks.
Hope it helps you as well, cheers!
How to make a custom class Codable?
You could provide a subclass of ThreadSafeDictionary
which works with Codable Key
and Value
types. In my opinion it's best to pretend that we are dealing with a simple dictionary, so lets use a singleValueContainer
.
class CodableThreadSafeDictionary<Key: Codable & Hashable, Value: Codable>: ThreadSafeDictionary<Key, Value>, Codable {
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dictionary = try container.decode(DictionaryType.self)
protectedCache = CZMutexLock(dictionary)
}
public required init(dictionaryLiteral elements: (Key, Value)...) {
var dictionary = DictionaryType()
for (key, value) in elements {
dictionary[key] = value
}
protectedCache = CZMutexLock(dictionary)
super.init()
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
var dictionary = DictionaryType()
protectedCache.readLock {
dictionary = $0
}
try container.encode(dictionary)
}
}
But because of the fact that the protectedCache
is a fileprivate
property, you will need to put this implementation in the same file
Note!
You could consider limiting Key
to just a String
for a better compatibility with JSON format:
CodableThreadSafeDictionary<Key: String, Value: Codable>
Making class codable
You need a concrete type conforming to Codable
, you can't use a protocol conforming to Codable
.
final public class SourceListHeader<Item: Codable>: Codable {
var name: String = "Give me a name!"
var children = [Item]()
}
Trying to make a class codable in Swift but it is not the correct format?
You need to pass in the array to decode, don't pass in the array element type, then try to force-cast that to an array, that doesn't make any sense. YourType
and Array<YourType>
are two different and completely unrelated types, so you cannot cast one to the other and you need to use the specific type when calling JSONDecoder.decode(_:from:)
.
let array = try JSONDecoder().decode([Attribute].self, from: data)
Btw as already pointed out in your previous question, there is no need to manually write the init(from:)
and encode(to:)
methods or the CodingKeys
enum
since for your simple type, the compiler can auto-synthesise all of those for you. Also, if you used a struct
instead of class
, you'd also get the member wise initialiser for free.
Trying to make this class codable and decodable
Why do you have an empty class
with a nested struct
in it? The error comes from the fact that those properties are defined on Att
rather than Attribute
, so you need to encode those when extending Att
to conform to Encodable
.
Btw, you don't have any special encoding/decoding, so you don't need to declare the encoder/decoder functions manually, the compiler can synthesise them for you.
class Attribute: Codable {
struct Att: Codable {
var number: Int16
var label: String
var comments: String
}
}
Conforming class to codable protocol in swift4
No, you haven't to do it. Example:
import Foundation
class Message: Codable {
let id: Int = 1
let text: String = "Hello"
}
let message = Message()
let encoder = JSONEncoder()
let json = try encoder.encode(message)
print(String(data: json, encoding: String.Encoding.utf8)!)
How can I make my model conform to codable and save it locally on every change?
I like very much pawello2222's detailed response. If you continue to develop on iOS, you will eventually need to learn CoreData.
That said, if you don't have time to do that for your current project, personally, I would recommend using RealmSwift and creating Realm Parent, Child and Puppet objects. It would take you an hour to learn how to install and use RealmSwift.
Here's a start:
import RealmSwift
class Puppet: Object {
@objc dynamic var id = UUID()
@objc dynamic var name: String = ""
}
But if you want to save a small number of objects you could use UserDefaults - none of your objects are using unsupported types, so just declare them as codable and let the compiler do the conversion automatically.
Conforming class to codable protocol in swift4
It is quite straightforward. /p>
How to create a model class and access the values from JSON response using Codable in ios swift?
You need
// MARK: - Empty
struct UserDic: Codable {
let topping: [Topping]
let name: String
let batters: Batters
let ppu: Double
let type: String
let id: Int
}
// MARK: - Batters
struct Batters: Codable {
let batter: [Topping]
}
// MARK: - Topping
struct Topping: Codable {
let type: String
let id: Int
}
let jsonResponse = try JSONDecoder().decode(UserDic.self, from: Data(response.data!))
self.toppingVal = jsonResponse.batters.batter
self.userTbl.reloadData()
Related Topics
Take Screenshot of Host App Using iOS Share/Action Extensions
Querying in Firebase by Child of Child
Swift: Google Maps Draw Waypoint Polyline
Swift:Difference in '!' and '' in Swift
Get the String Up to a Specific Character
[_Nscftimer Copywithzone:]: Unrecognized Selector Sent to Instance
Swift: How to Create a Predicate with an Int Value
How to Add Documentation to Enum Associated Values in Swift
No Designated Init for Skshapenode(Circleofradius: Radius)
Table View Cellforrowatindexpath Warning
How to Convert Unmanaged<Cfdata> to Nsdata
How to Catch Error When Setting Launchpath in Nstask
Swiftui - How to Change Text Alignment of Label in Toggle
How to Create a Directory in Downloads Folder with Swift on MACos? Permission Exception