2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
29 #import "CKDKeyValueStore.h"
30 #import "CKDPersistentState.h"
35 g = [CKDKeyValueStore defaultStore:mystoreID];
36 [g setObject:obj forKey:@"foo"];
40 #define DONTUSENOTIFICATIONS true
42 static const int verboseCKDKVSDebugging = false;
45 #define pdebug(format...) \
47 if (verboseCKDKVSDebugging) \
52 #define pdebug(format...)
56 extern CFStringRef kCKDKVSRemoteStoreID;
57 CFStringRef kCKDKVSRemoteStoreID = CFSTR("REMOTE");
58 CFStringRef kCKDAWSRemoteStoreID = CFSTR("AWS");
60 NSString * const kCKDKVSWhoChangedItemKey = @"WhoChangedItemKey";
62 static NSString * const ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification = @"ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification";
64 // MARK: ----- CKDKeyValueStore -----
66 @implementation CKDKeyValueStore
68 - (id)initWithIdentifier:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
70 if (self = [super init])
73 self.identifier = identifier;
74 self->localKVS = true;
76 // copy blocks onto heap
77 itemsChangedCallback = Block_copy(itemsChangedBlock);
79 [[NSNotificationCenter defaultCenter] addObserver: self
80 selector: @selector (iCloudAccountAvailabilityChanged:)
81 name: NSUbiquityIdentityDidChangeNotification
84 [[NSNotificationCenter defaultCenter] addObserver:self
85 selector:@selector(cloudChanged:)
86 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
92 + (CKDKeyValueStore *)defaultStore:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
94 return [CKDKeyValueStoreCollection defaultStore:identifier itemsChangedBlock:itemsChangedBlock];
100 value = [CKDKeyValueStoreCollection enqueueSyncWithReply];
110 - (id)objectForKey:(NSString *)aKey
112 pdebug(@"retrieving value for key \"%@\"", aKey);
113 id value = [CKDKeyValueStoreCollection enqueueWithReply:aKey];
114 pdebug(@"retrieved value for key \"%@\": %@", aKey, value);
118 - (void)setObject:(id)anObject forKey:(NSString *)aKey
120 pdebug(@"setting value for key \"%@\"", aKey);
121 [CKDKeyValueStoreCollection enqueueWrite:anObject forKey:aKey from:self.identifier];
124 - (void)removeObjectForKey:(NSString *)aKey
126 pdebug(@"removing value for key \"%@\"", aKey);
127 [CKDKeyValueStoreCollection enqueueWrite:NULL forKey:aKey from:self.identifier];
130 - (NSDictionary *)dictionaryRepresentation
132 pdebug(@"retrieving dictionaryRepresentation");
133 id value = [CKDKeyValueStoreCollection enqueueWithReply:NULL];
134 pdebug(@"retrieved dictionaryRepresentation: %@", value);
138 - (void)setDictionaryRepresentation:(NSMutableDictionary *)initialValue
141 [CKDKeyValueStoreCollection enqueueWrite:initialValue forKey:NULL from:self.identifier];
144 - (void)clearPersistentStores
148 + (CFStringRef)remoteStoreID
150 return kCKDKVSRemoteStoreID;
153 // MARK: ----- copied from real kvs -----
155 - (id)initWithItemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
157 if (self = [super init])
159 // copy blocks onto heap
160 itemsChangedCallback = Block_copy(itemsChangedBlock);
162 [[NSNotificationCenter defaultCenter] addObserver: self
163 selector: @selector (iCloudAccountAvailabilityChanged:)
164 name: NSUbiquityIdentityDidChangeNotification
167 [[NSNotificationCenter defaultCenter] addObserver:self
168 selector:@selector(cloudChanged:)
169 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
178 [[NSNotificationCenter defaultCenter] removeObserver:self
179 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification object:nil];
181 [[NSNotificationCenter defaultCenter] removeObserver:self
182 name:NSUbiquityIdentityDidChangeNotification object:nil];
184 Block_release(itemsChangedCallback);
188 - (void)cloudChanged:(NSNotification*)notification
191 Posted when the value of one or more keys in the local key-value store changed due to incoming data pushed from iCloud.
192 This notification is sent only upon a change received from iCloud; it is not sent when your app sets a value.
194 The user info dictionary can contain the reason for the notification as well as a list of which values changed, as follows:
196 The value of the NSUbiquitousKeyValueStoreChangeReasonKey key, when present, indicates why the key-value store changed.
197 Its value is one of the constants in “Change Reason Values .” The value of the NSUbiquitousKeyValueStoreChangedKeysKey,
198 when present, is an array of strings, each the name of a key whose value changed. The notification object is the
199 NSUbiquitousKeyValueStore object whose contents changed.
202 NSUbiquitousKeyValueStoreServerChange NS_ENUM_AVAILABLE(10_7, 5_0),
203 NSUbiquitousKeyValueStoreInitialSyncChange NS_ENUM_AVAILABLE(10_7, 5_0),
204 NSUbiquitousKeyValueStoreQuotaViolationChange NS_ENUM_AVAILABLE(10_7, 5_0),
205 NSUbiquitousKeyValueStoreAccountChange NS_ENUM_AVAILABLE(10_8, 6_0)
210 NSDictionary *userInfo = [notification userInfo];
211 NSNumber *reason = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
212 NSInteger reasonValue = -1;
214 pdebug(@"cloudChanged notification: %@", notification);
216 NSString *whoChangedIt = [userInfo objectForKey:kCKDKVSWhoChangedItemKey];
218 if (self.identifier && whoChangedIt && [self.identifier isEqualToString:whoChangedIt])
220 pdebug(@"cloudChanged by us (%@), ignoring event", self.identifier);
226 reasonValue = [reason integerValue];
227 NSArray *reasonStrings = [NSArray arrayWithObjects:@"Server", @"InitialSync", @"QuotaViolation", @"Account", @"unknown", nil];
228 long ridx = (NSUbiquitousKeyValueStoreServerChange <= reasonValue && reasonValue <= NSUbiquitousKeyValueStoreAccountChange)?reasonValue : 5;
229 pdebug(@"cloudChanged with reason %ld (%@ Change)", (long)reasonValue, [reasonStrings objectAtIndex:ridx]);
232 if ((reasonValue == NSUbiquitousKeyValueStoreServerChange) ||
233 (reasonValue == NSUbiquitousKeyValueStoreInitialSyncChange))
235 NSArray *keysChangedInCloud = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
236 pdebug(@"keysChangedInCloud: %@", keysChangedInCloud);
237 NSMutableDictionary *changedValues = [NSMutableDictionary dictionaryWithCapacity:0];
238 [keysChangedInCloud enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
240 NSString *key = (NSString *)obj;
241 // itemChangedCallback(key, [self.store objectForKey:key]);
242 id anObject = @"FIXME"; //[self.store objectForKey:key];
243 [changedValues setObject:anObject forKey:key];
245 pdebug(@"storeChanged updated value for %@", key);
247 itemsChangedCallback((CFDictionaryRef)changedValues); // fix me *************************
255 // MARK: ----- CKDKeyValueStoreCollection -----
257 @implementation CKDKeyValueStoreCollection
261 if (self = [super init])
263 self.collection = [NSMutableDictionary dictionaryWithCapacity:0];
264 self->syncrequestqueue = dispatch_queue_create("syncrequestqueue", DISPATCH_QUEUE_SERIAL);
265 self->store = [NSMutableDictionary dictionaryWithCapacity:0];
270 // maybe should return (CKDKeyValueStore *), main thing is that it matches the protocol
271 + (id <CKDKVSDelegate>)defaultStore:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
273 // look it up in the collection and return singleton
274 if (identifier == NULL)
275 return (id <CKDKVSDelegate>)[NSUbiquitousKeyValueStore defaultStore];
277 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
278 id <CKDKVSDelegate> store = mall.collection[identifier];
281 store = [[CKDKeyValueStore alloc] initWithIdentifier:identifier itemsChangedBlock:itemsChangedBlock];
282 mall->_collection[identifier] = store;
290 static dispatch_once_t once;
291 static CKDKeyValueStoreCollection *sharedStoreCollection;
292 dispatch_once(&once, ^ { sharedStoreCollection = [[self alloc] init]; });
293 return sharedStoreCollection;
296 + (void)enqueueWrite:(id)anObject forKey:(NSString *)aKey from:(NSString *)identifier
298 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
299 dispatch_async(mall->syncrequestqueue, ^void ()
301 if (aKey==NULL && (CFGetTypeID(anObject)==CFDictionaryGetTypeID()))
303 [mall->store setDictionary:anObject];
304 [self postItemsChangedNotification:[anObject allKeys] from:identifier];
309 [mall->store setObject:anObject forKey:aKey];
311 [mall->store removeObjectForKey:aKey];
312 [CKDKeyValueStoreCollection postItemChangedNotification:aKey from:identifier];
318 + (id)enqueueWithReply:(NSString *)aKey
320 __block id value = NULL;
321 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
322 dispatch_sync(mall->syncrequestqueue, ^void ()
324 value = (aKey==NULL)?mall->store:[mall->store objectForKey:aKey];
329 + (BOOL)enqueueSyncWithReply
331 // basically a barrier
332 __block BOOL value = false;
333 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
334 dispatch_sync(mall->syncrequestqueue, ^void ()
341 + (void)postItemChangedNotification:(NSString *)keyThatChanged from:(NSString *)identifier
343 // convenience routine when a single key changes
344 NSArray *keysThatChanged = [NSArray arrayWithObject:keyThatChanged];
345 [self postItemsChangedNotification:keysThatChanged from:identifier];
346 // [keysThatChanged release];
349 + (void)postItemsChangedNotification:(NSArray *)keysThatChanged from:(NSString *)identifier
351 // add in array of keys plus the id of who changed it
352 NSDictionary *aUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
353 keysThatChanged, NSUbiquitousKeyValueStoreChangedKeysKey,
354 @(NSUbiquitousKeyValueStoreServerChange), NSUbiquitousKeyValueStoreChangeReasonKey,
355 identifier, kCKDKVSWhoChangedItemKey,
358 [[NSNotificationCenter defaultCenter] postNotificationName:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
359 object:nil userInfo:aUserInfo];
360 // NSArray *keysChangedInCloud = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];