Codable class does not conform to protocol Decodable
Asked Answered
B

12

115

Why am I getting a "Type 'Bookmark' does not conform to protocol 'Decodable'" error message?

class Bookmark: Codable {
   weak var publication: Publication?
   var indexPath: [Int]
   var locationInText = 0

   enum CodingKeys: String, CodingKey {
      case indexPath
      case locationInText
   }

   init(publication: Publication?, indexPath: [Int]) {
      self.publication = publication
      self.indexPath = indexPath
   }
}

I do not wish to save the publication var since the Publication owns the Bookmark but the bookmark needs to know which Publication it belongs to. The decode init of Publication will set the bookmark reference to itself.

Bessbessarabia answered 1/2, 2018 at 17:19 Comment(3)
Cannot reproduce in Swift 4.0.3 (Xcode 9.2) – please could you provide a minimal reproducible example?Maura
It would be nice to know why my question was downvoted so that I could actually learn something for the futureBessbessarabia
Not one of the downvoters, but your question would be much more useful with a self-contained example so that others can easily reproduce your problem (I still can't reproduce it).Maura
C
51

Why am I getting a "Type 'Bookmark' does not conform to protocol 'Decodable'" error message

It's either because Publication isn't Decodable (you have not shown what it is, so it's hard to tell) or because of the weak designation on publication.

Either way, it's easy to fix: you just need to implement init(from:) to complete the implementation of Decodable; the compiler is simply telling you that this implementation cannot be synthesized.

Coltish answered 1/2, 2018 at 17:22 Comment(7)
I believe you will then have a parallel issue for the Encodable side, but let's fix one thing at a time.Coltish
Works now thanks! Encodable side did not complain. Just curious as to why the compiler is perfectly happy if I remove CodingKeys altogether when I haven't implemented init(from:)?Bessbessarabia
Publication didn't have anything to do with the problem this time.Bessbessarabia
It's not decodable because publication isn't initialized after the object is decoded. weak var publication: Publication? = nil Provide a default value and it be become Decodable.Opal
@Opal If you think you have a better answer, please do give it as an actual answer.Coltish
My problem was working with SwiftUI and having one variable inside my class marked as @Publishable. Everything conforms to Codable, but the compiler still choked. Conformed manually, everything works fine.Hydroxylamine
Note: This can also happen when CodingKeys does not define keys for all variables in the struct. Especially, when some keys do not fit variable names at all.Candler
B
72

The compiler cannot synthesise the required init(from:) method due to the weak reference, so you need to write it yourself.

class Bookmark: Codable {
    weak var publication: Publication?
    var indexPath: [Int]
    var locationInText = 0

    private enum CodingKeys: String, CodingKey {
        case indexPath
        case locationInText
    }

    init(publication: Publication?, indexPath: [Int]) {
        self.publication = publication
        self.indexPath = indexPath
    }

    required init(from decoder:Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        indexPath = try values.decode([Int].self, forKey: .indexPath)
        locationInText = try values.decode(Int.self, forKey: .locationInText)
    }
}
Beefwitted answered 1/2, 2018 at 17:27 Comment(6)
Your answer was correct too but matt was first. I don't know what the proper thing to do here is, but enlighten me if needed. Thanx.Bessbessarabia
Glad I could help. It's up to you, which answer you accept, but if you found both useful, you can still upvote both regardless of which one do you accept.Ferdy
@Bessbessarabia up to you to choose which answer you think is best and would provide the most information to other people with the same questionSweepback
I found David's answer more helpful since it has sample code.Vair
@DávidPásztor I'm wondering why publication has anything to do with encoding/decoding of Bookmark, since it's excluded from the CodingKeys?Vaticination
auto-generated decoding requires all variables to be initialized afterward, instead of implementing init(from:) you can set a default value for variables not in the CodingKeys - weak var publication: Publication? = nilOpal
C
51

Why am I getting a "Type 'Bookmark' does not conform to protocol 'Decodable'" error message

It's either because Publication isn't Decodable (you have not shown what it is, so it's hard to tell) or because of the weak designation on publication.

Either way, it's easy to fix: you just need to implement init(from:) to complete the implementation of Decodable; the compiler is simply telling you that this implementation cannot be synthesized.

Coltish answered 1/2, 2018 at 17:22 Comment(7)
I believe you will then have a parallel issue for the Encodable side, but let's fix one thing at a time.Coltish
Works now thanks! Encodable side did not complain. Just curious as to why the compiler is perfectly happy if I remove CodingKeys altogether when I haven't implemented init(from:)?Bessbessarabia
Publication didn't have anything to do with the problem this time.Bessbessarabia
It's not decodable because publication isn't initialized after the object is decoded. weak var publication: Publication? = nil Provide a default value and it be become Decodable.Opal
@Opal If you think you have a better answer, please do give it as an actual answer.Coltish
My problem was working with SwiftUI and having one variable inside my class marked as @Publishable. Everything conforms to Codable, but the compiler still choked. Conformed manually, everything works fine.Hydroxylamine
Note: This can also happen when CodingKeys does not define keys for all variables in the struct. Especially, when some keys do not fit variable names at all.Candler
N
39

Another reason you could get this message is if your CodingKeys enum isn't exhaustive. If you have three properties in the data type, then your CodingKeys enum needs to have three property/name cases as well.

Nagaland answered 4/9, 2018 at 19:34 Comment(6)
Also check typos in CodingKeys. If there are only one different character, code wont compile. (It was my case, thanks @mikepj)Immodest
But surely the whole point of specifying your own CodingKeys is because you want to exclude one or more properties from being encoded?Vaticination
I was off by a single char, I was about to rip my hair out ty!Lunate
You do NOT need to exhaustively list your properties in the CodingKeys. If you do not list a property, you need to provide a default value for it.Opal
That was it in my case. Thanks.Hardshell
in my case, the error happened because of trying to use computed properties inside the CodingKeys enumOrthoclase
P
36

In a similar scenario, I was getting the same issue because the variable name in my CodingKeys was not the same as a class variable. See below

enter image description here

Prevaricator answered 13/4, 2021 at 4:12 Comment(2)
easy to make this mistake, thanks. I almost went down the custom init route, then saw your answerSophistication
wow, just made this mistake. ThanksRequite
M
33

On the hindsight, I received a similar error when trying to set Codable to my class which consisted of NSNumber type variables. See image below:

enter image description here

Changing NSNumber to primitive data type Int resolved the issue. See below:

enter image description here

I'm guessing this might be true for other datatypes that require bridging to Swift Standard Library Value Types such as NSString, NSArray and so on

Mulhouse answered 5/4, 2018 at 9:35 Comment(2)
Thank you! In my case I was trying to use NSNumber. Switched to Int per your suggestion and bam it worksDyedinthewool
@Dyedinthewool Haha. You're Welcome. As always happy to help :)Mulhouse
H
23

Simply because your CodingKeys enum is not exhaustive, add publication property to the enum to achieve that.

try this:

class Bookmark: Codable {
   weak var publication: Publication?
   var indexPath: [Int]
   var locationInText = 0

   // All your properties should be included
   enum CodingKeys: String, CodingKey {
      case indexPath
      case locationInText
      case publication   // this one was missing
   }
}

You wont need the init method anymore as the implementation now can be synthesized.

Hildebrand answered 30/5, 2019 at 9:59 Comment(2)
weak may only be applied to class and class-bound protocol typesMatron
Thank you I had a codable struct under a codable struct and I forgot to add the inner struct as the coding key.Fatuity
C
14

Any class to be a codeable must have it's all property codeable. Standard library types like String, Int, Double and Foundation types like Date, Data, and UR confirm the codeable protocol but some doesn't.

For e.g below Note class have all properties of string which confirm codable protocol so no error: String has codable protocol so class has no error

But UIImage don't confirm codable protocol so it throw error: Uiimage don't have codable it shows error

Creodont answered 21/8, 2020 at 4:29 Comment(1)
This was very helpful. I had a custom class as a property that was not codable.Hebel
N
6

You can omit a property from coding keys enum, only if it has a default value.

From apple docs

Omit properties from the CodingKeys enumeration if they won't be present when decoding instances, or if certain properties shouldn't be included in an encoded representation. A property omitted from CodingKeys needs a default value in order for its containing type to receive automatic conformance to Decodable or Codable.

Niela answered 3/6, 2019 at 10:43 Comment(0)
K
4

In-short, while implementing Codable, all properties which are non-primitive data type (mean class type or may it can be objective-c class) must be Codable.

   weak var publication: Publication?

in this case publication is of type class so Publication must have implemented Codable

Kanaka answered 3/1, 2019 at 8:9 Comment(1)
weak' may only be applied to class and class-bound protocol typesMatron
G
2

Bit of a daft one but in case it helps someone else. I got this error because I put enum CodingKeys: CodingKeys instead of enum CodingKeys: CodingKey.

Gibbons answered 26/5, 2020 at 10:23 Comment(1)
Very helpful, I had done the same mistakeSilurid
L
1

If you have tried all the above solutions and are still unable to fix the error then I think it could be because of the data type you are using for your data class fields.

In the question they have used 1 field weak var publication: Publication?, and if you have the same kind of class structure then maybe you should check if that Publication data class conforms to the Codable class or not.

Because it is mandatory to conform same protocol for the child class as well, as it's fields are also should be encodable and decodable.

Ludewig answered 26/12, 2022 at 9:20 Comment(0)
L
0

I had a similar issue which I stumbled upon this fix to. As I am new to Swift, I am unsure as to why it works! If anyone knows I would appreciate the knowledge.

I changed this:

let id, type: Int

to this:

let id: Int
let type: Int
Lakitalaks answered 16/8, 2018 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.