Search code examples
iosswiftgenericsrealm

Realmswift generic function call crashes when Result<Object> is returned


I have built a generic function to get any kind of data from realm, that seems to work just fine.

 func getData<T: Object>(withFilter: String) -> Results<T>? {
    if !checkRealm(){return nil}
    return realm!.objects(T.self).filter(withFilter)
}

I can't figure out though how to use this function to delete data. My delete function is the following:

    func removeData(withFilter: String) {

        let dataToDelete: Results<Object>? = getData(withFilter: withFilter)
        // *** The above line crashes the app ***

        if let dataToDeleteUnwrapped = dataToDelete{
            try? realm!.write {
                realm!.delete(dataToDeleteUnwrapped)
            }
    }
}

This results to an error, attached bellow. Though Results<Object>? crashes the app, Results<MyCustomObject>? works fine, but then my remove data function is not generic anymore.

Terminating app due to uncaught exception 'RLMException', reason: 'Object type 'RealmSwiftObject' is not managed by the Realm. If using a custom `objectClasses` / `objectTypes` array in your configuration, add `RealmSwiftObject` to the list of `objectClasses` / `objectTypes`.'

I am sure there is a nice short way to solve this one, but I can't figgure it out, so any help is appreciated.


Solution

  • Results cannot hold instances of several classes, all of its elements must be from the same type, so Results<Object> is not a valid type for the collection.

    Your current implementation of getData is wrong. You don't use the generic type T anywhere in your function. Generic types should be given to input arguments for generic functions. You need to call both getData and removeData on a specific Realm type inheriting from Object, since Result can only hold a single type and all your types have unique properties, so you can only use a predicate on a specific type.

    This is the correct generic implementation of getData to query Realm for a specific class with the given predicate.

    func getData<T:Object>(ofType: T.Type, withFilter: String) -> Results<T>? {
        if !checkRealm(){return nil}
        return realm!.objects(T.self).filter(withFilter)
    }
    

    You call it like this (the following example was tested and is working with the playground in the examples project from the Realm website):

    let people = getData(ofType: Person.self, withFilter: "cars.@count > 1 && spouse != nil")
    people?.first?.name //prints Jennifer when called after adding the objects to realm, but before deleting them in the example project
    

    Seeing how a generic function can be used on Realm with type constraints, you should be able to update your deletion function as well.