Search code examples
swiftconcurrencyswiftuiswift-concurrency

Conforming @MainActor class, or actor, to Codable


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!


Solution

  • 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)
        }
    }