Избегайте дублирования в массиве пользовательских объектов в базе данных области.Когда Первичный ключ существует только в реляционной таблице - PullRequest
0 голосов
/ 22 марта 2019

как избежать дублирования в массиве пользовательских объектов в базе данных области. Ниже приведен мой код и связанный 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($0)})
        }
        if let tracksList = try container.decodeIfPresent([Tracks].self, forKey: .tracks){
            tracksList.forEach({self.tracks.append($0)})
        }
    }

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

Как избежать дублирования в данных при одинаковом ответе от сервера.

1 Ответ

0 голосов
/ 22 марта 2019

Вы можете просто использовать Set, чтобы избавиться от дубликатов, прежде чем добавлять свои объекты в List.Просто убедитесь, что ваши типы соответствуют Hashable, который вы хотите добавить к Set.

Несколько общих советов: вам не нужно создавать CodingKeys, когда имена свойств соответствуют JSONключи, если вы не создадите пользовательский метод init(from decoder:), и вам не нужно создавать пользовательский метод init(from:), если вы не будете выполнять какие-то пользовательские действия, например, использовать decodeIfPresent и фильтровать дублирующиеся объекты.Для Playlists и Tracks вы можете положиться на синтезированный инициализатор.

Вам также не нужно добавлять элементы из массива в 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")
}
...