RLMSyncSession.mm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 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 "RLMSyncSession_Private.hpp"
  19. #import "RLMRealm_Private.hpp"
  20. #import "RLMSyncConfiguration_Private.hpp"
  21. #import "RLMSyncUser_Private.hpp"
  22. #import "RLMSyncUtil_Private.hpp"
  23. #import "sync/async_open_task.hpp"
  24. #import "sync/sync_session.hpp"
  25. using namespace realm;
  26. @interface RLMSyncErrorActionToken () {
  27. @public
  28. std::string _originalPath;
  29. BOOL _isValid;
  30. }
  31. @end
  32. @interface RLMProgressNotificationToken() {
  33. uint64_t _token;
  34. std::weak_ptr<SyncSession> _session;
  35. }
  36. @end
  37. @implementation RLMProgressNotificationToken
  38. - (void)suppressNextNotification {
  39. // No-op, but implemented in case this token is passed to
  40. // `-[RLMRealm commitWriteTransactionWithoutNotifying:]`.
  41. }
  42. - (void)invalidate {
  43. if (auto session = _session.lock()) {
  44. session->unregister_progress_notifier(_token);
  45. _session.reset();
  46. _token = 0;
  47. }
  48. }
  49. - (void)dealloc {
  50. if (_token != 0) {
  51. NSLog(@"RLMProgressNotificationToken released without unregistering a notification. "
  52. @"You must hold on to the RLMProgressNotificationToken and call "
  53. @"-[RLMProgressNotificationToken invalidate] when you no longer wish to receive "
  54. @"progress update notifications.");
  55. }
  56. }
  57. - (nullable instancetype)initWithTokenValue:(uint64_t)token
  58. session:(std::shared_ptr<SyncSession>)session {
  59. if (token == 0) {
  60. return nil;
  61. }
  62. if (self = [super init]) {
  63. _token = token;
  64. _session = session;
  65. return self;
  66. }
  67. return nil;
  68. }
  69. @end
  70. @interface RLMSyncSession ()
  71. @property (class, nonatomic, readonly) dispatch_queue_t notificationsQueue;
  72. @property (atomic, readwrite) RLMSyncConnectionState connectionState;
  73. @end
  74. @implementation RLMSyncSession
  75. + (dispatch_queue_t)notificationsQueue {
  76. static dispatch_queue_t queue;
  77. static dispatch_once_t onceToken;
  78. dispatch_once(&onceToken, ^{
  79. queue = dispatch_queue_create("io.realm.sync.sessionsNotificationQueue", DISPATCH_QUEUE_SERIAL);
  80. });
  81. return queue;
  82. }
  83. static RLMSyncConnectionState convertConnectionState(SyncSession::ConnectionState state) {
  84. switch (state) {
  85. case SyncSession::ConnectionState::Disconnected: return RLMSyncConnectionStateDisconnected;
  86. case SyncSession::ConnectionState::Connecting: return RLMSyncConnectionStateConnecting;
  87. case SyncSession::ConnectionState::Connected: return RLMSyncConnectionStateConnected;
  88. }
  89. }
  90. - (instancetype)initWithSyncSession:(std::shared_ptr<SyncSession> const&)session {
  91. if (self = [super init]) {
  92. _session = session;
  93. _connectionState = convertConnectionState(session->connection_state());
  94. // No need to save the token as RLMSyncSession always outlives the
  95. // underlying SyncSession
  96. session->register_connection_change_callback([=](auto, auto newState) {
  97. dispatch_async(dispatch_get_main_queue(), ^{
  98. self.connectionState = convertConnectionState(newState);
  99. });
  100. });
  101. return self;
  102. }
  103. return nil;
  104. }
  105. - (RLMSyncConfiguration *)configuration {
  106. if (auto session = _session.lock()) {
  107. return [[RLMSyncConfiguration alloc] initWithRawConfig:session->config()];
  108. }
  109. return nil;
  110. }
  111. - (NSURL *)realmURL {
  112. if (auto session = _session.lock()) {
  113. if (auto url = session->full_realm_url()) {
  114. return [NSURL URLWithString:@(url->c_str())];
  115. }
  116. }
  117. return nil;
  118. }
  119. - (RLMSyncUser *)parentUser {
  120. if (auto session = _session.lock()) {
  121. return [[RLMSyncUser alloc] initWithSyncUser:session->user()];
  122. }
  123. return nil;
  124. }
  125. - (RLMSyncSessionState)state {
  126. if (auto session = _session.lock()) {
  127. if (session->state() == SyncSession::PublicState::Inactive) {
  128. return RLMSyncSessionStateInactive;
  129. }
  130. return RLMSyncSessionStateActive;
  131. }
  132. return RLMSyncSessionStateInvalid;
  133. }
  134. - (void)suspend {
  135. if (auto session = _session.lock()) {
  136. session->log_out();
  137. }
  138. }
  139. - (void)resume {
  140. if (auto session = _session.lock()) {
  141. session->revive_if_needed();
  142. }
  143. }
  144. - (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback {
  145. if (auto session = _session.lock()) {
  146. queue = queue ?: dispatch_get_main_queue();
  147. session->wait_for_upload_completion([=](std::error_code err) {
  148. NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err);
  149. dispatch_async(queue, ^{
  150. callback(error);
  151. });
  152. });
  153. return YES;
  154. }
  155. return NO;
  156. }
  157. - (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue callback:(void(^)(NSError *))callback {
  158. if (auto session = _session.lock()) {
  159. queue = queue ?: dispatch_get_main_queue();
  160. session->wait_for_download_completion([=](std::error_code err) {
  161. NSError *error = (err == std::error_code{}) ? nil : make_sync_error(err);
  162. dispatch_async(queue, ^{
  163. callback(error);
  164. });
  165. });
  166. return YES;
  167. }
  168. return NO;
  169. }
  170. - (RLMProgressNotificationToken *)addProgressNotificationForDirection:(RLMSyncProgressDirection)direction
  171. mode:(RLMSyncProgressMode)mode
  172. block:(RLMProgressNotificationBlock)block {
  173. if (auto session = _session.lock()) {
  174. dispatch_queue_t queue = RLMSyncSession.notificationsQueue;
  175. auto notifier_direction = (direction == RLMSyncProgressDirectionUpload
  176. ? SyncSession::NotifierType::upload
  177. : SyncSession::NotifierType::download);
  178. bool is_streaming = (mode == RLMSyncProgressModeReportIndefinitely);
  179. uint64_t token = session->register_progress_notifier([=](uint64_t transferred, uint64_t transferrable) {
  180. dispatch_async(queue, ^{
  181. block((NSUInteger)transferred, (NSUInteger)transferrable);
  182. });
  183. }, notifier_direction, is_streaming);
  184. return [[RLMProgressNotificationToken alloc] initWithTokenValue:token session:std::move(session)];
  185. }
  186. return nil;
  187. }
  188. + (void)immediatelyHandleError:(RLMSyncErrorActionToken *)token {
  189. if (!token->_isValid) {
  190. return;
  191. }
  192. token->_isValid = NO;
  193. SyncManager::shared().immediately_run_file_actions(std::move(token->_originalPath));
  194. }
  195. + (nullable RLMSyncSession *)sessionForRealm:(RLMRealm *)realm {
  196. auto& config = realm->_realm->config().sync_config;
  197. if (!config) {
  198. return nil;
  199. }
  200. if (auto session = config->user->session_for_on_disk_path(realm->_realm->config().path)) {
  201. return [[RLMSyncSession alloc] initWithSyncSession:session];
  202. }
  203. return nil;
  204. }
  205. @end
  206. // MARK: - Error action token
  207. @implementation RLMSyncErrorActionToken
  208. - (instancetype)initWithOriginalPath:(std::string)originalPath {
  209. if (self = [super init]) {
  210. _isValid = YES;
  211. _originalPath = std::move(originalPath);
  212. return self;
  213. }
  214. return nil;
  215. }
  216. @end
  217. @implementation RLMAsyncOpenTask {
  218. bool _cancel;
  219. NSMutableArray<RLMProgressNotificationBlock> *_blocks;
  220. }
  221. - (void)addProgressNotificationOnQueue:(dispatch_queue_t)queue block:(RLMProgressNotificationBlock)block {
  222. auto wrappedBlock = ^(NSUInteger transferred_bytes, NSUInteger transferrable_bytes) {
  223. dispatch_async(queue, ^{
  224. @autoreleasepool {
  225. block(transferred_bytes, transferrable_bytes);
  226. }
  227. });
  228. };
  229. @synchronized (self) {
  230. if (_task) {
  231. _task->register_download_progress_notifier(wrappedBlock);
  232. }
  233. else if (!_cancel) {
  234. if (!_blocks) {
  235. _blocks = [NSMutableArray new];
  236. }
  237. [_blocks addObject:wrappedBlock];
  238. }
  239. }
  240. }
  241. - (void)addProgressNotificationBlock:(RLMProgressNotificationBlock)block {
  242. [self addProgressNotificationOnQueue:dispatch_get_main_queue() block:block];
  243. }
  244. - (void)cancel {
  245. @synchronized (self) {
  246. if (_task) {
  247. _task->cancel();
  248. }
  249. else {
  250. _cancel = true;
  251. _blocks = nil;
  252. }
  253. }
  254. }
  255. - (void)setTask:(std::shared_ptr<realm::AsyncOpenTask>)task {
  256. @synchronized (self) {
  257. _task = task;
  258. if (_cancel) {
  259. _task->cancel();
  260. }
  261. for (RLMProgressNotificationBlock block in _blocks) {
  262. _task->register_download_progress_notifier(block);
  263. }
  264. _blocks = nil;
  265. }
  266. }
  267. @end