how to avoid duplications in array of custom objects in realm Database, Below is my code and related JSON Please correct me if I do something wrong in the model.
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"
}
]
}
and Models are as follow
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)
}
}
and this is how I save the data.
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")
}
How to avoid duplications into the data when there is same response from server.
You can just use Set
to get rid of the duplicates before adding your objects to a List
. Just make sure you make your types conform to Hashable
that you want to add to a Set
.
Some general advice: you don't need to create CodingKeys
when the property names match the JSON keys unless you create a custom init(from decoder:)
method and you don't need to create a custom init(from:)
method unless you do some custom stuff, like use decodeIfPresent
and filter duplicate objects. For Playlists
and Tracks
, you can rely on the synthetised initializer.
You also don't need to add elements from an array to a List
in a loop, just use append(objectsIn:)
, which accepts a Sequence
as its input argument.
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"
}
}
If you want to make sure you don't add any objects twice to Realm
, you need to use add(_:,update:)
instead of add
and use the primaryKey
to avoid adding elements with the same keys.
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")
}