From 4414aae4bcd10a23997974a6ddce65ca4a4df895 Mon Sep 17 00:00:00 2001 From: Adam Wulf Date: Wed, 22 Jun 2016 14:16:41 -0500 Subject: [PATCH] Adding ckquery --- .../AgileCloudSDK.xcodeproj/project.pbxproj | 32 +++++++ AgileCloudSDK/AgileCloudSDK/AgileCloudSDK.h | 2 + AgileCloudSDK/AgileCloudSDK/CKDatabase.m | 15 +++- AgileCloudSDK/AgileCloudSDK/CKFilter.m | 3 +- .../AgileCloudSDK/CKModifyRecordsOperation.m | 6 +- .../AgileCloudSDK/CKQuery+AgileDictionary.h | 15 ++++ .../AgileCloudSDK/CKQuery+AgileDictionary.m | 34 ++++++++ AgileCloudSDK/AgileCloudSDK/CKQuery.h | 49 +++++++++++ AgileCloudSDK/AgileCloudSDK/CKQuery.m | 21 +++++ .../AgileCloudSDK/CKQueryOperation.h | 56 ++++++++++++ .../AgileCloudSDK/CKQueryOperation.m | 87 +++++++++++++++++++ .../AgileCloudSDK/CKRecord+AgileDictionary.h | 2 + .../AgileCloudSDK/CKRecord+AgileDictionary.m | 18 +++- AgileCloudSDK/AgileCloudSDK/CKRecordZone.m | 2 + .../NSSortDescriptor+AgileDictionary.h | 15 ++++ .../NSSortDescriptor+AgileDictionary.m | 22 +++++ 16 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.h create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.m create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQuery.h create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQuery.m create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQueryOperation.h create mode 100644 AgileCloudSDK/AgileCloudSDK/CKQueryOperation.m create mode 100644 AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.h create mode 100644 AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.m diff --git a/AgileCloudSDK/AgileCloudSDK.xcodeproj/project.pbxproj b/AgileCloudSDK/AgileCloudSDK.xcodeproj/project.pbxproj index 130f12d..61a9c84 100644 --- a/AgileCloudSDK/AgileCloudSDK.xcodeproj/project.pbxproj +++ b/AgileCloudSDK/AgileCloudSDK.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ C5233DD01BA671C20075BC5C /* CKSubscription+AgileDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = C5233DCE1BA671C20075BC5C /* CKSubscription+AgileDictionary.m */; }; C5233DD31BA672D70075BC5C /* CKNotificationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = C5233DD11BA672D70075BC5C /* CKNotificationInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; C5233DD41BA672D70075BC5C /* CKNotificationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C5233DD21BA672D70075BC5C /* CKNotificationInfo.m */; }; + C52561891D1B51FB00EA9896 /* CKQueryOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = C52561871D1B51FB00EA9896 /* CKQueryOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C525618A1D1B51FB00EA9896 /* CKQueryOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = C52561881D1B51FB00EA9896 /* CKQueryOperation.m */; }; C53390F11B8A5CE200DD1596 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C53390F01B8A5CE200DD1596 /* JavaScriptCore.framework */; }; C53391021B8AB36300DD1596 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C53391011B8AB36300DD1596 /* WebKit.framework */; }; C53391081B8AB67400DD1596 /* CKMediator.h in Headers */ = {isa = PBXBuildFile; fileRef = C53391061B8AB67400DD1596 /* CKMediator.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -98,6 +100,12 @@ C5A573C71B89153700264AC9 /* CKReference.m in Sources */ = {isa = PBXBuildFile; fileRef = C5A573AC1B89153700264AC9 /* CKReference.m */; }; C5A573C91B89191800264AC9 /* CKError.m in Sources */ = {isa = PBXBuildFile; fileRef = C5A573C81B89191800264AC9 /* CKError.m */; }; C5A66A3C1BAB565E00867BF5 /* container-config-format.json in Resources */ = {isa = PBXBuildFile; fileRef = C5A66A3B1BAB565E00867BF5 /* container-config-format.json */; }; + C5BDE7C61D1B0EA800F8FF64 /* CKQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = C5BDE7C41D1B0EA800F8FF64 /* CKQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C5BDE7C71D1B0EA800F8FF64 /* CKQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = C5BDE7C51D1B0EA800F8FF64 /* CKQuery.m */; }; + C5BDE7CA1D1B104800F8FF64 /* CKQuery+AgileDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = C5BDE7C81D1B104800F8FF64 /* CKQuery+AgileDictionary.h */; }; + C5BDE7CB1D1B104800F8FF64 /* CKQuery+AgileDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = C5BDE7C91D1B104800F8FF64 /* CKQuery+AgileDictionary.m */; }; + C5BDE7CE1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = C5BDE7CC1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.h */; }; + C5BDE7CF1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = C5BDE7CD1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.m */; }; C5E43FD31B9BBD9500CD93CA /* CKMediatorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = C5E43FD21B9BBD9500CD93CA /* CKMediatorDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; C5EBCD531B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = C5EBCD511B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.h */; }; C5EBCD541B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = C5EBCD521B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.m */; }; @@ -129,6 +137,8 @@ C5233DD21BA672D70075BC5C /* CKNotificationInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CKNotificationInfo.m; path = AgileCloudSDK/CKNotificationInfo.m; sourceTree = SOURCE_ROOT; }; C5233DD51BA675710075BC5C /* CKSubscription_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CKSubscription_Private.h; path = AgileCloudSDK/CKSubscription_Private.h; sourceTree = SOURCE_ROOT; }; C5233DD61BA67DEE0075BC5C /* CKRecordZoneID_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CKRecordZoneID_Private.h; path = AgileCloudSDK/CKRecordZoneID_Private.h; sourceTree = SOURCE_ROOT; }; + C52561871D1B51FB00EA9896 /* CKQueryOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKQueryOperation.h; path = AgileCloudSDK/CKQueryOperation.h; sourceTree = SOURCE_ROOT; }; + C52561881D1B51FB00EA9896 /* CKQueryOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CKQueryOperation.m; path = AgileCloudSDK/CKQueryOperation.m; sourceTree = SOURCE_ROOT; }; C53390F01B8A5CE200DD1596 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; C53391011B8AB36300DD1596 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; C53391061B8AB67400DD1596 /* CKMediator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKMediator.h; path = AgileCloudSDK/CKMediator.h; sourceTree = SOURCE_ROOT; }; @@ -210,6 +220,12 @@ C5A573C81B89191800264AC9 /* CKError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CKError.m; path = AgileCloudSDK/CKError.m; sourceTree = SOURCE_ROOT; }; C5A66A3B1BAB565E00867BF5 /* container-config-format.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "container-config-format.json"; sourceTree = ""; }; C5A66A3D1BAB57B700867BF5 /* CKContainer_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CKContainer_Private.h; path = AgileCloudSDK/CKContainer_Private.h; sourceTree = SOURCE_ROOT; }; + C5BDE7C41D1B0EA800F8FF64 /* CKQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKQuery.h; path = AgileCloudSDK/CKQuery.h; sourceTree = SOURCE_ROOT; }; + C5BDE7C51D1B0EA800F8FF64 /* CKQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CKQuery.m; path = AgileCloudSDK/CKQuery.m; sourceTree = SOURCE_ROOT; }; + C5BDE7C81D1B104800F8FF64 /* CKQuery+AgileDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CKQuery+AgileDictionary.h"; path = "AgileCloudSDK/CKQuery+AgileDictionary.h"; sourceTree = SOURCE_ROOT; }; + C5BDE7C91D1B104800F8FF64 /* CKQuery+AgileDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CKQuery+AgileDictionary.m"; path = "AgileCloudSDK/CKQuery+AgileDictionary.m"; sourceTree = SOURCE_ROOT; }; + C5BDE7CC1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSSortDescriptor+AgileDictionary.h"; path = "AgileCloudSDK/NSSortDescriptor+AgileDictionary.h"; sourceTree = SOURCE_ROOT; }; + C5BDE7CD1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSSortDescriptor+AgileDictionary.m"; path = "AgileCloudSDK/NSSortDescriptor+AgileDictionary.m"; sourceTree = SOURCE_ROOT; }; C5E03A751B941D5600FFFA28 /* CKDatabaseOperation_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CKDatabaseOperation_Private.h; path = AgileCloudSDK/CKDatabaseOperation_Private.h; sourceTree = SOURCE_ROOT; }; C5E43FD21B9BBD9500CD93CA /* CKMediatorDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CKMediatorDelegate.h; path = AgileCloudSDK/CKMediatorDelegate.h; sourceTree = SOURCE_ROOT; }; C5EBCD511B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CKRecordZoneID+AgileDictionary.h"; path = "AgileCloudSDK/CKRecordZoneID+AgileDictionary.h"; sourceTree = SOURCE_ROOT; }; @@ -364,6 +380,8 @@ C55554731BA7A3B100347276 /* CKNotificationInfo+AgileDictionary.m */, C5A573841B8911F700264AC9 /* CKOperation.h */, C5A573A91B89153700264AC9 /* CKOperation.m */, + C52561871D1B51FB00EA9896 /* CKQueryOperation.h */, + C52561881D1B51FB00EA9896 /* CKQueryOperation.m */, C5A5736C1B89115D00264AC9 /* CKRecord.h */, C5EBCD551B9ECEBD006B602E /* CKRecord_Private.h */, C5A5739D1B89153600264AC9 /* CKRecord.m */, @@ -404,6 +422,12 @@ C5150AF21BA6A3CD00850891 /* CKFilter_Private.h */, C5150AC51BA6936600850891 /* CKFilter.m */, C5150AC81BA6948400850891 /* CKFilterType.h */, + C5BDE7C41D1B0EA800F8FF64 /* CKQuery.h */, + C5BDE7C51D1B0EA800F8FF64 /* CKQuery.m */, + C5BDE7C81D1B104800F8FF64 /* CKQuery+AgileDictionary.h */, + C5BDE7C91D1B104800F8FF64 /* CKQuery+AgileDictionary.m */, + C5BDE7CC1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.h */, + C5BDE7CD1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.m */, ); name = Source; sourceTree = ""; @@ -418,6 +442,7 @@ C578F6E51B9EC51F00F5D43E /* CKRecord+AgileDictionary.h in Headers */, C5A5737D1B89115D00264AC9 /* CKNotification.h in Headers */, C5A573751B89115D00264AC9 /* CKDatabaseOperation.h in Headers */, + C5BDE7CA1D1B104800F8FF64 /* CKQuery+AgileDictionary.h in Headers */, C5A5737F1B89115D00264AC9 /* CKRecordID.h in Headers */, C56E2FBF1BA4C26D000365E0 /* CKAsset+AgileDictionary.h in Headers */, C5A573731B89115D00264AC9 /* CKContainer.h in Headers */, @@ -441,6 +466,7 @@ C55554741BA7A3B100347276 /* CKNotificationInfo+AgileDictionary.h in Headers */, 0ABE0F311CEE715700F5F2DA /* NSApplication+AgileCloudSDK.h in Headers */, C5A573851B8911F700264AC9 /* CKOperation.h in Headers */, + C5BDE7CE1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.h in Headers */, C55E96EF1BA15EA6007B4A9B /* NSArray+AgileMap.h in Headers */, C5A5738D1B8912D600264AC9 /* CKError.h in Headers */, C5A573761B89115D00264AC9 /* CKDefines.h in Headers */, @@ -449,8 +475,10 @@ C5150AE01BA698C400850891 /* NSNumber+CKFilterType.h in Headers */, C5A573891B89122300264AC9 /* CKDiscoveredUserInfo.h in Headers */, C5150AE81BA6991000850891 /* CLLocation+CKFilterType.h in Headers */, + C5BDE7C61D1B0EA800F8FF64 /* CKQuery.h in Headers */, C5150AD81BA697C900850891 /* CKReference+CKFilterType.h in Headers */, C5150AD41BA6975900850891 /* NSString+CKFilterType.h in Headers */, + C52561891D1B51FB00EA9896 /* CKQueryOperation.h in Headers */, C5150ADC1BA6987D00850891 /* NSDate+CKFilterType.h in Headers */, C5150AC61BA6936600850891 /* CKFilter.h in Headers */, C5150AEC1BA69BAF00850891 /* CLLocation+AgileDictionary.h in Headers */, @@ -541,6 +569,7 @@ buildActionMask = 2147483647; files = ( C5A573B71B89153700264AC9 /* CKModifySubscriptionsOperation.m in Sources */, + C5BDE7CB1D1B104800F8FF64 /* CKQuery+AgileDictionary.m in Sources */, C5150AC71BA6936600850891 /* CKFilter.m in Sources */, C5A573B11B89153700264AC9 /* CKDatabaseOperation.m in Sources */, C5EF10351BD045A00083BE81 /* CKRecordZone+AgileDictionary.m in Sources */, @@ -559,6 +588,7 @@ C5A573B51B89153700264AC9 /* CKModifyRecordsOperation.m in Sources */, C5A573C91B89191800264AC9 /* CKError.m in Sources */, C58B3C081B8EDFF10077F1C0 /* JSValue+AgileCloudSDKExtensions.m in Sources */, + C5BDE7CF1D1B10FE00F8FF64 /* NSSortDescriptor+AgileDictionary.m in Sources */, C5EBCD541B9ECC52006B602E /* CKRecordZoneID+AgileDictionary.m in Sources */, 0ABE0F321CEE715F00F5F2DA /* NSApplication+AgileCloudSDK.m in Sources */, C5A573B01B89153700264AC9 /* CKContainer.m in Sources */, @@ -567,6 +597,7 @@ C5A573AD1B89153700264AC9 /* CKMarkNotificationsReadOperation.m in Sources */, C5A573C71B89153700264AC9 /* CKReference.m in Sources */, C5233DD01BA671C20075BC5C /* CKSubscription+AgileDictionary.m in Sources */, + C525618A1D1B51FB00EA9896 /* CKQueryOperation.m in Sources */, C5A573BA1B89153700264AC9 /* CKServerChangeToken.m in Sources */, C5A573B91B89153700264AC9 /* CKRecordZone.m in Sources */, C5A573C01B89153700264AC9 /* CKRecordZoneID.m in Sources */, @@ -578,6 +609,7 @@ C5A573C11B89153700264AC9 /* CKAsset.m in Sources */, C5A573B61B89153700264AC9 /* CKModifyRecordZonesOperation.m in Sources */, C5A573B41B89153700264AC9 /* CKFetchRecordZonesOperation.m in Sources */, + C5BDE7C71D1B0EA800F8FF64 /* CKQuery.m in Sources */, C5A573C61B89153700264AC9 /* CKRecordID.m in Sources */, C5EEC9A81BAC79000027B4F1 /* CKBlockOperation.m in Sources */, C53391091B8AB67400DD1596 /* CKMediator.m in Sources */, diff --git a/AgileCloudSDK/AgileCloudSDK/AgileCloudSDK.h b/AgileCloudSDK/AgileCloudSDK/AgileCloudSDK.h index 136b35a..832d041 100644 --- a/AgileCloudSDK/AgileCloudSDK/AgileCloudSDK.h +++ b/AgileCloudSDK/AgileCloudSDK/AgileCloudSDK.h @@ -29,6 +29,8 @@ #import #import #import +#import +#import #import #import #import diff --git a/AgileCloudSDK/AgileCloudSDK/CKDatabase.m b/AgileCloudSDK/AgileCloudSDK/CKDatabase.m index a6897ec..42053fe 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKDatabase.m +++ b/AgileCloudSDK/AgileCloudSDK/CKDatabase.m @@ -12,9 +12,11 @@ #import "CKMediator_Private.h" #import "Defines.h" #import "CKRecordID+AgileDictionary.h" +#import "CKRecordZoneID+AgileDictionary.h" #import "CKRecord+AgileDictionary.h" #import "CKSubscription+AgileDictionary.h" #import "CKSubscription_Private.h" +#import "CKQuery+AgileDictionary.h" #import "CKRecord_Private.h" #import "CKContainer_Private.h" #import "CKBlockOperation.h" @@ -25,6 +27,7 @@ #import "CKModifySubscriptionsOperation.h" #import "CKRecordZone.h" #import "CKRecordZoneID.h" +#import "CKQueryOperation.h" #import "CKError.h" @@ -117,7 +120,17 @@ - (void)deleteRecordWithID:(CKRecordID *)recordID completionHandler:(void (^)(CK - (void)performQuery:(CKQuery *)query inZoneWithID:(CKRecordZoneID *)zoneID completionHandler:(void (^)(NSArray /* CKRecord */ *results, NSError *error))completionHandler { - @throw kAbstractMethodException; + + NSMutableArray* allFetchedRecords = [NSMutableArray array]; + CKQueryOperation *queryOp = [[CKQueryOperation alloc] initWithQuery:query]; + queryOp.zoneID = zoneID; + queryOp.recordFetchedBlock = ^(CKRecord* fetchedRecord){ + [allFetchedRecords addObject:fetchedRecord]; + }; + queryOp.queryCompletionBlock = ^(CKQueryCursor* cursor, NSError* error){ + completionHandler(allFetchedRecords, error); + }; + [self addOperation:queryOp]; } - (void)fetchAllRecordZonesWithCompletionHandler:(void (^)(NSArray /* CKRecordZone */ *zones, NSError *error))completionHandler { diff --git a/AgileCloudSDK/AgileCloudSDK/CKFilter.m b/AgileCloudSDK/AgileCloudSDK/CKFilter.m index 5d3d55b..e3625fc 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKFilter.m +++ b/AgileCloudSDK/AgileCloudSDK/CKFilter.m @@ -7,6 +7,7 @@ #import "CKFilter.h" #import "CKRecord_Private.h" +#import "CKRecord+AgileDictionary.h" NSString *const CK_EQUALS = @"EQUALS"; NSString *const CK_NOT_EQUALS = @"NOT_EQUALS"; @@ -64,7 +65,7 @@ - (NSDictionary *)asAgileDictionary { return @{ @"comparator": self.comparator, @"fieldName": self.fieldName, @"fieldValue": @{@"type": self.fieldType, - @"value": [self.fieldValue respondsToSelector:@selector(asAgileDictionary)] ? [self.fieldValue asAgileDictionary] : self.fieldValue} }; + @"value": [CKRecord recordFieldDictionaryForValue:self.fieldValue][@"value"] } }; } #pragma mark - NSSecureCoding diff --git a/AgileCloudSDK/AgileCloudSDK/CKModifyRecordsOperation.m b/AgileCloudSDK/AgileCloudSDK/CKModifyRecordsOperation.m index dafbb30..11f4d81 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKModifyRecordsOperation.m +++ b/AgileCloudSDK/AgileCloudSDK/CKModifyRecordsOperation.m @@ -138,7 +138,11 @@ - (void)start { [self setExecuting:NO]; [self setFinished:YES]; }]; - } + }else{ + // if no records were modified, then immediately end this operation + [self setExecuting:NO]; + [self setFinished:YES]; + } } @end diff --git a/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.h b/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.h new file mode 100644 index 0000000..5b29af4 --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.h @@ -0,0 +1,15 @@ +// +// CKQuery+AgileDictionary.h +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import "CKQuery.h" + +@interface CKQuery (AgileDictionary) + +- (NSDictionary *)asAgileDictionary; + +@end diff --git a/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.m b/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.m new file mode 100644 index 0000000..e2132e3 --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQuery+AgileDictionary.m @@ -0,0 +1,34 @@ +// +// CKQuery+AgileDictionary.m +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import "CKQuery+AgileDictionary.h" +#import "CKFilter_Private.h" +#import "NSSortDescriptor+AgileDictionary.h" + +@implementation CKQuery (AgileDictionary) + +- (NSDictionary *)asAgileDictionary { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:self.recordType forKey:@"recordType"]; + + NSMutableArray* filterArray = [NSMutableArray array]; + for (CKFilter* filter in self.filters) { + [filterArray addObject:[filter asAgileDictionary]]; + } + [dictionary setObject:filterArray forKey:@"filterBy"]; + + NSMutableArray* sortArray = [NSMutableArray array]; + for (NSSortDescriptor* sort in self.sortDescriptors) { + [sortArray addObject:[sort asAgileDictionary]]; + } + [dictionary setObject:sortArray forKey:@"sortBy"]; + + return dictionary; +} + +@end diff --git a/AgileCloudSDK/AgileCloudSDK/CKQuery.h b/AgileCloudSDK/AgileCloudSDK/CKQuery.h new file mode 100644 index 0000000..e9d8ae2 --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQuery.h @@ -0,0 +1,49 @@ +// +// CKQuery.h +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import +#import "CKFilter.h" + +NS_ASSUME_NONNULL_BEGIN +NS_CLASS_AVAILABLE(10_10, 8_0) +@interface CKQuery : NSObject + +/* + + Only AND compound predicates are allowed. + + Key names must begin with either an upper or lower case character ([a-zA-Z]) and may be followed by characters, numbers, or underscores ([0-9a-zA-Z_]). Keypaths may only resolve to the currently evaluated object, so the '.' character is not allowed in key names. + + A limited subset of classes are allowed as predicate arguments: + NSString + NSDate + NSData + NSNumber + NSArray + CKReference + CKRecord + CLLocation + + Any other class as an argument will result in an error when executing the query. + + */ + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithRecordType:(NSString *)recordType predicate:(NSPredicate *)predicate NS_UNAVAILABLE; + +- (instancetype)initWithRecordType:(NSString *)recordType filters:(NSArray*)filters NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly, copy) NSString *recordType; +@property (nonatomic, readonly, copy) NSArray*filters; + +@property (nonatomic, copy, nullable) NSArray *sortDescriptors; + +@end +NS_ASSUME_NONNULL_END diff --git a/AgileCloudSDK/AgileCloudSDK/CKQuery.m b/AgileCloudSDK/AgileCloudSDK/CKQuery.m new file mode 100644 index 0000000..c794aca --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQuery.m @@ -0,0 +1,21 @@ +// +// CKQuery.m +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import "CKQuery.h" + +@implementation CKQuery + +- (instancetype)initWithRecordType:(NSString *)recordType filters:(NSArray*)filters{ + if(self = [super init]){ + _filters = [filters copy]; + _recordType = recordType; + } + return self; +} + +@end diff --git a/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.h b/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.h new file mode 100644 index 0000000..66bb491 --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.h @@ -0,0 +1,56 @@ +// +// CKQueryOperation.h +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +NS_CLASS_AVAILABLE(10_10, 8_0) +@interface CKQueryCursor : NSObject +- (instancetype)init NS_UNAVAILABLE; +@end + +@class CKQuery, CKRecord, CKRecordZoneID; + +/* Query operations have a dynamically defined maximum number of results. If the results of a query + trip this max, your completion block will invoked with a cursor. Issue a new query with that cursor + to fetch the next batch of results. */ +CK_EXTERN const NSUInteger CKQueryOperationMaximumResults NS_AVAILABLE(10_10, 8_0); + +NS_CLASS_AVAILABLE(10_10, 8_0) +@interface CKQueryOperation : CKDatabaseOperation + +- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithQuery:(CKQuery *)query; +- (instancetype)initWithCursor:(CKQueryCursor *)cursor NS_UNAVAILABLE; + +@property (nonatomic, copy, nullable) CKQuery *query; +//@property (nonatomic, copy, nullable) CKQueryCursor *cursor; + +/* This property indicates which record zone to query. For query operations constructed using a cursor, this + property is ignored and instead will be evaluated in the record zone in which the cursor was originally created. */ +@property (nonatomic, copy, nullable) CKRecordZoneID *zoneID; + +/* Defaults to CKQueryOperationMaximumResults. + Queries may return fewer than resultsLimit in some scenarios: + - There are legitimately fewer than 'resultsLimit' number of records matching the query (and visible to the current user). + - During the process of querying and fetching the results, some records were deleted, or became un-readable by the current user. + When determining if there are more records to fetch, always check for the presence of a cursor in queryCompletionBlock. */ +@property (nonatomic, assign) NSUInteger resultsLimit; + +/* Declares which user-defined keys should be fetched and added to the resulting CKRecords. If nil, declares the entire record should be downloaded. If set to an empty array, declares that no user fields should be downloaded. Defaults to nil. */ +@property (nonatomic, copy, nullable) NSArray *desiredKeys; + +/* This block will be called once for every record that is returned as a result of the query. The callbacks will happen in the order that the results were sorted in. */ +@property (nonatomic, copy, nullable) void (^recordFetchedBlock)(CKRecord *record); + +/* This block is called when the operation completes. + The [NSOperation completionBlock] will also be called if both are set. */ +@property (nonatomic, copy, nullable) void (^queryCompletionBlock)(CKQueryCursor * __nullable cursor, NSError * __nullable operationError); + +@end +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.m b/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.m new file mode 100644 index 0000000..fdecd5a --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/CKQueryOperation.m @@ -0,0 +1,87 @@ +// +// CKQueryOperation.m +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import "CKQueryOperation.h" +#import "CKDatabaseOperation_Private.h" +#import "CKQuery+AgileDictionary.h" +#import "CKRecordZoneID+AgileDictionary.h" +#import "CKRecord_Private.h" +#import "CKDatabase_Private.h" + +const NSUInteger CKQueryOperationMaximumResults = 200; + +@implementation CKQueryOperation + +- (instancetype)init{ + if(self = [super init]){ + + } + return self; +} +- (instancetype)initWithQuery:(CKQuery *)query{ + if(self = [super init]){ + _query = query; + } + return self; +} + +- (void)start { + [self setExecuting:YES]; + + NSDictionary *requestDictionary = @{ + @"query": [self.query asAgileDictionary], + @"zoneID": [self.zoneID asAgileDictionary], + @"resultsLimit": @(200) + }; + + [self.database sendPOSTRequestTo:@"records/query" withJSON:requestDictionary completionHandler:^(id jsonResponse, NSError *error) { + NSMutableArray* fetchedRecords = [NSMutableArray array]; + if ([jsonResponse isKindOfClass:[NSDictionary class]] && jsonResponse[@"records"]) { + [jsonResponse[@"records"] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSError* recordError = nil; + CKRecord* record; + CKRecordID* recordID; + if (obj[@"serverErrorCode"]) { + recordError = [[NSError alloc] initWithCKErrorDictionary:obj]; + if (obj[@"recordName"]) { + recordID = [[CKRecordID alloc] initWithRecordName:obj[@"recordName"] zoneID:self.zoneID]; + } + } + else { + record = [[CKRecord alloc] initWithDictionary:obj inZone:self.zoneID]; + recordID = record.recordID; + } + if (record) { + NSArray* errs = [record synchronouslyDownloadAllAssetsWithProgressBlock:nil]; + if ([errs count]) { + recordError = errs[0]; + } + else { + [fetchedRecords addObject:record]; + } + } + + if (self.recordFetchedBlock && record) { + self.recordFetchedBlock(record); + } + }]; + } + else if (!error) { + error = [[NSError alloc] initWithCKErrorDictionary:jsonResponse]; + } + + if (self.queryCompletionBlock) { + self.queryCompletionBlock(nil, error); + } + + [self setExecuting:NO]; + [self setFinished:YES]; + }]; +} + +@end diff --git a/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.h b/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.h index 43d3050..6e8207c 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.h +++ b/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.h @@ -9,6 +9,8 @@ @interface CKRecord (AgileDictionary) ++(NSDictionary*) recordFieldDictionaryForValue:(NSObject*)val; + - (NSDictionary *)asAgileDictionary; @end diff --git a/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.m b/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.m index d428440..a69b63e 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.m +++ b/AgileCloudSDK/AgileCloudSDK/CKRecord+AgileDictionary.m @@ -30,13 +30,22 @@ - (NSDictionary *)agileFieldsDictionary { NSMutableDictionary *output = [NSMutableDictionary dictionary]; for (NSString *key in [self allKeys]) { - output[key] = @{ @"value": [self encodedObject:[self objectForKey:key]] }; + output[key] = [CKRecord recordFieldDictionaryForValue:[self objectForKey:key]]; } return output; } -- (id)encodedObject:(NSObject *)val { ++(NSDictionary*) recordFieldDictionaryForValue:(NSObject*)val{ + + if([val respondsToSelector:@selector(asAgileDictionary)]){ + return @{ @"value": [(id)val asAgileDictionary] }; + } + + return @{ @"value": [CKRecord encodedObject:val] }; +} + ++ (id)encodedObject:(NSObject *)val { if ([val isKindOfClass:[NSString class]]) { return val; } @@ -72,10 +81,11 @@ - (id)encodedObject:(NSObject *)val { else if ([val isKindOfClass:[CKAsset class]]) { return [(CKAsset *)val asAgileDictionary]; } + NSMutableDictionary *output = [NSMutableDictionary dictionary]; - for (NSString *key in [self allKeys]) { - output[key] = @{ @"value": [self objectForKey:key] }; + for (NSString *key in [(id)val allKeys]) { + output[key] = @{ @"value": [(id)val objectForKey:key] }; } return output; diff --git a/AgileCloudSDK/AgileCloudSDK/CKRecordZone.m b/AgileCloudSDK/AgileCloudSDK/CKRecordZone.m index 45c212d..6af665a 100644 --- a/AgileCloudSDK/AgileCloudSDK/CKRecordZone.m +++ b/AgileCloudSDK/AgileCloudSDK/CKRecordZone.m @@ -9,6 +9,8 @@ #import "CKRecordZone.h" #import "CKRecordZoneID.h" +NSString *const CKRecordZoneDefaultName = @"_defaultZone"; + @implementation CKRecordZone + (CKRecordZone *)defaultRecordZone { diff --git a/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.h b/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.h new file mode 100644 index 0000000..f467810 --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.h @@ -0,0 +1,15 @@ +// +// NSSortDescriptor+AgileDictionary.h +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import + +@interface NSSortDescriptor (AgileDictionary) + +- (NSDictionary *)asAgileDictionary; + +@end diff --git a/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.m b/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.m new file mode 100644 index 0000000..cc9cd5f --- /dev/null +++ b/AgileCloudSDK/AgileCloudSDK/NSSortDescriptor+AgileDictionary.m @@ -0,0 +1,22 @@ +// +// NSSortDescriptor+AgileDictionary.m +// AgileCloudSDK +// +// Created by Adam Wulf on 6/22/16. +// Copyright © 2016 AgileBits. All rights reserved. +// + +#import "NSSortDescriptor+AgileDictionary.h" + +@implementation NSSortDescriptor (AgileDictionary) + +- (NSDictionary *)asAgileDictionary { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:self.key forKey:@"fieldName"]; + // make sure booleans encode as integers + [dictionary setObject:[NSNumber numberWithInteger:self.ascending] forKey:@"ascending"]; + + return dictionary; +} + +@end