Skip to content

Commit

Permalink
RCOCOA-2289: Update Base url API (#8560)
Browse files Browse the repository at this point in the history
* Add support for updating Atlas Device Sync's base url.

* Solve PR comments

* Solve PR comments and solve private API issues

* Solve PR comments

* Don't wrap callback

---------

Co-authored-by: Nikola Irinchev <[email protected]>
  • Loading branch information
dianaafanador3 and nirinchev committed May 2, 2024
1 parent 3d12581 commit 771f922
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 9 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ store. Xcode 15.1 is now the minimum supported version.
([Core #7552](https://github.com/realm/realm-core/pull/7552)).
* Improve perfomance of IN queries and chained OR equality queries for
UUID/ObjectId types. ([.Net #3566](https://github.com/realm/realm-dotnet/issues/3566))
* Added support for updating Atlas Device Sync's base url, in case the need to roam between
servers (cloud and/or edge server). This API is private and can only be imported using
`@_spi(Private)`
```swift
@_spi(RealmSwiftExperimental) import RealmSwift

try await app.updateBaseUrl(to: "https://services.cloud.mongodb.com")
```
([#8486](https://github.com/realm/realm-swift/issues/8486)).
* Enable building RealmSwift as a dynamic framework when installing via SPM, which
lets us supply a privacy manifest. When RealmSwift is built as a static
library you must supply your own manifest, as Xcode does not build static
Expand Down
17 changes: 16 additions & 1 deletion Realm/ObjectServerTests/AsyncSyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import Realm
import Realm.Private
import RealmSwift
@_spi(RealmSwiftExperimental) import RealmSwift
import XCTest

#if canImport(RealmTestSupport)
Expand Down Expand Up @@ -78,6 +78,21 @@ class AsyncAwaitSyncTests: SwiftSyncTestCase {
}
}

func testUpdateBaseUrl() async throws {
let app = App(id: appId)
XCTAssertNotNil(app.baseURL)
XCTAssertEqual(app.baseURL, "http://localhost:9090")

try await app.updateBaseUrl(to: "http://localhost:8080")
XCTAssertEqual(app.baseURL, "http://localhost:8080")

try await app.updateBaseUrl(to: "http://localhost:7070/")
XCTAssertEqual(app.baseURL, "http://localhost:7070")

try await app.updateBaseUrl(to: nil)
XCTAssertEqual(app.baseURL, "https://services.cloud.mongodb.com")
}

@MainActor func testAsyncOpenStandalone() async throws {
try autoreleasepool {
let configuration = Realm.Configuration(objectTypes: [SwiftPerson.self])
Expand Down
26 changes: 26 additions & 0 deletions Realm/ObjectServerTests/RLMObjectServerTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,32 @@ - (NSArray *)defaultObjectTypes {

#pragma mark - Authentication and Tokens

- (void)testUpdateBaseUrl {
RLMApp *app = self.app;
XCTAssertEqual(app.baseURL, @"http://localhost:9090");

XCTestExpectation *expectation = [self expectationWithDescription:@"should update base url"];
[app updateBaseURL:@"http://localhost:8080" completion:^(NSError *error) {
XCTAssertNil(error);
[expectation fulfill];
}];
XCTAssertEqual(app.baseURL, @"http://localhost:8080");

XCTestExpectation *expectation1 = [self expectationWithDescription:@"should update base url"];
[app updateBaseURL:@"http://localhost:7070/" completion:^(NSError *error) {
XCTAssertNil(error);
[expectation1 fulfill];
}];
XCTAssertEqual(app.baseURL, @"http://localhost:7070");

XCTestExpectation *expectation2 = [self expectationWithDescription:@"should update base url to default value"];
[app updateBaseURL:nil completion:^(NSError *error) {
XCTAssertNil(error);
[expectation2 fulfill];
}];
XCTAssertEqual(app.baseURL, @"https://services.cloud.mongodb.com");
}

- (void)testAnonymousAuthentication {
RLMUser *syncUser = self.anonymousUser;
RLMUser *currentUser = [self.app currentUser];
Expand Down
41 changes: 40 additions & 1 deletion Realm/ObjectServerTests/SwiftObjectServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import Combine
import Realm
import Realm.Private
import RealmSwift
@_spi(RealmSwiftExperimental) import RealmSwift
import XCTest

#if canImport(RealmTestSupport)
Expand Down Expand Up @@ -62,6 +62,45 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
]
}

func testUpdateBaseUrl() {
let app = App(id: appId)
XCTAssertNotNil(app.baseURL)
XCTAssertEqual(app.baseURL, "http://localhost:9090")

let ex = expectation(description: "update base url")
app.updateBaseUrl(to: "http://localhost:8080", { result in
switch result {
case .success:
ex.fulfill()
case .failure:
XCTFail("Should not return an error")
}
})
XCTAssertEqual(app.baseURL, "http://localhost:8080")

let ex1 = expectation(description: "update base url")
app.updateBaseUrl(to: "http://localhost:7070/", { result in
switch result {
case .success:
ex1.fulfill()
case .failure:
XCTFail("Should not return an error")
}
})
XCTAssertEqual(app.baseURL, "http://localhost:7070")

let ex2 = expectation(description: "update base url")
app.updateBaseUrl(to: nil, { result in
switch result {
case .success:
ex2.fulfill()
case .failure:
XCTFail("Should not return an error")
}
})
XCTAssertEqual(app.baseURL, "https://services.cloud.mongodb.com")
}

func testBasicSwiftSync() throws {
XCTAssert(try openRealm().isEmpty, "Freshly synced Realm was not empty...")
}
Expand Down
19 changes: 19 additions & 0 deletions Realm/RLMApp.mm
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,25 @@ - (RLMEmailPasswordAuth *)emailPasswordAuth {
return [[RLMEmailPasswordAuth alloc] initWithApp:_app];
}

- (NSString *)baseUrl {
return getOptionalString(_app->get_base_url()) ?: RLMStringViewToNSString(_app->default_base_url());
}

- (void)updateBaseURL:(NSString * _Nullable)baseURL completion:(nonnull RLMOptionalErrorBlock)completionHandler {
auto completion = ^(std::optional<app::AppError> error) {
if (error) {
return completionHandler(makeError(*error));
}

completionHandler(nil);
};
return RLMTranslateError([&] {
NSString *url = (baseURL ?: @"");
NSString *newUrl = [url stringByReplacingOccurrencesOfString:@"/" withString:@"" options:0 range:NSMakeRange(url.length-1, 1)];
return _app->update_base_url(newUrl.UTF8String, completion);
});
}

- (void)loginWithCredential:(RLMCredentials *)credentials
completion:(RLMUserCompletionBlock)completionHandler {
auto completion = ^(std::shared_ptr<app::User> user, std::optional<app::AppError> error) {
Expand Down
9 changes: 9 additions & 0 deletions Realm/RLMApp_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ typedef void(^RLMAppNotificationBlock)(RLMApp *);
@end

@interface RLMApp ()
/// A custom base URL to request against. If not set or set to nil, the default base url for app services will be returned.
@property (nonatomic, readonly) NSString *baseURL;
/// Returns all currently cached Apps
+ (NSArray<RLMApp *> *)allApps;
/// Subscribe to notifications for this RLMApp.
Expand All @@ -40,6 +42,13 @@ typedef void(^RLMAppNotificationBlock)(RLMApp *);
+ (RLMApp *_Nullable)cachedAppWithId:(NSString *)appId;

+ (void)resetAppCache;

/// Updates the base url used by Atlas device sync, in case the need to roam between servers (cloud and/or edge server).
/// @param baseURL The new base url to connect to. Setting `nil` will reset the base url to the default url.
/// @note Updating the base URL would trigger a client reset.
- (void)updateBaseURL:(NSString *_Nullable)baseURL
completion:(RLMOptionalErrorBlock)completionHandler NS_REFINED_FOR_SWIFT;

@end

@interface RLMAppConfiguration ()
Expand Down
55 changes: 49 additions & 6 deletions RealmSwift/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ let credentials = Credentials.JWT(token: myToken)
public typealias App = RLMApp

public extension App {
/**
Updates the base url used by Atlas device sync, in case the need to roam between servers (cloud and/or edge server).
- parameter url: The new base url to connect to. Setting `nil` will reset the base url to the default url.
- parameter completion: A callback invoked after completion.
- note: Updating the base URL would trigger a client reset.
*/
@preconcurrency
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@_spi(RealmSwiftExperimental) func updateBaseUrl(to url: String?, _ completion: @Sendable @escaping (Error?) -> Void) {
self.__updateBaseURL(url, completion: completion)
}

/**
Login to a user for the Realm app.
Expand All @@ -237,21 +249,52 @@ public extension App {
}
}

/// Login to a user for the Realm app.
/// @param credentials The credentials identifying the user.
/// @returns A publisher that eventually return `User` or `Error`.
/**
Updates the base url used by Atlas device sync, in case the need to roam between servers (cloud and/or edge server).
- parameter url: The new base url to connect to. Setting `nil` will reset the base url to the default url.
- parameter completion: A callback invoked after completion. Will return `Result.success` or `Result.failure(Error)`.
- note: Updating the base URL would trigger a client reset.
*/
@preconcurrency
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@_spi(RealmSwiftExperimental) func updateBaseUrl(to url: String?, _ completion: @Sendable @escaping (Result<Void, Error>) -> Void) {
self.__updateBaseURL(url, completion: { error in
if let error = error {
completion(.failure(error))
} else {
completion(.success(()))
}
})
}

/**
Login to a user for the Realm app.
- parameter credentials: The credentials identifying the user.
- returns: A publisher that eventually return `User` or `Error`.
*/
@available(macOS 10.15, watchOS 6.0, iOS 13.0, tvOS 13.0, *)
func login(credentials: Credentials) -> Future<User, Error> {
return future { self.login(credentials: credentials, $0) }
}

/// Login to a user for the Realm app.
/// @param credentials The credentials identifying the user.
/// @returns A publisher that eventually return `User` or `Error`.
/**
Login to a user for the Realm app.
- parameter credentials: The credentials identifying the user.
*/
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
func login(credentials: Credentials) async throws -> User {
try await __login(withCredential: ObjectiveCSupport.convert(object: credentials))
}

/**
Updates the base url used by Atlas device sync, in case the need to roam between servers (cloud and/or edge server).
- parameter url: The new base url to connect to. Setting `nil` will reset the base url to the default url.
- note: Updating the base URL would trigger a client reset.
*/
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@_spi(RealmSwiftExperimental) func updateBaseUrl(to url: String?) async throws {
try await __updateBaseURL(url)
}
}

/// Use this delegate to be provided a callback once authentication has succeed or failed
Expand Down
2 changes: 1 addition & 1 deletion dependencies.list
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VERSION=10.49.2
REALM_CORE_VERSION=v14.6.0-7-gb2bb4f550
STITCH_VERSION=8bf8ebcff6e804586c30a6ccbadb060753071a42
STITCH_VERSION=40f356fbaf61e8df4f897e58357cd6a2c211825a

0 comments on commit 771f922

Please sign in to comment.