How do I call MongoDb Realm functions from iOS?

I read the documentation here https://docs.mongodb.com/realm/ios/call-a-function/ but I still don’t understand how to call a mongodb function from realm.

  • I am supposed to create a BSONArray to pass in arguments. How do I do this? Is there any documentation on how to use BSONArrays from swift? If so, where?
  • Is functions only available on iOS 13+? I get a compiler error that “subscript(dynamicMember)” is only available on iOS 13. If that is the case, shouldn’t this be listed in the documentation?

It is a new platform, so I understand that documentation is still a bit lacking, but it would be awesome to be able to use cloud functions now that the swift SDK is out of beta :wink:

1 Like

The usage in the guide is pretty close and you need to be running macOS 10.15 or higher, iOS 13+ Swift 4.2 or higher

using this function called ‘sum’ set up in the console

exports = function(a, b) {
  return a + b;
};

here’s the calling code (note I am not using let user = app.currentUser!)

guard let user = your_app.currentUser() else {
   fatalError("Logged out?")
}

user.functions.sum([1, 2]) { sum, error in
    guard error == nil else {
        print("Function call failed: \(error!.localizedDescription)")
        return
    }
    guard case let .double(value) = sum else {
        print("Unexpected non-double result: \(sum ?? "nil")");
        return
    }
    print("Called function 'sum' and got result: \(value)")
    assert(value == 3)
}

Thanks I did read the documentation. The sample works. But lacks context and there are cases that are not as easy as passing in an int array.

Example: I have an analytics function that I use to track events that I want to forward to MongoDb Realm via a cloud function.

   func trackEvent(eventId:String, eventParams:[String:String]){
        
        guard let user = app.currentUser else {
            return
        }

        //Hardcoding the events works just like the sample
        user.functions.trackEvent(["hardCodedEventId",["hardCodedParam1":"hardCodedParamValue1"]]){ result, error in
            
        }
        
        //This will result in a confusing compiler error "Cannot convert value of type '[String : String]' t expected element 
        //type 'Array<AnyBSON>.ArrayLiteralElement' (aka 'AnyBSON"
     
        user.functions.sum([eventId, eventParams]) { result, error in
            
        }
        
        //To get the results I want I have to do this
        var propertyArgs = Document()
        properties.forEach { (key, value) in
            propertyArgs[key] = AnyBSON(value)
        }
        user.functions.trackEvent([AnyBSON(eventId), AnyBSON(propertyArgs)]) { result, error in 

        }
    }

I could not figure out how to do this based on the documentation, instead I had to dig through the mongodb realm source code. Also, there is no mention that functions does not work for iOS 12. It is mentioned that you need to pass in a BsonArray but no link to documentation to show you what that is and how you convert simple values.

I figured it out now, but I definitely think docs could be improved here.

2 Likes

After also trying out the Kotlin version, I have additional feedback. The kotlin example is also lacking. It is using the same “sum” example with a list of integers. What about more complex arguments? The AnyBson class used in Swift does not seem to exist in Kotlin instead there is something called a CodecRegistry, that I guess is supposed to be used to endcode/decode more complex objects, but this isn’t even mentioned in the documentation. Why isn’t there an example of this?

Also, I don’t understand why the API:s are designed differently on Swift/Kotlin.

Swift:
user.functions.myMethod([argumentsOfAnyBsonType]) { result, error in
}

Kotlin:
val functionsManager = realmApp.getFunctions(user)
user.callFunctionAsync(“myMethod”, listOf(argumentsOfAnyType), Integer::class.java) { result ->
}

I strongly prefer the kotlin method as the swift version uses dynamic member lookup which breaks code completion, but I guess this is a matter of preference. But the differences in API:s makes it hard to port code from one platform to the other.

Were you able to find a resolution to this? Can you pass in arrays of non-primitive type as function arguments?

I haven’t looked into this anymore and I am still not using MongoDb Realm in production. But it looks like you need to convert your arguments to an array of AnyBson