避免领域数据库中自定义对象数组的重复。当主键仅存在于关系 table 中时

Avoid duplication in array of custom object in realm database. When Primary key is exist in the relational table only

如何避免领域数据库中自定义对象数组的重复,下面是我的代码和相关的JSON如果我在模型中做错了,请纠正我。

JSON ->

{
  "playlists": [
    {
      "id": "1f23bd3e-cc01-11e8-b25e-784f435e4a9a",
      "name": "disney nostalgia",
      "duration": 361,
    },
    {
      "id": "2e1f0e02-cc05-11e8-9efe-784f435e4a9a",
      "name": "songs from aladdin",
      "duration": 331,
    }
  ],
    "tracks": [
        {
          "id": "3e986a2a-cc01-11e8-bb04-784f435e4a9a",
          "name": "I'll Make a Man Out of You",
          "artist": "Donny Osmond & Chorus"
        },
        {
          "id": "aff8bcee-cc04-11e8-8c18-784f435e4a9a",
          "name": "A Whole New World",
          "artist": "Lea Salonga, Brad Kane"
        }
      ]
}

型号如下

class Songs: Object, Codable {
    let playlists = List<Playlists>()
    let tracks = List<Tracks>()
    enum CodingKeys: String, CodingKey {
        case playlists
        case tracks
    }

    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let playLists = try container.decodeIfPresent([Playlists].self, forKey: .playlists){
            playLists.forEach({self.playlists.append([=12=])})
        }
        if let tracksList = try container.decodeIfPresent([Tracks].self, forKey: .tracks){
            tracksList.forEach({self.tracks.append([=12=])})
        }
    }

    func encode(to encoder: Encoder) throws {
        //
    }
}

class Playlists: Object, Codable {
    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    @objc dynamic var duration: Int = 0


    enum CodingKeys: String, CodingKey {
        case id
        case name
        case duration
    }

    override static func primaryKey() -> String? {
        return "id"
    }

    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.duration = try container.decode(Int.self, forKey: .duration)
    }
}

class Tracks: Object, Codable {
    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    @objc dynamic var artist: Int = 0


    enum CodingKeys: String, CodingKey {
        case id
        case name
        case artist
    }

    override static func primaryKey() -> String? {
        return "id"
    }

    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.artist = try container.decode(Int.self, forKey: .artist)
    }
}

这就是我保存数据的方式。

SongsData = try jsonDecoder.decode(Songs.self, from: data)
   let realm = try! Realm()
   try! realm.write {
          realm.add(SongsData)
    } catch {
          Logger.log.printOnConsole(string: "Unable to convert to data")
    }

当服务器响应相同时,如何避免数据重复。

在将对象添加到 List 之前,您可以只使用 Set 删除重复项。只需确保您的类型符合要添加到 Set.

Hashable

一些一般性建议:当 属性 名称与 JSON 键匹配时,您不需要创建 CodingKeys 除非您创建自定义 init(from decoder:) 方法并且您不需要创建自定义 init(from:) 方法,除非您执行一些自定义操作,例如使用 decodeIfPresent 和过滤重复对象。对于 PlaylistsTracks,您可以依赖合成初始化器。

您也不需要在循环中将数组中的元素添加到 List,只需使用 append(objectsIn:),它接受 Sequence 作为其输入参数。

class Songs: Object, Decodable {
    let playlists = List<Playlists>()
    let tracks = List<Tracks>()

    enum CodingKeys: String, CodingKey {
        case playlists, tracks
    }

    required convenience public init(from decoder: Decoder) throws {
        self.init()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let playLists = try container.decodeIfPresent([Playlists].self, forKey: .playlists){
            let uniquePlaylists = Set(playLists)
            self.playlists.append(objectsIn: uniquePlaylists)
        }

        if let tracksList = try container.decodeIfPresent([Tracks].self, forKey: .tracks){
            let uniqueTrackList = Set(tracksList)
            self.tracks.append(objectsIn: uniqueTrackList)
        }
    }
}

class Playlists: Object, Codable, Hashable {
    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    @objc dynamic var duration: Int = 0

    override static func primaryKey() -> String? {
        return "id"
    }

}

class Tracks: Object, Codable, Hashable {
    @objc dynamic var id: String = ""
    @objc dynamic var name: String = ""
    @objc dynamic var artist: Int = 0

    override static func primaryKey() -> String? {
        return "id"
    }
}

如果您想确保不向 Realm 添加任何对象两次,您需要使用 add(_:,update:) 而不是 add 并使用 primaryKey以避免添加具有相同键的元素。

SongsData = try jsonDecoder.decode(Songs.self, from: data)
let realm = try! Realm()
try! realm.write {
      realm.add(SongsData, update: true)
} catch {
      Logger.log.printOnConsole(string: "Unable to convert to data")
}