RLMApp.mm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2020 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMApp_Private.hpp"
  19. #import "RLMBSON_Private.hpp"
  20. #import "RLMCredentials_Private.hpp"
  21. #import "RLMEmailPasswordAuth.h"
  22. #import "RLMPushClient_Private.hpp"
  23. #import "RLMSyncManager_Private.hpp"
  24. #import "RLMUser_Private.hpp"
  25. #import "RLMUtil.hpp"
  26. #import <realm/object-store/sync/sync_manager.hpp>
  27. #import <realm/sync/config.hpp>
  28. #if !defined(REALM_COCOA_VERSION)
  29. #import "RLMVersion.h"
  30. #endif
  31. using namespace realm;
  32. #pragma mark CocoaNetworkTransport
  33. namespace {
  34. /// Internal transport struct to bridge RLMNetworkingTransporting to the GenericNetworkTransport.
  35. class CocoaNetworkTransport : public realm::app::GenericNetworkTransport {
  36. public:
  37. CocoaNetworkTransport(id<RLMNetworkTransport> transport) : m_transport(transport) {};
  38. void send_request_to_server(const app::Request request,
  39. std::function<void(const app::Response)> completion) override {
  40. // Convert the app::Request to an RLMRequest
  41. auto rlmRequest = [RLMRequest new];
  42. rlmRequest.url = @(request.url.data());
  43. rlmRequest.body = @(request.body.data());
  44. NSMutableDictionary *headers = [NSMutableDictionary new];
  45. for (auto header : request.headers) {
  46. headers[@(header.first.data())] = @(header.second.data());
  47. }
  48. rlmRequest.headers = headers;
  49. rlmRequest.method = static_cast<RLMHTTPMethod>(request.method);
  50. rlmRequest.timeout = request.timeout_ms / 1000;
  51. // Send the request through to the Cocoa level transport
  52. [m_transport sendRequestToServer:rlmRequest completion:^(RLMResponse *response) {
  53. __block std::map<std::string, std::string> bridgingHeaders;
  54. [response.headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL *) {
  55. bridgingHeaders[key.UTF8String] = value.UTF8String;
  56. }];
  57. // Convert the RLMResponse to an app:Response and pass downstream to
  58. // the object store
  59. completion({
  60. .body = response.body.UTF8String,
  61. .headers = bridgingHeaders,
  62. .http_status_code = static_cast<int>(response.httpStatusCode),
  63. .custom_status_code = static_cast<int>(response.customStatusCode)
  64. });
  65. }];
  66. }
  67. id<RLMNetworkTransport> transport() const {
  68. return m_transport;
  69. }
  70. private:
  71. id<RLMNetworkTransport> m_transport;
  72. };
  73. }
  74. #pragma mark RLMAppConfiguration
  75. @implementation RLMAppConfiguration {
  76. realm::app::App::Config _config;
  77. }
  78. - (instancetype)initWithConfig:(const realm::app::App::Config &)config {
  79. if (self = [super init]) {
  80. _config = config;
  81. return self;
  82. }
  83. return nil;
  84. }
  85. - (instancetype)initWithBaseURL:(nullable NSString *)baseURL
  86. transport:(nullable id<RLMNetworkTransport>)transport
  87. localAppName:(nullable NSString *)localAppName
  88. localAppVersion:(nullable NSString *)localAppVersion {
  89. return [self initWithBaseURL:baseURL
  90. transport:transport
  91. localAppName:localAppName
  92. localAppVersion:localAppVersion
  93. defaultRequestTimeoutMS:6000];
  94. }
  95. - (instancetype)initWithBaseURL:(nullable NSString *)baseURL
  96. transport:(nullable id<RLMNetworkTransport>)transport
  97. localAppName:(NSString *)localAppName
  98. localAppVersion:(nullable NSString *)localAppVersion
  99. defaultRequestTimeoutMS:(NSUInteger)defaultRequestTimeoutMS {
  100. if (self = [super init]) {
  101. self.baseURL = baseURL;
  102. self.transport = transport;
  103. self.localAppName = localAppName;
  104. self.localAppVersion = localAppVersion;
  105. self.defaultRequestTimeoutMS = defaultRequestTimeoutMS;
  106. _config.platform = "Realm Cocoa";
  107. RLMNSStringToStdString(_config.platform_version, [[NSProcessInfo processInfo] operatingSystemVersionString]);
  108. RLMNSStringToStdString(_config.sdk_version, REALM_COCOA_VERSION);
  109. return self;
  110. }
  111. return nil;
  112. }
  113. - (realm::app::App::Config&)config {
  114. return _config;
  115. }
  116. - (void)setAppId:(NSString *)appId {
  117. RLMNSStringToStdString(_config.app_id, appId);
  118. }
  119. - (NSString *)baseURL {
  120. if (_config.base_url) {
  121. return @(_config.base_url->c_str());
  122. }
  123. return nil;
  124. }
  125. - (void)setBaseURL:(nullable NSString *)baseURL {
  126. std::string base_url;
  127. RLMNSStringToStdString(base_url, baseURL);
  128. _config.base_url = base_url.empty() ? util::none : util::Optional(base_url);
  129. return;
  130. }
  131. - (id<RLMNetworkTransport>)transport {
  132. return static_cast<CocoaNetworkTransport*>(_config.transport_generator().get())->transport();
  133. }
  134. - (void)setTransport:(id<RLMNetworkTransport>)transport {
  135. if (transport) {
  136. _config.transport_generator = [transport]{
  137. return std::make_unique<CocoaNetworkTransport>(transport);
  138. };
  139. } else {
  140. _config.transport_generator = []{
  141. return std::make_unique<CocoaNetworkTransport>([RLMNetworkTransport new]);
  142. };
  143. }
  144. }
  145. - (NSString *)localAppName {
  146. if (_config.local_app_name) {
  147. return @((_config.base_url)->c_str());
  148. }
  149. return nil;
  150. }
  151. - (void)setLocalAppName:(nullable NSString *)localAppName {
  152. std::string local_app_name;
  153. RLMNSStringToStdString(local_app_name, localAppName);
  154. _config.local_app_name = local_app_name.empty() ? util::none : util::Optional(local_app_name);
  155. return;
  156. }
  157. - (NSString *)localAppVersion {
  158. if (_config.local_app_version) {
  159. return @(_config.base_url->c_str());
  160. }
  161. return nil;
  162. }
  163. - (void)setLocalAppVersion:(nullable NSString *)localAppVersion {
  164. std::string local_app_version;
  165. RLMNSStringToStdString(local_app_version, localAppVersion);
  166. _config.local_app_version = local_app_version.empty() ? util::none : util::Optional(local_app_version);
  167. return;
  168. }
  169. - (NSUInteger)defaultRequestTimeoutMS {
  170. return _config.default_request_timeout_ms.value_or(6000U);
  171. }
  172. - (void)setDefaultRequestTimeoutMS:(NSUInteger)defaultRequestTimeoutMS {
  173. _config.default_request_timeout_ms = (uint64_t)defaultRequestTimeoutMS;
  174. }
  175. @end
  176. NSError *RLMAppErrorToNSError(realm::app::AppError const& appError) {
  177. return [[NSError alloc] initWithDomain:@(appError.error_code.category().name())
  178. code:appError.error_code.value()
  179. userInfo:@{
  180. @(appError.error_code.category().name()) : @(appError.error_code.message().data()),
  181. NSLocalizedDescriptionKey : @(appError.message.c_str())
  182. }];
  183. }
  184. #pragma mark RLMAppSubscriptionToken
  185. @implementation RLMAppSubscriptionToken {
  186. @public
  187. std::unique_ptr<realm::Subscribable<app::App>::Token> _token;
  188. }
  189. - (instancetype)initWithToken:(realm::Subscribable<app::App>::Token&&)token {
  190. if (self = [super init]) {
  191. _token = std::make_unique<realm::Subscribable<app::App>::Token>(std::move(token));
  192. return self;
  193. }
  194. return nil;
  195. }
  196. - (NSUInteger)value {
  197. return _token->value();
  198. }
  199. @end
  200. #pragma mark RLMApp
  201. @interface RLMApp() <ASAuthorizationControllerDelegate> {
  202. std::shared_ptr<realm::app::App> _app;
  203. __weak id<RLMASLoginDelegate> _authorizationDelegate API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0));
  204. }
  205. @end
  206. @implementation RLMApp : NSObject
  207. - (instancetype)initWithApp:(std::shared_ptr<realm::app::App>)app {
  208. if (self = [super init]) {
  209. _configuration = [[RLMAppConfiguration alloc] initWithConfig:app->config()];
  210. _app = app;
  211. _syncManager = [[RLMSyncManager alloc] initWithSyncManager:_app->sync_manager()];
  212. return self;
  213. }
  214. return nil;
  215. }
  216. - (instancetype)initWithId:(NSString *)appId
  217. configuration:(RLMAppConfiguration *)configuration
  218. rootDirectory:(NSURL *)rootDirectory {
  219. if ([appId length] == 0) {
  220. @throw RLMException(@"AppId cannot be an empty string");
  221. }
  222. if (self = [super init]) {
  223. if (!configuration) {
  224. configuration = [[RLMAppConfiguration alloc] initWithBaseURL:nil
  225. transport:nil
  226. localAppName:nil
  227. localAppVersion:nil];
  228. }
  229. _configuration = configuration;
  230. [_configuration setAppId:appId];
  231. _app = RLMTranslateError([&] {
  232. return app::App::get_shared_app(configuration.config,
  233. [RLMSyncManager configurationWithRootDirectory:rootDirectory appId:appId]);
  234. });
  235. _syncManager = [[RLMSyncManager alloc] initWithSyncManager:_app->sync_manager()];
  236. return self;
  237. }
  238. return nil;
  239. }
  240. static NSMutableDictionary *s_apps = [NSMutableDictionary new];
  241. static std::mutex& s_appMutex = *new std::mutex();
  242. + (void)resetAppCache {
  243. std::lock_guard<std::mutex> lock(s_appMutex);
  244. [s_apps removeAllObjects];
  245. app::App::clear_cached_apps();
  246. }
  247. + (instancetype)appWithId:(NSString *)appId
  248. configuration:(RLMAppConfiguration *)configuration
  249. rootDirectory:(NSURL *)rootDirectory {
  250. std::lock_guard<std::mutex> lock(s_appMutex);
  251. if (RLMApp *app = s_apps[appId]) {
  252. return app;
  253. }
  254. RLMApp *app = [[RLMApp alloc] initWithId:appId configuration:configuration rootDirectory:rootDirectory];
  255. s_apps[appId] = app;
  256. return app;
  257. }
  258. + (instancetype)appWithId:(NSString *)appId configuration:(RLMAppConfiguration *)configuration {
  259. return [self appWithId:appId configuration:configuration rootDirectory:nil];
  260. }
  261. + (instancetype)appWithId:(NSString *)appId {
  262. return [self appWithId:appId configuration:nil];
  263. }
  264. - (std::shared_ptr<realm::app::App>)_realmApp {
  265. return _app;
  266. }
  267. - (NSDictionary<NSString *, RLMUser *> *)allUsers {
  268. NSMutableDictionary *buffer = [NSMutableDictionary new];
  269. for (auto&& user : _app->sync_manager()->all_users()) {
  270. NSString *identity = @(user->identity().c_str());
  271. buffer[identity] = [[RLMUser alloc] initWithUser:std::move(user) app:self];
  272. }
  273. return buffer;
  274. }
  275. - (RLMUser *)currentUser {
  276. if (auto user = _app->sync_manager()->get_current_user()) {
  277. return [[RLMUser alloc] initWithUser:user app:self];
  278. }
  279. return nil;
  280. }
  281. - (RLMEmailPasswordAuth *)emailPasswordAuth {
  282. return [[RLMEmailPasswordAuth alloc] initWithApp: self];
  283. }
  284. - (void)loginWithCredential:(RLMCredentials *)credentials
  285. completion:(RLMUserCompletionBlock)completionHandler {
  286. auto completion = ^(std::shared_ptr<SyncUser> user, util::Optional<app::AppError> error) {
  287. if (error && error->error_code) {
  288. return completionHandler(nil, RLMAppErrorToNSError(*error));
  289. }
  290. completionHandler([[RLMUser alloc] initWithUser:user app:self], nil);
  291. };
  292. return RLMTranslateError([&] {
  293. return _app->log_in_with_credentials(credentials.appCredentials, completion);
  294. });
  295. }
  296. - (RLMUser *)switchToUser:(RLMUser *)syncUser {
  297. return RLMTranslateError([&] {
  298. return [[RLMUser alloc] initWithUser:_app->switch_user(syncUser._syncUser) app:self];
  299. });
  300. }
  301. - (RLMPushClient *)pushClientWithServiceName:(NSString *)serviceName {
  302. return RLMTranslateError([&] {
  303. return [[RLMPushClient alloc] initWithPushClient:_app->push_notification_client(serviceName.UTF8String)];
  304. });
  305. }
  306. #pragma mark - Sign In With Apple Extension
  307. - (void)setAuthorizationDelegate:(id<RLMASLoginDelegate>)authorizationDelegate API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0)) {
  308. _authorizationDelegate = authorizationDelegate;
  309. }
  310. - (id<RLMASLoginDelegate>)authorizationDelegate API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0)) {
  311. return _authorizationDelegate;
  312. }
  313. - (void)setASAuthorizationControllerDelegateForController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0)) {
  314. controller.delegate = self;
  315. }
  316. - (void)authorizationController:(__unused ASAuthorizationController *)controller
  317. didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0)) {
  318. NSString *jwt = [[NSString alloc] initWithData:((ASAuthorizationAppleIDCredential *)authorization.credential).identityToken
  319. encoding:NSUTF8StringEncoding];
  320. [self loginWithCredential:[RLMCredentials credentialsWithAppleToken:jwt]
  321. completion:^(RLMUser *user, NSError *error) {
  322. if (user) {
  323. [self.authorizationDelegate authenticationDidCompleteWithUser:user];
  324. } else {
  325. [self.authorizationDelegate authenticationDidCompleteWithError:error];
  326. }
  327. }];
  328. }
  329. - (void)authorizationController:(__unused ASAuthorizationController *)controller
  330. didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0)) {
  331. [self.authorizationDelegate authenticationDidCompleteWithError:error];
  332. }
  333. - (RLMAppSubscriptionToken *)subscribe:(RLMAppNotificationBlock)block {
  334. return [[RLMAppSubscriptionToken alloc] initWithToken:_app->subscribe([block, self] (auto&) {
  335. block(self);
  336. })];
  337. }
  338. - (void)unsubscribe:(RLMAppSubscriptionToken *)token {
  339. return _app->unsubscribe(*token->_token);
  340. }
  341. @end