Making Class Codable

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



Leave a reply



Submit