Takeshi Yokemura
Takeshi Yokemura

Reputation: 425

Swift 4 Codable: Cannot exclude a property

I'm developing a simple music sequencer app. This kind of app tends to have a complex data structure which has to be saved/loaded, so the introduction of Codable protocol in Swift4 is totally a good news for me.

My problem is this: I have to have a non-Codable property. It doesn't have to be coded because it's a temporary variable only kept alive while the app is active. So I've just tried to exclude by implementing CodingKey, but the compiler still give me the error "Type 'Song' does not conform to protocol 'Decodable'".

Specifically I want to exclude "musicSequence" in the code below.

class Song : Codable { //Type 'Song' does not conform to protocol 'Decodable'
    var songName : String = "";    
    var tempo : Double = 120;

    // Musical structure
    var tracks : [Track] = [] // "Track" is my custom class, which conforms Codable as well    
    // Tones
    var tones = [Int : ToneSettings] (); // ToneSettings is also my custom Codable class

    var musicSequence : MusicSequence? = nil; // I get the error because of this line

    private enum CodingKeys: String, CodingKey {
        case songName
        case tempo
        case tracks
        case tones
    }

    func createMIDISequence () {
        // Create MIDI sequence based on "tracks" above
        // and keep it as instance variable "musicSequence"
    }
}

Does anybody have any ideas?

Upvotes: 4

Views: 2968

Answers (1)

Rob Napier
Rob Napier

Reputation: 299345

(See below for a strange turn of events.)

Your use of CodingKeys is already taking care of you encoding. You still get that for free. But you'll need to tell the system how to handle decoding by hand:

required init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    songName = try values.decode(String.self, forKey: .songName)
    tempo = try values.decode(Double.self, forKey: .tempo)
    tracks = try values.decode([Track].self, forKey: .tracks)
    tones = try values.decode([Int: ToneSettings].self, forKey: .tones)
}

It's not quite smart enough to figure out that musicSequence can and should default to nil (and maybe that would be too smart anyway).

It's probably worth opening a defect at bugs.swift.org to ask for this Decodable to be automatic. It should be able to figure it out in cases where you provide CodingKeys and there is a default value.


EDIT: When I first answered this, I exactly duplicated your error. But when I tried to do it again, copying your code fresh, the error doesn't show up. The following code compiles and runs in a playground:

import Foundation

struct Track: Codable {}
struct ToneSettings: Codable {}
struct MusicSequence {}

class Song : Codable { //Type 'Song' does not conform to protocol 'Decodable'
    var songName : String = "";
    var tempo : Double = 120;

    // Musical structure
    var tracks : [Track] = [] // "Track" is my custom class, which conforms Codable as well
    // Tones
    var tones = [Int : ToneSettings] (); // ToneSettings is also my custom Codable class

    var musicSequence : MusicSequence? = nil; // I get the error because of this line

    private enum CodingKeys: String, CodingKey {
        case songName
        case tempo
        case tracks
        case tones
    }

    func createMIDISequence () {
        // Create MIDI sequence based on "tracks" above
        // and keep it as instance variable "musicSequence"
    }
}

let song = Song()
let data = try JSONEncoder().encode(song)
String(data: data, encoding: .utf8)

let song2 = try JSONDecoder().decode(Song.self, from: data)

I'm wondering if there's a compiler bug here; make sure to test this with the new beta.

Upvotes: 6

Related Questions