0

In my app I load stored data from Realm and then make a call to the API to fetch the most current state of user's objects. After that I filter which objects are new and which should be deleted, like that:

// tracks is an array of objects downloaded from API and passed to the method
    let result = realm.objects(RealmTrack.self)

    print("Discovering new tracks")
    // Discover new tracks
    let new = tracks.filter({ track in
        !result.contains(where: { $0.trackUid == track.trackUid })
    })

    print("Discovering old tracks")
    // Discover old tracks in database
    let old = result.filter({ track in
        !tracks.contains(where: { $0.trackUid == track.trackUid })
    })

Then I'm performing a write transaction to realm, it goes well without any errors.

    print("Modifying database")
        try realm.write {
            //realm.create(RealmTrack.self, value: realms, update: .modified)
            realm.add(realms, update: Realm.UpdatePolicy.modified)
            //realm.add(realms)
            realm.delete(old)
        }

My problem is that each RealmTrack can have other associated Realm objects:

dynamic var _primaryAlbum: RealmAlbum?

dynamic var _primaryArtist: RealmArtist?

And both RealmAlbum and RealmArtist have a list of RealmTrack objects:

let tracks = List<RealmTrack>()

After deleting old tracks, how can I get remaining RealmAlbum and RealmArtist which don't have any associated RealmTrack's with them? I have to delete them as well

What I tried is just after the write transaction block, check which objects have empty list, but surprisingly it's always 0:

//realm.objects(RealmAlbum.self).count always returns 0 here
        let invalidatedAlbums = realm.objects(RealmAlbum.self).filter {
            $0.tracks.count == 0
            //$0.isInvalidated == true
        }

        let invalidatedArtists = realm.objects(RealmArtist.self).filter {
            $0.tracks.count == 0
            //$0.isInvalidated == true
        }

        try realm.write {
            realm.delete(invalidatedAlbums)
            realm.delete(invalidatedArtists)
        }

EDIT: Added class declarations

RealmTrack

@objcMembers class RealmTrack: Object, AWTrack {

        dynamic var _source: String = ""
        var source: AWMediaSource {
            return AWMediaSource(rawValue: _source)!
        }

        dynamic var _trackName: String = ""
        var trackName: String {
            return _trackName
        }

        dynamic var _trackUid: String = ""
        var trackUid: String {
            return _trackUid
        }

        dynamic var _playableUrl: String? = ""
        var playableUrl: String? {
            return _playableUrl
        }

        dynamic var _duration: TimeInterval = 0
        var duration: TimeInterval {
            return _duration
        }

        dynamic var _albumTrackNumber: Int = 0
        var albumTrackNumber: Int {
            return _albumTrackNumber
        }

        dynamic var _localizedTitle: String = ""
        var localizedTitle: String {
            return _localizedTitle
        }

        dynamic var _localizedAlbumTitle: String = ""
        var localizedAlbumTitle: String {
            return _localizedAlbumTitle
        }

        dynamic var _localizedAlbumArtist: String = ""
        var localizedAlbumArtist: String {
            return _localizedAlbumArtist
        }

        dynamic var _localizedArtist: String = ""
        var localizedArtist: String {
            return _localizedArtist
        }

        dynamic var _albumUid: String = ""
        var albumUid: String {
            return _albumUid
        }

        dynamic var _artistUid: String = ""
        var artistUid: String {
            return _artistUid
        }

        dynamic var _primaryAlbum: RealmAlbum?
        var primaryAlbum: AWAlbum? {
            return _primaryAlbum
        }

        dynamic var _primaryArtist: RealmArtist?
        var primaryArtist: AWArtist? {
            return _primaryArtist
        }

        override class func primaryKey() -> String? {
            return "_trackUid"
        }

RealmAlbum

@objcMembers class RealmAlbum: Object, AWAlbum {

dynamic var _source: String = ""
var source: AWMediaSource {
    return AWMediaSource(rawValue: _source)!
}

dynamic var _albumName: String = ""
var albumName: String {
    return _albumName
}

dynamic var _albumUid: String = ""
var albumUid: String {
    return _albumUid
}

dynamic var _artistName: String = ""
var artistName: String {
    return _artistName
}

let tracks = List<RealmTrack>()

dynamic var _primaryArtist: RealmArtist?
var primaryArtist: AWArtist? {
    return _primaryArtist
}

override class func primaryKey() -> String? {
    return "_albumUid"
}

RealmArtist

@objcMembers class RealmArtist: Object, AWArtist {

dynamic var _source: String = ""
var source: AWMediaSource {
    return AWMediaSource(rawValue: _source)!
}

dynamic var _artistUid: String = ""
var artistUid: String {
    return _artistUid
}

dynamic var _artistName: String = ""
var artistName: String {
    return _artistName
}


let tracks = List<RealmTrack>()

override class func primaryKey() -> String? {
    return "_artistUid"
}
Adam
  • 1,776
  • 1
  • 17
  • 28
  • Have you tried linking objects to tracks from RealmAlbum and RealmArtist? – EpicPandaForce Jul 08 '19 at 12:12
  • I was convinced that this is done automatically by creating a List of objects in RealmAlbum or RealmArtist – Adam Jul 08 '19 at 12:16
  • In that case, you don't have enough of your Realm schema here to give you a conclusive answer. Please add RealmAlbum, RealmArtist and RealmTrack classes. Technically, just knowing which one links to which (and using what type of link) is sufficient. – EpicPandaForce Jul 08 '19 at 12:40
  • I have added class declarations to the question – Adam Jul 08 '19 at 12:51
  • Thanks. It's a tricky situation. In this scenario, your RealmAlbum/RealmArtist has a List of tracks; it's the `primaryAlbum`/`primaryArtist` that you could back out using LinkingObjects. What you CAN do however (as you have `primaryAlbum` and `primaryArtist` fields), is that you create a LinkingObjects by `primaryAlbum` in album, and by `primaryArtist` in artist; and you can create a query to check the count of these linking objects. If there is no track pointing by primary* to this artist/album, then there is no track associated with it. Assuming 1 track always belongs to 1 artist / 1 album. – EpicPandaForce Jul 08 '19 at 13:44
  • The first issue I am seeing is in your initial statement; *fetch the most current state of user's objects* and then *filter for.. new and which should be deleted*. Those statements are really not how Realm works. While you *can* do that, it's defeating the essence of Realm - which are live, updating objects that are event driven. In other words, if a realm object is new, Realm will automatically tell you app about it. If it changes are is deleted, Realm will tell you about that event. If an object changes (tracks added or removed) it will let you know. – Jay Jul 08 '19 at 19:45
  • @Jay I was referring to what I was doing in https://stackoverflow.com/a/39352718/2413303 (the first EDIT section) except that was in Android. – EpicPandaForce Jul 15 '19 at 16:04
  • @EpicPandaForce I see. The OP has not responded so maybe they resolved their issue but it seems like they want to delete RealmAlbums and Artists that have no tracks associated with them - which is what my answer to this question will help accomplish. However, I may have misunderstood the question. – Jay Jul 15 '19 at 16:12

1 Answers1

0

The full scope of the question is a bit unclear to me so I may not be fully understanding what is being asked so let me attempt to answer this question:

How can I query for objects that have nothing in a list property.

If you have an object that has a list property of other objects like this:

class PersonClass: Object {
   @objc dynamic var person_name = ""
   let dogs = List<DogClass>()
}

class DogClass: Object {
   @objc dynamic var dog_name = ""
}

and suppose you have a person with two dogs in their list and then a person with no dogs in their list. You goal is to query for all people that have no dogs in their list.

let personResults = realm.objects(PersonClass.self).filter("dogs.@count == 0")

@count is an aggregate expression supported on List and Results properties.

Jay
  • 34,438
  • 18
  • 52
  • 81