0
votes

I am having an issue with objects seemingly not conforming to the Equatable protocol after being loaded from a plist. Some background: I have an object TransmitterList type that holds an array of type Transmitters. On first start up everything works as it should and I am able to delete items by calling

if let index = transmitterList.transmitters.firstIndex(of: transmitter) {
    transmitterList.transmitters.remove(at: index)
}

This method works perfectly on the first run, the issue comes after the app data has been saved and reloaded after terminating the app. All saving and loading is done using the Codable Protocol and Plist encoders and decoders. The transmitters in the transmitterList are still there but when trying to delete a transmitter the call to firstIndex(of:) returns nil. Other methods that rely on the Equatable protocol also do not function correctly. It seems as if the transmitters that got saved no longer conform to Equatable. However, when adding new transmitters after restarting I am able to successfully delete any newly added transmitters.

I was able to create a work around by comparing some of the transmitters attributes and removing them that way but it doesnt seem very robust. Really want to understand what the underlying cause could be for the transmitters to no longer be conforming to Equatable.

Any help is appreciated, thank you!!

Updated with saving and loading code.

This is the dataModel object I am using which simply saves an array of TransmitterLists. There is no custom encoding or decoding in the Transmitter class, it simply conforms to Codable without any additional methods.

class DataModel {

    // MARK: - Properties
    var transmitterLists = [TransmitterList]()

    // MARK: - Initialization
    init() {
        loadData()
    }

    // MARK: - Data Persistence

     func documentsDirectory() -> URL {

         let paths = FileManager.default.urls(for: .documentDirectory,
                                              in: .userDomainMask)
         return paths[0]
     }

     func dataFilePath() -> URL {

         return documentsDirectory().appendingPathComponent("FrequencyCoordinator.plist")

     }

     func saveData() {

         let encoder = PropertyListEncoder()

         do {

             let data = try encoder.encode(transmitterLists)

             try data.write(to: dataFilePath(), options: Data.WritingOptions.atomic)

             print("DATA SAVED!!")
         } catch {
             print("That did not work: \(error.localizedDescription)")
         }

     }

     func loadData() {

         let path = dataFilePath()

         if let data = try? Data(contentsOf: path) {

             let decoder = PropertyListDecoder()

             do {
                print("DATA LOADED")


                 transmitterLists = try decoder.decode([TransmitterList].self, from: data)


             } catch {
                 print("That didnt work ;-< \(error.localizedDescription)")
             }
         }

     }


}

Adding in the Transmitter class. Any additional types also conform to Codable but only by declaring in the definition. Perhaps there is more work to be done in making sure types conform correctly?

class Transmitter: Equatable, Codable {

    static func == (lhs: Transmitter, rhs: Transmitter) -> Bool {
        return lhs.id == rhs.id
    }


    var id: Int 
    var name: String
    var transmitterType: TransmitterType
    var block: Block
    var frequency: Frequency
    var doesOverlap: Bool = false
    var transmitterOverlapRange: ClosedRange<Double>
    var imProductOverlapRanges = [ClosedRange<Double>]()
    var overlapsWith = [Transmitter]()
    var doesHaveTwoTransmitterIMInterference = false
    var doesHaveThreeTransmitterIMInterference = false
    var twoTransmitterIMProductsOverlap = [TwoTransmitterIMProduct]()
    var threeTransmitterIMProductsOverlap = [ThreeTransmitterIMProduct]()
    var isDisabled = false

    var frequencyIsSet: Bool {
        get {
            self.frequency.literal != 0.0
        }
    }

    init(name: String, type: TransmitterType, block: Block, frequency: Frequency, transmitterOverlapRange: ClosedRange<Double>) {
        self.id = DataModel.nextTransmitterID()
        self.name = name
        self.transmitterType = type
        self.block = block
        self.frequency = frequency
        self.transmitterOverlapRange = transmitterOverlapRange

    }

    enum TransmitterType: Int, Codable {
        case talent
        case hops
        case ifb
    }

}
1
“Really want to understand what the underlying cause could be for the transmitters to no longer be conforming to Equatable.” They do conform to Equatable. But on your definition of Equatable for this object type, they are no longer equal.matt
I suppose you are right. Though I am not entirely sure what is changing between saving the data and loading it back in. This problem is also wreaking havoc on other parts of the app as well. Transmitters that were saved and loaded are seemingly no longer able to have their frequency changed. All labels update accordingly to the new frequency but print statements reveal behind the scenes that the frequency of the transmitters have not actually changed. These problems only arrive after saving and loading.DHollis
Are you using custom methods .init(decoder:) and encode(encoder:) methods for transmitter objects? If so can you post them, as I suspect you are not decoding the same as you are encoding.flanker
@flanker updated original question with saving and loading methods.DHollis
It's not the load/save methods that are relevant, it's how TransmitterList (and any child type) is encoded/decoded. I.e. how they are made to conform to Codable. Can you add the Transmitter class to the question?flanker

1 Answers

0
votes

You still have not shown the relevant code. You are complaining that

if let index = transmitterList.transmitters.firstIndex(of: transmitter)

fails, but you have not shown where that line occurs in your code, nor have you told us what the transmitter is or what's in the transmitterList.

However, you've shown enough to make it possible to take a guess. You have redefined Transmitter's equatability such that two Transmitters are equal if and only if their id values are equal.

static func == (lhs: Transmitter, rhs: Transmitter) -> Bool {
    return lhs.id == rhs.id
}

Therefore we may conclude that in this line:

if let index = transmitterList.transmitters.firstIndex(of: transmitter)

... your transmitter has a different id from any of the transmitters in the list. And this would make sense if this is a new transmitter, because it would be generated with a fresh id.

And indeed, you said:

I was able to create a work around by comparing some of the transmitters attributes and removing them that way but it doesnt seem very robust

No indeed. But this shows that you seem to be expecting one Transmitter to be equatable with another based on "some of its attributes". It isn't. Your only point of equality is the id. So we can conclude that you are trying to find a transmitter with a unique id in a list of other transmitters — and of course it isn't there, because a unique id is, by definition, unique.