Search code examples
iosswiftrealm

How would one create a List<AnyRealmObject> in Swift?


OK, first, I know that there is no such thing as AnyRealmObject.

But I have a need to have something the behaves just like a Realm List, with the exception that any kind of Realm Object can be added to the list -- they don't all have to be the same type.

Currently, I have something like this:

enter code here
class Family: Object {
   var pets: List<Pet>
}

class Pet: Object {
    var dog: Dog?
    var cat: Cat?
    var rabbit: Rabbit?
}

Currently, if I wanted to add in, say, Bird, I'd have to modify the Pet object. I don't want to keep modifying that class.

What I really want to do is this:

class Family: Object {
  var pets: List<Object>
}

Or, maybe, define a Pet protocol, that must be an Object, and have var pets: List<Pet>

The point is, I want a databag that can contain any Realm Object that I pass into it. The only requirement for the databag is that the objects must be Realm Objects.

Now, since Realm doesn't allow for this, how could I do this, anyway? I was thinking of creating something like a Realm ObjectReference class:

class ObjectReference: Object {
   var className: String
   var primaryKeyValue: String

   public init(with object: Object) {
      className = ???
      primaryKeyValue = ???
   }

   public func object() -> Object? {
      guard let realm = realm else { return nil }
      var type = ???
      var primaryKey: AnyObject = ???
      return realm.object(ofType: type, forPrimaryKey: primaryKey)(
   }
}

The stuff with the ??? is what I'm asking about. If there's a better way of doing this I'm all ears. I think my approach is ok, I just don't know how to fill in the blanks, here.


Solution

  • (I'm assuming that you are writing an application, and that the context of the code samples and problem you provided is in terms of application code, not creating a library.)

    Your approach seems to be a decent one given Realm's current limitations; I can't think of anything better off the top of my head. You can use NSClassFromString() to turn your className string into a Swift metaclass object you can use with the object(ofType:...) API:

    public func object() -> Object? {
        let applicationName = // (application name goes here)
    
        guard let realm = realm else { return nil }
        guard let type = NSClassFromString("\(applicationName).\(className)") as? Object.Type else {
            print("Error: \(className) isn't the name of a Realm class.")
            return nil
        }
        var primaryKey: String = primaryKeyValue
        return realm.object(ofType: type, forPrimaryKey: primaryKey)(
    }
    

    My recommendation is that you keep things simple and use strings exclusively as primary keys. If you really need to be able to use arbitrary types as primary keys you can take a look at our dynamic API for ideas as to how to extract the primary key value for a given object. (Note that although this API is technically a public API we don't generally offer support for it nor do we encourage its use except when the typed APIs are inadequate.)

    In the future, we hope to offer enhanced support for subclassing and polymorphism. Depending on how this feature is designed, it might allow us to introduce APIs to allow subclasses of a parent object type to be inserted into a list (although that poses its own problems).