123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- #import "RLMSyncSubscription.h"
- #import "RLMObjectSchema_Private.hpp"
- #import "RLMObject_Private.hpp"
- #import "RLMProperty_Private.hpp"
- #import "RLMRealm_Private.hpp"
- #import "RLMResults_Private.hpp"
- #import "RLMUtil.hpp"
- #import "object_store.hpp"
- #import "sync/partial_sync.hpp"
- using namespace realm;
- @interface RLMSyncSubscription ()
- @property (nonatomic, readwrite) RLMSyncSubscriptionState state;
- @property (nonatomic, readwrite, nullable) NSError *error;
- @end
- @implementation RLMSyncSubscription {
- partial_sync::SubscriptionNotificationToken _token;
- util::Optional<partial_sync::Subscription> _subscription;
- RLMRealm *_realm;
- }
- - (instancetype)initWithName:(NSString *)name results:(Results const&)results realm:(RLMRealm *)realm {
- if (!(self = [super init]))
- return nil;
- _name = [name copy];
- _realm = realm;
- try {
- _subscription = partial_sync::subscribe(results, name ? util::make_optional<std::string>(name.UTF8String) : util::none);
- }
- catch (std::exception const& e) {
- @throw RLMException(e);
- }
- self.state = (RLMSyncSubscriptionState)_subscription->state();
- __weak auto weakSelf = self;
- _token = _subscription->add_notification_callback([weakSelf] {
- auto self = weakSelf;
- if (!self)
- return;
-
-
- if (auto error = self->_subscription->error()) {
- try {
- std::rethrow_exception(error);
- } catch (...) {
- NSError *nsError;
- RLMRealmTranslateException(&nsError);
- if (!self.error || ![self.error isEqual:nsError])
- self.error = nsError;
- }
- }
- else if (self.error) {
- self.error = nil;
- }
- auto status = (RLMSyncSubscriptionState)self->_subscription->state();
- if (status != self.state) {
- if (status == RLMSyncSubscriptionStateCreating) {
-
-
-
-
- if (self.state != RLMSyncSubscriptionStateInvalidated)
- self.state = RLMSyncSubscriptionStateInvalidated;
- }
- else {
- self.state = status;
- }
- }
- });
- return self;
- }
- - (void)unsubscribe {
- partial_sync::unsubscribe(*_subscription);
- }
- @end
- @interface RLMSyncSubscriptionObject : RLMObjectBase
- @end
- @implementation RLMSyncSubscriptionObject {
- util::Optional<NotificationToken> _token;
- Object _obj;
- }
- - (NSString *)name {
- return _row.is_attached() ? RLMStringDataToNSString(_row.get_string(_row.get_column_index("name"))) : nil;
- }
- - (RLMSyncSubscriptionState)state {
- if (!_row.is_attached()) {
- return RLMSyncSubscriptionStateInvalidated;
- }
- return (RLMSyncSubscriptionState)_row.get_int(_row.get_column_index("status"));
- }
- - (NSError *)error {
- if (!_row.is_attached()) {
- return nil;
- }
- StringData err = _row.get_string(_row.get_column_index("error_message"));
- if (!err.size()) {
- return nil;
- }
- return [NSError errorWithDomain:RLMErrorDomain
- code:RLMErrorFail
- userInfo:@{NSLocalizedDescriptionKey: RLMStringDataToNSString(err)}];
- }
- - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
- if (depth == 0) {
- return @"<Maximum depth exceeded>";
- }
- auto objectType = _row.get_string(_row.get_column_index("matches_property"));
- objectType = objectType.substr(0, objectType.size() - strlen("_matches"));
- return [NSString stringWithFormat:@"RLMSyncSubscription {\n\tname = %@\n\tobjectType = %@\n\tquery = %@\n\tstatus = %@\n\terror = %@\n}",
- self.name, RLMStringDataToNSString(objectType),
- RLMStringDataToNSString(_row.get_string(_row.get_column_index("query"))),
- @(self.state), self.error];
- }
- - (void)unsubscribe {
- if (_row) {
- partial_sync::unsubscribe(Object(_realm->_realm, *_info->objectSchema, _row));
- }
- }
- - (void)addObserver:(id)observer
- forKeyPath:(NSString *)keyPath
- options:(NSKeyValueObservingOptions)options
- context:(void *)context {
-
-
-
- if (!_token) {
- struct {
- __weak RLMSyncSubscriptionObject *weakSelf;
- void before(realm::CollectionChangeSet const&) {
- @autoreleasepool {
- [weakSelf willChangeValueForKey:@"state"];
- }
- }
- void after(realm::CollectionChangeSet const&) {
- @autoreleasepool {
- [weakSelf didChangeValueForKey:@"state"];
- }
- }
- void error(std::exception_ptr) {}
- } callback{self};
- _obj = Object(_realm->_realm, *_info->objectSchema, _row);
- _token = _obj.add_notification_callback(callback);
- }
- [super addObserver:observer forKeyPath:keyPath options:options context:context];
- }
- @end
- RLMResultsSetInfo::RLMResultsSetInfo(__unsafe_unretained RLMRealm *const realm)
- : osObjectSchema(ObjectSchema(realm->_realm->read_group(), partial_sync::result_sets_type_name))
- , rlmObjectSchema([RLMObjectSchema objectSchemaForObjectStoreSchema:osObjectSchema])
- , info(realm, rlmObjectSchema, &osObjectSchema)
- {
- rlmObjectSchema.accessorClass = [RLMSyncSubscriptionObject class];
- }
- RLMClassInfo& RLMResultsSetInfo::get(__unsafe_unretained RLMRealm *const realm) {
- if (!realm->_resultsSetInfo) {
- realm->_resultsSetInfo = std::make_unique<RLMResultsSetInfo>(realm);
- }
- return realm->_resultsSetInfo->info;
- }
- @interface RLMSubscriptionResults : RLMResults
- @end
- @implementation RLMSubscriptionResults
- + (instancetype)resultsWithRealm:(RLMRealm *)realm {
- auto table = ObjectStore::table_for_object_type(realm->_realm->read_group(), partial_sync::result_sets_type_name);
- if (!table) {
- @throw RLMException(@"-[RLMRealm subscriptions] can only be called on a Realm using query-based sync");
- }
-
-
-
- auto query = table->where().ends_with(table->get_column_index("matches_property"), "_matches");
- return [self resultsWithObjectInfo:RLMResultsSetInfo::get(realm)
- results:Results(realm->_realm, std::move(query))];
- }
- - (RLMResults *)sortedResultsUsingDescriptors:(__unused NSArray<RLMSortDescriptor *> *)properties {
- @throw RLMException(@"Sorting subscription results is currently not implemented");
- }
- - (RLMResults *)distinctResultsUsingKeyPaths:(__unused NSArray<NSString *> *)keyPaths {
- @throw RLMException(@"Distincting subscription results is currently not implemented");
- }
- @end
- @implementation RLMResults (SyncSubscription)
- - (RLMSyncSubscription *)subscribe {
- return [[RLMSyncSubscription alloc] initWithName:nil results:_results realm:self.realm];
- }
- - (RLMSyncSubscription *)subscribeWithName:(NSString *)subscriptionName {
- return [[RLMSyncSubscription alloc] initWithName:subscriptionName results:_results realm:self.realm];
- }
- - (RLMSyncSubscription *)subscribeWithName:(NSString *)subscriptionName limit:(NSUInteger)limit {
- return [[RLMSyncSubscription alloc] initWithName:subscriptionName results:_results.limit(limit) realm:self.realm];
- }
- @end
- @implementation RLMRealm (SyncSubscription)
- - (RLMResults<RLMSyncSubscription *> *)subscriptions {
- [self verifyThread];
- return [RLMSubscriptionResults resultsWithRealm:self];
- }
- - (nullable RLMSyncSubscription *)subscriptionWithName:(NSString *)name {
- [self verifyThread];
- auto& info = RLMResultsSetInfo::get(self);
- if (!info.table()) {
- @throw RLMException(@"-[RLMRealm subcriptionWithName:] can only be called on a Realm using query-based sync");
- }
- auto row = info.table()->find_first(info.table()->get_column_index("name"),
- RLMStringDataWithNSString(name));
- if (row == npos) {
- return nil;
- }
- RLMObjectBase *acc = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, self, &info);
- acc->_row = info.table()->get(row);
- return (RLMSyncSubscription *)acc;
- }
- @end
- RLMSyncSubscription *RLMCastToSyncSubscription(id obj) {
- return obj;
- }
|