Skip to content

Commit

Permalink
Fix the type signature of async User.functions
Browse files Browse the repository at this point in the history
The async and Future versions of this were incorrectly defined as taking
exactly one argument which had to be an array of BSON values rather than any
number of BSON arguments.
  • Loading branch information
tgoyne committed Aug 20, 2024
1 parent 9eeb64b commit 7a1f377
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ x.y.z Release notes (yyyy-MM-dd)
non-null return value despite always returning `nil` (since v10.29.0).
* Eliminate several clang static analyzer warnings which did not report actual
bugs.
* The async and Future versions of `User.functions` only worked for functions
which took exactly one argument, which had to be an array ([#8669](https://github.com/realm/realm-swift/issues/8669), since 10.16.0).

### Compatibility
* Realm Studio: 15.0.0 or later.
Expand Down
2 changes: 1 addition & 1 deletion Realm/ObjectServerTests/AsyncSyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ class AsyncAwaitSyncTests: SwiftSyncTestCase {

func testUserCallFunctionAsyncAwait() async throws {
let user = try await self.app.login(credentials: basicCredentials())

Check notice on line 352 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_16 beta 4 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L352

Sending main actor-isolated value of type 'Credentials' with later accesses to nonisolated context risks causing data races; this is an error in the Swift 6 language mode
guard case let .int32(sum) = try await user.functions.sum([1, 2, 3, 4, 5]) else {
guard case let .int32(sum) = try await user.functions.sum(.array([1, 2, 3, 4, 5])) else {
return XCTFail("Should be int32")
}
XCTAssertEqual(sum, 15)
Expand Down
2 changes: 1 addition & 1 deletion Realm/ObjectServerTests/SwiftObjectServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
let credentials = Credentials.emailPassword(email: email, password: password)
let syncUser = app.login(credentials: credentials).await(self)

let bson = syncUser.functions.sum([1, 2, 3, 4, 5]).await(self)
let bson = syncUser.functions.sum(.array([1, 2, 3, 4, 5])).await(self)
guard case let .int32(sum) = bson else {
XCTFail("unexpected bson type in sum: \(bson)")
return
Expand Down
38 changes: 31 additions & 7 deletions RealmSwift/Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -667,19 +667,33 @@ public struct FunctionCallable: Sendable {
fileprivate let name: String
fileprivate let user: User

@available(*, deprecated, message: "Explicitly specify .array(arg)")
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public func dynamicallyCall(withArguments args: [[AnyBSON]]) -> Future<AnyBSON, Error> {
return future { promise in
let objcArgs = args.first!.map(ObjectiveCSupport.convertBson)
self.user.__callFunctionNamed(name, arguments: objcArgs) { (bson: RLMBSON?, error: Error?) in
if let b = bson.map(ObjectiveCSupport.convertBson), let bson = b {
promise(.success(bson))
} else {
promise(.failure(error ?? Realm.Error.callFailed))
}
}
}
}

/// The implementation of @dynamicCallable that allows for `Future<AnyBSON, Error>` callable return.
///
/// let cancellable = user.functions.sum([1, 2, 3, 4, 5])
/// let cancellable = user.functions.sum(.array([1, 2, 3, 4, 5]))
/// .sink(receiveCompletion: { result in
/// }, receiveValue: { value in
/// // Returned value from function
/// })
///
@preconcurrency
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public func dynamicallyCall(withArguments args: [[AnyBSON]]) -> Future<AnyBSON, Error> {
public func dynamicallyCall(withArguments args: [AnyBSON]) -> Future<AnyBSON, Error> {
return future { promise in
let objcArgs = args.first!.map(ObjectiveCSupport.convertBson)
let objcArgs = args.map(ObjectiveCSupport.convertBson)
self.user.__callFunctionNamed(name, arguments: objcArgs) { (bson: RLMBSON?, error: Error?) in
if let b = bson.map(ObjectiveCSupport.convertBson), let bson = b {
promise(.success(bson))
Expand Down Expand Up @@ -1134,14 +1148,24 @@ public extension User {

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension FunctionCallable {
@available(*, deprecated, message: "Explicitly specify .array(arg)")
public func dynamicallyCall(withArguments args: [[AnyBSON]]) async throws -> AnyBSON {
let objcArgs = args.first!.map(ObjectiveCSupport.convertBson)
let ret = try await user.__callFunctionNamed(name, arguments: objcArgs)
if let bson = ObjectiveCSupport.convertBson(object: ret) {
return bson
}
throw Realm.Error.callFailed
}

/// The implementation of @dynamicMemberLookup that allows for `async await` callable return.
///
/// guard case let .int32(sum) = try await user.functions.sum([1, 2, 3, 4, 5]) else {
/// guard case let .int32(sum) = try await user.functions.sum(.array([1, 2, 3, 4, 5])) else {
/// return
/// }
///
public func dynamicallyCall(withArguments args: [[AnyBSON]]) async throws -> AnyBSON {
let objcArgs = args.first!.map(ObjectiveCSupport.convertBson)
public func dynamicallyCall(withArguments args: [AnyBSON]) async throws -> AnyBSON {
let objcArgs = args.map(ObjectiveCSupport.convertBson)
let ret = try await user.__callFunctionNamed(name, arguments: objcArgs)
if let bson = ObjectiveCSupport.convertBson(object: ret) {
return bson
Expand Down

0 comments on commit 7a1f377

Please sign in to comment.