Conforming #MainActor class, or actor, to Codable - swift

How does one add Codable conformance to a class that needs to be isolated to the MainActor?
For example, the following code gives compiler errors:
#MainActor final class MyClass: Codable {
var value: Int
enum CodingKeys: String, CodingKey {
case value
}
init(from decoder: Decoder) throws { // <-- Compiler error: Initializer 'init(from:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Decodable'
let data = try decoder.container(keyedBy: CodingKeys.self)
self.value = try data.decode(Int.self, forKey: .value)
}
func encode(to encoder: Encoder) throws { // <-- Compiler error: Instance method 'encode(to:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Encodable'
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
}
}
I'm definitely struggling to get my head around actors and #MainActor at the moment!

There isn't anything about the class you've provided that needs to be isolated to the main actor, so don't isolate the class as a whole. If there are other members that you have not shown us that do need to be isolated to the main actor, isolate them.
Example:
final class MyClass: Codable {
private var value: Int
#MainActor init(value: Int) {
self.value = value
}
#MainActor func setMyValue(to newValue:Int) {
self.value = newValue
}
#MainActor func getMyValue() -> Int {
self.value
}
enum CodingKeys: String, CodingKey {
case value
}
init(from decoder: Decoder) throws {
let data = try decoder.container(keyedBy: CodingKeys.self)
self.value = try data.decode(Int.self, forKey: .value)
}
func encode(to encoder: Encoder) throws { // <-- Compiler error: Instance method 'encode(to:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Encodable'
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
}
}

Related

Swift struct with custom encoder and decoder cannot conform to 'Encodable'

[Edited to provide a minimal reproducible example ]
This is the complete struct without non relevant vars and functions
InstrumentSet.swift
import Foundation
struct InstrumentsSet: Identifiable, Codable {
private enum CodingKeys: String, CodingKey {
case name = "setName"
case tracks = "instrumentsConfig"
}
var id: String { name }
var name: String
var tracks: [Track]
}
Track.swift
import Foundation
extension InstrumentsSet {
struct Track: Identifiable, Encodable {
private enum TrackKeys: String, CodingKey {
case id = "trackId"
case effects
}
let id: String
var effects: [Effect]?
}
}
extension InstrumentsSet.Track: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: TrackKeys.self)
id = try container.decode(String.self, forKey: .id)
effects = try container.decodeIfPresent([Effect].self, forKey: .effects)
}
}
Effect.swift
import Foundation
import AudioKit
import SoundpipeAudioKit
extension InstrumentsSet.Track {
enum Effect: Decodable {
private enum EffectKeys: String, CodingKey {
case effectType = "effectName"
case cutoffFrequency
case resonance
}
case lowPassFilter(LowPassFilterEffect)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: EffectKeys.self)
let effectType = try container.decode(Effect.EffectType.self, forKey: .effectType)
switch effectType {
case .lowPassFilter:
let cutOffFrequency = try container.decode(ValueAndRange.self, forKey: .cutoffFrequency)
let resonance = try container.decode(ValueAndRange.self, forKey: .resonance)
self = .lowPassFilter(LowPassFilterEffect(cutOffFrequency: cutOffFrequency, resonance: resonance))
default:
fatalError("Not implemented!")
}
}
}
}
extension InstrumentsSet.Track.Effect {
enum EffectType: String, Decodable {
case lowPassFilter
}
}
extension InstrumentsSet.Track.Effect: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: EffectKeys.self)
//FIXME: This is the location of the error: Type 'ValueAndRange.Type' cannot conform to 'Encodable'
try container.encode(ValueAndRange.self, forKey: .cutoffFrequency)
}
}
The problem is ValueAndRange.self not not conforming to Encodable
I've followed multiple examples to get to this implementation:
import Foundation
import AudioKit
struct ValueAndRange: Encodable {
private enum ValueRangeKeys: String, CodingKey {
case value
case range
}
static var zero: ValueAndRange { .init(value: 0, range: [0, 0]) }
var value: AUValue
var range: [Double]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: ValueRangeKeys.self)
try container.encode(value, forKey: .value)
try container.encode(range, forKey: .range)
}
}
extension ValueAndRange: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ValueRangeKeys.self)
value = try container.decode(AUValue.self, forKey: .value)
range = try container.decode([Double].self, forKey: .range)
}
}
I cannot see why this struct should not conform to Encodable. Maybe any of you got betters eyes (and brains) then I got?
Your encode function is incorrectly trying to encode a type, ValueAndRange.self, rather than a value.
Looking at the init(from:) method I think your encode function should look something like this
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: EffectKeys.self)
switch self {
case .lowPassFilter(let effect):
try container.encode(effect.cutOffFrequency, forKey: .cutoffFrequency)
try container.encode(effect.resonance, forKey: .resonance)
}
}
I didn't include .effectType in this code since I am uncertain of its usage (isn't it always the same hard coded string?).

Error while I'm trying to decode using CodingKeys

This my struct
import Foundation
struct Settings: Hashable, Decodable{
var Id = UUID()
var userNotificationId : Int
}
Coding Keys
private enum CodingKeys: String, CodingKey{
**case userNotificationId = "usuarioNotificacionMovilId"** (this is the line that gets me errors)
}
init
init(userNotificationId: Int){
self.userNotificationId = userNotificationId
}
Decoder
init(from decoder: Decoder) throws{
let container = try decoder.container(keyedBy: CodingKeys.self)
userNotificationId = try container.decodeIfPresent(Int.self, forKey: .userNotificationId) ?? 0
}
Encoder
init(from encoder: Encoder) throws{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(userNotificationId, forKey: .userNotificationId)
}
I get the following error inside the coding method
'self' used before all stored properties are initialized
What is init(from encoder: Encoder) supposed to be? You're not conforming to Encodable, and if you were, you would need to implement func encode(to encoder: Encoder) throws, not another initializer.
That said, your explicit implementation of init(from decoder: Decoder) throws does nothing different from what the compiler would synthesize for you, so it's better to remove it entirely as well.
struct Settings: Hashable, Decodable {
let id = UUID()
let userNotificationId: Int
private enum CodingKeys: String, CodingKey{
case userNotificationId = "usuarioNotificacionMovilId"
}
init(userNotificationId: Int) {
self.userNotificationId = userNotificationId
}
}
is probably all you need.

Swift 4 decoding/encoding a generic data structure

I have a generic Queue data structure which utilizes an array as its list, I am having hard time making the queue conform to encodable and decodable. I have another class which uses the queue which also needs to be Codable but it cannot be coddle unless its member variables are.
I have tried conforming the queue to encodable and decodable, encoding the data and saving it to user defaults but this does not seem to be working, in fact my init(from decoder) function is even stuck in an infinite loop for whatever reason. I could really use some assistance
//My Queue
public struct Queue<T: Codable> {
private var dataSource = [T]()
private var userDefaults = UserDefaults()
public init() {}
public func isEmpty() -> Bool{
return dataSource.isEmpty
}
public mutating func enqueue( element: T){
dataSource.append(element)
}
public mutating func dequeue() -> T?{
return isEmpty() ? nil : dataSource.removeFirst()
}
public func peek() -> T? {
return isEmpty() ? nil : dataSource.first
}
public func getCount() -> Int {
return dataSource.count
}
public func printQueue(){
print(dataSource)
}
}
public enum Error: String, Swift.Error{
case queueNotFound = "Queue Not Found!"
}
extension Queue: Encodable, Decodable {
public func encode(to encoder: Encoder) throws
{
let jsonEncoder = JSONEncoder()
let encodedData = try jsonEncoder.encode(dataSource)
userDefaults.set(encodedData, forKey: "queue")
print(encodedData)
//var container = encoder.container(keyedBy: CodingKey.self)
}
public init(from decoder: Decoder) throws
{
print("intilaizing")
let jsonDecoder = JSONDecoder()
guard let data = userDefaults.data(forKey: "queue"), let _ = try? jsonDecoder.decode(Queue.self, from: data)
else {
throw Error.queueNotFound
}
}
Any class should be able to addd this queue as a data member, when I implement the queue the encode function works I believe but the decoder causes some infinite loop
You are encoding dataSource – which is [T] – but are decoding Queue, this cannot work. Try
public init(from decoder: Decoder) throws
{
print("initializing")
guard let data = userDefaults.data(forKey: "queue") else { throw Error.queueNotFound }
dataSource = try JSONDecoder().decode([T].self, from: data)
}
By the way in your code the decoded value – as well as a potential DecodingError – is unused which makes no sense.
extension Queue {
enum CodingKeys: String, CodingKey {
case dataSource
}
extension Queue: Encodable, Decodable {
public func encode(to encoder: Encoder) throws
{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(dataSource, forKey: .dataSource)
}
public init(from decoder: Decoder) throws
{
print("initializing Queue:")
let data = try decoder.container(keyedBy: CodingKeys.self)
dataSource = try data.decode(Array.self, forKey: .dataSource)
}
I switched over to using CodingKeys instead, and changed the type to Array.self (Array) which essentially is the same type as the dataSource variable ([T])

`RLMArray` does not conform to protocol 'Encodable'

I want to Do:
Using JSONDecoder(), I convert json to Realm object.
And I save this object to Realm databases.
Problem:
RLMArray don't apply Codable protocol.
I could be conformed Decodable protocol, but Codable I couldn't.
Error Message:
Type 'Person' does not conform to protocol 'Encodable'
Code:
public class Hobby: Object, Codable {
#objc dynamic var title: String?
#objc dynamic var category: String?
}
public class Person: Object, Codable { // Error: Type 'Person' does not conform to protocol 'Encodable'
#objc dynamic var name: String?
#objc dynamic var hobbies: RLMArray<Hobby>?
required convenience public init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
hobbies = try container.decode(RLMArray<Hobby>?.self, forKey: .hobbies)
}
}
func sample() {
let person = try? JSONDecoder().decode(Person.self, from: "{\"name\" : \"aaa\",\"hobbies\" : [{\"title\" : \"fishing\",\"category\" : \"outdoor\"},{\"title\" : \"reading\",\"type\" : \"indoor\"}]}".data(using: .utf8)!)
print(person)
let realm = try! Realm()
try! realm.write {
realm.add(person!)
}
}
Do you have some ideas?
Swift4
RealmSwift
Codable is the exact same as Decodable + Encodable. If you want to conform to Codable you will need to implement the encoding functions, which for your Person object would be:
enum CodingKeys: String, CodingKey {
case name
case hobbies
// or: case hobbies = "customHobbiesKey" if you want to encode to a different key
}
func encode(to encoder: Encoder) throws {
do {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(hobbies, forKey: .hobbies)
} catch {
print(error)
}
}
Add this to your Person class, and then implement the same thing for your Hobby class.
Because i'm not sure if you even want to encode: If all you need to do is create Realm-Objects from Json I would simply replace 'Codable' with the'Decodable'-Protocol.
EDIT: I noticed the issue is about the RLMArray. I'm not sure how codable works with RLMArray, but if it doesn't work you could try replacing the declaration with
let hobbies = List<Hobby>()
and then in init() replace the 'hobbies' line with:
let tempHobbyList: [Hobby] = try container.decode([Hobby].self, forKey: .hobbies)
self.hobbies.append(objectsIn: tempHobbyList)
That's how I got my lists with realmObjects to work with codable

How to implement Codable in a custom subclass (Swift 4) [duplicate]

Should the use of class inheritance break the Decodability of class. For example, the following code
class Server : Codable {
var id : Int?
}
class Development : Server {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here
output is:
1
name is nil
Now if I reverse this, name decodes but id does not.
class Server {
var id : Int?
}
class Development : Server, Codable {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil")
output is:
id is nil
Large Building Development
And you can't express Codable in both classes.
I believe in the case of inheritance you must implement Coding yourself. That is, you must specify CodingKeys and implement init(from:) and encode(to:) in both superclass and subclass. Per the WWDC video (around 49:28, pictured below), you must call super with the super encoder/decoder.
required init(from decoder: Decoder) throws {
// Get our container for this subclass' coding keys
let container = try decoder.container(keyedBy: CodingKeys.self)
myVar = try container.decode(MyType.self, forKey: .myVar)
// otherVar = ...
// Get superDecoder for superclass and call super.init(from:) with it
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)
}
The video seems to stop short of showing the encoding side (but it's container.superEncoder() for the encode(to:) side) but it works in much the same way in your encode(to:) implementation. I can confirm this works in this simple case (see playground code below).
I'm still struggling with some odd behavior myself with a much more complex model I'm converting from NSCoding, which has lots of newly-nested types (including struct and enum) that's exhibiting this unexpected nil behavior and "shouldn't be". Just be aware there may be edge cases that involve nested types.
Edit: Nested types seem to work fine in my test playground; I now suspect something wrong with self-referencing classes (think children of tree nodes) with a collection of itself that also contains instances of that class' various subclasses. A test of a simple self-referencing class decodes fine (that is, no subclasses) so I'm now focusing my efforts on why the subclasses case fails.
Update June 25 '17: I ended up filing a bug with Apple about this. rdar://32911973 - Unfortunately an encode/decode cycle of an array of Superclass that contains Subclass: Superclass elements will result in all elements in the array being decoded as Superclass (the subclass' init(from:) is never called, resulting in data loss or worse).
//: Fully-Implemented Inheritance
class FullSuper: Codable {
var id: UUID?
init() {}
private enum CodingKeys: String, CodingKey { case id }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
}
}
class FullSub: FullSuper {
var string: String?
private enum CodingKeys: String, CodingKey { case string }
override init() { super.init() }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let superdecoder = try container.superDecoder()
try super.init(from: superdecoder)
string = try container.decode(String.self, forKey: .string)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)
let superencoder = container.superEncoder()
try super.encode(to: superencoder)
}
}
let fullSub = FullSub()
fullSub.id = UUID()
fullSub.string = "FullSub"
let fullEncoder = PropertyListEncoder()
let fullData = try fullEncoder.encode(fullSub)
let fullDecoder = PropertyListDecoder()
let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)
Both the super- and subclass properties are restored in fullSubDecoded.
Found This Link - Go down to inheritance section
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)
}
For Decoding I did this:
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
total = try values.decode(Int.self, forKey: .total)
}
private enum CodingKeys: String, CodingKey
{
case total
}
🚀 Swift introduced Property Wrappers in 5.1 I implemented a library called SerializedSwift that uses the power of property wrappers to Decode and Encode JSON data to objects.
One of my main goals was, to make inherited object to decode out of the box, without additonal init(from decoder: Decoder) overrides.
import SerializedSwift
class User: Serializable {
#Serialized
var name: String
#Serialized("globalId")
var id: String?
#Serialized(alternateKey: "mobileNumber")
var phoneNumber: String?
#Serialized(default: 0)
var score: Int
required init() {}
}
// Inherited object
class PowerUser: User {
#Serialized
var powerName: String?
#Serialized(default: 0)
var credit: Int
}
It also supports custom coding keys, alternate keys, default values, custom transformation classes and many more features to be included in the future.
Available on GitHub (SerializedSwift).
I was able to make it work by making my base class and subclasses conform to Decodable instead of Codable. If I used Codable it would crash in odd ways, such as getting a EXC_BAD_ACCESS when accessing a field of the subclass, yet the debugger could display all the subclass values with no problem.
Additionally, passing the superDecoder to the base class in super.init() didn't work. I just passed the decoder from the subclass to the base class.
How about using the following way?
protocol Parent: Codable {
var inheritedProp: Int? {get set}
}
struct Child: Parent {
var inheritedProp: Int?
var title: String?
enum CodingKeys: String, CodingKey {
case inheritedProp = "inherited_prop"
case title = "short_title"
}
}
Additional info on composition: http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/
Here is a library TypePreservingCodingAdapter to do just that (can be installed with Cocoapods or SwiftPackageManager).
The code below compiles and works just fine with Swift 4.2. Unfortunately for every subclass you'll need to implement encoding and decoding of properties on your own.
import TypePreservingCodingAdapter
import Foundation
// redeclared your types with initializers
class Server: Codable {
var id: Int?
init(id: Int?) {
self.id = id
}
}
class Development: Server {
var name: String?
var userId: Int?
private enum CodingKeys: String, CodingKey {
case name
case userId
}
init(id: Int?, name: String?, userId: Int?) {
self.name = name
self.userId = userId
super.init(id: id)
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name)
userId = try container.decodeIfPresent(Int.self, forKey: .userId)
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(userId, forKey: .userId)
}
}
// create and adapter
let adapter = TypePreservingCodingAdapter()
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// inject it into encoder and decoder
encoder.userInfo[.typePreservingAdapter] = adapter
decoder.userInfo[.typePreservingAdapter] = adapter
// register your types with adapter
adapter.register(type: Server.self).register(type: Development.self)
let server = Server(id: 1)
let development = Development(id: 2, name: "dev", userId: 42)
let servers: [Server] = [server, development]
// wrap specific object with Wrap helper object
let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })
// decode object back and unwrap them force casting to a common ancestor type
let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }
// check that decoded object are of correct types
print(decodedServers.first is Server) // prints true
print(decodedServers.last is Development) // prints true
Swift 5
The compiler synthesises decodable code only for a type that directly adopts Codable protocol so that you observe decoding for a single of your type in inheritance.
But you can try next generic approach with KeyValueCoding package (https://github.com/ikhvorost/KeyValueCoding) and this package provides access to all properties metadata and allows to get/set any property for pure swift types dynamically. The idea is to make a base Coding class which adopts KeyValueCoding and implements decoding of all available properties in init(from: Decoder):
class Coding: KeyValueCoding, Decodable {
typealias DecodeFunc = (KeyedDecodingContainer<_CodingKey>, _CodingKey) throws -> Any?
struct _CodingKey: CodingKey {
let stringValue: String
let intValue: Int?
init(stringValue: String) {
self.stringValue = stringValue
self.intValue = Int(stringValue)
}
init(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
}
static func decodeType<T: Decodable>(_: T.Type) -> (type: T.Type, f: DecodeFunc) {
(T.self, { try $0.decode(T.self, forKey: $1) })
}
static var decodeTypes: [(Any.Type, DecodeFunc)] = [
decodeType(Int.self),
decodeType(Int?.self),
decodeType(String.self),
decodeType(String?.self),
// Other types to support...
]
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: _CodingKey.self)
try container.allKeys.forEach { codingKey in
let key = codingKey.stringValue
guard let property = (properties.first { $0.name == key }),
let item = (Self.decodeTypes.first { property.type == $0.0 })
else {
return
}
var this = self
this[key] = try item.1(container, codingKey)
}
}
}
It is important to provide all supported types to decode in decodeTypes variable.
How to use:
class Server: Coding {
var id: Int?
}
class Development : Server {
var name: String = ""
}
class User: Development {
var userId: Int = 0
}
func decode() {
let json = "{\"id\": 1, \"name\": \"Large Building Development\", \"userId\": 123}"
do {
let user = try JSONDecoder().decode(User.self, from:json.data(using: .utf8)!)
print(user.id, user.name, user.userId) // Optional(1) Large Building Development 123
}
catch {
print(error.localizedDescription)
}
}