RLMSyncSession.mm 7.7 KB

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