added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMObject.mm
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2014 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
19 #import "RLMObject_Private.hpp"
20
21 #import "RLMAccessor.h"
22 #import "RLMArray.h"
23 #import "RLMCollection_Private.hpp"
24 #import "RLMObjectBase_Private.h"
25 #import "RLMObjectSchema_Private.hpp"
26 #import "RLMObjectStore.h"
27 #import "RLMProperty.h"
28 #import "RLMQueryUtil.hpp"
29 #import "RLMRealm_Private.hpp"
30 #import "RLMSchema_Private.h"
31
32 #import "collection_notifications.hpp"
33 #import "object.hpp"
34
35 @interface RLMPropertyChange ()
36 @property (nonatomic, readwrite, strong) NSString *name;
37 @property (nonatomic, readwrite, strong, nullable) id previousValue;
38 @property (nonatomic, readwrite, strong, nullable) id value;
39 @end
40
41 // We declare things in RLMObject which are actually implemented in RLMObjectBase
42 // for documentation's sake, which leads to -Wunimplemented-method warnings.
43 // Other alternatives to this would be to disable -Wunimplemented-method for this
44 // file (but then we could miss legitimately missing things), or declaring the
45 // inherited things in a category (but they currently aren't nicely grouped for
46 // that).
47 @implementation RLMObject
48
49 // synthesized in RLMObjectBase
50 @dynamic invalidated, realm, objectSchema;
51
52 #pragma mark - Designated Initializers
53
54 - (instancetype)init {
55     return [super init];
56 }
57
58 - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
59     return [super initWithValue:value schema:schema];
60 }
61
62 - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema {
63     return [super initWithRealm:realm schema:schema];
64 }
65
66 #pragma mark - Convenience Initializers
67
68 - (instancetype)initWithValue:(id)value {
69     return [super initWithValue:value schema:RLMSchema.partialPrivateSharedSchema];
70 }
71
72 #pragma mark - Class-based Object Creation
73
74 + (instancetype)createInDefaultRealmWithValue:(id)value {
75     return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, false);
76 }
77
78 + (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value {
79     return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, false);
80 }
81
82 + (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value {
83     return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value];
84 }
85
86 + (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value {
87     // verify primary key
88     RLMObjectSchema *schema = [self sharedSchema];
89     if (!schema.primaryKeyProperty) {
90         NSString *reason = [NSString stringWithFormat:@"'%@' does not have a primary key and can not be updated", schema.className];
91         @throw [NSException exceptionWithName:@"RLMExecption" reason:reason userInfo:nil];
92     }
93     return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true);
94 }
95
96 #pragma mark - Subscripting
97
98 - (id)objectForKeyedSubscript:(NSString *)key {
99     return RLMObjectBaseObjectForKeyedSubscript(self, key);
100 }
101
102 - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key {
103     RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj);
104 }
105
106 #pragma mark - Getting & Querying
107
108 + (RLMResults *)allObjects {
109     return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil);
110 }
111
112 + (RLMResults *)allObjectsInRealm:(__unsafe_unretained RLMRealm *const)realm {
113     return RLMGetObjects(realm, self.className, nil);
114 }
115
116 + (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... {
117     va_list args;
118     va_start(args, predicateFormat);
119     RLMResults *results = [self objectsWhere:predicateFormat args:args];
120     va_end(args);
121     return results;
122 }
123
124 + (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args {
125     return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
126 }
127
128 + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... {
129     va_list args;
130     va_start(args, predicateFormat);
131     RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args];
132     va_end(args);
133     return results;
134 }
135
136 + (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args {
137     return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]];
138 }
139
140 + (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate {
141     return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate);
142 }
143
144 + (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate {
145     return RLMGetObjects(realm, self.className, predicate);
146 }
147
148 + (instancetype)objectForPrimaryKey:(id)primaryKey {
149     return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey);
150 }
151
152 + (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey {
153     return RLMGetObject(realm, self.className, primaryKey);
154 }
155
156 #pragma mark - Other Instance Methods
157
158 - (BOOL)isEqualToObject:(RLMObject *)object {
159     return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object);
160 }
161
162 - (RLMNotificationToken *)addNotificationBlock:(RLMObjectChangeBlock)block {
163     return RLMObjectAddNotificationBlock(self, ^(NSArray<NSString *> *propertyNames,
164                                                  NSArray *oldValues, NSArray *newValues, NSError *error) {
165         if (error) {
166             block(false, nil, error);
167         }
168         else if (!propertyNames) {
169             block(true, nil, nil);
170         }
171         else {
172             auto properties = [NSMutableArray arrayWithCapacity:propertyNames.count];
173             for (NSUInteger i = 0, count = propertyNames.count; i < count; ++i) {
174                 auto prop = [RLMPropertyChange new];
175                 prop.name = propertyNames[i];
176                 prop.previousValue = RLMCoerceToNil(oldValues[i]);
177                 prop.value = RLMCoerceToNil(newValues[i]);
178                 [properties addObject:prop];
179             }
180             block(false, properties, nil);
181         }
182     });
183 }
184
185 + (NSString *)className {
186     return [super className];
187 }
188
189 #pragma mark - Default values for schema definition
190
191 + (NSArray *)indexedProperties {
192     return @[];
193 }
194
195 + (NSDictionary *)linkingObjectsProperties {
196     return @{};
197 }
198
199 + (NSDictionary *)defaultPropertyValues {
200     return nil;
201 }
202
203 + (NSString *)primaryKey {
204     return nil;
205 }
206
207 + (NSArray *)ignoredProperties {
208     return nil;
209 }
210
211 + (NSArray *)requiredProperties {
212     return @[];
213 }
214
215 @end
216
217 @implementation RLMDynamicObject
218
219 + (BOOL)shouldIncludeInDefaultSchema {
220     return NO;
221 }
222
223 - (id)valueForUndefinedKey:(NSString *)key {
224     return RLMDynamicGetByName(self, key, false);
225 }
226
227 - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
228     RLMDynamicValidatedSet(self, key, value);
229 }
230
231 @end
232
233 @implementation RLMWeakObjectHandle {
234     realm::Row _row;
235     RLMClassInfo *_info;
236     Class _objectClass;
237 }
238
239 - (instancetype)initWithObject:(RLMObjectBase *)object {
240     if (!(self = [super init])) {
241         return nil;
242     }
243
244     _row = object->_row;
245     _info = object->_info;
246     _objectClass = object.class;
247
248     return self;
249 }
250
251 - (RLMObjectBase *)object {
252     RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info);
253     object->_row = std::move(_row);
254     return object;
255 }
256
257 - (id)copyWithZone:(__unused NSZone *)zone {
258     RLMWeakObjectHandle *copy = [[RLMWeakObjectHandle alloc] init];
259     copy->_row = _row;
260     copy->_info = _info;
261     copy->_objectClass = _objectClass;
262     return copy;
263 }
264
265 @end
266
267 static bool treatFakeObjectAsRLMObject = false;
268 void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) {
269     treatFakeObjectAsRLMObject = flag;
270 }
271
272 BOOL RLMIsObjectOrSubclass(Class klass) {
273     if (RLMIsKindOfClass(klass, RLMObjectBase.class)) {
274         return YES;
275     }
276
277     if (treatFakeObjectAsRLMObject) {
278         static Class FakeObjectClass = NSClassFromString(@"FakeObject");
279         return RLMIsKindOfClass(klass, FakeObjectClass);
280     }
281     return NO;
282 }
283
284 BOOL RLMIsObjectSubclass(Class klass) {
285     auto isSubclass = [](Class class1, Class class2) {
286         class1 = class_getSuperclass(class1);
287         return RLMIsKindOfClass(class1, class2);
288     };
289     if (isSubclass(class_getSuperclass(klass), RLMObjectBase.class)) {
290         return YES;
291     }
292
293     if (treatFakeObjectAsRLMObject) {
294         static Class FakeObjectClass = NSClassFromString(@"FakeObject");
295         return isSubclass(klass, FakeObjectClass);
296     }
297     return NO;
298 }
299
300 @interface RLMObjectNotificationToken : RLMCancellationToken
301 @end
302 @implementation RLMObjectNotificationToken {
303 @public
304     realm::Object _object;
305 }
306 @end
307
308 RLMNotificationToken *RLMObjectAddNotificationBlock(RLMObjectBase *obj, RLMObjectNotificationCallback block) {
309     if (!obj->_realm) {
310         @throw RLMException(@"Only objects which are managed by a Realm support change notifications");
311     }
312     [obj->_realm verifyNotificationsAreSupported:true];
313
314     struct {
315         void (^block)(NSArray<NSString *> *, NSArray *, NSArray *, NSError *);
316         RLMObjectBase *object;
317
318         NSArray<NSString *> *propertyNames = nil;
319         NSArray *oldValues = nil;
320         bool deleted = false;
321
322         void populateProperties(realm::CollectionChangeSet const& c) {
323             if (propertyNames) {
324                 return;
325             }
326             if (!c.deletions.empty()) {
327                 deleted = true;
328                 return;
329             }
330             if (c.columns.empty()) {
331                 return;
332             }
333
334             auto properties = [NSMutableArray new];
335             for (size_t i = 0; i < c.columns.size(); ++i) {
336                 if (c.columns[i].empty()) {
337                     continue;
338                 }
339                 if (auto prop = object->_info->propertyForTableColumn(i)) {
340                     [properties addObject:prop.name];
341                 }
342             }
343             if (properties.count) {
344                 propertyNames = properties;
345             }
346         }
347
348         NSArray *readValues(realm::CollectionChangeSet const& c) {
349             if (c.empty()) {
350                 return nil;
351             }
352             populateProperties(c);
353             if (!propertyNames) {
354                 return nil;
355             }
356
357             auto values = [NSMutableArray arrayWithCapacity:propertyNames.count];
358             for (NSString *name in propertyNames) {
359                 id value = [object valueForKey:name];
360                 if (!value || [value isKindOfClass:[RLMArray class]]) {
361                     [values addObject:NSNull.null];
362                 }
363                 else {
364                     [values addObject:value];
365                 }
366             }
367             return values;
368         }
369
370         void before(realm::CollectionChangeSet const& c) {
371             @autoreleasepool {
372                 oldValues = readValues(c);
373             }
374         }
375
376         void after(realm::CollectionChangeSet const& c) {
377             @autoreleasepool {
378                 auto newValues = readValues(c);
379                 if (deleted) {
380                     block(nil, nil, nil, nil);
381                 }
382                 else if (newValues) {
383                     block(propertyNames, oldValues, newValues, nil);
384                 }
385                 propertyNames = nil;
386                 oldValues = nil;
387             }
388         }
389
390         void error(std::exception_ptr err) {
391             @autoreleasepool {
392                 try {
393                     rethrow_exception(err);
394                 }
395                 catch (...) {
396                     NSError *error = nil;
397                     RLMRealmTranslateException(&error);
398                     block(nil, nil, nil, error);
399                 }
400             }
401         }
402     } callback{block, obj};
403
404     realm::Object object(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
405     auto token = [[RLMObjectNotificationToken alloc] initWithToken:object.add_notification_callback(callback) realm:obj->_realm];
406     token->_object = std::move(object);
407     return token;
408 }
409
410 @implementation RLMPropertyChange
411 @end