diff --git a/GJEntry.h b/GJEntry.h new file mode 100644 index 0000000..c022f32 --- /dev/null +++ b/GJEntry.h @@ -0,0 +1,24 @@ +// +// GJEntry.h +// GoJournal +// +// Created by Varindra Hart on 10/13/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import +#import +@interface GJEntry : PFObject + +@property (nonatomic) PFFile *file; +@property (nonatomic) NSString *mediaType; +@property (nonatomic) NSString *fileExt; +@property (nonatomic) PFGeoPoint *location; +@property (nonatomic) NSString *textMedia; +@property (nonatomic) NSDate *createdDate; + ++ (NSString *) parseClassName; +- (void)fileFromImage:(UIImage *)image; +- (void)fileFromVideoURL:(NSURL *)url; +- (NSURL *)urlFromMediaFile; +@end diff --git a/GJEntry.m b/GJEntry.m new file mode 100644 index 0000000..eec9760 --- /dev/null +++ b/GJEntry.m @@ -0,0 +1,51 @@ +// +// GJEntry.m +// GoJournal +// +// Created by Varindra Hart on 10/13/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "GJEntry.h" + +@implementation GJEntry + +@dynamic file; +@dynamic mediaType; +@dynamic fileExt; +@dynamic location; +@dynamic textMedia; +@dynamic createdDate; + ++ (NSString *) parseClassName{ + + return @"GJEntry"; +} + +- (void)fileFromImage:(UIImage *)image{ + + NSData *data = UIImageJPEGRepresentation(image, .8); + + self.file = [PFFile fileWithData:data]; + +} + +- (void)fileFromVideoURL:(NSURL *)url{ + + NSData *data = [NSData dataWithContentsOfURL:url]; + self.file = [PFFile fileWithData:data contentType:@"video/mov"]; + +} + +- (NSURL *)urlFromMediaFile{ + + if (![self.mediaType isEqualToString: @"public.video"]) { + return nil; + } + + NSString *stringUrl = self.file.url; + NSURL *url = [NSURL URLWithString:stringUrl]; + return url; + +} +@end diff --git a/GJOutings.h b/GJOutings.h new file mode 100644 index 0000000..9615001 --- /dev/null +++ b/GJOutings.h @@ -0,0 +1,19 @@ +// +// GJOutings.h +// GoJournal +// +// Created by Varindra Hart on 10/13/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "GJEntry.h" + +@interface GJOutings : PFObject + +@property (nonatomic) NSString *outingName; +@property (nonatomic) NSDate *createdDate; +@property (nonatomic) NSMutableArray *entriesArray; + ++ (NSString *)parseClassName; +- (instancetype)initWithNewEntriesArray; +@end diff --git a/GJOutings.m b/GJOutings.m new file mode 100644 index 0000000..26fea63 --- /dev/null +++ b/GJOutings.m @@ -0,0 +1,34 @@ +// +// GJOutings.m +// GoJournal +// +// Created by Varindra Hart on 10/13/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "GJOutings.h" + +@implementation GJOutings + +@dynamic outingName; +@dynamic createdDate; +@dynamic entriesArray; + ++ (NSString *)parseClassName{ + + return @"GJOutings"; +} + +- (instancetype)initWithNewEntriesArray{ + + if (self = [super init]){ + + self.entriesArray = [NSMutableArray new]; + return self; + + } + + else + return nil; +} +@end diff --git a/GMSMarker+GJEntryArray.h b/GMSMarker+GJEntryArray.h new file mode 100644 index 0000000..98bd57b --- /dev/null +++ b/GMSMarker+GJEntryArray.h @@ -0,0 +1,15 @@ +// +// GMSMarker+GJEntryArray.h +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import +#import "GJOutings.h" + +@interface GMSMarker (GJEntryArray) + + +@end diff --git a/GMSMarker+GJEntryArray.m b/GMSMarker+GJEntryArray.m new file mode 100644 index 0000000..349d096 --- /dev/null +++ b/GMSMarker+GJEntryArray.m @@ -0,0 +1,19 @@ +// +// GMSMarker+GJEntryArray.m +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "GMSMarker+GJEntryArray.h" + +@implementation GMSMarker (GJEntryArray) + + + +//- (void)setEntriesArrayForLocation:(NSMutableArray *)entriesArrayForLocation { +// self.entriesArrayForLocation = entriesArrayForLocation; +//} + +@end diff --git a/GMSMarker_GJEntriesArray.h b/GMSMarker_GJEntriesArray.h new file mode 100644 index 0000000..df3cbc0 --- /dev/null +++ b/GMSMarker_GJEntriesArray.h @@ -0,0 +1,18 @@ +// +// GMSMarker_GJEntriesArray.h +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import +#import +#import "GJOutings.h" +#import "GJEntry.h" + +@interface GMSMarker () + +@property (nonatomic) NSMutableArray *entriesArrayForLocation; + +@end diff --git a/GMSMarker_WithArray.h b/GMSMarker_WithArray.h new file mode 100644 index 0000000..f8b8b36 --- /dev/null +++ b/GMSMarker_WithArray.h @@ -0,0 +1,14 @@ +// +// GMSMarker_WithArray.h +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import +#import "GJEntry.h" + +@interface GMSMarker_WithArray : GMSMarker +@property (nonatomic) NSMutableArray *entriesArrayForLocation; +@end diff --git a/GMSMarker_WithArray.m b/GMSMarker_WithArray.m new file mode 100644 index 0000000..5feff16 --- /dev/null +++ b/GMSMarker_WithArray.m @@ -0,0 +1,13 @@ +// +// GMSMarker_WithArray.m +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "GMSMarker_WithArray.h" + +@implementation GMSMarker_WithArray + +@end diff --git a/HikerLite.xcodeproj/project.pbxproj b/HikerLite.xcodeproj/project.pbxproj new file mode 100644 index 0000000..603a874 --- /dev/null +++ b/HikerLite.xcodeproj/project.pbxproj @@ -0,0 +1,757 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3B7D1C1B1BD2F75F00406412 /* EntryCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C181BD2F75F00406412 /* EntryCell.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C1C1BD2F75F00406412 /* EntryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C191BD2F75F00406412 /* EntryCell.xib */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C1F1BD2F79E00406412 /* TripCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C231BD2F7E900406412 /* vid.m4v in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C211BD2F7E900406412 /* vid.m4v */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C241BD2F7E900406412 /* vid2.m4v in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C221BD2F7E900406412 /* vid2.m4v */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C271BD3EE4500406412 /* TextEntryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C2A1BD4249700406412 /* HLMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C291BD4249700406412 /* HLMapView.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C2D1BD43E6900406412 /* OutingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C2C1BD43E6900406412 /* OutingCell.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C2F1BD43E7B00406412 /* OutingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C2E1BD43E7B00406412 /* OutingCell.xib */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C351BD51CCB00406412 /* OutingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C341BD51CCB00406412 /* OutingsViewController.m */; settings = {ASSET_TAGS = (); }; }; + B7711B1F931201E40D1551B8 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A35CDD57FC026BC52468D2A5 /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + C76CA15F1BD47B0D00981F39 /* GMSMarker_WithArray.m in Sources */ = {isa = PBXBuildFile; fileRef = C76CA15E1BD47B0D00981F39 /* GMSMarker_WithArray.m */; settings = {ASSET_TAGS = (); }; }; + C7A6EAC91BD2E59E0064BD4A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAC81BD2E59E0064BD4A /* main.m */; }; + C7A6EACC1BD2E59E0064BD4A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */; }; + C7A6EAD21BD2E59E0064BD4A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */; }; + C7A6EAD41BD2E59E0064BD4A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */; }; + C7A6EAD71BD2E59E0064BD4A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */; }; + C7A6EAE21BD2E59F0064BD4A /* HikerLiteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */; }; + C7A6EAED1BD2E59F0064BD4A /* HikerLiteUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */; }; + C7A6EAFF1BD2EBD80064BD4A /* GJOutings.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */; }; + C7A6EB001BD2EBD80064BD4A /* GJEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */; }; + C7A6EB021BD2EECE0064BD4A /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */; }; + C7A6EB0F1BD409E90064BD4A /* GMSMarker+GJEntryArray.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C7A6EADE1BD2E59E0064BD4A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C7A6EABC1BD2E59E0064BD4A /* Project object */; + proxyType = 1; + remoteGlobalIDString = C7A6EAC31BD2E59E0064BD4A; + remoteInfo = HikerLite; + }; + C7A6EAE91BD2E59F0064BD4A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C7A6EABC1BD2E59E0064BD4A /* Project object */; + proxyType = 1; + remoteGlobalIDString = C7A6EAC31BD2E59E0064BD4A; + remoteInfo = HikerLite; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 3B7D1C171BD2F75F00406412 /* EntryCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EntryCell.h; sourceTree = ""; }; + 3B7D1C181BD2F75F00406412 /* EntryCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EntryCell.m; sourceTree = ""; }; + 3B7D1C191BD2F75F00406412 /* EntryCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EntryCell.xib; sourceTree = ""; }; + 3B7D1C1D1BD2F79E00406412 /* TripCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TripCollectionViewController.h; sourceTree = ""; }; + 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TripCollectionViewController.m; sourceTree = ""; }; + 3B7D1C211BD2F7E900406412 /* vid.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = vid.m4v; sourceTree = ""; }; + 3B7D1C221BD2F7E900406412 /* vid2.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = vid2.m4v; sourceTree = ""; }; + 3B7D1C251BD3EE4500406412 /* TextEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextEntryViewController.h; sourceTree = ""; }; + 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextEntryViewController.m; sourceTree = ""; }; + 3B7D1C281BD4249700406412 /* HLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLMapView.h; sourceTree = ""; }; + 3B7D1C291BD4249700406412 /* HLMapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLMapView.m; sourceTree = ""; }; + 3B7D1C2B1BD43E6900406412 /* OutingCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutingCell.h; sourceTree = ""; }; + 3B7D1C2C1BD43E6900406412 /* OutingCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OutingCell.m; sourceTree = ""; }; + 3B7D1C2E1BD43E7B00406412 /* OutingCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OutingCell.xib; sourceTree = ""; }; + 3B7D1C331BD51CCB00406412 /* OutingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutingsViewController.h; sourceTree = ""; }; + 3B7D1C341BD51CCB00406412 /* OutingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OutingsViewController.m; sourceTree = ""; }; + 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + A35CDD57FC026BC52468D2A5 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C76CA15C1BD4762D00981F39 /* GMSMarker_GJEntriesArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GMSMarker_GJEntriesArray.h; sourceTree = ""; }; + C76CA15D1BD47B0D00981F39 /* GMSMarker_WithArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GMSMarker_WithArray.h; sourceTree = ""; }; + C76CA15E1BD47B0D00981F39 /* GMSMarker_WithArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GMSMarker_WithArray.m; sourceTree = ""; }; + C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HikerLite.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAC81BD2E59E0064BD4A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + C7A6EACA1BD2E59E0064BD4A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + C7A6EAD11BD2E59E0064BD4A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C7A6EAD61BD2E59E0064BD4A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C7A6EAD81BD2E59E0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HikerLiteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HikerLiteTests.m; sourceTree = ""; }; + C7A6EAE31BD2E59F0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HikerLiteUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HikerLiteUITests.m; sourceTree = ""; }; + C7A6EAEE1BD2E59F0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EAFB1BD2EBB90064BD4A /* GJOutings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GJOutings.h; sourceTree = ""; }; + C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GJOutings.m; sourceTree = ""; }; + C7A6EAFD1BD2EBD80064BD4A /* GJEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GJEntry.h; sourceTree = ""; }; + C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GJEntry.m; sourceTree = ""; }; + C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + C7A6EB0D1BD409E90064BD4A /* GMSMarker+GJEntryArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GMSMarker+GJEntryArray.h"; sourceTree = ""; }; + C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GMSMarker+GJEntryArray.m"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C7A6EAC11BD2E59E0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EB021BD2EECE0064BD4A /* CoreLocation.framework in Frameworks */, + B7711B1F931201E40D1551B8 /* Pods.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EADA1BD2E59E0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE51BD2E59F0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 10491F166C7E17D64159B238 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */, + A35CDD57FC026BC52468D2A5 /* Pods.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 2372FEC4BA0F8EDC2DE27371 /* Pods */ = { + isa = PBXGroup; + children = ( + 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */, + 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 3B7D1C201BD2F7CB00406412 /* Resources */ = { + isa = PBXGroup; + children = ( + 3B7D1C211BD2F7E900406412 /* vid.m4v */, + 3B7D1C221BD2F7E900406412 /* vid2.m4v */, + ); + name = Resources; + sourceTree = ""; + }; + C7A6EABB1BD2E59E0064BD4A = { + isa = PBXGroup; + children = ( + C7A6EB0C1BD4099B0064BD4A /* Categories */, + C7A6EAFA1BD2EAF80064BD4A /* Parse */, + C7A6EAC61BD2E59E0064BD4A /* HikerLite */, + C7A6EAE01BD2E59E0064BD4A /* HikerLiteTests */, + C7A6EAEB1BD2E59F0064BD4A /* HikerLiteUITests */, + C7A6EAC51BD2E59E0064BD4A /* Products */, + 2372FEC4BA0F8EDC2DE27371 /* Pods */, + 10491F166C7E17D64159B238 /* Frameworks */, + ); + sourceTree = ""; + }; + C7A6EAC51BD2E59E0064BD4A /* Products */ = { + isa = PBXGroup; + children = ( + C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */, + C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */, + C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + C7A6EAC61BD2E59E0064BD4A /* HikerLite */ = { + isa = PBXGroup; + children = ( + C7A6EACA1BD2E59E0064BD4A /* AppDelegate.h */, + C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */, + 3B7D1C171BD2F75F00406412 /* EntryCell.h */, + 3B7D1C181BD2F75F00406412 /* EntryCell.m */, + 3B7D1C191BD2F75F00406412 /* EntryCell.xib */, + 3B7D1C281BD4249700406412 /* HLMapView.h */, + 3B7D1C291BD4249700406412 /* HLMapView.m */, + 3B7D1C1D1BD2F79E00406412 /* TripCollectionViewController.h */, + 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */, + 3B7D1C251BD3EE4500406412 /* TextEntryViewController.h */, + 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */, + 3B7D1C2B1BD43E6900406412 /* OutingCell.h */, + 3B7D1C2C1BD43E6900406412 /* OutingCell.m */, + 3B7D1C2E1BD43E7B00406412 /* OutingCell.xib */, + 3B7D1C331BD51CCB00406412 /* OutingsViewController.h */, + 3B7D1C341BD51CCB00406412 /* OutingsViewController.m */, + 3B7D1C201BD2F7CB00406412 /* Resources */, + C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */, + C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */, + C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */, + C7A6EAD81BD2E59E0064BD4A /* Info.plist */, + C7A6EAC71BD2E59E0064BD4A /* Supporting Files */, + ); + path = HikerLite; + sourceTree = ""; + }; + C7A6EAC71BD2E59E0064BD4A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + C7A6EAC81BD2E59E0064BD4A /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + C7A6EAE01BD2E59E0064BD4A /* HikerLiteTests */ = { + isa = PBXGroup; + children = ( + C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */, + C7A6EAE31BD2E59F0064BD4A /* Info.plist */, + ); + path = HikerLiteTests; + sourceTree = ""; + }; + C7A6EAEB1BD2E59F0064BD4A /* HikerLiteUITests */ = { + isa = PBXGroup; + children = ( + C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */, + C7A6EAEE1BD2E59F0064BD4A /* Info.plist */, + ); + path = HikerLiteUITests; + sourceTree = ""; + }; + C7A6EAFA1BD2EAF80064BD4A /* Parse */ = { + isa = PBXGroup; + children = ( + C7A6EAFB1BD2EBB90064BD4A /* GJOutings.h */, + C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */, + C7A6EAFD1BD2EBD80064BD4A /* GJEntry.h */, + C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */, + ); + name = Parse; + sourceTree = ""; + }; + C7A6EB0C1BD4099B0064BD4A /* Categories */ = { + isa = PBXGroup; + children = ( + C7A6EB0D1BD409E90064BD4A /* GMSMarker+GJEntryArray.h */, + C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */, + C76CA15C1BD4762D00981F39 /* GMSMarker_GJEntriesArray.h */, + C76CA15D1BD47B0D00981F39 /* GMSMarker_WithArray.h */, + C76CA15E1BD47B0D00981F39 /* GMSMarker_WithArray.m */, + ); + name = Categories; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C7A6EAC31BD2E59E0064BD4A /* HikerLite */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF11BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLite" */; + buildPhases = ( + 6572F60BC1D6A5602B00B0EF /* Check Pods Manifest.lock */, + C7A6EAC01BD2E59E0064BD4A /* Sources */, + C7A6EAC11BD2E59E0064BD4A /* Frameworks */, + C7A6EAC21BD2E59E0064BD4A /* Resources */, + A00DA03C0324E026CFABE807 /* Embed Pods Frameworks */, + 855589BC5EA84BD821CA49F1 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HikerLite; + productName = HikerLite; + productReference = C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */; + productType = "com.apple.product-type.application"; + }; + C7A6EADC1BD2E59E0064BD4A /* HikerLiteTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF41BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteTests" */; + buildPhases = ( + C7A6EAD91BD2E59E0064BD4A /* Sources */, + C7A6EADA1BD2E59E0064BD4A /* Frameworks */, + C7A6EADB1BD2E59E0064BD4A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C7A6EADF1BD2E59E0064BD4A /* PBXTargetDependency */, + ); + name = HikerLiteTests; + productName = HikerLiteTests; + productReference = C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + C7A6EAE71BD2E59F0064BD4A /* HikerLiteUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF71BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteUITests" */; + buildPhases = ( + C7A6EAE41BD2E59F0064BD4A /* Sources */, + C7A6EAE51BD2E59F0064BD4A /* Frameworks */, + C7A6EAE61BD2E59F0064BD4A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C7A6EAEA1BD2E59F0064BD4A /* PBXTargetDependency */, + ); + name = HikerLiteUITests; + productName = HikerLiteUITests; + productReference = C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C7A6EABC1BD2E59E0064BD4A /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = "Varindra Hart"; + TargetAttributes = { + C7A6EAC31BD2E59E0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3HZSHG8SUA; + }; + C7A6EADC1BD2E59E0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3L76772298; + TestTargetID = C7A6EAC31BD2E59E0064BD4A; + }; + C7A6EAE71BD2E59F0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3L76772298; + TestTargetID = C7A6EAC31BD2E59E0064BD4A; + }; + }; + }; + buildConfigurationList = C7A6EABF1BD2E59E0064BD4A /* Build configuration list for PBXProject "HikerLite" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C7A6EABB1BD2E59E0064BD4A; + productRefGroup = C7A6EAC51BD2E59E0064BD4A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C7A6EAC31BD2E59E0064BD4A /* HikerLite */, + C7A6EADC1BD2E59E0064BD4A /* HikerLiteTests */, + C7A6EAE71BD2E59F0064BD4A /* HikerLiteUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C7A6EAC21BD2E59E0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7D1C1C1BD2F75F00406412 /* EntryCell.xib in Resources */, + C7A6EAD71BD2E59E0064BD4A /* LaunchScreen.storyboard in Resources */, + 3B7D1C231BD2F7E900406412 /* vid.m4v in Resources */, + C7A6EAD41BD2E59E0064BD4A /* Assets.xcassets in Resources */, + 3B7D1C241BD2F7E900406412 /* vid2.m4v in Resources */, + 3B7D1C2F1BD43E7B00406412 /* OutingCell.xib in Resources */, + C7A6EAD21BD2E59E0064BD4A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EADB1BD2E59E0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE61BD2E59F0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6572F60BC1D6A5602B00B0EF /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 855589BC5EA84BD821CA49F1 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + A00DA03C0324E026CFABE807 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C7A6EAC01BD2E59E0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7D1C1B1BD2F75F00406412 /* EntryCell.m in Sources */, + C7A6EAFF1BD2EBD80064BD4A /* GJOutings.m in Sources */, + 3B7D1C271BD3EE4500406412 /* TextEntryViewController.m in Sources */, + 3B7D1C2D1BD43E6900406412 /* OutingCell.m in Sources */, + C7A6EB0F1BD409E90064BD4A /* GMSMarker+GJEntryArray.m in Sources */, + C7A6EACC1BD2E59E0064BD4A /* AppDelegate.m in Sources */, + C7A6EB001BD2EBD80064BD4A /* GJEntry.m in Sources */, + C76CA15F1BD47B0D00981F39 /* GMSMarker_WithArray.m in Sources */, + 3B7D1C2A1BD4249700406412 /* HLMapView.m in Sources */, + C7A6EAC91BD2E59E0064BD4A /* main.m in Sources */, + 3B7D1C351BD51CCB00406412 /* OutingsViewController.m in Sources */, + 3B7D1C1F1BD2F79E00406412 /* TripCollectionViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAD91BD2E59E0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EAE21BD2E59F0064BD4A /* HikerLiteTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE41BD2E59F0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EAED1BD2E59F0064BD4A /* HikerLiteUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C7A6EADF1BD2E59E0064BD4A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C7A6EAC31BD2E59E0064BD4A /* HikerLite */; + targetProxy = C7A6EADE1BD2E59E0064BD4A /* PBXContainerItemProxy */; + }; + C7A6EAEA1BD2E59F0064BD4A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C7A6EAC31BD2E59E0064BD4A /* HikerLite */; + targetProxy = C7A6EAE91BD2E59F0064BD4A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C7A6EAD11BD2E59E0064BD4A /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C7A6EAD61BD2E59E0064BD4A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C7A6EAEF1BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C7A6EAF01BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C7A6EAF21BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = HikerLite/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-l\"c++\"", + "-l\"icucore\"", + "-l\"z\"", + "-framework", + "\"AFNetworking\"", + "-framework", + "\"AVFoundation\"", + "-framework", + "\"Accelerate\"", + "-framework", + "\"Bolts\"", + "-framework", + "\"CoreBluetooth\"", + "-framework", + "\"CoreData\"", + "-framework", + "\"CoreGraphics\"", + "-framework", + "\"CoreLocation\"", + "-framework", + "\"CoreText\"", + "-framework", + "\"GLKit\"", + "-framework", + "\"GoogleMaps\"", + "-framework", + "\"ImageIO\"", + "-framework", + "\"LiquidFloatingActionButton\"", + "-framework", + "\"OpenGLES\"", + "-framework", + "\"Parse\"", + "-framework", + "\"ParseUI\"", + "-framework", + "\"QuartzCore\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-ObjC", + ); + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.elberdev.HikerLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Debug; + }; + C7A6EAF31BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = HikerLite/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.elberdev.HikerLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Release; + }; + C7A6EAF51BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = HikerLiteTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HikerLite.app/HikerLite"; + }; + name = Debug; + }; + C7A6EAF61BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = HikerLiteTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HikerLite.app/HikerLite"; + }; + name = Release; + }; + C7A6EAF81BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = HikerLiteUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = HikerLite; + USES_XCTRUNNER = YES; + }; + name = Debug; + }; + C7A6EAF91BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = HikerLiteUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = HikerLite; + USES_XCTRUNNER = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C7A6EABF1BD2E59E0064BD4A /* Build configuration list for PBXProject "HikerLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAEF1BD2E59F0064BD4A /* Debug */, + C7A6EAF01BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF11BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF21BD2E59F0064BD4A /* Debug */, + C7A6EAF31BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF41BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF51BD2E59F0064BD4A /* Debug */, + C7A6EAF61BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF71BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF81BD2E59F0064BD4A /* Debug */, + C7A6EAF91BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C7A6EABC1BD2E59E0064BD4A /* Project object */; +} diff --git a/HikerLite.xcodeproj/project.pbxproj.orig b/HikerLite.xcodeproj/project.pbxproj.orig new file mode 100644 index 0000000..8d2561c --- /dev/null +++ b/HikerLite.xcodeproj/project.pbxproj.orig @@ -0,0 +1,695 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3B7D1C1A1BD2F75F00406412 /* Entry.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C161BD2F75F00406412 /* Entry.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C1B1BD2F75F00406412 /* EntryCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C181BD2F75F00406412 /* EntryCell.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C1C1BD2F75F00406412 /* EntryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C191BD2F75F00406412 /* EntryCell.xib */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C1F1BD2F79E00406412 /* TripCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C231BD2F7E900406412 /* vid.m4v in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C211BD2F7E900406412 /* vid.m4v */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C241BD2F7E900406412 /* vid2.m4v in Resources */ = {isa = PBXBuildFile; fileRef = 3B7D1C221BD2F7E900406412 /* vid2.m4v */; settings = {ASSET_TAGS = (); }; }; + 3B7D1C271BD3EE4500406412 /* TextEntryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */; settings = {ASSET_TAGS = (); }; }; + B7711B1F931201E40D1551B8 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A35CDD57FC026BC52468D2A5 /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + C7A6EAC91BD2E59E0064BD4A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAC81BD2E59E0064BD4A /* main.m */; }; + C7A6EACC1BD2E59E0064BD4A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */; }; + C7A6EAD21BD2E59E0064BD4A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */; }; + C7A6EAD41BD2E59E0064BD4A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */; }; + C7A6EAD71BD2E59E0064BD4A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */; }; + C7A6EAE21BD2E59F0064BD4A /* HikerLiteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */; }; + C7A6EAED1BD2E59F0064BD4A /* HikerLiteUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */; }; + C7A6EAFF1BD2EBD80064BD4A /* GJOutings.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */; }; + C7A6EB001BD2EBD80064BD4A /* GJEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */; }; + C7A6EB021BD2EECE0064BD4A /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */; }; + C7A6EB081BD404DF0064BD4A /* HLMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EB071BD404DF0064BD4A /* HLMapView.m */; }; + C7A6EB0F1BD409E90064BD4A /* GMSMarker+GJEntryArray.m in Sources */ = {isa = PBXBuildFile; fileRef = C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C7A6EADE1BD2E59E0064BD4A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C7A6EABC1BD2E59E0064BD4A /* Project object */; + proxyType = 1; + remoteGlobalIDString = C7A6EAC31BD2E59E0064BD4A; + remoteInfo = HikerLite; + }; + C7A6EAE91BD2E59F0064BD4A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C7A6EABC1BD2E59E0064BD4A /* Project object */; + proxyType = 1; + remoteGlobalIDString = C7A6EAC31BD2E59E0064BD4A; + remoteInfo = HikerLite; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 3B7D1C151BD2F75F00406412 /* Entry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Entry.h; sourceTree = ""; }; + 3B7D1C161BD2F75F00406412 /* Entry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Entry.m; sourceTree = ""; }; + 3B7D1C171BD2F75F00406412 /* EntryCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EntryCell.h; sourceTree = ""; }; + 3B7D1C181BD2F75F00406412 /* EntryCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EntryCell.m; sourceTree = ""; }; + 3B7D1C191BD2F75F00406412 /* EntryCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EntryCell.xib; sourceTree = ""; }; + 3B7D1C1D1BD2F79E00406412 /* TripCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TripCollectionViewController.h; sourceTree = ""; }; + 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TripCollectionViewController.m; sourceTree = ""; }; + 3B7D1C211BD2F7E900406412 /* vid.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = vid.m4v; sourceTree = ""; }; + 3B7D1C221BD2F7E900406412 /* vid2.m4v */ = {isa = PBXFileReference; lastKnownFileType = file; path = vid2.m4v; sourceTree = ""; }; + 3B7D1C251BD3EE4500406412 /* TextEntryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextEntryViewController.h; sourceTree = ""; }; + 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextEntryViewController.m; sourceTree = ""; }; + 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + A35CDD57FC026BC52468D2A5 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HikerLite.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAC81BD2E59E0064BD4A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + C7A6EACA1BD2E59E0064BD4A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + C7A6EAD11BD2E59E0064BD4A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C7A6EAD61BD2E59E0064BD4A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C7A6EAD81BD2E59E0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HikerLiteTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HikerLiteTests.m; sourceTree = ""; }; + C7A6EAE31BD2E59F0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HikerLiteUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HikerLiteUITests.m; sourceTree = ""; }; + C7A6EAEE1BD2E59F0064BD4A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C7A6EAFB1BD2EBB90064BD4A /* GJOutings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GJOutings.h; sourceTree = ""; }; + C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GJOutings.m; sourceTree = ""; }; + C7A6EAFD1BD2EBD80064BD4A /* GJEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GJEntry.h; sourceTree = ""; }; + C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GJEntry.m; sourceTree = ""; }; + C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + C7A6EB061BD404DF0064BD4A /* HLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HLMapView.h; sourceTree = ""; }; + C7A6EB071BD404DF0064BD4A /* HLMapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HLMapView.m; sourceTree = ""; }; + C7A6EB0D1BD409E90064BD4A /* GMSMarker+GJEntryArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GMSMarker+GJEntryArray.h"; sourceTree = ""; }; + C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GMSMarker+GJEntryArray.m"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C7A6EAC11BD2E59E0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EB021BD2EECE0064BD4A /* CoreLocation.framework in Frameworks */, + B7711B1F931201E40D1551B8 /* Pods.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EADA1BD2E59E0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE51BD2E59F0064BD4A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 10491F166C7E17D64159B238 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C7A6EB011BD2EECE0064BD4A /* CoreLocation.framework */, + A35CDD57FC026BC52468D2A5 /* Pods.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 2372FEC4BA0F8EDC2DE27371 /* Pods */ = { + isa = PBXGroup; + children = ( + 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */, + 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 3B7D1C201BD2F7CB00406412 /* Resources */ = { + isa = PBXGroup; + children = ( + 3B7D1C211BD2F7E900406412 /* vid.m4v */, + 3B7D1C221BD2F7E900406412 /* vid2.m4v */, + ); + name = Resources; + sourceTree = ""; + }; + C7A6EABB1BD2E59E0064BD4A = { + isa = PBXGroup; + children = ( + C7A6EB0C1BD4099B0064BD4A /* Categories */, + C7A6EAFA1BD2EAF80064BD4A /* Parse */, + C7A6EAC61BD2E59E0064BD4A /* HikerLite */, + C7A6EAE01BD2E59E0064BD4A /* HikerLiteTests */, + C7A6EAEB1BD2E59F0064BD4A /* HikerLiteUITests */, + C7A6EAC51BD2E59E0064BD4A /* Products */, + 2372FEC4BA0F8EDC2DE27371 /* Pods */, + 10491F166C7E17D64159B238 /* Frameworks */, + ); + sourceTree = ""; + }; + C7A6EAC51BD2E59E0064BD4A /* Products */ = { + isa = PBXGroup; + children = ( + C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */, + C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */, + C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + C7A6EAC61BD2E59E0064BD4A /* HikerLite */ = { + isa = PBXGroup; + children = ( + C7A6EACA1BD2E59E0064BD4A /* AppDelegate.h */, + C7A6EACB1BD2E59E0064BD4A /* AppDelegate.m */, + 3B7D1C151BD2F75F00406412 /* Entry.h */, + 3B7D1C161BD2F75F00406412 /* Entry.m */, + 3B7D1C171BD2F75F00406412 /* EntryCell.h */, + 3B7D1C181BD2F75F00406412 /* EntryCell.m */, + 3B7D1C191BD2F75F00406412 /* EntryCell.xib */, + 3B7D1C1D1BD2F79E00406412 /* TripCollectionViewController.h */, + 3B7D1C1E1BD2F79E00406412 /* TripCollectionViewController.m */, +<<<<<<< HEAD + C7A6EB061BD404DF0064BD4A /* HLMapView.h */, + C7A6EB071BD404DF0064BD4A /* HLMapView.m */, +======= + 3B7D1C251BD3EE4500406412 /* TextEntryViewController.h */, + 3B7D1C261BD3EE4500406412 /* TextEntryViewController.m */, +>>>>>>> 11e49f228e2aced68ae585fed2304373c5ce8d11 + 3B7D1C201BD2F7CB00406412 /* Resources */, + C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */, + C7A6EAD31BD2E59E0064BD4A /* Assets.xcassets */, + C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */, + C7A6EAD81BD2E59E0064BD4A /* Info.plist */, + C7A6EAC71BD2E59E0064BD4A /* Supporting Files */, + ); + path = HikerLite; + sourceTree = ""; + }; + C7A6EAC71BD2E59E0064BD4A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + C7A6EAC81BD2E59E0064BD4A /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + C7A6EAE01BD2E59E0064BD4A /* HikerLiteTests */ = { + isa = PBXGroup; + children = ( + C7A6EAE11BD2E59F0064BD4A /* HikerLiteTests.m */, + C7A6EAE31BD2E59F0064BD4A /* Info.plist */, + ); + path = HikerLiteTests; + sourceTree = ""; + }; + C7A6EAEB1BD2E59F0064BD4A /* HikerLiteUITests */ = { + isa = PBXGroup; + children = ( + C7A6EAEC1BD2E59F0064BD4A /* HikerLiteUITests.m */, + C7A6EAEE1BD2E59F0064BD4A /* Info.plist */, + ); + path = HikerLiteUITests; + sourceTree = ""; + }; + C7A6EAFA1BD2EAF80064BD4A /* Parse */ = { + isa = PBXGroup; + children = ( + C7A6EAFB1BD2EBB90064BD4A /* GJOutings.h */, + C7A6EAFC1BD2EBD80064BD4A /* GJOutings.m */, + C7A6EAFD1BD2EBD80064BD4A /* GJEntry.h */, + C7A6EAFE1BD2EBD80064BD4A /* GJEntry.m */, + ); + name = Parse; + sourceTree = ""; + }; + C7A6EB0C1BD4099B0064BD4A /* Categories */ = { + isa = PBXGroup; + children = ( + C7A6EB0D1BD409E90064BD4A /* GMSMarker+GJEntryArray.h */, + C7A6EB0E1BD409E90064BD4A /* GMSMarker+GJEntryArray.m */, + ); + name = Categories; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C7A6EAC31BD2E59E0064BD4A /* HikerLite */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF11BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLite" */; + buildPhases = ( + 6572F60BC1D6A5602B00B0EF /* Check Pods Manifest.lock */, + C7A6EAC01BD2E59E0064BD4A /* Sources */, + C7A6EAC11BD2E59E0064BD4A /* Frameworks */, + C7A6EAC21BD2E59E0064BD4A /* Resources */, + A00DA03C0324E026CFABE807 /* Embed Pods Frameworks */, + 855589BC5EA84BD821CA49F1 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HikerLite; + productName = HikerLite; + productReference = C7A6EAC41BD2E59E0064BD4A /* HikerLite.app */; + productType = "com.apple.product-type.application"; + }; + C7A6EADC1BD2E59E0064BD4A /* HikerLiteTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF41BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteTests" */; + buildPhases = ( + C7A6EAD91BD2E59E0064BD4A /* Sources */, + C7A6EADA1BD2E59E0064BD4A /* Frameworks */, + C7A6EADB1BD2E59E0064BD4A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C7A6EADF1BD2E59E0064BD4A /* PBXTargetDependency */, + ); + name = HikerLiteTests; + productName = HikerLiteTests; + productReference = C7A6EADD1BD2E59E0064BD4A /* HikerLiteTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + C7A6EAE71BD2E59F0064BD4A /* HikerLiteUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C7A6EAF71BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteUITests" */; + buildPhases = ( + C7A6EAE41BD2E59F0064BD4A /* Sources */, + C7A6EAE51BD2E59F0064BD4A /* Frameworks */, + C7A6EAE61BD2E59F0064BD4A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C7A6EAEA1BD2E59F0064BD4A /* PBXTargetDependency */, + ); + name = HikerLiteUITests; + productName = HikerLiteUITests; + productReference = C7A6EAE81BD2E59F0064BD4A /* HikerLiteUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C7A6EABC1BD2E59E0064BD4A /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = "Varindra Hart"; + TargetAttributes = { + C7A6EAC31BD2E59E0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3HZSHG8SUA; + }; + C7A6EADC1BD2E59E0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3L76772298; + TestTargetID = C7A6EAC31BD2E59E0064BD4A; + }; + C7A6EAE71BD2E59F0064BD4A = { + CreatedOnToolsVersion = 7.0; + DevelopmentTeam = 3L76772298; + TestTargetID = C7A6EAC31BD2E59E0064BD4A; + }; + }; + }; + buildConfigurationList = C7A6EABF1BD2E59E0064BD4A /* Build configuration list for PBXProject "HikerLite" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C7A6EABB1BD2E59E0064BD4A; + productRefGroup = C7A6EAC51BD2E59E0064BD4A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C7A6EAC31BD2E59E0064BD4A /* HikerLite */, + C7A6EADC1BD2E59E0064BD4A /* HikerLiteTests */, + C7A6EAE71BD2E59F0064BD4A /* HikerLiteUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C7A6EAC21BD2E59E0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7D1C1C1BD2F75F00406412 /* EntryCell.xib in Resources */, + C7A6EAD71BD2E59E0064BD4A /* LaunchScreen.storyboard in Resources */, + 3B7D1C231BD2F7E900406412 /* vid.m4v in Resources */, + C7A6EAD41BD2E59E0064BD4A /* Assets.xcassets in Resources */, + 3B7D1C241BD2F7E900406412 /* vid2.m4v in Resources */, + C7A6EAD21BD2E59E0064BD4A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EADB1BD2E59E0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE61BD2E59F0064BD4A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6572F60BC1D6A5602B00B0EF /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 855589BC5EA84BD821CA49F1 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + A00DA03C0324E026CFABE807 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C7A6EAC01BD2E59E0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7D1C1B1BD2F75F00406412 /* EntryCell.m in Sources */, + C7A6EAFF1BD2EBD80064BD4A /* GJOutings.m in Sources */, + 3B7D1C271BD3EE4500406412 /* TextEntryViewController.m in Sources */, + 3B7D1C1A1BD2F75F00406412 /* Entry.m in Sources */, + C7A6EB081BD404DF0064BD4A /* HLMapView.m in Sources */, + C7A6EB0F1BD409E90064BD4A /* GMSMarker+GJEntryArray.m in Sources */, + C7A6EACC1BD2E59E0064BD4A /* AppDelegate.m in Sources */, + C7A6EB001BD2EBD80064BD4A /* GJEntry.m in Sources */, + C7A6EAC91BD2E59E0064BD4A /* main.m in Sources */, + 3B7D1C1F1BD2F79E00406412 /* TripCollectionViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAD91BD2E59E0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EAE21BD2E59F0064BD4A /* HikerLiteTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7A6EAE41BD2E59F0064BD4A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C7A6EAED1BD2E59F0064BD4A /* HikerLiteUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C7A6EADF1BD2E59E0064BD4A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C7A6EAC31BD2E59E0064BD4A /* HikerLite */; + targetProxy = C7A6EADE1BD2E59E0064BD4A /* PBXContainerItemProxy */; + }; + C7A6EAEA1BD2E59F0064BD4A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C7A6EAC31BD2E59E0064BD4A /* HikerLite */; + targetProxy = C7A6EAE91BD2E59F0064BD4A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + C7A6EAD01BD2E59E0064BD4A /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C7A6EAD11BD2E59E0064BD4A /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C7A6EAD51BD2E59E0064BD4A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C7A6EAD61BD2E59E0064BD4A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C7A6EAEF1BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C7A6EAF01BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C7A6EAF21BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C8E718DDC744BFA3EAAA868 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + INFOPLIST_FILE = HikerLite/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Debug; + }; + C7A6EAF31BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8454CEDF3E921FC6E6CE073A /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + INFOPLIST_FILE = HikerLite/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLite; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Release; + }; + C7A6EAF51BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = HikerLiteTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HikerLite.app/HikerLite"; + }; + name = Debug; + }; + C7A6EAF61BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = HikerLiteTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HikerLite.app/HikerLite"; + }; + name = Release; + }; + C7A6EAF81BD2E59F0064BD4A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = HikerLiteUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = HikerLite; + USES_XCTRUNNER = YES; + }; + name = Debug; + }; + C7A6EAF91BD2E59F0064BD4A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = HikerLiteUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = nyc.c4q.vhart.HikerLiteUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = HikerLite; + USES_XCTRUNNER = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C7A6EABF1BD2E59E0064BD4A /* Build configuration list for PBXProject "HikerLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAEF1BD2E59F0064BD4A /* Debug */, + C7A6EAF01BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF11BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF21BD2E59F0064BD4A /* Debug */, + C7A6EAF31BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF41BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF51BD2E59F0064BD4A /* Debug */, + C7A6EAF61BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C7A6EAF71BD2E59F0064BD4A /* Build configuration list for PBXNativeTarget "HikerLiteUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C7A6EAF81BD2E59F0064BD4A /* Debug */, + C7A6EAF91BD2E59F0064BD4A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C7A6EABC1BD2E59E0064BD4A /* Project object */; +} diff --git a/HikerLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HikerLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ca50003 --- /dev/null +++ b/HikerLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HikerLite.xcworkspace/contents.xcworkspacedata b/HikerLite.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..049cff4 --- /dev/null +++ b/HikerLite.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/HikerLite/AppDelegate.h b/HikerLite/AppDelegate.h new file mode 100644 index 0000000..0b771cf --- /dev/null +++ b/HikerLite/AppDelegate.h @@ -0,0 +1,16 @@ +// +// AppDelegate.h +// HikerLite +// +// Created by Varindra Hart on 10/17/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/HikerLite/AppDelegate.m b/HikerLite/AppDelegate.m new file mode 100644 index 0000000..83e1cf4 --- /dev/null +++ b/HikerLite/AppDelegate.m @@ -0,0 +1,57 @@ +// +// AppDelegate.m +// HikerLite +// +// Created by Varindra Hart on 10/17/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "AppDelegate.h" +#import +#import "GJEntry.h" +#import "GJOutings.h" +@import GoogleMaps; + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + [Parse setApplicationId:@"hlnFxyJQxmm2sch6SIlwRVIve0uI1nn5n5ygTZyS" clientKey:@"7ZwLOo8CTi8ZVnT9qK98URVtVsvqeJFPwiDfbO2Y"]; + + + [GJOutings registerSubclass]; + [GJEntry registerSubclass]; + + [GMSServices provideAPIKey:@"AIzaSyDWOxAPyTwNK28M-bUSxv0UfRXywVlwYKQ"]; + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/HikerLite/Assets.xcassets/AppIcon.appiconset/Contents.json b/HikerLite/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..36d2c80 --- /dev/null +++ b/HikerLite/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/Contents.json b/HikerLite/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/HikerLite/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/cameraIcon.imageset/Contents.json b/HikerLite/Assets.xcassets/cameraIcon.imageset/Contents.json new file mode 100644 index 0000000..e41aa25 --- /dev/null +++ b/HikerLite/Assets.xcassets/cameraIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "cameraIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/cameraIcon.imageset/cameraIcon.png b/HikerLite/Assets.xcassets/cameraIcon.imageset/cameraIcon.png new file mode 100644 index 0000000..e000206 Binary files /dev/null and b/HikerLite/Assets.xcassets/cameraIcon.imageset/cameraIcon.png differ diff --git a/HikerLite/Assets.xcassets/checkmark.imageset/Contents.json b/HikerLite/Assets.xcassets/checkmark.imageset/Contents.json new file mode 100644 index 0000000..07764d4 --- /dev/null +++ b/HikerLite/Assets.xcassets/checkmark.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkmark.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/checkmark.imageset/checkmark.png b/HikerLite/Assets.xcassets/checkmark.imageset/checkmark.png new file mode 100644 index 0000000..b9917fe Binary files /dev/null and b/HikerLite/Assets.xcassets/checkmark.imageset/checkmark.png differ diff --git a/HikerLite/Assets.xcassets/composeIcon.imageset/Contents.json b/HikerLite/Assets.xcassets/composeIcon.imageset/Contents.json new file mode 100644 index 0000000..39cc238 --- /dev/null +++ b/HikerLite/Assets.xcassets/composeIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "composeIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/composeIcon.imageset/composeIcon.png b/HikerLite/Assets.xcassets/composeIcon.imageset/composeIcon.png new file mode 100644 index 0000000..49eb04a Binary files /dev/null and b/HikerLite/Assets.xcassets/composeIcon.imageset/composeIcon.png differ diff --git a/HikerLite/Assets.xcassets/outingsIcon.imageset/Contents.json b/HikerLite/Assets.xcassets/outingsIcon.imageset/Contents.json new file mode 100644 index 0000000..9a24722 --- /dev/null +++ b/HikerLite/Assets.xcassets/outingsIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "outingsIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/outingsIcon.imageset/outingsIcon.png b/HikerLite/Assets.xcassets/outingsIcon.imageset/outingsIcon.png new file mode 100644 index 0000000..2c5da2f Binary files /dev/null and b/HikerLite/Assets.xcassets/outingsIcon.imageset/outingsIcon.png differ diff --git a/HikerLite/Assets.xcassets/pinIcon.imageset/Contents.json b/HikerLite/Assets.xcassets/pinIcon.imageset/Contents.json new file mode 100644 index 0000000..fa599a1 --- /dev/null +++ b/HikerLite/Assets.xcassets/pinIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "pinIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/pinIcon.imageset/pinIcon.png b/HikerLite/Assets.xcassets/pinIcon.imageset/pinIcon.png new file mode 100644 index 0000000..d2f88c0 Binary files /dev/null and b/HikerLite/Assets.xcassets/pinIcon.imageset/pinIcon.png differ diff --git a/HikerLite/Assets.xcassets/wilderness1.imageset/Contents.json b/HikerLite/Assets.xcassets/wilderness1.imageset/Contents.json new file mode 100644 index 0000000..acb29e9 --- /dev/null +++ b/HikerLite/Assets.xcassets/wilderness1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "wilderness1.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/wilderness1.imageset/wilderness1.jpg b/HikerLite/Assets.xcassets/wilderness1.imageset/wilderness1.jpg new file mode 100644 index 0000000..e75730e Binary files /dev/null and b/HikerLite/Assets.xcassets/wilderness1.imageset/wilderness1.jpg differ diff --git a/HikerLite/Assets.xcassets/wilderness2.imageset/Contents.json b/HikerLite/Assets.xcassets/wilderness2.imageset/Contents.json new file mode 100644 index 0000000..918692b --- /dev/null +++ b/HikerLite/Assets.xcassets/wilderness2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "wilderness2.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HikerLite/Assets.xcassets/wilderness2.imageset/wilderness2.jpg b/HikerLite/Assets.xcassets/wilderness2.imageset/wilderness2.jpg new file mode 100644 index 0000000..19f12e6 Binary files /dev/null and b/HikerLite/Assets.xcassets/wilderness2.imageset/wilderness2.jpg differ diff --git a/HikerLite/Base.lproj/LaunchScreen.storyboard b/HikerLite/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..900bc17 --- /dev/null +++ b/HikerLite/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HikerLite/Base.lproj/Main.storyboard b/HikerLite/Base.lproj/Main.storyboard new file mode 100644 index 0000000..90a7ceb --- /dev/null +++ b/HikerLite/Base.lproj/Main.storyboard @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HikerLite/EntryCell.h b/HikerLite/EntryCell.h new file mode 100644 index 0000000..279fcbb --- /dev/null +++ b/HikerLite/EntryCell.h @@ -0,0 +1,19 @@ +// +// EntryCell.h +// GoJournal +// +// Created by Elber Carneiro on 10/10/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import +#import + +@interface EntryCell : UICollectionViewCell + +@property (weak, nonatomic) IBOutlet PFImageView *photoView; +@property (weak, nonatomic) IBOutlet UILabel *descriptionLabel; +@property (weak, nonatomic) IBOutlet UIView *videoView; +@property (weak, nonatomic) IBOutlet UIView *descriptionContainer; + +@end diff --git a/HikerLite/EntryCell.m b/HikerLite/EntryCell.m new file mode 100644 index 0000000..6e7689e --- /dev/null +++ b/HikerLite/EntryCell.m @@ -0,0 +1,13 @@ +// +// EntryCell.m +// GoJournal +// +// Created by Elber Carneiro on 10/10/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "EntryCell.h" + +@implementation EntryCell + +@end diff --git a/HikerLite/EntryCell.xib b/HikerLite/EntryCell.xib new file mode 100644 index 0000000..82fc992 --- /dev/null +++ b/HikerLite/EntryCell.xib @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HikerLite/HLMapView.h b/HikerLite/HLMapView.h new file mode 100644 index 0000000..57a78ab --- /dev/null +++ b/HikerLite/HLMapView.h @@ -0,0 +1,16 @@ +// +// HLMapView.h +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import +#import "GJOutings.h" + +@interface HLMapView : UIViewController + +@property (nonatomic) GJOutings *currentOuting; + +@end diff --git a/HikerLite/HLMapView.m b/HikerLite/HLMapView.m new file mode 100644 index 0000000..035cd01 --- /dev/null +++ b/HikerLite/HLMapView.m @@ -0,0 +1,153 @@ +// +// HLMapView.m +// HikerLite +// +// Created by Varindra Hart on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "HLMapView.h" +@import GoogleMaps; +#import "GMSMarker+GJEntryArray.h" +#import "GMSMarker_GJEntriesArray.h" +#import "GMSMarker_WithArray.h" + +@interface HLMapView () +@property (nonatomic) GMSMapView *mapView_; +@property (nonatomic) NSMutableArray *markersArray; +@property (nonatomic) NSMutableArray *allEntries; +@end + +@implementation HLMapView + +- (void)viewDidLoad { + [super viewDidLoad]; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss:)]; + [self setUpMapsAndMarkers]; + // Do any additional setup after loading the view. +} + +- (void)setUpMapsAndMarkers { + + self.allEntries = [[NSMutableArray alloc] initWithArray:self.currentOuting.entriesArray]; + [self.allEntries removeObjectIdenticalTo:[NSNull null]]; + + self.markersArray = [NSMutableArray new]; + [self makeSectionalMarkersForMap]; + +// CLLocationCoordinate2D center = [self findCenterForMarkers]; + + + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:40.744 + longitude:-73.938 + zoom:12]; + self.mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.mapView_.myLocationEnabled = YES; + //self.viewForMap = self.mapView_; + self.view = self.mapView_; + // Creates a marker in the center of the map. + GMSMarker *marker = [[GMSMarker alloc] init]; + marker.position = CLLocationCoordinate2DMake(40.744,-73.938); + // marker.title = @"Sydney"; + // marker.snippet = @"Australia"; + marker.map = self.mapView_; + + for (GMSMarker_WithArray *marker in self.markersArray) { + marker.map = self.mapView_; + marker.appearAnimation = kGMSMarkerAnimationPop; + } +} + +- (void)dismiss:(UIBarButtonItem *)doneButton { + + [self dismissViewControllerAnimated:YES completion:nil]; + +} + +#pragma mark - Location Methods and Sorting; + +- (CLLocationCoordinate2D)findCenterPoint:(CLLocationCoordinate2D)_lo1 :(CLLocationCoordinate2D)_loc2 { + CLLocationCoordinate2D center; + + double lon1 = _lo1.longitude * M_PI / 180; + double lon2 = _loc2.longitude * M_PI / 180; + + double lat1 = _lo1.latitude * M_PI / 180; + double lat2 = _loc2.latitude * M_PI / 180; + + double dLon = lon2 - lon1; + + double x = cos(lat2) * cos(dLon); + double y = cos(lat2) * sin(dLon); + + double lat3 = atan2( sin(lat1) + sin(lat2), sqrt((cos(lat1) + x) * (cos(lat1) + x) + y * y) ); + double lon3 = lon1 + atan2(y, cos(lat1) + x); + + center.latitude = lat3 * 180 / M_PI; + center.longitude = lon3 * 180 / M_PI; + + return center; +} + +- (void)makeSectionalMarkersForMap { + + while (self.allEntries.count != 0) { + + GMSMarker_WithArray *newMarker = [[GMSMarker_WithArray alloc]init]; + newMarker.entriesArrayForLocation = [NSMutableArray new]; + + GJEntry *topEntry = self.allEntries.firstObject; + newMarker.position = CLLocationCoordinate2DMake(topEntry.location.latitude, topEntry.location.longitude); + + [newMarker.entriesArrayForLocation addObject:topEntry]; + [self.allEntries removeObject:topEntry]; + + for (GJEntry *entry in self.allEntries) { + + + GJEntry *comparisonEntry = newMarker.entriesArrayForLocation.firstObject; + if (!(entry ==NULL) ) { + if([comparisonEntry.location distanceInKilometersTo:entry.location] < .25f) + [newMarker.entriesArrayForLocation addObject:entry]; + } + } + + for (GJEntry *entry in newMarker.entriesArrayForLocation) { + + for (int i =0 ; i + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSLocationAlwaysUsageDescription + Message 2 + NSLocationWhenInUseUsageDescription + Message 1 + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/HikerLite/OutingCell.h b/HikerLite/OutingCell.h new file mode 100644 index 0000000..3bd4e9a --- /dev/null +++ b/HikerLite/OutingCell.h @@ -0,0 +1,18 @@ +// +// OutingCell.h +// HikerLite +// +// Created by Elber Carneiro on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import + +@interface OutingCell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UIImageView *previewIcon; +@property (weak, nonatomic) IBOutlet UILabel *name; +@property (weak, nonatomic) IBOutlet UIView *containerView; +@property (weak, nonatomic) IBOutlet UIImageView *checkmark; + +@end diff --git a/HikerLite/OutingCell.m b/HikerLite/OutingCell.m new file mode 100644 index 0000000..1ad137d --- /dev/null +++ b/HikerLite/OutingCell.m @@ -0,0 +1,23 @@ +// +// OutingCell.m +// HikerLite +// +// Created by Elber Carneiro on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "OutingCell.h" + +@implementation OutingCell + +- (void)awakeFromNib { + self.containerView.layer.cornerRadius = 10; + self.selectionStyle = UITableViewCellSelectionStyleNone; + self.checkmark.image = [UIImage imageNamed:@"checkmark"]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; +} + +@end diff --git a/HikerLite/OutingCell.xib b/HikerLite/OutingCell.xib new file mode 100644 index 0000000..d03967d --- /dev/null +++ b/HikerLite/OutingCell.xib @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HikerLite/OutingsViewController.h b/HikerLite/OutingsViewController.h new file mode 100644 index 0000000..73b5d30 --- /dev/null +++ b/HikerLite/OutingsViewController.h @@ -0,0 +1,13 @@ +// +// OutingsViewController.h +// HikerLite +// +// Created by Elber Carneiro on 10/19/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import + +@interface OutingsViewController : UIViewController + +@end diff --git a/HikerLite/OutingsViewController.m b/HikerLite/OutingsViewController.m new file mode 100644 index 0000000..eb59778 --- /dev/null +++ b/HikerLite/OutingsViewController.m @@ -0,0 +1,156 @@ +// +// OutingsViewController.m +// HikerLite +// +// Created by Elber Carneiro on 10/19/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "GJOutings.h" +#import "OutingCell.h" +#import "OutingsViewController.h" + +@interface OutingsViewController () + +@property (nonatomic) NSMutableArray *outings; +@property (weak, nonatomic) IBOutlet UITableView *tableView; +@property (weak, nonatomic) IBOutlet UIView *createOutingView; +@property (weak, nonatomic) IBOutlet UIButton *saveButton; +@property (weak, nonatomic) IBOutlet UITextField *createOutingName; + +@end + +@implementation OutingsViewController + +static NSString * const cellIdentifier = @"outingCellIdentifier"; +static NSString * const selectedOuting = @"selectedOuting"; + +#pragma mark - Lifecycle methods + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss:)]; + + [self setupCells]; + + [self fetchOutings]; + + [self setupCreateOutingView]; +} + +#pragma mark - Setup methods + +- (void)setupCells { + + UINib *cellNib = [UINib nibWithNibName:@"OutingCell" bundle:nil]; + [self.tableView registerNib:cellNib forCellReuseIdentifier:cellIdentifier]; + + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; +} + +- (void)setupCreateOutingView { + self.createOutingView.layer.cornerRadius = 10; + self.createOutingView.layer.borderColor = [UIColor redColor].CGColor; + self.createOutingView.layer.borderWidth = 1; + + self.createOutingName.delegate = self; + self.createOutingName.placeholder = @"New outing name"; +} + +- (void)fetchOutings { + PFQuery *query = [PFQuery queryWithClassName:@"GJOutings"]; + //[query includeKey:@"entriesArray"]; + [query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) { + if (objects.count!=0) { + + NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdDate" ascending:NO]; + [objects sortedArrayUsingDescriptors:@[descriptor]]; + + self.outings = [NSMutableArray arrayWithArray:objects]; + NSLog(@"self.outings: %@", self.outings); + + [self.tableView reloadData]; + } + }]; +} + +#pragma mark - Button action methods + +- (IBAction)didTapSave:(UIButton *)sender { + + GJOutings *newOuting = [[GJOutings alloc]initWithNewEntriesArray]; + newOuting.createdDate = [NSDate date]; + newOuting.outingName = self.createOutingName.text; + [newOuting saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) { + + NSLog(@"Outing created"); + [self.outings addObject:newOuting]; + [self.tableView reloadData]; + + }]; + + [self.createOutingName endEditing:YES]; + self.createOutingName.text = @""; +} + +- (void)dismiss:(UIBarButtonSystemItem)sender { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark - UITextFieldDelegate + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + self.createOutingName.placeholder = @""; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField { + self.createOutingName.placeholder = @"New outing name"; +} + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.outings.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + OutingCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; + + cell.name.text = [self.outings[indexPath.row] outingName]; + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 60; +} + +#pragma mark - UITableViewDelegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [[NSUserDefaults standardUserDefaults] setValue:@(indexPath.row) forKey:selectedOuting]; + + OutingCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; + + NSLog(@"Changed selected outing to %d", indexPath.row); + cell.checkmark.hidden = NO; +} + +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { + + OutingCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; + + NSLog(@"Changed selected outing to %d", indexPath.row); + cell.checkmark.hidden = YES; +} + + +@end diff --git a/HikerLite/TextEntryViewController.h b/HikerLite/TextEntryViewController.h new file mode 100644 index 0000000..15db300 --- /dev/null +++ b/HikerLite/TextEntryViewController.h @@ -0,0 +1,17 @@ +// +// TextEntryViewController.h +// HikerLite +// +// Created by Elber Carneiro on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "GJOutings.h" +#import + +@interface TextEntryViewController : UIViewController + +@property (nonatomic) GJOutings *currentOuting; +@property (nonatomic) CLLocationManager *locationManager; + +@end diff --git a/HikerLite/TextEntryViewController.m b/HikerLite/TextEntryViewController.m new file mode 100644 index 0000000..4550df4 --- /dev/null +++ b/HikerLite/TextEntryViewController.m @@ -0,0 +1,83 @@ +// +// TextEntryViewController.m +// HikerLite +// +// Created by Elber Carneiro on 10/18/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import "TextEntryViewController.h" +#import "GJEntry.h" + +@interface TextEntryViewController () + +@property (weak, nonatomic) IBOutlet UIView *containerView; +@property (weak, nonatomic) IBOutlet UITextView *entryTextView; +@property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardOffsetConstraint; + +@end + +@implementation TextEntryViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.automaticallyAdjustsScrollViewInsets = NO; + + self.containerView.layer.cornerRadius = 10; + [self.entryTextView becomeFirstResponder]; + + [self registerForKeyboardNotifications]; +} + +// Setting up keyboard notifications. +- (void)registerForKeyboardNotifications +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillShow:) + name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillHide:) + name:UIKeyboardWillHideNotification object:nil]; +} + +// Called when the UIKeyboardWillShowNotification is sent. +- (void)keyboardWillShow:(NSNotification *)notification { + + NSDictionary *info = [notification userInfo]; + CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; + + if (self.keyboardOffsetConstraint.constant == 16) { + self.keyboardOffsetConstraint.constant += kbSize.height; + } +} + +- (void)keyboardWillHide:(NSNotification *)notification { + + self.keyboardOffsetConstraint.constant = 16; +} + +#pragma mark - Button actions + +- (IBAction)didTapSave:(UIBarButtonItem *)sender { + + GJEntry *newEntry = [GJEntry new]; + newEntry.createdDate = [NSDate date]; + newEntry.mediaType = @"public.text"; + newEntry.textMedia = self.entryTextView.text; + newEntry.location = [PFGeoPoint geoPointWithLocation:self.locationManager.location]; + + [self.currentOuting.entriesArray addObject:newEntry]; + [self.currentOuting saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) { + NSLog(@"Text saved"); + }]; + + [self.view endEditing:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (IBAction)didTapCancel:(UIBarButtonItem *)sender { + [self.view endEditing:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/HikerLite/TripCollectionViewController.h b/HikerLite/TripCollectionViewController.h new file mode 100644 index 0000000..7d6a161 --- /dev/null +++ b/HikerLite/TripCollectionViewController.h @@ -0,0 +1,17 @@ +// +// TripCollectionViewController.h +// GoJournal +// +// Created by Elber Carneiro on 10/11/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import +#import "LiquidFloatingActionButton-Swift.h" +#import "GJOutings.h" + +@interface TripCollectionViewController : UIViewController + +@property (nonatomic) GJOutings *currentOuting; + +@end diff --git a/HikerLite/TripCollectionViewController.m b/HikerLite/TripCollectionViewController.m new file mode 100644 index 0000000..829a877 --- /dev/null +++ b/HikerLite/TripCollectionViewController.m @@ -0,0 +1,453 @@ +// +// TripCollectionViewController.m +// GoJournal +// +// Created by Elber Carneiro on 10/11/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "EntryCell.h" +#import "HLMapView.h" +#import "LiquidFloatingActionButton-Swift.h" +#import "OutingsViewController.h" +#import "TextEntryViewController.h" +#import "TripCollectionViewController.h" + +#import +#import +#import + +@interface TripCollectionViewController () + +@property (nonatomic) UICollectionView *collectionView; +@property (nonatomic) NSMutableArray *cells; +@property (nonatomic) LiquidFloatingActionButton *floatingActionButton; +@property (nonatomic) CGFloat latitude; +@property (nonatomic) CGFloat longitude; +@property (nonatomic) NSString *forecast; +@property (nonatomic) CLLocationManager *locationManager; +@property (nonatomic) UIImagePickerController *imagePicker; +@property (nonatomic) NSInteger selectedOuting; + +@end + +@implementation TripCollectionViewController + +static NSString * const reuseIdentifier = @"entryCellIdentifier"; +static NSString * const selectedOuting = @"selectedOuting"; +static NSString * const apiKey = @"53bac750b0228783a50a48bda0d2d1ce"; + +#pragma mark - Lifecycle methods + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self setupCollectionView]; + + [self setupFloatingActionButton]; + + [self setupLocation]; + + [self setupLocationManager]; + + [self setupImagePicker]; + + [self fetchSelectedOuting]; + + [self fetchWeatherData]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + self.currentOuting = nil; + [self fetchOutings]; + + [self.floatingActionButton close]; +} + +#pragma mark - Setup methods + +- (void)setupCollectionView { + + // collection view layout setup + UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init]; + NSLog(@"layout: %@", layout); + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + // collection view setup + self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout]; + [self.collectionView setDataSource:self]; + [self.collectionView setDelegate:self]; + [self.collectionView setBackgroundColor:[UIColor whiteColor]]; + + // register cell prototype + [self.collectionView registerNib:[UINib nibWithNibName:@"EntryCell" bundle:nil] forCellWithReuseIdentifier:reuseIdentifier]; + + // add collection view to view controller + [self.view addSubview:self.collectionView]; +} + +- (void)setupFloatingActionButton { + + NSInteger buttonRadius = floor(0.135 * self.view.frame.size.width); + NSLog(@"self.view.frame.size.width: %f", self.view.frame.size.width); + + CGRect frame = CGRectMake(self.view.frame.size.width - buttonRadius - 16, + self.view.frame.size.height - buttonRadius - 16, + buttonRadius, + buttonRadius); + + self.floatingActionButton = [[LiquidFloatingActionButton alloc] initWithFrame:frame]; + self.floatingActionButton.delegate = self; + self.floatingActionButton.dataSource = self; + + self.cells = [[NSMutableArray alloc] init]; + + LiquidFloatingCell *cellCamera = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"cameraIcon"]]; + LiquidFloatingCell *cellText = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"composeIcon"]]; + LiquidFloatingCell *cellMap = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"pinIcon"]]; + LiquidFloatingCell *cellOutings = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"outingsIcon"]]; + + [self.cells addObject:cellCamera]; + [self.cells addObject:cellText]; + [self.cells addObject:cellMap]; + [self.cells addObject:cellOutings]; + + [self.view addSubview:self.floatingActionButton]; +} + +- (void)setupLocation { + self.latitude = 40.7; + self.longitude = -74.0; +} + +- (void)setupLocationManager { + + if (self.locationManager == nil) { + self.locationManager = [[CLLocationManager alloc]init]; + self.locationManager.delegate = self; + + if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { + [self.locationManager requestAlwaysAuthorization]; + } + if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { + [self.locationManager requestWhenInUseAuthorization]; + } + + [self.locationManager startUpdatingLocation]; + } + +} + +- (void)setupImagePicker{ + + self.imagePicker = [UIImagePickerController new]; + self.imagePicker.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *)kUTTypeMovie, (NSString *) kUTTypeImage ,nil]; + self.imagePicker.videoMaximumDuration = 5.0f; + self.imagePicker.videoQuality = UIImagePickerControllerQualityTypeMedium; + +} + +#pragma mark - Data fetching + +- (void)fetchWeatherData { + + AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init]; + + NSString *stringURL = [NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/%f,%f", apiKey,self.longitude, self.latitude]; + NSLog(@"%@", stringURL); + + [manager GET:stringURL parameters: nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) + { + + NSTimeInterval now = [responseObject[@"currently"][@"time"] doubleValue]; + NSLog(@"now: %f", now); + + NSDictionary *hourlyData = responseObject[@"hourly"][@"data"]; + NSLog(@"hourly data: %@", hourlyData); + + + for (NSDictionary *data in hourlyData) { + NSTimeInterval dataTime = [data[@"time"] doubleValue]; + if (dataTime > now) { + self.forecast = data[@"icon"]; + NSLog(@"dataTime: %f, forecast: %@", dataTime, self.forecast); + break; + } + } + + [self.view setNeedsDisplay]; + + } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { + NSLog(@"%@", error); + }]; +} + +- (void)fetchOutings { + + PFQuery *query = [PFQuery queryWithClassName:@"GJOutings"]; + [query includeKey:@"entriesArray"]; + [query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) { + if (objects.count!=0) { + NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"createdDate" ascending:YES]; + [objects sortedArrayUsingDescriptors:@[descriptor]]; + + self.currentOuting = objects[self.selectedOuting]; + NSLog(@"entries count: %ld", self.currentOuting.entriesArray.count); + [self.currentOuting.entriesArray removeObjectIdenticalTo:[NSNull null]]; + [self.collectionView reloadData]; + + } else { + + self.currentOuting = [[GJOutings alloc]initWithNewEntriesArray]; + self.currentOuting.createdDate = [NSDate date]; + self.currentOuting.outingName = @"Demo Outing"; + + [self.currentOuting saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) { + NSLog(@"Outing created"); + }]; + } + }]; + +} + +- (void)fetchSelectedOuting { + if ([[NSUserDefaults standardUserDefaults] valueForKey:selectedOuting] == nil) { + self.selectedOuting = 0; + [[NSUserDefaults standardUserDefaults] setValue:@(self.selectedOuting) forKey:selectedOuting]; + } else { + self.selectedOuting = [[[NSUserDefaults standardUserDefaults] valueForKey:selectedOuting] integerValue]; + } + + NSLog(@"self.selectedOuting: %ld", self.selectedOuting); +} + +#pragma mark - LiquidFloatingActionButtonDataSource + +- (NSInteger)numberOfCells:(LiquidFloatingActionButton *)liquidFloatingActionButton { + return self.cells.count; +} + +- (LiquidFloatingCell *)cellForIndex:(NSInteger)index { + return self.cells[index]; +} + +#pragma mark - LiquidFloatingActionButtonDelegate + +- (void)liquidFloatingActionButton:(LiquidFloatingActionButton *)liquidFloatingActionButton didSelectItemAtIndex:(NSInteger)index { + + switch (index) { + case 0: + [self cameraAction]; + break; + case 1: + [self performSegueWithIdentifier:@"textSegue" sender:self]; + break; + case 2: + [self performSegueWithIdentifier:@"mapSegue" sender:self]; + break; + default: + [self performSegueWithIdentifier:@"outingsSegue" sender:self]; + break; + } + +} + +#pragma mark - UICollectionView customization + +// cell size +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + + return CGSizeMake(self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +// Layout: Set Edges +- (UIEdgeInsets)collectionView: +(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + // return UIEdgeInsetsMake(0,8,0,8); // top, left, bottom, right + return UIEdgeInsetsMake(0,0,0,0); // top, left, bottom, right +} + +// snapping while scrolling +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { + + // Determine which table cell the scrolling will stop on. + CGFloat cellWidth = self.collectionView.bounds.size.width; + NSInteger cellIndex = floor(targetContentOffset->x / cellWidth); + + // Round to the next cell if the scrolling will stop over halfway to the next cell. + if ((targetContentOffset->x - (floor(targetContentOffset->x / cellWidth) * cellWidth)) > cellWidth) { + cellIndex++; + } + + // Adjust stopping point to exact beginning of cell. + targetContentOffset->x = cellIndex * cellWidth; +} + +#pragma mark + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return self.currentOuting.entriesArray.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + EntryCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; + + if ([self.currentOuting.entriesArray[indexPath.row].mediaType isEqualToString:@"public.image"]) { + + cell.photoView.hidden = NO; + cell.descriptionContainer.hidden = YES; + cell.descriptionLabel.hidden = YES; + cell.videoView.hidden = YES; + + cell.photoView.layer.cornerRadius = 10; + + cell.photoView.file = [self.currentOuting.entriesArray[indexPath.row] file]; + [cell.photoView loadInBackground:^(UIImage * _Nullable image, NSError * _Nullable error) { + NSLog(@"Image loaded!"); + }]; + + } else if ([self.currentOuting.entriesArray[indexPath.row].mediaType isEqualToString:@"public.movie"]) { + + cell.photoView.hidden = YES; + cell.descriptionContainer.hidden = YES; + cell.descriptionLabel.hidden = YES; + cell.videoView.hidden = NO; + + cell.videoView.layer.cornerRadius = 10; + + GJEntry *videoEntry = self.currentOuting.entriesArray[indexPath.row]; + + [videoEntry.file getDataInBackgroundWithBlock:^(NSData * _Nullable data, NSError * _Nullable error) { + + if (data) { + + NSString *urlString = [[NSString alloc]initWithData:data encoding:NSUTF16StringEncoding]; + NSURL *url = [NSURL URLWithString:urlString]; + + // ASYNC LOADING: + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); + + dispatch_async(queue, ^{ + NSLog(@"%@",videoEntry.file.url); + AVAsset *asset = [AVAsset assetWithURL:url]; + AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset]; + + dispatch_sync(dispatch_get_main_queue(), ^{ + + AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; + + AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:player]; + //NSLog(@"%@", CGRectCreateDictionaryRepresentation(cell.videoView.bounds)); + layer.frame = cell.videoView.bounds; + layer.videoGravity = AVLayerVideoGravityResizeAspect; + + [player play]; + + [cell.videoView.layer addSublayer:layer]; + }); + }); + } + }]; + } else { + + cell.photoView.hidden = YES; + cell.descriptionContainer.hidden = NO; + cell.descriptionLabel.hidden = NO; + cell.videoView.hidden = YES; + + cell.descriptionContainer.layer.cornerRadius = 10; + cell.descriptionContainer.layer.borderWidth = 1; + // pick a better green + cell.descriptionContainer.layer.borderColor = [UIColor greenColor].CGColor; + + cell.descriptionLabel.text = [self.currentOuting.entriesArray[indexPath.row] textMedia]; + } + + return cell; + +} + +#pragma mark - Camera and Video Methods + +- (void)cameraAction { + + self.imagePicker.delegate = self; + self.imagePicker.allowsEditing = YES; + self.imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; + + [self presentViewController:self.imagePicker animated:YES completion:NULL]; +} + + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { + + NSLog(@"%@",info); + + GJEntry *newEntry = [GJEntry new]; + [self.currentOuting.entriesArray addObject:newEntry]; + newEntry.mediaType = info[UIImagePickerControllerMediaType]; + newEntry.createdDate = [NSDate date]; + newEntry.location = [PFGeoPoint geoPointWithLocation:self.locationManager.location]; + + if ([newEntry.mediaType isEqualToString:@"public.image"]) { + UIImage *chosenImage = info[UIImagePickerControllerEditedImage]; + + [newEntry fileFromImage:chosenImage]; + + [self.currentOuting saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) { + + NSLog(@"succeeded at saving"); + }]; + } + + else if([newEntry.mediaType isEqualToString:@"public.movie"]){ + NSURL *movieURL = info[UIImagePickerControllerMediaURL]; + [newEntry fileFromVideoURL:movieURL]; + + [self.currentOuting saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) { + NSLog(@"succeeded at saving video"); + }]; + + } + + [picker dismissViewControllerAnimated:YES completion:NULL]; +} + +#pragma mark - Navigation + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + + if ([segue.identifier isEqualToString:@"textSegue"]) { + + TextEntryViewController *vc = (TextEntryViewController *) [[segue destinationViewController] topViewController]; + vc.currentOuting = self.currentOuting; + vc.locationManager = self.locationManager; + + } else if ([segue.identifier isEqualToString:@"mapSegue"]) { + + HLMapView *vc = (HLMapView *) [[segue destinationViewController] topViewController]; + vc.currentOuting = self.currentOuting; + + } +} + + +@end diff --git a/HikerLite/TripCollectionViewController.m.orig b/HikerLite/TripCollectionViewController.m.orig new file mode 100644 index 0000000..d6cfded --- /dev/null +++ b/HikerLite/TripCollectionViewController.m.orig @@ -0,0 +1,370 @@ +// +// TripCollectionViewController.m +// GoJournal +// +// Created by Elber Carneiro on 10/11/15. +// Copyright © 2015 Elber Carneiro. All rights reserved. +// + +#import "TripCollectionViewController.h" +#import "Entry.h" +#import "EntryCell.h" +#import "LiquidFloatingActionButton-Swift.h" +#import +#import + +<<<<<<< HEAD +@interface TripCollectionViewController () +======= +@interface TripCollectionViewController () +>>>>>>> 11e49f228e2aced68ae585fed2304373c5ce8d11 + +@property (nonatomic) NSMutableArray *entries; +@property (nonatomic) UICollectionView *collectionView; +@property (nonatomic) NSMutableArray *cells; +@property (nonatomic) LiquidFloatingActionButton *floatingActionButton; +@property (nonatomic) CGFloat latitude; +@property (nonatomic) CGFloat longitude; +@property (nonatomic) NSString *forecast; +@property (nonatomic) CLLocationManager *locationManager; + +@end + +@implementation TripCollectionViewController + +static NSString * const reuseIdentifier = @"entryCellIdentifier"; +static NSString * const apiKey = @"53bac750b0228783a50a48bda0d2d1ce"; + +#pragma mark - Lifecycle methods + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self setupCollectionView]; + + [self setupFloatingActionButton]; + + [self setupLocation]; + + [self setupLocationManager]; + + [self fetchWeatherData]; + + self.entries = [[NSMutableArray alloc] init]; + [self setupDemoContent]; +} + +#pragma mark - Setup methods + +- (void)setupCollectionView { + + // collection view layout setup + UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init]; + NSLog(@"layout: %@", layout); + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + // collection view setup + self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout]; + [self.collectionView setDataSource:self]; + [self.collectionView setDelegate:self]; + [self.collectionView setBackgroundColor:[UIColor whiteColor]]; + + // register cell prototype + [self.collectionView registerNib:[UINib nibWithNibName:@"EntryCell" bundle:nil] forCellWithReuseIdentifier:reuseIdentifier]; + + // add collection view to view controller + [self.view addSubview:self.collectionView]; +} + +- (void)setupFloatingActionButton { + + NSInteger buttonRadius = floor(0.135 * self.view.frame.size.width); + NSLog(@"self.view.frame.size.width: %f", self.view.frame.size.width); + + CGRect frame = CGRectMake(self.view.frame.size.width - buttonRadius - 16, + self.view.frame.size.height - buttonRadius - 16, + buttonRadius, + buttonRadius); + + self.floatingActionButton = [[LiquidFloatingActionButton alloc] initWithFrame:frame]; + self.floatingActionButton.delegate = self; + self.floatingActionButton.dataSource = self; + + self.cells = [[NSMutableArray alloc] init]; + + LiquidFloatingCell *cellCamera = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"cameraIcon"]]; + LiquidFloatingCell *cellText = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"composeIcon"]]; + LiquidFloatingCell *cellMap = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"pinIcon"]]; + LiquidFloatingCell *cellOutings = [[LiquidFloatingCell alloc] initWithIcon:[UIImage imageNamed:@"outingsIcon"]]; + + [self.cells addObject:cellCamera]; + [self.cells addObject:cellText]; + [self.cells addObject:cellMap]; + [self.cells addObject:cellOutings]; + + [self.view addSubview:self.floatingActionButton]; +} + +- (void)setupLocation { + self.latitude = 40.7; + self.longitude = -74.0; +} + + +- (void)setupLocationManager { + + if (self.locationManager == nil) { + self.locationManager = [[CLLocationManager alloc]init]; + self.locationManager.delegate = self; + + if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]){ + [self.locationManager requestAlwaysAuthorization]; + } + if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]){ + [self.locationManager requestWhenInUseAuthorization]; + } + + [self.locationManager startUpdatingLocation]; + } + +} + + + +- (void)fetchWeatherData { + + AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init]; + + NSString *stringURL = [NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/%f,%f", apiKey,self.longitude, self.latitude]; + NSLog(@"%@", stringURL); + + [manager GET:stringURL parameters: nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) + { + + NSTimeInterval now = [responseObject[@"currently"][@"time"] doubleValue]; + NSLog(@"now: %f", now); + + NSDictionary *hourlyData = responseObject[@"hourly"][@"data"]; + NSLog(@"hourly data: %@", hourlyData); + + + for (NSDictionary *data in hourlyData) { + NSTimeInterval dataTime = [data[@"time"] doubleValue]; + if (dataTime > now) { + self.forecast = data[@"icon"]; + NSLog(@"dataTime: %f, forecast: %@", dataTime, self.forecast); + break; + } + } + + + [self.view setNeedsDisplay]; + + } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { + NSLog(@"%@", error); + }]; +} + +- (void)setupDemoContent { + self.currentOuting = [[GJOutings alloc]initWithNewEntriesArray]; + + UIImage *image1 = [UIImage imageNamed:@"wilderness1"]; + GJEntry *entryOne = [GJEntry new]; + entryOne.mediaType = @"public.image"; + + [self.currentOuting.entriesArray addObject:entryOne]; + + UIImage *image2 = [UIImage imageNamed:@"wilderness2"]; + Entry *entryTwo = [[Entry alloc] initWithImage:image2]; + [self.entries addObject:entryTwo]; + + NSString *text1 = @"It's a beautiful sunny day! I hear the birds in my ear and in my mind. Seriously, they won't stop chirping. On an on and on. All afternoon. I haven't seen another human being for two days now. The berries are getting harder and harder to find. I almost caught a squirrel. So hungry."; + Entry *entryThree = [[Entry alloc] initWithText:text1]; + [self.entries addObject:entryThree]; + + UIImage *image3 = [UIImage imageNamed:@"wilderness2"]; + Entry *entryFour = [[Entry alloc] initWithImage:image3]; + [self.entries addObject:entryFour]; + + NSString *path = [[NSBundle mainBundle] pathForResource:@"vid" ofType:@"m4v"]; + //NSLog(@"%@", path); + NSURL *url = [NSURL fileURLWithPath:path]; + Entry *entryFive = [[Entry alloc] initWithVideoURL:url]; + [self.entries addObject:entryFive]; + + NSString *path2 = [[NSBundle mainBundle] pathForResource:@"vid2" ofType:@"m4v"]; + //NSLog(@"%@", path2); + NSURL *url2 = [NSURL fileURLWithPath:path2]; + Entry *entrySix = [[Entry alloc] initWithVideoURL:url2]; + [self.entries addObject:entrySix]; + [self.entries addObject:entrySix]; +} + +#pragma mark - LiquidFloatingActionButtonDataSource + +- (NSInteger)numberOfCells:(LiquidFloatingActionButton *)liquidFloatingActionButton { + return self.cells.count; +} + +- (LiquidFloatingCell *)cellForIndex:(NSInteger)index { + return self.cells[index]; +} + +#pragma mark - LiquidFloatingActionButtonDelegate + +- (void)liquidFloatingActionButton:(LiquidFloatingActionButton *)liquidFloatingActionButton didSelectItemAtIndex:(NSInteger)index { + +} + +#pragma mark - UICollectionView customization + +// cell size +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + + return CGSizeMake(self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { + return 0.0; +} + +// Layout: Set Edges +- (UIEdgeInsets)collectionView: +(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + // return UIEdgeInsetsMake(0,8,0,8); // top, left, bottom, right + return UIEdgeInsetsMake(0,0,0,0); // top, left, bottom, right +} + +// snapping while scrolling +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { + + // Determine which table cell the scrolling will stop on. + CGFloat cellWidth = self.collectionView.bounds.size.width; + NSInteger cellIndex = floor(targetContentOffset->x / cellWidth); + + // Round to the next cell if the scrolling will stop over halfway to the next cell. + if ((targetContentOffset->x - (floor(targetContentOffset->x / cellWidth) * cellWidth)) > cellWidth) { + cellIndex++; + } + + // Adjust stopping point to exact beginning of cell. + targetContentOffset->x = cellIndex * cellWidth; +} + +#pragma mark + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return self.entries.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + EntryCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; + + if (self.entries[indexPath.row].image != nil) { + + cell.photoView.hidden = NO; + cell.descriptionLabel.hidden = YES; + cell.videoView.hidden = YES; + + cell.photoView.layer.cornerRadius = 10; + + cell.photoView.image = self.entries[indexPath.row].image; + + } else if (self.entries[indexPath.row].video != nil) { + + cell.photoView.hidden = YES; + cell.descriptionLabel.hidden = YES; + cell.videoView.hidden = NO; + + cell.videoView.layer.cornerRadius = 10; + + // ASYNC LOADING: + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); + + dispatch_async(queue, ^{ + + AVAsset *asset = [AVAsset assetWithURL:self.entries[indexPath.row].video]; + AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset]; + + dispatch_sync(dispatch_get_main_queue(), ^{ + AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; + + AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:player]; + //NSLog(@"%@", CGRectCreateDictionaryRepresentation(cell.videoView.bounds)); + layer.frame = cell.videoView.bounds; + layer.videoGravity = AVLayerVideoGravityResizeAspect; + + [player play]; + + [cell.videoView.layer addSublayer:layer]; + }); + }); + + } else { + + cell.photoView.hidden = YES; + cell.descriptionLabel.hidden = NO; + cell.videoView.hidden = YES; + + cell.descriptionLabel.layer.cornerRadius = 10; + + cell.descriptionLabel.text = self.entries[indexPath.row].text; + } + + return cell; + +} + +#pragma mark + +/* +// Uncomment this method to specify if the specified item should be highlighted during tracking +- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} +*/ + +/* +// Uncomment this method to specify if the specified item should be selected +- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} +*/ + +/* +// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item +- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { + return NO; +} + +- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { + +} +*/ + +#pragma mark - Navigation + +/* +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/HikerLite/main.m b/HikerLite/main.m new file mode 100644 index 0000000..0e252d1 --- /dev/null +++ b/HikerLite/main.m @@ -0,0 +1,16 @@ +// +// main.m +// HikerLite +// +// Created by Varindra Hart on 10/17/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/HikerLite/vid.m4v b/HikerLite/vid.m4v new file mode 100644 index 0000000..f56594e Binary files /dev/null and b/HikerLite/vid.m4v differ diff --git a/HikerLite/vid2.m4v b/HikerLite/vid2.m4v new file mode 100644 index 0000000..2ad3ff7 Binary files /dev/null and b/HikerLite/vid2.m4v differ diff --git a/HikerLiteTests/HikerLiteTests.m b/HikerLiteTests/HikerLiteTests.m new file mode 100644 index 0000000..6338d05 --- /dev/null +++ b/HikerLiteTests/HikerLiteTests.m @@ -0,0 +1,39 @@ +// +// HikerLiteTests.m +// HikerLiteTests +// +// Created by Varindra Hart on 10/17/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import + +@interface HikerLiteTests : XCTestCase + +@end + +@implementation HikerLiteTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/HikerLiteTests/Info.plist b/HikerLiteTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/HikerLiteTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/HikerLiteUITests/HikerLiteUITests.m b/HikerLiteUITests/HikerLiteUITests.m new file mode 100644 index 0000000..e3a5451 --- /dev/null +++ b/HikerLiteUITests/HikerLiteUITests.m @@ -0,0 +1,38 @@ +// +// HikerLiteUITests.m +// HikerLiteUITests +// +// Created by Varindra Hart on 10/17/15. +// Copyright © 2015 Varindra Hart. All rights reserved. +// + +#import + +@interface HikerLiteUITests : XCTestCase + +@end + +@implementation HikerLiteUITests + +- (void)setUp { + [super setUp]; + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + [[[XCUIApplication alloc] init] launch]; +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +@end diff --git a/HikerLiteUITests/Info.plist b/HikerLiteUITests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/HikerLiteUITests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..9ed37ca --- /dev/null +++ b/Podfile @@ -0,0 +1,8 @@ +pod 'Parse' +pod 'ParseUI', '~> 1.1' +pod 'LiquidFloatingActionButton', :git => 'https://github.com/yoavlt/LiquidFloatingActionButton.git', :branch => 'swift-2.0' +pod 'GoogleMaps' +pod "AFNetworking", "~> 2.6" + +use_frameworks! +platform :ios, '8.2' diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..09e0d1e --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,58 @@ +PODS: + - AFNetworking (2.6.1): + - AFNetworking/NSURLConnection (= 2.6.1) + - AFNetworking/NSURLSession (= 2.6.1) + - AFNetworking/Reachability (= 2.6.1) + - AFNetworking/Security (= 2.6.1) + - AFNetworking/Serialization (= 2.6.1) + - AFNetworking/UIKit (= 2.6.1) + - AFNetworking/NSURLConnection (2.6.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/NSURLSession (2.6.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (2.6.1) + - AFNetworking/Security (2.6.1) + - AFNetworking/Serialization (2.6.1) + - AFNetworking/UIKit (2.6.1): + - AFNetworking/NSURLConnection + - AFNetworking/NSURLSession + - Bolts/Tasks (1.3.0) + - GoogleMaps (1.10.4) + - LiquidFloatingActionButton (0.1.1) + - Parse (1.9.0): + - Bolts/Tasks (>= 1.3.0) + - ParseUI (1.1.6): + - Bolts/Tasks (~> 1.2) + - Parse (~> 1.8) + +DEPENDENCIES: + - AFNetworking (~> 2.6) + - GoogleMaps + - LiquidFloatingActionButton (from `https://github.com/yoavlt/LiquidFloatingActionButton.git`, + branch `swift-2.0`) + - Parse + - ParseUI (~> 1.1) + +EXTERNAL SOURCES: + LiquidFloatingActionButton: + :branch: swift-2.0 + :git: https://github.com/yoavlt/LiquidFloatingActionButton.git + +CHECKOUT OPTIONS: + LiquidFloatingActionButton: + :commit: 6c2a30a548d1f44e78b54571e616d637c92b5206 + :git: https://github.com/yoavlt/LiquidFloatingActionButton.git + +SPEC CHECKSUMS: + AFNetworking: 8e4e60500beb8bec644cf575beee72990a76d399 + Bolts: 805a4a87413e49d4a0c2b7d469084cbc46b09342 + GoogleMaps: 134bcf57e8d489efbc0c29d532ed421554fabdb2 + LiquidFloatingActionButton: dcdae1e59eb07eccbf60c027001a4d6731c345c9 + Parse: 712efbc476d4f47b0f96b70db7e53101575753aa + ParseUI: 8a22e448c03f825203d8c379c80523517f68baa3 + +COCOAPODS: 0.38.2 diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h new file mode 100644 index 0000000..cf6def4 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h @@ -0,0 +1,70 @@ +// AFHTTPRequestOperation.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import "AFURLConnectionOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. + */ +@interface AFHTTPRequestOperation : AFURLConnectionOperation + +///------------------------------------------------ +/// @name Getting HTTP URL Connection Information +///------------------------------------------------ + +/** + The last HTTP response received by the operation's connection. + */ +@property (readonly, nonatomic, strong, nullable) NSHTTPURLResponse *response; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. + + @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error. + */ +@property (readonly, nonatomic, strong, nullable) id responseObject; + +///----------------------------------------------------------- +/// @name Setting Completion Block Success / Failure Callbacks +///----------------------------------------------------------- + +/** + Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. + + This method should be overridden in subclasses in order to specify the response object passed into the success block. + + @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. + @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. + */ +- (void)setCompletionBlockWithSuccess:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m new file mode 100644 index 0000000..b8deda8 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m @@ -0,0 +1,206 @@ +// AFHTTPRequestOperation.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFHTTPRequestOperation.h" + +static dispatch_queue_t http_request_operation_processing_queue() { + static dispatch_queue_t af_http_request_operation_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_http_request_operation_processing_queue; +} + +static dispatch_group_t http_request_operation_completion_group() { + static dispatch_group_t af_http_request_operation_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_http_request_operation_completion_group = dispatch_group_create(); + }); + + return af_http_request_operation_completion_group; +} + +#pragma mark - + +@interface AFURLConnectionOperation () +@property (readwrite, nonatomic, strong) NSURLRequest *request; +@property (readwrite, nonatomic, strong) NSURLResponse *response; +@end + +@interface AFHTTPRequestOperation () +@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; +@property (readwrite, nonatomic, strong) id responseObject; +@property (readwrite, nonatomic, strong) NSError *responseSerializationError; +@property (readwrite, nonatomic, strong) NSRecursiveLock *lock; +@end + +@implementation AFHTTPRequestOperation +@dynamic response; +@dynamic lock; + +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest { + self = [super initWithRequest:urlRequest]; + if (!self) { + return nil; + } + + self.responseSerializer = [AFHTTPResponseSerializer serializer]; + + return self; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [self.lock lock]; + _responseSerializer = responseSerializer; + self.responseObject = nil; + self.responseSerializationError = nil; + [self.lock unlock]; +} + +- (id)responseObject { + [self.lock lock]; + if (!_responseObject && [self isFinished] && !self.error) { + NSError *error = nil; + self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; + if (error) { + self.responseSerializationError = error; + } + } + [self.lock unlock]; + + return _responseObject; +} + +- (NSError *)error { + if (_responseSerializationError) { + return _responseSerializationError; + } else { + return [super error]; + } +} + +#pragma mark - AFHTTPRequestOperation + +- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" +#pragma clang diagnostic ignored "-Wgnu" + self.completionBlock = ^{ + if (self.completionGroup) { + dispatch_group_enter(self.completionGroup); + } + + dispatch_async(http_request_operation_processing_queue(), ^{ + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + id responseObject = self.responseObject; + if (self.error) { + if (failure) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(self, self.error); + }); + } + } else { + if (success) { + dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ + success(self, responseObject); + }); + } + } + } + + if (self.completionGroup) { + dispatch_group_leave(self.completionGroup); + } + }); + }; +#pragma clang diagnostic pop +} + +#pragma mark - AFURLRequestOperation + +- (void)pause { + [super pause]; + + u_int64_t offset = 0; + if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { + offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; + } else { + offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; + } + + NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; + if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { + [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; + } + [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; + self.request = mutableURLRequest; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestOperation *operation = [super copyWithZone:zone]; + + operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h new file mode 100644 index 0000000..d2385ed --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h @@ -0,0 +1,326 @@ +// AFHTTPRequestOperationManager.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +#import "AFHTTPRequestOperation.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" +#import "AFSecurityPolicy.h" +#import "AFNetworkReachabilityManager.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management. + + ## Subclassing Notes + + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. + + ## Methods to Override + + To change the behavior of all request operation construction for an `AFHTTPRequestOperationManager` subclass, override `HTTPRequestOperationWithRequest:success:failure`. + + ## Serialization + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + + ## URL Construction Using Relative Paths + + For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. + + Below are a few examples of how `baseURL` and relative paths interact: + + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + + Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + ## Network Reachability Monitoring + + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + + ## NSSecureCoding & NSCopying Caveats + + `AFHTTPRequestOperationManager` conforms to the `NSSecureCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however: + + - Archives and copies of HTTP clients will be initialized with an empty operation queue. + - NSSecureCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set. + */ +@interface AFHTTPRequestOperationManager : NSObject + +/** + The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. + */ +@property (readonly, nonatomic, strong, nullable) NSURL *baseURL; + +/** + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + + @warning `requestSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to a JSON serializer, which serializes data from responses with a `application/json` MIME type, and falls back to the raw data object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + The operation queue on which request operations are scheduled and run. + */ +@property (nonatomic, strong) NSOperationQueue *operationQueue; + +///------------------------------- +/// @name Managing URL Credentials +///------------------------------- + +/** + Whether request operations should consult the credential storage for authenticating the connection. `YES` by default. + + @see AFURLConnectionOperation -shouldUseCredentialStorage + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + The credential used by request operations for authentication challenges. + + @see AFURLConnectionOperation -credential + */ +@property (nonatomic, strong, nullable) NSURLCredential *credential; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created request operations to evaluate server trust for secure connections. `AFHTTPRequestOperationManager` uses the `defaultPolicy` unless otherwise specified. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///------------------------------------ +/// @name Managing Network Reachability +///------------------------------------ + +/** + The network reachability manager. `AFHTTPRequestOperationManager` uses the `sharedManager` by default. + */ +@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; + +///------------------------------- +/// @name Managing Callback Queues +///------------------------------- + +/** + The dispatch queue for the `completionBlock` of request operations. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign, nullable) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for the `completionBlock` of request operations. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign, nullable) dispatch_group_t completionGroup; +#endif + +///--------------------------------------------- +/// @name Creating and Initializing HTTP Clients +///--------------------------------------------- + +/** + Creates and returns an `AFHTTPRequestOperationManager` object. + */ ++ (instancetype)manager; + +/** + Initializes an `AFHTTPRequestOperationManager` object with the specified base URL. + + This is the designated initializer. + + @param url The base URL for the HTTP client. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(nullable NSURL *)url NS_DESIGNATED_INITIALIZER; + +///--------------------------------------- +/// @name Managing HTTP Request Operations +///--------------------------------------- + +/** + Creates an `AFHTTPRequestOperation`, and sets the response serializers to that of the HTTP client. + + @param request The request object to be loaded asynchronously during execution of the operation. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred. + */ +- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +///--------------------------- +/// @name Making HTTP Requests +///--------------------------- + +/** + Creates and runs an `AFHTTPRequestOperation` with a `GET` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)GET:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `HEAD` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single arguments: the request operation. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)HEAD:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a multipart `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(nullable id)parameters + constructingBodyWithBlock:(nullable void (^)(id formData))block + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `PUT` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)PUT:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `PATCH` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)PATCH:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +/** + Creates and runs an `AFHTTPRequestOperation` with a `DELETE` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred. + + @see -HTTPRequestOperationWithRequest:success:failure: + */ +- (nullable AFHTTPRequestOperation *)DELETE:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(nullable void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m new file mode 100644 index 0000000..60739e5 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m @@ -0,0 +1,284 @@ +// AFHTTPRequestOperationManager.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import "AFHTTPRequestOperationManager.h" +#import "AFHTTPRequestOperation.h" + +#import +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +@interface AFHTTPRequestOperationManager () +@property (readwrite, nonatomic, strong) NSURL *baseURL; +@end + +@implementation AFHTTPRequestOperationManager + ++ (instancetype)manager { + return [[self alloc] initWithBaseURL:nil]; +} + +- (instancetype)init { + return [self initWithBaseURL:nil]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url { + self = [super init]; + if (!self) { + return nil; + } + + // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected + if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { + url = [url URLByAppendingPathComponent:@""]; + } + + self.baseURL = url; + + self.requestSerializer = [AFHTTPRequestSerializer serializer]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + + self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + + self.operationQueue = [[NSOperationQueue alloc] init]; + + self.shouldUseCredentialStorage = YES; + + return self; +} + +#pragma mark - + +#ifdef _SYSTEMCONFIGURATION_H +#endif + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + NSParameterAssert(requestSerializer); + + _requestSerializer = requestSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + _responseSerializer = responseSerializer; +} + +#pragma mark - + +- (AFHTTPRequestOperation *)HTTPRequestOperationWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + return [self HTTPRequestOperationWithRequest:request success:success failure:failure]; +} + +- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + operation.responseSerializer = self.responseSerializer; + operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; + operation.credential = self.credential; + operation.securityPolicy = self.securityPolicy; + + [operation setCompletionBlockWithSuccess:success failure:failure]; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +#pragma mark - + +- (AFHTTPRequestOperation *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(AFHTTPRequestOperation *requestOperation, __unused id responseObject) { + if (success) { + success(requestOperation); + } + } failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +- (AFHTTPRequestOperation *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success + failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure +{ + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; + + [self.operationQueue addOperation:operation]; + + return operation; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; + + self = [self initWithBaseURL:baseURL]; + if (!self) { + return nil; + } + + self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; + [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL]; + + HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; + HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; + + return HTTPClient; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h new file mode 100644 index 0000000..e516e6d --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1,253 @@ +// AFHTTPSessionManager.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#if !TARGET_OS_WATCH +#import +#endif +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +#import "AFURLSessionManager.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths. + + ## Subclassing Notes + + Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application. + + For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect. + + ## Methods to Override + + To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`. + + ## Serialization + + Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to ``. + + Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `` + + ## URL Construction Using Relative Paths + + For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`. + + Below are a few examples of how `baseURL` and relative paths interact: + + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + + Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || TARGET_OS_WATCH + +NS_ASSUME_NONNULL_BEGIN + +@interface AFHTTPSessionManager : AFURLSessionManager + +/** + The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods. + */ +@property (readonly, nonatomic, strong, nullable) NSURL *baseURL; + +/** + Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies. + + @warning `requestSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns an `AFHTTPSessionManager` object. + */ ++ (instancetype)manager; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + @param url The base URL for the HTTP client. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(nullable NSURL *)url; + +/** + Initializes an `AFHTTPSessionManager` object with the specified base URL. + + This is the designated initializer. + + @param url The base URL for the HTTP client. + @param configuration The configuration used to create the managed session. + + @return The newly-initialized HTTP client + */ +- (instancetype)initWithBaseURL:(nullable NSURL *)url + sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +///--------------------------- +/// @name Making HTTP Requests +///--------------------------- + +/** + Creates and runs an `NSURLSessionDataTask` with a `GET` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `HEAD` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + constructingBodyWithBlock:(nullable void (^)(id formData))block + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PUT` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `PATCH` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +/** + Creates and runs an `NSURLSessionDataTask` with a `DELETE` request. + + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded according to the client request serializer. + @param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer. + @param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred. + + @see -dataTaskWithRequest:completionHandler: + */ +- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(nullable id)parameters + success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m new file mode 100644 index 0000000..bd9163f --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m @@ -0,0 +1,323 @@ +// AFHTTPSessionManager.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFHTTPSessionManager.h" + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || TARGET_WATCH_OS + +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" + +#import +#import + +#ifdef _SYSTEMCONFIGURATION_H +#import +#import +#import +#import +#import +#endif + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_WATCH +#import +#endif + +@interface AFHTTPSessionManager () +@property (readwrite, nonatomic, strong) NSURL *baseURL; +@end + +@implementation AFHTTPSessionManager +@dynamic responseSerializer; + ++ (instancetype)manager { + return [[[self class] alloc] initWithBaseURL:nil]; +} + +- (instancetype)init { + return [self initWithBaseURL:nil]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url { + return [self initWithBaseURL:url sessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + return [self initWithBaseURL:nil sessionConfiguration:configuration]; +} + +- (instancetype)initWithBaseURL:(NSURL *)url + sessionConfiguration:(NSURLSessionConfiguration *)configuration +{ + self = [super initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected + if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { + url = [url URLByAppendingPathComponent:@""]; + } + + self.baseURL = url; + + self.requestSerializer = [AFHTTPRequestSerializer serializer]; + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + return self; +} + +#pragma mark - + +#ifdef _SYSTEMCONFIGURATION_H +#endif + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + NSParameterAssert(requestSerializer); + + _requestSerializer = requestSerializer; +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + NSParameterAssert(responseSerializer); + + [super setResponseSerializer:responseSerializer]; +} + +#pragma mark - + +- (NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)HEAD:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) { + if (success) { + success(task); + } + } failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(task, error); + } + } else { + if (success) { + success(task, responseObject); + } + } + }]; + + [task resume]; + + return task; +} + +- (NSURLSessionDataTask *)PUT:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)PATCH:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)DELETE:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *task, id responseObject))success + failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + success:(void (^)(NSURLSessionDataTask *, id))success + failure:(void (^)(NSURLSessionDataTask *, NSError *))failure +{ + NSError *serializationError = nil; + NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; + if (serializationError) { + if (failure) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ + failure(nil, serializationError); + }); +#pragma clang diagnostic pop + } + + return nil; + } + + __block NSURLSessionDataTask *dataTask = nil; + dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { + if (error) { + if (failure) { + failure(dataTask, error); + } + } else { + if (success) { + success(dataTask, responseObject); + } + } + }]; + + return dataTask; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + if (!configuration) { + NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; + if (configurationIdentifier) { +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) + configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; +#else + configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; +#endif + } + } + + self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; + if (!self) { + return nil; + } + + self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; + self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; + if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; + } else { + [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; + } + [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; + [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; + + HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; + HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; + + return HTTPClient; +} + +@end + +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h new file mode 100644 index 0000000..e2eb945 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1,207 @@ +// AFNetworkReachabilityManager.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#if !TARGET_OS_WATCH +#import + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { + AFNetworkReachabilityStatusUnknown = -1, + AFNetworkReachabilityStatusNotReachable = 0, + AFNetworkReachabilityStatusReachableViaWWAN = 1, + AFNetworkReachabilityStatusReachableViaWiFi = 2, +}; + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + + Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. + + See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) + + @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. + */ +@interface AFNetworkReachabilityManager : NSObject + +/** + The current network reachability status. + */ +@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; + +/** + Whether or not the network is currently reachable. + */ +@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; + +/** + Whether or not the network is currently reachable via WWAN. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; + +/** + Whether or not the network is currently reachable via WiFi. + */ +@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Returns the shared network reachability manager. + */ ++ (instancetype)sharedManager; + +/** + Creates and returns a network reachability manager for the specified domain. + + @param domain The domain used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified domain. + */ ++ (instancetype)managerForDomain:(NSString *)domain; + +/** + Creates and returns a network reachability manager for the socket address. + + @param address The socket address (`sockaddr_in`) used to evaluate network reachability. + + @return An initialized network reachability manager, actively monitoring the specified socket address. + */ ++ (instancetype)managerForAddress:(const void *)address; + +/** + Initializes an instance of a network reachability manager from the specified reachability object. + + @param reachability The reachability object to monitor. + + @return An initialized network reachability manager, actively monitoring the specified reachability. + */ +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; + +///-------------------------------------------------- +/// @name Starting & Stopping Reachability Monitoring +///-------------------------------------------------- + +/** + Starts monitoring for changes in network reachability status. + */ +- (void)startMonitoring; + +/** + Stops monitoring for changes in network reachability status. + */ +- (void)stopMonitoring; + +///------------------------------------------------- +/// @name Getting Localized Reachability Description +///------------------------------------------------- + +/** + Returns a localized string representation of the current network reachability status. + */ +- (NSString *)localizedNetworkReachabilityStatusString; + +///--------------------------------------------------- +/// @name Setting Network Reachability Change Callback +///--------------------------------------------------- + +/** + Sets a callback to be executed when the network availability of the `baseURL` host changes. + + @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. + */ +- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Network Reachability + + The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. + + enum { + AFNetworkReachabilityStatusUnknown, + AFNetworkReachabilityStatusNotReachable, + AFNetworkReachabilityStatusReachableViaWWAN, + AFNetworkReachabilityStatusReachableViaWiFi, + } + + `AFNetworkReachabilityStatusUnknown` + The `baseURL` host reachability is not known. + + `AFNetworkReachabilityStatusNotReachable` + The `baseURL` host cannot be reached. + + `AFNetworkReachabilityStatusReachableViaWWAN` + The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. + + `AFNetworkReachabilityStatusReachableViaWiFi` + The `baseURL` host can be reached via a Wi-Fi connection. + + ### Keys for Notification UserInfo Dictionary + + Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. + + `AFNetworkingReachabilityNotificationStatusItem` + A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. + The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. + */ + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when network reachability changes. + This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. + + @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). + */ +FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification; +FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem; + +///-------------------- +/// @name Functions +///-------------------- + +/** + Returns a localized string representation of an `AFNetworkReachabilityStatus` value. + */ +FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); + +NS_ASSUME_NONNULL_END +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m new file mode 100644 index 0000000..2e5e2ed --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m @@ -0,0 +1,262 @@ +// AFNetworkReachabilityManager.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFNetworkReachabilityManager.h" +#if !TARGET_OS_WATCH + +#import +#import +#import +#import +#import + +NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; +NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; + +typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); + +typedef NS_ENUM(NSUInteger, AFNetworkReachabilityAssociation) { + AFNetworkReachabilityForAddress = 1, + AFNetworkReachabilityForAddressPair = 2, + AFNetworkReachabilityForName = 3, +}; + +NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { + switch (status) { + case AFNetworkReachabilityStatusNotReachable: + return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWWAN: + return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); + case AFNetworkReachabilityStatusReachableViaWiFi: + return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); + case AFNetworkReachabilityStatusUnknown: + default: + return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); + } +} + +static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { + BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); + BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); + BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); + BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); + BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); + + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; + if (isNetworkReachable == NO) { + status = AFNetworkReachabilityStatusNotReachable; + } +#if TARGET_OS_IPHONE + else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { + status = AFNetworkReachabilityStatusReachableViaWWAN; + } +#endif + else { + status = AFNetworkReachabilityStatusReachableViaWiFi; + } + + return status; +} + +static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + AFNetworkReachabilityStatusBlock block = (__bridge AFNetworkReachabilityStatusBlock)info; + if (block) { + block(status); + } + + + dispatch_async(dispatch_get_main_queue(), ^{ + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; + }); + +} + +static const void * AFNetworkReachabilityRetainCallback(const void *info) { + return Block_copy(info); +} + +static void AFNetworkReachabilityReleaseCallback(const void *info) { + if (info) { + Block_release(info); + } +} + +@interface AFNetworkReachabilityManager () +@property (readwrite, nonatomic, strong) id networkReachability; +@property (readwrite, nonatomic, assign) AFNetworkReachabilityAssociation networkReachabilityAssociation; +@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; +@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; +@end + +@implementation AFNetworkReachabilityManager + ++ (instancetype)sharedManager { + static AFNetworkReachabilityManager *_sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + struct sockaddr_in address; + bzero(&address, sizeof(address)); + address.sin_len = sizeof(address); + address.sin_family = AF_INET; + + _sharedManager = [self managerForAddress:&address]; + }); + + return _sharedManager; +} + ++ (instancetype)managerForDomain:(NSString *)domain { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + manager.networkReachabilityAssociation = AFNetworkReachabilityForName; + + return manager; +} + ++ (instancetype)managerForAddress:(const void *)address { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + manager.networkReachabilityAssociation = AFNetworkReachabilityForAddress; + + return manager; +} + +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { + self = [super init]; + if (!self) { + return nil; + } + + self.networkReachability = CFBridgingRelease(reachability); + self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + + return self; +} + +- (instancetype)init NS_UNAVAILABLE +{ + return nil; +} + +- (void)dealloc { + [self stopMonitoring]; +} + +#pragma mark - + +- (BOOL)isReachable { + return [self isReachableViaWWAN] || [self isReachableViaWiFi]; +} + +- (BOOL)isReachableViaWWAN { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; +} + +- (BOOL)isReachableViaWiFi { + return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; +} + +#pragma mark - + +- (void)startMonitoring { + [self stopMonitoring]; + + if (!self.networkReachability) { + return; + } + + __weak __typeof(self)weakSelf = self; + AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + + strongSelf.networkReachabilityStatus = status; + if (strongSelf.networkReachabilityStatusBlock) { + strongSelf.networkReachabilityStatusBlock(status); + } + + }; + + id networkReachability = self.networkReachability; + SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + switch (self.networkReachabilityAssociation) { + case AFNetworkReachabilityForName: + break; + case AFNetworkReachabilityForAddress: + case AFNetworkReachabilityForAddressPair: + default: { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ + SCNetworkReachabilityFlags flags; + SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags); + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + dispatch_async(dispatch_get_main_queue(), ^{ + callback(status); + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }]; + + + }); + }); + } + break; + } +} + +- (void)stopMonitoring { + if (!self.networkReachability) { + return; + } + + SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); +} + +#pragma mark - + +- (NSString *)localizedNetworkReachabilityStatusString { + return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); +} + +#pragma mark - + +- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { + self.networkReachabilityStatusBlock = block; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { + if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { + return [NSSet setWithObject:@"networkReachabilityStatus"]; + } + + return [super keyPathsForValuesAffectingValueForKey:key]; +} + +@end +#endif diff --git a/Pods/AFNetworking/AFNetworking/AFNetworking.h b/Pods/AFNetworking/AFNetworking/AFNetworking.h new file mode 100644 index 0000000..6d442bb --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworking.h @@ -0,0 +1,46 @@ +// AFNetworking.h +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +#ifndef _AFNETWORKING_ + #define _AFNETWORKING_ + + #import "AFURLRequestSerialization.h" + #import "AFURLResponseSerialization.h" + #import "AFSecurityPolicy.h" +#if !TARGET_OS_WATCH + #import "AFNetworkReachabilityManager.h" + #import "AFURLConnectionOperation.h" + #import "AFHTTPRequestOperation.h" + #import "AFHTTPRequestOperationManager.h" +#endif + +#if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ + ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) || \ + TARGET_OS_WATCH ) + #import "AFURLSessionManager.h" + #import "AFHTTPSessionManager.h" +#endif + +#endif /* _AFNETWORKING_ */ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h new file mode 100644 index 0000000..3c38da8 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1,142 @@ +// AFSecurityPolicy.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, +}; + +/** + `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + + Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. + */ + +NS_ASSUME_NONNULL_BEGIN + +@interface AFSecurityPolicy : NSObject + +/** + The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. + */ +@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; + +/** + The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle. Note that if you create an array with duplicate certificates, the duplicate certificates will be removed. Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches. + */ +@property (nonatomic, strong, nullable) NSArray *pinnedCertificates; + +/** + Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL allowInvalidCertificates; + +/** + Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. + */ +@property (nonatomic, assign) BOOL validatesDomainName; + +///----------------------------------------- +/// @name Getting Specific Security Policies +///----------------------------------------- + +/** + Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. + + @return The default security policy. + */ ++ (instancetype)defaultPolicy; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a security policy with the specified pinning mode. + + @param pinningMode The SSL pinning mode. + + @return A new security policy. + */ ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; + +///------------------------------ +/// @name Evaluating Server Trust +///------------------------------ + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from a server. + + @param serverTrust The X.509 certificate trust of the server. + + @return Whether or not to trust the server. + + @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; + +/** + Whether or not the specified server trust should be accepted, based on the security policy. + + This method should be used when responding to an authentication challenge from a server. + + @param serverTrust The X.509 certificate trust of the server. + @param domain The domain of serverTrust. If `nil`, the domain will not be validated. + + @return Whether or not to trust the server. + */ +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(nullable NSString *)domain; + +@end + +NS_ASSUME_NONNULL_END + +///---------------- +/// @name Constants +///---------------- + +/** + ## SSL Pinning Modes + + The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. + + enum { + AFSSLPinningModeNone, + AFSSLPinningModePublicKey, + AFSSLPinningModeCertificate, + } + + `AFSSLPinningModeNone` + Do not used pinned certificates to validate servers. + + `AFSSLPinningModePublicKey` + Validate host certificates against public keys of pinned certificates. + + `AFSSLPinningModeCertificate` + Validate host certificates against pinned certificates. +*/ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m new file mode 100644 index 0000000..e8eaa65 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m @@ -0,0 +1,311 @@ +// AFSecurityPolicy.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFSecurityPolicy.h" + +#import + +#if !TARGET_OS_IOS && !TARGET_OS_WATCH +static NSData * AFSecKeyGetData(SecKeyRef key) { + CFDataRef data = NULL; + + __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); + + return (__bridge_transfer NSData *)data; + +_out: + if (data) { + CFRelease(data); + } + + return nil; +} +#endif + +static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { +#if TARGET_OS_IOS || TARGET_OS_WATCH + return [(__bridge id)key1 isEqual:(__bridge id)key2]; +#else + return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; +#endif +} + +static id AFPublicKeyForCertificate(NSData *certificate) { + id allowedPublicKey = nil; + SecCertificateRef allowedCertificate; + SecCertificateRef allowedCertificates[1]; + CFArrayRef tempCertificates = nil; + SecPolicyRef policy = nil; + SecTrustRef allowedTrust = nil; + SecTrustResultType result; + + allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); + __Require_Quiet(allowedCertificate != NULL, _out); + + allowedCertificates[0] = allowedCertificate; + tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); + + policy = SecPolicyCreateBasicX509(); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); + __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); + + allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); + +_out: + if (allowedTrust) { + CFRelease(allowedTrust); + } + + if (policy) { + CFRelease(policy); + } + + if (tempCertificates) { + CFRelease(tempCertificates); + } + + if (allowedCertificate) { + CFRelease(allowedCertificate); + } + + return allowedPublicKey; +} + +static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { + BOOL isValid = NO; + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); + + isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); + +_out: + return isValid; +} + +static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; + } + + return [NSArray arrayWithArray:trustChain]; +} + +static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + + SecCertificateRef someCertificates[] = {certificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); + + SecTrustRef trust; + __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); + + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); + + [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; + + _out: + if (trust) { + CFRelease(trust); + } + + if (certificates) { + CFRelease(certificates); + } + + continue; + } + CFRelease(policy); + + return [NSArray arrayWithArray:trustChain]; +} + +#pragma mark - + +@interface AFSecurityPolicy() +@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; +@property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; +@end + +@implementation AFSecurityPolicy + ++ (NSArray *)defaultPinnedCertificates { + static NSArray *_defaultPinnedCertificates = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates]; + }); + + return _defaultPinnedCertificates; +} + ++ (instancetype)defaultPolicy { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = AFSSLPinningModeNone; + + return securityPolicy; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = pinningMode; + + [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; + + return securityPolicy; +} + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + self.validatesDomainName = YES; + + return self; +} + +- (void)setPinnedCertificates:(NSArray *)pinnedCertificates { + _pinnedCertificates = [[NSOrderedSet orderedSetWithArray:pinnedCertificates] array]; + + if (self.pinnedCertificates) { + NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]]; + for (NSData *certificate in self.pinnedCertificates) { + id publicKey = AFPublicKeyForCertificate(certificate); + if (!publicKey) { + continue; + } + [mutablePinnedPublicKeys addObject:publicKey]; + } + self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys]; + } else { + self.pinnedPublicKeys = nil; + } +} + +#pragma mark - + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { + return [self evaluateServerTrust:serverTrust forDomain:nil]; +} + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust + forDomain:(NSString *)domain +{ + if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { + // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html + // According to the docs, you should only trust your provided certs for evaluation. + // Pinned certificates are added to the trust. Without pinned certificates, + // there is nothing to evaluate against. + // + // From Apple Docs: + // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). + // Instead, add your own (self-signed) CA certificate to the list of trusted anchors." + NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); + return NO; + } + + NSMutableArray *policies = [NSMutableArray array]; + if (self.validatesDomainName) { + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; + } else { + [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; + } + + SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); + + if (self.SSLPinningMode == AFSSLPinningModeNone) { + if (self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust)){ + return YES; + } else { + return NO; + } + } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + return NO; + } + + NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); + switch (self.SSLPinningMode) { + case AFSSLPinningModeNone: + default: + return NO; + case AFSSLPinningModeCertificate: { + NSMutableArray *pinnedCertificates = [NSMutableArray array]; + for (NSData *certificateData in self.pinnedCertificates) { + [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; + } + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); + + if (!AFServerTrustIsValid(serverTrust)) { + return NO; + } + + NSUInteger trustedCertificateCount = 0; + for (NSData *trustChainCertificate in serverCertificates) { + if ([self.pinnedCertificates containsObject:trustChainCertificate]) { + trustedCertificateCount++; + } + } + return trustedCertificateCount > 0; + } + case AFSSLPinningModePublicKey: { + NSUInteger trustedPublicKeyCount = 0; + NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); + + for (id trustChainPublicKey in publicKeys) { + for (id pinnedPublicKey in self.pinnedPublicKeys) { + if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { + trustedPublicKeyCount += 1; + } + } + } + return trustedPublicKeyCount > 0; + } + } + + return NO; +} + +#pragma mark - NSKeyValueObserving + ++ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { + return [NSSet setWithObject:@"pinnedCertificates"]; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h new file mode 100644 index 0000000..c21119e --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h @@ -0,0 +1,344 @@ +// AFURLConnectionOperation.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" +#import "AFSecurityPolicy.h" + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods. + + ## Subclassing Notes + + This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors. + + If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes. + + ## NSURLConnection Delegate Methods + + `AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods: + + - `connection:didReceiveResponse:` + - `connection:didReceiveData:` + - `connectionDidFinishLoading:` + - `connection:didFailWithError:` + - `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:` + - `connection:willCacheResponse:` + - `connectionShouldUseCredentialStorage:` + - `connection:needNewBodyStream:` + - `connection:willSendRequestForAuthenticationChallenge:` + + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. + + ## Callbacks and Completion Blocks + + The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this. + + Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/). + + ## SSL Pinning + + Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle. + + SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice. + + Connections will be validated on all matching certificates with a `.cer` extension in the bundle root. + + ## NSCoding & NSCopying Conformance + + `AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind: + + ### NSCoding Caveats + + - Encoded operations do not include any block or stream properties. Be sure to set `completionBlock`, `outputStream`, and any callback blocks as necessary when using `-initWithCoder:` or `NSKeyedUnarchiver`. + - Operations are paused on `encodeWithCoder:`. If the operation was encoded while paused or still executing, its archived state will return `YES` for `isReady`. Otherwise, the state of an operation when encoding will remain unchanged. + + ### NSCopying Caveats + + - `-copy` and `-copyWithZone:` return a new operation with the `NSURLRequest` of the original. So rather than an exact copy of the operation at that particular instant, the copying mechanism returns a completely new instance, which can be useful for retrying operations. + - A copy of an operation will not include the `outputStream` of the original. + - Operation copies do not include `completionBlock`, as it often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied. + */ + +NS_ASSUME_NONNULL_BEGIN + +@interface AFURLConnectionOperation : NSOperation + +///------------------------------- +/// @name Accessing Run Loop Modes +///------------------------------- + +/** + The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`. + */ +@property (nonatomic, strong) NSSet *runLoopModes; + +///----------------------------------------- +/// @name Getting URL Connection Information +///----------------------------------------- + +/** + The request used by the operation's connection. + */ +@property (readonly, nonatomic, strong) NSURLRequest *request; + +/** + The last response received by the operation's connection. + */ +@property (readonly, nonatomic, strong, nullable) NSURLResponse *response; + +/** + The error, if any, that occurred in the lifecycle of the request. + */ +@property (readonly, nonatomic, strong, nullable) NSError *error; + +///---------------------------- +/// @name Getting Response Data +///---------------------------- + +/** + The data received during the request. + */ +@property (readonly, nonatomic, strong, nullable) NSData *responseData; + +/** + The string representation of the response data. + */ +@property (readonly, nonatomic, copy, nullable) NSString *responseString; + +/** + The string encoding of the response. + + If the response does not specify a valid string encoding, `responseStringEncoding` will return `NSUTF8StringEncoding`. + */ +@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; + +///------------------------------- +/// @name Managing URL Credentials +///------------------------------- + +/** + Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. + + This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. + */ +@property (nonatomic, assign) BOOL shouldUseCredentialStorage; + +/** + The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. + + This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. + */ +@property (nonatomic, strong, nullable) NSURLCredential *credential; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used to evaluate server trust for secure connections. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +///------------------------ +/// @name Accessing Streams +///------------------------ + +/** + The input stream used to read data to be sent during the request. + + This property acts as a proxy to the `HTTPBodyStream` property of `request`. + */ +@property (nonatomic, strong) NSInputStream *inputStream; + +/** + The output stream that is used to write data received until the request is finished. + + By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request, with the intermediary `outputStream` property set to `nil`. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set. + */ +@property (nonatomic, strong, nullable) NSOutputStream *outputStream; + +///--------------------------------- +/// @name Managing Callback Queues +///--------------------------------- + +/** + The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_USE_OBJC +@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign, nullable) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_USE_OBJC +@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign, nullable) dispatch_group_t completionGroup; +#endif + +///--------------------------------------------- +/// @name Managing Request Operation Information +///--------------------------------------------- + +/** + The user info dictionary for the receiver. + */ +@property (nonatomic, strong) NSDictionary *userInfo; +// FIXME: It doesn't seem that this userInfo is used anywhere in the implementation. + +///------------------------------------------------------ +/// @name Initializing an AFURLConnectionOperation Object +///------------------------------------------------------ + +/** + Initializes and returns a newly allocated operation object with a url connection configured with the specified url request. + + This is the designated initializer. + + @param urlRequest The request object to be used by the operation connection. + */ +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest NS_DESIGNATED_INITIALIZER; + +///---------------------------------- +/// @name Pausing / Resuming Requests +///---------------------------------- + +/** + Pauses the execution of the request operation. + + A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished, cancelled, or paused operation has no effect. + */ +- (void)pause; + +/** + Whether the request operation is currently paused. + + @return `YES` if the operation is currently paused, otherwise `NO`. + */ +- (BOOL)isPaused; + +/** + Resumes the execution of the paused request operation. + + Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request. + */ +- (void)resume; + +///---------------------------------------------- +/// @name Configuring Backgrounding Task Behavior +///---------------------------------------------- + +/** + Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task. + + @param handler A handler to be called shortly before the application’s remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the application’s suspension momentarily while the application is notified. + */ +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(nullable void (^)(void))handler NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extensions."); +#endif + +///--------------------------------- +/// @name Setting Progress Callbacks +///--------------------------------- + +/** + Sets a callback to be called when an undetermined number of bytes have been uploaded to the server. + + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setUploadProgressBlock:(nullable void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block; + +/** + Sets a callback to be called when an undetermined number of bytes have been downloaded from the server. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setDownloadProgressBlock:(nullable void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block; + +///------------------------------------------------- +/// @name Setting NSURLConnection Delegate Callbacks +///------------------------------------------------- + +/** + Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`. + + @param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol). + + If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates. + */ +- (void)setWillSendRequestForAuthenticationChallengeBlock:(nullable void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block; + +/** + Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDataDelegate` method `connection:willSendRequest:redirectResponse:`. + + @param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect. + */ +- (void)setRedirectResponseBlock:(nullable NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block; + + +/** + Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`. + + @param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request. + */ +- (void)setCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block; + +/// + +/** + + */ ++ (NSArray *)batchOfRequestOperations:(nullable NSArray *)operations + progressBlock:(nullable void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(nullable void (^)(NSArray *operations))completionBlock; + +@end + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when an operation begins executing. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationDidStartNotification; + +/** + Posted when an operation finishes. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationDidFinishNotification; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m new file mode 100644 index 0000000..0568005 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m @@ -0,0 +1,792 @@ +// AFURLConnectionOperation.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLConnectionOperation.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import +#endif + +#if !__has_feature(objc_arc) +#error AFNetworking must be built with ARC. +// You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files. +#endif + +typedef NS_ENUM(NSInteger, AFOperationState) { + AFOperationPausedState = -1, + AFOperationReadyState = 1, + AFOperationExecutingState = 2, + AFOperationFinishedState = 3, +}; + +static dispatch_group_t url_request_operation_completion_group() { + static dispatch_group_t af_url_request_operation_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_request_operation_completion_group = dispatch_group_create(); + }); + + return af_url_request_operation_completion_group; +} + +static dispatch_queue_t url_request_operation_completion_queue() { + static dispatch_queue_t af_url_request_operation_completion_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT ); + }); + + return af_url_request_operation_completion_queue; +} + +static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock"; + +NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start"; +NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish"; + +typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge); +typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse); +typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse); +typedef void (^AFURLConnectionOperationBackgroundTaskCleanupBlock)(); + +static inline NSString * AFKeyPathFromOperationState(AFOperationState state) { + switch (state) { + case AFOperationReadyState: + return @"isReady"; + case AFOperationExecutingState: + return @"isExecuting"; + case AFOperationFinishedState: + return @"isFinished"; + case AFOperationPausedState: + return @"isPaused"; + default: { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" + return @"state"; +#pragma clang diagnostic pop + } + } +} + +static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) { + switch (fromState) { + case AFOperationReadyState: + switch (toState) { + case AFOperationPausedState: + case AFOperationExecutingState: + return YES; + case AFOperationFinishedState: + return isCancelled; + default: + return NO; + } + case AFOperationExecutingState: + switch (toState) { + case AFOperationPausedState: + case AFOperationFinishedState: + return YES; + default: + return NO; + } + case AFOperationFinishedState: + return NO; + case AFOperationPausedState: + return toState == AFOperationReadyState; + default: { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" + switch (toState) { + case AFOperationPausedState: + case AFOperationReadyState: + case AFOperationExecutingState: + case AFOperationFinishedState: + return YES; + default: + return NO; + } + } +#pragma clang diagnostic pop + } +} + +@interface AFURLConnectionOperation () +@property (readwrite, nonatomic, assign) AFOperationState state; +@property (readwrite, nonatomic, strong) NSRecursiveLock *lock; +@property (readwrite, nonatomic, strong) NSURLConnection *connection; +@property (readwrite, nonatomic, strong) NSURLRequest *request; +@property (readwrite, nonatomic, strong) NSURLResponse *response; +@property (readwrite, nonatomic, strong) NSError *error; +@property (readwrite, nonatomic, strong) NSData *responseData; +@property (readwrite, nonatomic, copy) NSString *responseString; +@property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding; +@property (readwrite, nonatomic, assign) long long totalBytesRead; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationBackgroundTaskCleanupBlock backgroundTaskCleanup; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse; +@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse; + +- (void)operationDidStart; +- (void)finish; +- (void)cancelConnection; +@end + +@implementation AFURLConnectionOperation +@synthesize outputStream = _outputStream; + ++ (void)networkRequestThreadEntryPoint:(id)__unused object { + @autoreleasepool { + [[NSThread currentThread] setName:@"AFNetworking"]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; + [runLoop run]; + } +} + ++ (NSThread *)networkRequestThread { + static NSThread *_networkRequestThread = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; + [_networkRequestThread start]; + }); + + return _networkRequestThread; +} + +- (instancetype)initWithRequest:(NSURLRequest *)urlRequest { + NSParameterAssert(urlRequest); + + self = [super init]; + if (!self) { + return nil; + } + + _state = AFOperationReadyState; + + self.lock = [[NSRecursiveLock alloc] init]; + self.lock.name = kAFNetworkingLockName; + + self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes]; + + self.request = urlRequest; + + self.shouldUseCredentialStorage = YES; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + + return self; +} + +- (instancetype)init NS_UNAVAILABLE +{ + return nil; +} + +- (void)dealloc { + if (_outputStream) { + [_outputStream close]; + _outputStream = nil; + } + + if (_backgroundTaskCleanup) { + _backgroundTaskCleanup(); + } +} + +#pragma mark - + +- (void)setResponseData:(NSData *)responseData { + [self.lock lock]; + if (!responseData) { + _responseData = nil; + } else { + _responseData = [NSData dataWithBytes:responseData.bytes length:responseData.length]; + } + [self.lock unlock]; +} + +- (NSString *)responseString { + [self.lock lock]; + if (!_responseString && self.response && self.responseData) { + self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding]; + } + [self.lock unlock]; + + return _responseString; +} + +- (NSStringEncoding)responseStringEncoding { + [self.lock lock]; + if (!_responseStringEncoding && self.response) { + NSStringEncoding stringEncoding = NSUTF8StringEncoding; + if (self.response.textEncodingName) { + CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName); + if (IANAEncoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding); + } + } + + self.responseStringEncoding = stringEncoding; + } + [self.lock unlock]; + + return _responseStringEncoding; +} + +- (NSInputStream *)inputStream { + return self.request.HTTPBodyStream; +} + +- (void)setInputStream:(NSInputStream *)inputStream { + NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; + mutableRequest.HTTPBodyStream = inputStream; + self.request = mutableRequest; +} + +- (NSOutputStream *)outputStream { + if (!_outputStream) { + self.outputStream = [NSOutputStream outputStreamToMemory]; + } + + return _outputStream; +} + +- (void)setOutputStream:(NSOutputStream *)outputStream { + [self.lock lock]; + if (outputStream != _outputStream) { + if (_outputStream) { + [_outputStream close]; + } + + _outputStream = outputStream; + } + [self.lock unlock]; +} + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler { + [self.lock lock]; + if (!self.backgroundTaskCleanup) { + UIApplication *application = [UIApplication sharedApplication]; + UIBackgroundTaskIdentifier __block backgroundTaskIdentifier = UIBackgroundTaskInvalid; + __weak __typeof(self)weakSelf = self; + + self.backgroundTaskCleanup = ^(){ + if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; + backgroundTaskIdentifier = UIBackgroundTaskInvalid; + } + }; + + backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ + __strong __typeof(weakSelf)strongSelf = weakSelf; + + if (handler) { + handler(); + } + + if (strongSelf) { + [strongSelf cancel]; + strongSelf.backgroundTaskCleanup(); + } + }]; + } + [self.lock unlock]; +} +#endif + +#pragma mark - + +- (void)setState:(AFOperationState)state { + if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) { + return; + } + + [self.lock lock]; + NSString *oldStateKey = AFKeyPathFromOperationState(self.state); + NSString *newStateKey = AFKeyPathFromOperationState(state); + + [self willChangeValueForKey:newStateKey]; + [self willChangeValueForKey:oldStateKey]; + _state = state; + [self didChangeValueForKey:oldStateKey]; + [self didChangeValueForKey:newStateKey]; + [self.lock unlock]; +} + +- (void)pause { + if ([self isPaused] || [self isFinished] || [self isCancelled]) { + return; + } + + [self.lock lock]; + if ([self isExecuting]) { + [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; + }); + } + + self.state = AFOperationPausedState; + [self.lock unlock]; +} + +- (void)operationDidPause { + [self.lock lock]; + [self.connection cancel]; + [self.lock unlock]; +} + +- (BOOL)isPaused { + return self.state == AFOperationPausedState; +} + +- (void)resume { + if (![self isPaused]) { + return; + } + + [self.lock lock]; + self.state = AFOperationReadyState; + + [self start]; + [self.lock unlock]; +} + +#pragma mark - + +- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block { + self.uploadProgress = block; +} + +- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block { + self.downloadProgress = block; +} + +- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block { + self.authenticationChallenge = block; +} + +- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block { + self.cacheResponse = block; +} + +- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block { + self.redirectResponse = block; +} + +#pragma mark - NSOperation + +- (void)setCompletionBlock:(void (^)(void))block { + [self.lock lock]; + if (!block) { + [super setCompletionBlock:nil]; + } else { + __weak __typeof(self)weakSelf = self; + [super setCompletionBlock:^ { + __strong __typeof(weakSelf)strongSelf = weakSelf; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group(); + dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue(); +#pragma clang diagnostic pop + + dispatch_group_async(group, queue, ^{ + block(); + }); + + dispatch_group_notify(group, url_request_operation_completion_queue(), ^{ + [strongSelf setCompletionBlock:nil]; + }); + }]; + } + [self.lock unlock]; +} + +- (BOOL)isReady { + return self.state == AFOperationReadyState && [super isReady]; +} + +- (BOOL)isExecuting { + return self.state == AFOperationExecutingState; +} + +- (BOOL)isFinished { + return self.state == AFOperationFinishedState; +} + +- (BOOL)isConcurrent { + return YES; +} + +- (void)start { + [self.lock lock]; + if ([self isCancelled]) { + [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } else if ([self isReady]) { + self.state = AFOperationExecutingState; + + [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } + [self.lock unlock]; +} + +- (void)operationDidStart { + [self.lock lock]; + if (![self isCancelled]) { + self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; + + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; + for (NSString *runLoopMode in self.runLoopModes) { + [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; + [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; + } + + [self.outputStream open]; + [self.connection start]; + } + [self.lock unlock]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; + }); +} + +- (void)finish { + [self.lock lock]; + self.state = AFOperationFinishedState; + [self.lock unlock]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; + }); +} + +- (void)cancel { + [self.lock lock]; + if (![self isFinished] && ![self isCancelled]) { + [super cancel]; + + if ([self isExecuting]) { + [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; + } + } + [self.lock unlock]; +} + +- (void)cancelConnection { + NSDictionary *userInfo = nil; + if ([self.request URL]) { + userInfo = @{NSURLErrorFailingURLErrorKey : [self.request URL]}; + } + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; + + if (![self isFinished]) { + if (self.connection) { + [self.connection cancel]; + [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error]; + } else { + // Accommodate race condition where `self.connection` has not yet been set before cancellation + self.error = error; + [self finish]; + } + } +} + +#pragma mark - + ++ (NSArray *)batchOfRequestOperations:(NSArray *)operations + progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock + completionBlock:(void (^)(NSArray *operations))completionBlock +{ + if (!operations || [operations count] == 0) { + return @[[NSBlockOperation blockOperationWithBlock:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(@[]); + } + }); + }]]; + } + + __block dispatch_group_t group = dispatch_group_create(); + NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(operations); + } + }); + }]; + + for (AFURLConnectionOperation *operation in operations) { + operation.completionGroup = group; + void (^originalCompletionBlock)(void) = [operation.completionBlock copy]; + __weak __typeof(operation)weakOperation = operation; + operation.completionBlock = ^{ + __strong __typeof(weakOperation)strongOperation = weakOperation; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue(); +#pragma clang diagnostic pop + dispatch_group_async(group, queue, ^{ + if (originalCompletionBlock) { + originalCompletionBlock(); + } + + NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx, BOOL __unused *stop) { + return [op isFinished]; + }] count]; + + if (progressBlock) { + progressBlock(numberOfFinishedOperations, [operations count]); + } + + dispatch_group_leave(group); + }); + }; + + dispatch_group_enter(group); + [batchedOperation addDependency:operation]; + } + + return [operations arrayByAddingObject:batchedOperation]; +} + +#pragma mark - NSObject + +- (NSString *)description { + [self.lock lock]; + NSString *description = [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response]; + [self.lock unlock]; + return description; +} + +#pragma mark - NSURLConnectionDelegate + +- (void)connection:(NSURLConnection *)connection +willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (self.authenticationChallenge) { + self.authenticationChallenge(connection, challenge); + return; + } + + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] cancelAuthenticationChallenge:challenge]; + } + } else { + if ([challenge previousFailureCount] == 0) { + if (self.credential) { + [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { + return self.shouldUseCredentialStorage; +} + +- (NSURLRequest *)connection:(NSURLConnection *)connection + willSendRequest:(NSURLRequest *)request + redirectResponse:(NSURLResponse *)redirectResponse +{ + if (self.redirectResponse) { + return self.redirectResponse(connection, request, redirectResponse); + } else { + return request; + } +} + +- (void)connection:(NSURLConnection __unused *)connection + didSendBodyData:(NSInteger)bytesWritten + totalBytesWritten:(NSInteger)totalBytesWritten +totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.uploadProgress) { + self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } + }); +} + +- (void)connection:(NSURLConnection __unused *)connection +didReceiveResponse:(NSURLResponse *)response +{ + self.response = response; +} + +- (void)connection:(NSURLConnection __unused *)connection + didReceiveData:(NSData *)data +{ + NSUInteger length = [data length]; + while (YES) { + NSInteger totalNumberOfBytesWritten = 0; + if ([self.outputStream hasSpaceAvailable]) { + const uint8_t *dataBuffer = (uint8_t *)[data bytes]; + + NSInteger numberOfBytesWritten = 0; + while (totalNumberOfBytesWritten < (NSInteger)length) { + numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)]; + if (numberOfBytesWritten == -1) { + break; + } + + totalNumberOfBytesWritten += numberOfBytesWritten; + } + + break; + } else { + [self.connection cancel]; + if (self.outputStream.streamError) { + [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError]; + } + return; + } + } + + dispatch_async(dispatch_get_main_queue(), ^{ + self.totalBytesRead += (long long)length; + + if (self.downloadProgress) { + self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength); + } + }); +} + +- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection { + self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + + [self.outputStream close]; + if (self.responseData) { + self.outputStream = nil; + } + + self.connection = nil; + + [self finish]; +} + +- (void)connection:(NSURLConnection __unused *)connection + didFailWithError:(NSError *)error +{ + self.error = error; + + [self.outputStream close]; + if (self.responseData) { + self.outputStream = nil; + } + + self.connection = nil; + + [self finish]; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection + willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + if (self.cacheResponse) { + return self.cacheResponse(connection, cachedResponse); + } else { + if ([self isCancelled]) { + return nil; + } + + return cachedResponse; + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURLRequest *request = [decoder decodeObjectOfClass:[NSURLRequest class] forKey:NSStringFromSelector(@selector(request))]; + + self = [self initWithRequest:request]; + if (!self) { + return nil; + } + + self.state = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(state))] integerValue]; + self.response = [decoder decodeObjectOfClass:[NSHTTPURLResponse class] forKey:NSStringFromSelector(@selector(response))]; + self.error = [decoder decodeObjectOfClass:[NSError class] forKey:NSStringFromSelector(@selector(error))]; + self.responseData = [decoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(responseData))]; + self.totalBytesRead = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(totalBytesRead))] longLongValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [self pause]; + + [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))]; + + switch (self.state) { + case AFOperationExecutingState: + case AFOperationPausedState: + [coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))]; + break; + default: + [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))]; + break; + } + + [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))]; + [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))]; + [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))]; + [coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request]; + + operation.uploadProgress = self.uploadProgress; + operation.downloadProgress = self.downloadProgress; + operation.authenticationChallenge = self.authenticationChallenge; + operation.cacheResponse = self.cacheResponse; + operation.redirectResponse = self.redirectResponse; + operation.completionQueue = self.completionQueue; + operation.completionGroup = self.completionGroup; + + return operation; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h new file mode 100644 index 0000000..ef4d366 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1,473 @@ +// AFURLRequestSerialization.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#if TARGET_OS_IOS +#import +#elif TARGET_OS_WATCH +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary. + + For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`. + */ +@protocol AFURLRequestSerialization + +/** + Returns a request with the specified parameters encoded into a copy of the original request. + + @param request The original request. + @param parameters The parameters to be encoded. + @param error The error that occurred while attempting to encode the request parameters. + + @return A serialized request. + */ +- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(nullable id)parameters + error:(NSError * __nullable __autoreleasing *)error; + +@end + +#pragma mark - + +/** + + */ +typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) { + AFHTTPRequestQueryStringDefaultStyle = 0, +}; + +@protocol AFMultipartFormData; + +/** + `AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPRequestSerializer : NSObject + +/** + The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. + */ +@property (nonatomic, assign) NSStringEncoding stringEncoding; + +/** + Whether created requests can use the device’s cellular radio (if present). `YES` by default. + + @see NSMutableURLRequest -setAllowsCellularAccess: + */ +@property (nonatomic, assign) BOOL allowsCellularAccess; + +/** + The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. + + @see NSMutableURLRequest -setCachePolicy: + */ +@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; + +/** + Whether created requests should use the default cookie handling. `YES` by default. + + @see NSMutableURLRequest -setHTTPShouldHandleCookies: + */ +@property (nonatomic, assign) BOOL HTTPShouldHandleCookies; + +/** + Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default + + @see NSMutableURLRequest -setHTTPShouldUsePipelining: + */ +@property (nonatomic, assign) BOOL HTTPShouldUsePipelining; + +/** + The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. + + @see NSMutableURLRequest -setNetworkServiceType: + */ +@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; + +/** + The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. + + @see NSMutableURLRequest -setTimeoutInterval: + */ +@property (nonatomic, assign) NSTimeInterval timeoutInterval; + +///--------------------------------------- +/// @name Configuring HTTP Request Headers +///--------------------------------------- + +/** + Default HTTP header field values to be applied to serialized requests. By default, these include the following: + + - `Accept-Language` with the contents of `NSLocale +preferredLanguages` + - `User-Agent` with the contents of various bundle identifiers and OS designations + + @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`. + */ +@property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +/** + Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header. + + @param field The HTTP header to set a default value for + @param value The value set as default for the specified header, or `nil` + */ +- (void)setValue:(nullable NSString *)value +forHTTPHeaderField:(NSString *)field; + +/** + Returns the value for the HTTP headers set in the request serializer. + + @param field The HTTP header to retrieve the default value for + + @return The value set as default for the specified header, or `nil` + */ +- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field; + +/** + Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header. + + @param username The HTTP basic auth username + @param password The HTTP basic auth password + */ +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password; + +/** + @deprecated This method has been deprecated. Use -setValue:forHTTPHeaderField: instead. + */ +- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token DEPRECATED_ATTRIBUTE; + + +/** + Clears any existing value for the "Authorization" HTTP header. + */ +- (void)clearAuthorizationHeader; + +///------------------------------------------------------- +/// @name Configuring Query String Parameter Serialization +///------------------------------------------------------- + +/** + HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default. + */ +@property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI; + +/** + Set the method of query string serialization according to one of the pre-defined styles. + + @param style The serialization style. + + @see AFHTTPRequestQueryStringSerializationStyle + */ +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; + +/** + Set the a custom method of query string serialization according to the specified block. + + @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request. + */ +- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; + +///------------------------------- +/// @name Creating Request Objects +///------------------------------- + +/** + @deprecated This method has been deprecated. Use -requestWithMethod:URLString:parameters:error: instead. + */ +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters DEPRECATED_ATTRIBUTE; + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string. + + If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body. + + @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. + @param error The error that occurred while constructing the request. + + @return An `NSMutableURLRequest` object. + */ +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable id)parameters + error:(NSError * __nullable __autoreleasing *)error; + +/** + @deprecated This method has been deprecated. Use -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error: instead. + */ +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block DEPRECATED_ATTRIBUTE; + +/** + Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 + + Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream. + + @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`. + @param URLString The URL string used to create the request URL. + @param parameters The parameters to be encoded and set in the request HTTP body. + @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. + @param error The error that occurred while constructing the request. + + @return An `NSMutableURLRequest` object + */ +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(nullable NSDictionary *)parameters + constructingBodyWithBlock:(nullable void (^)(id formData))block + error:(NSError * __nullable __autoreleasing *)error; + +/** + Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished. + + @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`. + @param fileURL The file URL to write multipart form contents to. + @param handler A handler block to execute. + + @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request. + + @see https://github.com/AFNetworking/AFNetworking/issues/1398 + */ +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(nullable void (^)(NSError * __nullable error))handler; + +@end + +#pragma mark - + +/** + The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`. + */ +@protocol AFMultipartFormData + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. + + The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended, otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * __nullable __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`. + @param mimeType The declared MIME type of the file data. This parameter must not be `nil`. + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + + @return `YES` if the file data was successfully appended otherwise `NO`. + */ +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * __nullable __autoreleasing *)error; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary. + + @param inputStream The input stream to be appended to the form data + @param name The name to be associated with the specified input stream. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`. + @param length The length of the specified input stream in bytes. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + @param fileName The filename to be associated with the specified data. This parameter must not be `nil`. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + */ +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType; + +/** + Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. + + @param data The data to be encoded and appended to the form data. + @param name The name to be associated with the specified data. This parameter must not be `nil`. + */ + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name; + + +/** + Appends HTTP headers, followed by the encoded data and the multipart form boundary. + + @param headers The HTTP headers to be appended to the form data. + @param body The data to be encoded and appended to the form data. This parameter must not be `nil`. + */ +- (void)appendPartWithHeaders:(nullable NSDictionary *)headers + body:(NSData *)body; + +/** + Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream. + + When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth. + + @param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb. + @param delay Duration of delay each time a packet is read. By default, no delay is set. + */ +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay; + +@end + +#pragma mark - + +/** + `AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`. + */ +@interface AFJSONRequestSerializer : AFHTTPRequestSerializer + +/** + Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONWritingOptions writingOptions; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param writingOptions The specified JSON writing options. + */ ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions; + +@end + +#pragma mark - + +/** + `AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`. + */ +@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + @warning The `writeOptions` property is currently unused. + */ +@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param writeOptions The property list write options. + + @warning The `writeOptions` property is currently unused. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions; + +@end + +#pragma mark - + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLRequestSerializationErrorDomain` + + ### Constants + + `AFURLRequestSerializationErrorDomain` + AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLRequestErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLRequestErrorKey` + The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey; + +/** + ## Throttling Bandwidth for HTTP Request Input Streams + + @see -throttleBandwidthWithPacketSize:delay: + + ### Constants + + `kAFUploadStream3GSuggestedPacketSize` + Maximum packet size, in number of bytes. Equal to 16kb. + + `kAFUploadStream3GSuggestedDelay` + Duration of delay each time a packet is read. Equal to 0.2 seconds. + */ +FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize; +FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m new file mode 100644 index 0000000..e387c24 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m @@ -0,0 +1,1425 @@ +// AFURLRequestSerialization.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLRequestSerialization.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +#import +#else +#import +#endif + +NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request"; +NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response"; + +typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error); + +static NSString * AFBase64EncodedStringFromString(NSString *string) { + NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; + NSUInteger length = [data length]; + NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; + + uint8_t *input = (uint8_t *)[data bytes]; + uint8_t *output = (uint8_t *)[mutableData mutableBytes]; + + for (NSUInteger i = 0; i < length; i += 3) { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) { + value <<= 8; + if (j < length) { + value |= (0xFF & input[j]); + } + } + + static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + NSUInteger idx = (i / 3) * 4; + output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; + output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; + } + + return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; +} + +/** + Returns a percent-escaped string following RFC 3986 for a query string key or value. + RFC 3986 states that the following characters are "reserved" characters. + - General Delimiters: ":", "#", "[", "]", "@", "?", "/" + - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" + + In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow + query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" + should be percent-escaped in the query string. + - parameter string: The string to be percent-escaped. + - returns: The percent-escaped string. + */ +static NSString * AFPercentEscapedStringFromString(NSString *string) { + static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 + static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; + + NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; + + // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 + // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + + static NSUInteger const batchSize = 50; + + NSInteger index = 0; + NSMutableString *escaped = @"".mutableCopy; + + while (index < string.length) { + NSUInteger length = MIN(string.length - index, batchSize); + NSRange range = NSMakeRange(index, length); + + // To avoid breaking up character sequences such as 👴🏻👮🏽 + range = [string rangeOfComposedCharacterSequencesForRange:range]; + + NSString *substring = [string substringWithRange:range]; + NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; + [escaped appendString:encoded]; + + index += range.length; + } + + return escaped; +} + +#pragma mark - + +@interface AFQueryStringPair : NSObject +@property (readwrite, nonatomic, strong) id field; +@property (readwrite, nonatomic, strong) id value; + +- (id)initWithField:(id)field value:(id)value; + +- (NSString *)URLEncodedStringValue; +@end + +@implementation AFQueryStringPair + +- (id)initWithField:(id)field value:(id)value { + self = [super init]; + if (!self) { + return nil; + } + + self.field = field; + self.value = value; + + return self; +} + +- (NSString *)URLEncodedStringValue { + if (!self.value || [self.value isEqual:[NSNull null]]) { + return AFPercentEscapedStringFromString([self.field description]); + } else { + return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])]; + } +} + +@end + +#pragma mark - + +FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary); +FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); + +static NSString * AFQueryStringFromParameters(NSDictionary *parameters) { + NSMutableArray *mutablePairs = [NSMutableArray array]; + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + [mutablePairs addObject:[pair URLEncodedStringValue]]; + } + + return [mutablePairs componentsJoinedByString:@"&"]; +} + +NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) { + return AFQueryStringPairsFromKeyAndValue(nil, dictionary); +} + +NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)]; + + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionary = value; + // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries + for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + id nestedValue = dictionary[nestedKey]; + if (nestedValue) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; + } + } + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *array = value; + for (id nestedValue in array) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; + } + } else if ([value isKindOfClass:[NSSet class]]) { + NSSet *set = value; + for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)]; + } + } else { + [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; + } + + return mutableQueryStringComponents; +} + +#pragma mark - + +@interface AFStreamingMultipartFormData : NSObject +- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding; + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData; +@end + +#pragma mark - + +static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { + static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; + }); + + return _AFHTTPRequestSerializerObservedKeyPaths; +} + +static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext; + +@interface AFHTTPRequestSerializer () +@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders; +@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; +@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; +@end + +@implementation AFHTTPRequestSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = NSUTF8StringEncoding; + + self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; + + // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + NSMutableArray *acceptLanguagesComponents = [NSMutableArray array]; + [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + float q = 1.0f - (idx * 0.1f); + [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]]; + *stop = q <= 0.5f; + }]; + [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; + + NSString *userAgent = nil; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" +#if TARGET_OS_IOS + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; +#elif TARGET_OS_WATCH + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]]; +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; +#endif +#pragma clang diagnostic pop + if (userAgent) { + if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { + NSMutableString *mutableUserAgent = [userAgent mutableCopy]; + if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) { + userAgent = mutableUserAgent; + } + } + [self setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + + // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; + + self.mutableObservedChangedKeyPaths = [NSMutableSet set]; + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; + } + } + + return self; +} + +- (void)dealloc { + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { + [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; + } + } +} + +#pragma mark - + +// Workarounds for crashing behavior using Key-Value Observing with XCTest +// See https://github.com/AFNetworking/AFNetworking/issues/2523 + +- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { + [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; + _allowsCellularAccess = allowsCellularAccess; + [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))]; +} + +- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { + [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; + _cachePolicy = cachePolicy; + [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))]; +} + +- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; + _HTTPShouldHandleCookies = HTTPShouldHandleCookies; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; +} + +- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining { + [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; + _HTTPShouldUsePipelining = HTTPShouldUsePipelining; + [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))]; +} + +- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { + [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; + _networkServiceType = networkServiceType; + [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))]; +} + +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval { + [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; + _timeoutInterval = timeoutInterval; + [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))]; +} + +#pragma mark - + +- (NSDictionary *)HTTPRequestHeaders { + return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; +} + +- (void)setValue:(NSString *)value +forHTTPHeaderField:(NSString *)field +{ + [self.mutableHTTPRequestHeaders setValue:value forKey:field]; +} + +- (NSString *)valueForHTTPHeaderField:(NSString *)field { + return [self.mutableHTTPRequestHeaders valueForKey:field]; +} + +- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username + password:(NSString *)password +{ + NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password]; + [self setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"]; +} + +- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token { + [self setValue:[NSString stringWithFormat:@"Token token=\"%@\"", token] forHTTPHeaderField:@"Authorization"]; +} + +- (void)clearAuthorizationHeader { + [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"]; +} + +#pragma mark - + +- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style { + self.queryStringSerializationStyle = style; + self.queryStringSerialization = nil; +} + +- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block { + self.queryStringSerialization = block; +} + +#pragma mark - + +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters +{ + return [self requestWithMethod:method URLString:URLString parameters:parameters error:nil]; +} + +- (NSMutableURLRequest *)requestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(URLString); + + NSURL *url = [NSURL URLWithString:URLString]; + + NSParameterAssert(url); + + NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; + mutableRequest.HTTPMethod = method; + + for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { + if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { + [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; + } + } + + mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; + + return mutableRequest; +} + +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block +{ + return [self multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:nil]; +} + +- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + constructingBodyWithBlock:(void (^)(id formData))block + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(method); + NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); + + NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; + + __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; + + if (parameters) { + for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) { + NSData *data = nil; + if ([pair.value isKindOfClass:[NSData class]]) { + data = pair.value; + } else if ([pair.value isEqual:[NSNull null]]) { + data = [NSData data]; + } else { + data = [[pair.value description] dataUsingEncoding:self.stringEncoding]; + } + + if (data) { + [formData appendPartWithFormData:data name:[pair.field description]]; + } + } + } + + if (block) { + block(formData); + } + + return [formData requestByFinalizingMultipartFormData]; +} + +- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request + writingStreamContentsToFile:(NSURL *)fileURL + completionHandler:(void (^)(NSError *error))handler +{ + NSParameterAssert(request.HTTPBodyStream); + NSParameterAssert([fileURL isFileURL]); + + NSInputStream *inputStream = request.HTTPBodyStream; + NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; + __block NSError *error = nil; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputStream open]; + [outputStream open]; + + while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { + uint8_t buffer[1024]; + + NSInteger bytesRead = [inputStream read:buffer maxLength:1024]; + if (inputStream.streamError || bytesRead < 0) { + error = inputStream.streamError; + break; + } + + NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead]; + if (outputStream.streamError || bytesWritten < 0) { + error = outputStream.streamError; + break; + } + + if (bytesRead == 0 && bytesWritten == 0) { + break; + } + } + + [outputStream close]; + [inputStream close]; + + if (handler) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler(error); + }); + } + }); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + mutableRequest.HTTPBodyStream = nil; + + return mutableRequest; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + NSString *query = nil; + if (parameters) { + if (self.queryStringSerialization) { + NSError *serializationError; + query = self.queryStringSerialization(request, parameters, &serializationError); + + if (serializationError) { + if (error) { + *error = serializationError; + } + + return nil; + } + } else { + switch (self.queryStringSerializationStyle) { + case AFHTTPRequestQueryStringDefaultStyle: + query = AFQueryStringFromParameters(parameters); + break; + } + } + } + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + if (query) { + mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; + } + } else { + // #2864: an empty string is a valid x-www-form-urlencoded payload + if (!query) { + query = @""; + } + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + } + [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; + } + + return mutableRequest; +} + +#pragma mark - NSKeyValueObserving + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { + return NO; + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(__unused id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == AFHTTPRequestSerializerObserverContext) { + if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { + [self.mutableObservedChangedKeyPaths removeObject:keyPath]; + } else { + [self.mutableObservedChangedKeyPaths addObject:keyPath]; + } + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; + self.queryStringSerializationStyle = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))]; + [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone]; + serializer.queryStringSerializationStyle = self.queryStringSerializationStyle; + serializer.queryStringSerialization = self.queryStringSerialization; + + return serializer; +} + +@end + +#pragma mark - + +static NSString * AFCreateMultipartFormBoundary() { + return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; +} + +static NSString * const kAFMultipartFormCRLF = @"\r\n"; + +static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) { + return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; +} + +static inline NSString * AFContentTypeForPathExtension(NSString *extension) { +#ifdef __UTTYPE__ + NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); + NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); + if (!contentType) { + return @"application/octet-stream"; + } else { + return contentType; + } +#else +#pragma unused (extension) + return @"application/octet-stream"; +#endif +} + +NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; +NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2; + +@interface AFHTTPBodyPart : NSObject +@property (nonatomic, assign) NSStringEncoding stringEncoding; +@property (nonatomic, strong) NSDictionary *headers; +@property (nonatomic, copy) NSString *boundary; +@property (nonatomic, strong) id body; +@property (nonatomic, assign) unsigned long long bodyContentLength; +@property (nonatomic, strong) NSInputStream *inputStream; + +@property (nonatomic, assign) BOOL hasInitialBoundary; +@property (nonatomic, assign) BOOL hasFinalBoundary; + +@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; +@property (readonly, nonatomic, assign) unsigned long long contentLength; + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@interface AFMultipartBodyStream : NSInputStream +@property (nonatomic, assign) NSUInteger numberOfBytesInPacket; +@property (nonatomic, assign) NSTimeInterval delay; +@property (nonatomic, strong) NSInputStream *inputStream; +@property (readonly, nonatomic, assign) unsigned long long contentLength; +@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty; + +- (id)initWithStringEncoding:(NSStringEncoding)encoding; +- (void)setInitialAndFinalBoundaries; +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart; +@end + +#pragma mark - + +@interface AFStreamingMultipartFormData () +@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, copy) NSString *boundary; +@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; +@end + +@implementation AFStreamingMultipartFormData + +- (id)initWithURLRequest:(NSMutableURLRequest *)urlRequest + stringEncoding:(NSStringEncoding)encoding +{ + self = [super init]; + if (!self) { + return nil; + } + + self.request = urlRequest; + self.stringEncoding = encoding; + self.boundary = AFCreateMultipartFormBoundary(); + self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; + + return self; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + + NSString *fileName = [fileURL lastPathComponent]; + NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]); + + return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; +} + +- (BOOL)appendPartWithFileURL:(NSURL *)fileURL + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + error:(NSError * __autoreleasing *)error +{ + NSParameterAssert(fileURL); + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + if (![fileURL isFileURL]) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)}; + if (error) { + *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo]; + } + + return NO; + } + + NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error]; + if (!fileAttributes) { + return NO; + } + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = fileURL; + bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue]; + [self.bodyStream appendHTTPBodyPart:bodyPart]; + + return YES; +} + +- (void)appendPartWithInputStream:(NSInputStream *)inputStream + name:(NSString *)name + fileName:(NSString *)fileName + length:(int64_t)length + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = mutableHeaders; + bodyPart.boundary = self.boundary; + bodyPart.body = inputStream; + + bodyPart.bodyContentLength = (unsigned long long)length; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)appendPartWithFileData:(NSData *)data + name:(NSString *)name + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType +{ + NSParameterAssert(name); + NSParameterAssert(fileName); + NSParameterAssert(mimeType); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithFormData:(NSData *)data + name:(NSString *)name +{ + NSParameterAssert(name); + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; + + [self appendPartWithHeaders:mutableHeaders body:data]; +} + +- (void)appendPartWithHeaders:(NSDictionary *)headers + body:(NSData *)body +{ + NSParameterAssert(body); + + AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = headers; + bodyPart.boundary = self.boundary; + bodyPart.bodyContentLength = [body length]; + bodyPart.body = body; + + [self.bodyStream appendHTTPBodyPart:bodyPart]; +} + +- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes + delay:(NSTimeInterval)delay +{ + self.bodyStream.numberOfBytesInPacket = numberOfBytes; + self.bodyStream.delay = delay; +} + +- (NSMutableURLRequest *)requestByFinalizingMultipartFormData { + if ([self.bodyStream isEmpty]) { + return self.request; + } + + // Reset the initial and final boundaries to ensure correct Content-Length + [self.bodyStream setInitialAndFinalBoundaries]; + [self.request setHTTPBodyStream:self.bodyStream]; + + [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"]; + [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; + + return self.request; +} + +@end + +#pragma mark - + +@interface NSStream () +@property (readwrite) NSStreamStatus streamStatus; +@property (readwrite, copy) NSError *streamError; +@end + +@interface AFMultipartBodyStream () +@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; +@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; +@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; +@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; +@property (readwrite, nonatomic, strong) NSOutputStream *outputStream; +@property (readwrite, nonatomic, strong) NSMutableData *buffer; +@end + +@implementation AFMultipartBodyStream +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-atomic-properties" +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100) +@synthesize delegate; +#endif +@synthesize streamStatus; +@synthesize streamError; +#pragma clang diagnostic pop + +- (id)initWithStringEncoding:(NSStringEncoding)encoding { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = encoding; + self.HTTPBodyParts = [NSMutableArray array]; + self.numberOfBytesInPacket = NSIntegerMax; + + return self; +} + +- (void)setInitialAndFinalBoundaries { + if ([self.HTTPBodyParts count] > 0) { + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + bodyPart.hasInitialBoundary = NO; + bodyPart.hasFinalBoundary = NO; + } + + [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES]; + [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; + } +} + +- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart { + [self.HTTPBodyParts addObject:bodyPart]; +} + +- (BOOL)isEmpty { + return [self.HTTPBodyParts count] == 0; +} + +#pragma mark - NSInputStream + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + if ([self streamStatus] == NSStreamStatusClosed) { + return 0; + } + + NSInteger totalNumberOfBytesRead = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) { + if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { + if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { + break; + } + } else { + NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead; + NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; + if (numberOfBytesRead == -1) { + self.streamError = self.currentHTTPBodyPart.inputStream.streamError; + break; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if (self.delay > 0.0f) { + [NSThread sleepForTimeInterval:self.delay]; + } + } + } + } +#pragma clang diagnostic pop + + return totalNumberOfBytesRead; +} + +- (BOOL)getBuffer:(__unused uint8_t **)buffer + length:(__unused NSUInteger *)len +{ + return NO; +} + +- (BOOL)hasBytesAvailable { + return [self streamStatus] == NSStreamStatusOpen; +} + +#pragma mark - NSStream + +- (void)open { + if (self.streamStatus == NSStreamStatusOpen) { + return; + } + + self.streamStatus = NSStreamStatusOpen; + + [self setInitialAndFinalBoundaries]; + self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator]; +} + +- (void)close { + self.streamStatus = NSStreamStatusClosed; +} + +- (id)propertyForKey:(__unused NSString *)key { + return nil; +} + +- (BOOL)setProperty:(__unused id)property + forKey:(__unused NSString *)key +{ + return NO; +} + +- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop + forMode:(__unused NSString *)mode +{} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + length += [bodyPart contentLength]; + } + + return length; +} + +#pragma mark - Undocumented CFReadStream Bridged Methods + +- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop + forMode:(__unused CFStringRef)aMode +{} + +- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags + callback:(__unused CFReadStreamClientCallBack)inCallback + context:(__unused CFStreamClientContext *)inContext { + return NO; +} + +#pragma mark - NSCopying + +-(id)copyWithZone:(NSZone *)zone { + AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; + + for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { + [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; + } + + [bodyStreamCopy setInitialAndFinalBoundaries]; + + return bodyStreamCopy; +} + +@end + +#pragma mark - + +typedef enum { + AFEncapsulationBoundaryPhase = 1, + AFHeaderPhase = 2, + AFBodyPhase = 3, + AFFinalBoundaryPhase = 4, +} AFHTTPBodyPartReadPhase; + +@interface AFHTTPBodyPart () { + AFHTTPBodyPartReadPhase _phase; + NSInputStream *_inputStream; + unsigned long long _phaseReadOffset; +} + +- (BOOL)transitionToNextPhase; +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length; +@end + +@implementation AFHTTPBodyPart + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + [self transitionToNextPhase]; + + return self; +} + +- (void)dealloc { + if (_inputStream) { + [_inputStream close]; + _inputStream = nil; + } +} + +- (NSInputStream *)inputStream { + if (!_inputStream) { + if ([self.body isKindOfClass:[NSData class]]) { + _inputStream = [NSInputStream inputStreamWithData:self.body]; + } else if ([self.body isKindOfClass:[NSURL class]]) { + _inputStream = [NSInputStream inputStreamWithURL:self.body]; + } else if ([self.body isKindOfClass:[NSInputStream class]]) { + _inputStream = self.body; + } else { + _inputStream = [NSInputStream inputStreamWithData:[NSData data]]; + } + } + + return _inputStream; +} + +- (NSString *)stringForHeaders { + NSMutableString *headerString = [NSMutableString string]; + for (NSString *field in [self.headers allKeys]) { + [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]]; + } + [headerString appendString:kAFMultipartFormCRLF]; + + return [NSString stringWithString:headerString]; +} + +- (unsigned long long)contentLength { + unsigned long long length = 0; + + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + length += [encapsulationBoundaryData length]; + + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + length += [headersData length]; + + length += _bodyContentLength; + + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + length += [closingBoundaryData length]; + + return length; +} + +- (BOOL)hasBytesAvailable { + // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer + if (_phase == AFFinalBoundaryPhase) { + return YES; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + switch (self.inputStream.streamStatus) { + case NSStreamStatusNotOpen: + case NSStreamStatusOpening: + case NSStreamStatusOpen: + case NSStreamStatusReading: + case NSStreamStatusWriting: + return YES; + case NSStreamStatusAtEnd: + case NSStreamStatusClosed: + case NSStreamStatusError: + default: + return NO; + } +#pragma clang diagnostic pop +} + +- (NSInteger)read:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ + NSInteger totalNumberOfBytesRead = 0; + + if (_phase == AFEncapsulationBoundaryPhase) { + NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFHeaderPhase) { + NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; + totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + if (_phase == AFBodyPhase) { + NSInteger numberOfBytesRead = 0; + + numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + if (numberOfBytesRead == -1) { + return -1; + } else { + totalNumberOfBytesRead += numberOfBytesRead; + + if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { + [self transitionToNextPhase]; + } + } + } + + if (_phase == AFFinalBoundaryPhase) { + NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); + totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; + } + + return totalNumberOfBytesRead; +} + +- (NSInteger)readData:(NSData *)data + intoBuffer:(uint8_t *)buffer + maxLength:(NSUInteger)length +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); + [data getBytes:buffer range:range]; +#pragma clang diagnostic pop + + _phaseReadOffset += range.length; + + if (((NSUInteger)_phaseReadOffset) >= [data length]) { + [self transitionToNextPhase]; + } + + return (NSInteger)range.length; +} + +- (BOOL)transitionToNextPhase { + if (![[NSThread currentThread] isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self transitionToNextPhase]; + }); + return YES; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + switch (_phase) { + case AFEncapsulationBoundaryPhase: + _phase = AFHeaderPhase; + break; + case AFHeaderPhase: + [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [self.inputStream open]; + _phase = AFBodyPhase; + break; + case AFBodyPhase: + [self.inputStream close]; + _phase = AFFinalBoundaryPhase; + break; + case AFFinalBoundaryPhase: + default: + _phase = AFEncapsulationBoundaryPhase; + break; + } + _phaseReadOffset = 0; +#pragma clang diagnostic pop + + return YES; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; + + bodyPart.stringEncoding = self.stringEncoding; + bodyPart.headers = self.headers; + bodyPart.bodyContentLength = self.bodyContentLength; + bodyPart.body = self.body; + bodyPart.boundary = self.boundary; + + return bodyPart; +} + +@end + +#pragma mark - + +@implementation AFJSONRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithWritingOptions:(NSJSONWritingOptions)0]; +} + ++ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions +{ + AFJSONRequestSerializer *serializer = [[self alloc] init]; + serializer.writingOptions = writingOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerialization + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + } + + [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFJSONRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.writingOptions = self.writingOptions; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFPropertyListRequestSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + writeOptions:(NSPropertyListWriteOptions)writeOptions +{ + AFPropertyListRequestSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.writeOptions = writeOptions; + + return serializer; +} + +#pragma mark - AFURLRequestSerializer + +- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request + withParameters:(id)parameters + error:(NSError *__autoreleasing *)error +{ + NSParameterAssert(request); + + if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { + return [super requestBySerializingRequest:request withParameters:parameters error:error]; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { + if (![request valueForHTTPHeaderField:field]) { + [mutableRequest setValue:value forHTTPHeaderField:field]; + } + }]; + + if (parameters) { + if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { + [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; + } + + [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]]; + } + + return mutableRequest; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone]; + serializer.format = self.format; + serializer.writeOptions = self.writeOptions; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h new file mode 100644 index 0000000..1396cfb --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1,311 @@ +// AFURLResponseSerialization.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. + + For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. + */ +@protocol AFURLResponseSerialization + +/** + The response object decoded from the data associated with a specified response. + + @param response The response to be processed. + @param data The response data to be decoded. + @param error The error that occurred while attempting to decode the response data. + + @return The object decoded from the specified response data. + */ +- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response + data:(nullable NSData *)data + error:(NSError * __nullable __autoreleasing *)error; + +@end + +#pragma mark - + +/** + `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. + + Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. + */ +@interface AFHTTPResponseSerializer : NSObject + +- (instancetype)init; + +/** + The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. + */ +@property (nonatomic, assign) NSStringEncoding stringEncoding; + +/** + Creates and returns a serializer with default configuration. + */ ++ (instancetype)serializer; + +///----------------------------------------- +/// @name Configuring Response Serialization +///----------------------------------------- + +/** + The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. + + See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + */ +@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes; + +/** + The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. + */ +@property (nonatomic, copy, nullable) NSSet *acceptableContentTypes; + +/** + Validates the specified response and data. + + In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. + + @param response The response to be validated. + @param data The data associated with the response. + @param error The error that occurred while attempting to validate the response. + + @return `YES` if the response is valid, otherwise `NO`. + */ +- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response + data:(nullable NSData *)data + error:(NSError * __nullable __autoreleasing *)error; + +@end + +#pragma mark - + + +/** + `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. + + By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: + + - `application/json` + - `text/json` + - `text/javascript` + */ +@interface AFJSONResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + */ +@property (nonatomic, assign) NSJSONReadingOptions readingOptions; + +/** + Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. + */ +@property (nonatomic, assign) BOOL removesKeysWithNullValues; + +/** + Creates and returns a JSON serializer with specified reading and writing options. + + @param readingOptions The specified JSON reading options. + */ ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; + +@end + +#pragma mark - + +/** + `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. + + By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +/** + `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: + + - `application/xml` + - `text/xml` + */ +@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. + */ +@property (nonatomic, assign) NSUInteger options; + +/** + Creates and returns an XML document serializer with the specified options. + + @param mask The XML document options. + */ ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; + +@end + +#endif + +#pragma mark - + +/** + `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. + + By default, `AFPropertyListResponseSerializer` accepts the following MIME types: + + - `application/x-plist` + */ +@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer + +- (instancetype)init; + +/** + The property list format. Possible values are described in "NSPropertyListFormat". + */ +@property (nonatomic, assign) NSPropertyListFormat format; + +/** + The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." + */ +@property (nonatomic, assign) NSPropertyListReadOptions readOptions; + +/** + Creates and returns a property list serializer with a specified format, read options, and write options. + + @param format The property list format. + @param readOptions The property list reading options. + */ ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions; + +@end + +#pragma mark - + +/** + `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. + + By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: + + - `image/tiff` + - `image/jpeg` + - `image/gif` + - `image/png` + - `image/ico` + - `image/x-icon` + - `image/bmp` + - `image/x-bmp` + - `image/x-xbitmap` + - `image/x-win-bitmap` + */ +@interface AFImageResponseSerializer : AFHTTPResponseSerializer + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +/** + The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. + */ +@property (nonatomic, assign) CGFloat imageScale; + +/** + Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. + */ +@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; +#endif + +@end + +#pragma mark - + +/** + `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. + */ +@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer + +/** + The component response serializers. + */ +@property (readonly, nonatomic, copy) NSArray *responseSerializers; + +/** + Creates and returns a compound serializer comprised of the specified response serializers. + + @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. + */ ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers; + +@end + +///---------------- +/// @name Constants +///---------------- + +/** + ## Error Domains + + The following error domain is predefined. + + - `NSString * const AFURLResponseSerializationErrorDomain` + + ### Constants + + `AFURLResponseSerializationErrorDomain` + AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain; + +/** + ## User info dictionary keys + + These keys may exist in the user info dictionary, in addition to those defined for NSError. + + - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` + - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` + + ### Constants + + `AFNetworkingOperationFailingURLResponseErrorKey` + The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + + `AFNetworkingOperationFailingURLResponseDataErrorKey` + The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey; + +FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m new file mode 100644 index 0000000..f95834f --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m @@ -0,0 +1,825 @@ +// AFURLResponseSerialization.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLResponseSerialization.h" + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_WATCH +#import +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#import +#endif + +NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response"; +NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response"; +NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data"; + +static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) { + if (!error) { + return underlyingError; + } + + if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) { + return error; + } + + NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy]; + mutableUserInfo[NSUnderlyingErrorKey] = underlyingError; + + return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo]; +} + +static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { + if ([error.domain isEqualToString:domain] && error.code == code) { + return YES; + } else if (error.userInfo[NSUnderlyingErrorKey]) { + return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain); + } + + return NO; +} + +static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { + if ([JSONObject isKindOfClass:[NSArray class]]) { + NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; + for (id value in (NSArray *)JSONObject) { + [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)]; + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray]; + } else if ([JSONObject isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject]; + for (id key in [(NSDictionary *)JSONObject allKeys]) { + id value = (NSDictionary *)JSONObject[key]; + if (!value || [value isEqual:[NSNull null]]) { + [mutableDictionary removeObjectForKey:key]; + } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) { + mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions); + } + } + + return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary]; + } + + return JSONObject; +} + +@implementation AFHTTPResponseSerializer + ++ (instancetype)serializer { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.stringEncoding = NSUTF8StringEncoding; + + self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; + self.acceptableContentTypes = nil; + + return self; +} + +#pragma mark - + +- (BOOL)validateResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError * __autoreleasing *)error +{ + BOOL responseIsValid = YES; + NSError *validationError = nil; + + if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) { + if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) { + if ([data length] > 0 && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError); + } + + responseIsValid = NO; + } + + if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) { + NSMutableDictionary *mutableUserInfo = [@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode], + NSURLErrorFailingURLErrorKey:[response URL], + AFNetworkingOperationFailingURLResponseErrorKey: response, + } mutableCopy]; + + if (data) { + mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data; + } + + validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError); + + responseIsValid = NO; + } + } + + if (error && !responseIsValid) { + *error = validationError; + } + + return responseIsValid; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + [self validateResponse:(NSHTTPURLResponse *)response data:data error:error]; + + return data; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))]; + [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone]; + serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone]; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFJSONResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithReadingOptions:(NSJSONReadingOptions)0]; +} + ++ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions { + AFJSONResponseSerializer *serializer = [[self alloc] init]; + serializer.readingOptions = readingOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + // Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization. + // See https://github.com/rails/rails/issues/1742 + NSStringEncoding stringEncoding = self.stringEncoding; + if (response.textEncodingName) { + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (encoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); + } + } + + id responseObject = nil; + NSError *serializationError = nil; + @autoreleasepool { + NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (responseString && ![responseString isEqualToString:@" "]) { + // Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character + // See http://stackoverflow.com/a/12843465/157142 + data = [responseString dataUsingEncoding:NSUTF8StringEncoding]; + + if (data) { + if ([data length] > 0) { + responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; + } else { + return nil; + } + } else { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", @"AFNetworking", nil), + NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", @"AFNetworking", nil), responseString] + }; + + serializationError = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; + } + } + } + + if (self.removesKeysWithNullValues && responseObject) { + responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); + } + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue]; + self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))]; + [coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.readingOptions = self.readingOptions; + serializer.removesKeysWithNullValues = self.removesKeysWithNullValues; + + return serializer; +} + +@end + +#pragma mark - + +@implementation AFXMLParserResponseSerializer + ++ (instancetype)serializer { + AFXMLParserResponseSerializer *serializer = [[self alloc] init]; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSHTTPURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + return [[NSXMLParser alloc] initWithData:data]; +} + +@end + +#pragma mark - + +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED + +@implementation AFXMLDocumentResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithXMLDocumentOptions:0]; +} + ++ (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask { + AFXMLDocumentResponseSerializer *serializer = [[self alloc] init]; + serializer.options = mask; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + NSError *serializationError = nil; + NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data options:self.options error:&serializationError]; + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return document; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.options = self.options; + + return serializer; +} + +@end + +#endif + +#pragma mark - + +@implementation AFPropertyListResponseSerializer + ++ (instancetype)serializer { + return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0]; +} + ++ (instancetype)serializerWithFormat:(NSPropertyListFormat)format + readOptions:(NSPropertyListReadOptions)readOptions +{ + AFPropertyListResponseSerializer *serializer = [[self alloc] init]; + serializer.format = format; + serializer.readOptions = readOptions; + + return serializer; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/x-plist", nil]; + + return self; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + + id responseObject; + NSError *serializationError = nil; + + if (data) { + responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError]; + } + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue]; + self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))]; + [coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.format = self.format; + serializer.readOptions = self.readOptions; + + return serializer; +} + +@end + +#pragma mark - + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#import + +@interface UIImage (AFNetworkingSafeImageLoading) ++ (UIImage *)af_safeImageWithData:(NSData *)data; +@end + +static NSLock* imageLock = nil; + +@implementation UIImage (AFNetworkingSafeImageLoading) + ++ (UIImage *)af_safeImageWithData:(NSData *)data { + UIImage* image = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + imageLock = [[NSLock alloc] init]; + }); + + [imageLock lock]; + image = [UIImage imageWithData:data]; + [imageLock unlock]; + return image; +} + +@end + +static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) { + UIImage *image = [UIImage af_safeImageWithData:data]; + if (image.images) { + return image; + } + + return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; +} + +static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) { + if (!data || [data length] == 0) { + return nil; + } + + CGImageRef imageRef = NULL; + CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + if ([response.MIMEType isEqualToString:@"image/png"]) { + imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { + imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); + + if (imageRef) { + CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); + CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); + + // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale + if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { + CGImageRelease(imageRef); + imageRef = NULL; + } + } + } + + CGDataProviderRelease(dataProvider); + + UIImage *image = AFImageWithDataAtScale(data, scale); + if (!imageRef) { + if (image.images || !image) { + return image; + } + + imageRef = CGImageCreateCopy([image CGImage]); + if (!imageRef) { + return nil; + } + } + + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); + + if (width * height > 1024 * 1024 || bitsPerComponent > 8) { + CGImageRelease(imageRef); + + return image; + } + + // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate + size_t bytesPerRow = 0; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); + + if (colorSpaceModel == kCGColorSpaceModelRGB) { + uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wassign-enum" + if (alpha == kCGImageAlphaNone) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaNoneSkipFirst; + } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; + } +#pragma clang diagnostic pop + } + + CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); + + CGColorSpaceRelease(colorSpace); + + if (!context) { + CGImageRelease(imageRef); + + return image; + } + + CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); + CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); + + CGContextRelease(context); + + UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; + + CGImageRelease(inflatedImageRef); + CGImageRelease(imageRef); + + return inflatedImage; +} +#endif + + +@implementation AFImageResponseSerializer + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil]; + +#if TARGET_OS_IOS + self.imageScale = [[UIScreen mainScreen] scale]; + self.automaticallyInflatesResponseImage = YES; +#elif TARGET_OS_WATCH + self.imageScale = [[WKInterfaceDevice currentDevice] screenScale]; + self.automaticallyInflatesResponseImage = YES; +#endif + + return self; +} + +#pragma mark - AFURLResponseSerializer + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { + if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { + return nil; + } + } + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + if (self.automaticallyInflatesResponseImage) { + return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); + } else { + return AFImageWithDataAtScale(data, self.imageScale); + } +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + // Ensure that the image is set to it's correct pixel width and height + NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data]; + NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])]; + [image addRepresentation:bitimage]; + + return image; +#endif + + return nil; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))]; +#if CGFLOAT_IS_DOUBLE + self.imageScale = [imageScale doubleValue]; +#else + self.imageScale = [imageScale floatValue]; +#endif + + self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; + [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + serializer.imageScale = self.imageScale; + serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage; +#endif + + return serializer; +} + +@end + +#pragma mark - + +@interface AFCompoundResponseSerializer () +@property (readwrite, nonatomic, copy) NSArray *responseSerializers; +@end + +@implementation AFCompoundResponseSerializer + ++ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers { + AFCompoundResponseSerializer *serializer = [[self alloc] init]; + serializer.responseSerializers = responseSerializers; + + return serializer; +} + +#pragma mark - AFURLResponseSerialization + +- (id)responseObjectForResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *__autoreleasing *)error +{ + for (id serializer in self.responseSerializers) { + if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) { + continue; + } + + NSError *serializerError = nil; + id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError]; + if (responseObject) { + if (error) { + *error = AFErrorWithUnderlyingError(serializerError, *error); + } + + return responseObject; + } + } + + return [super responseObjectForResponse:response data:data error:error]; +} + +#pragma mark - NSSecureCoding + +- (id)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeObject:self.responseSerializers forKey:NSStringFromSelector(@selector(responseSerializers))]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.responseSerializers = self.responseSerializers; + + return serializer; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h new file mode 100644 index 0000000..70e4ec9 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h @@ -0,0 +1,554 @@ +// AFURLSessionManager.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" +#import "AFSecurityPolicy.h" +#if !TARGET_OS_WATCH +#import "AFNetworkReachabilityManager.h" +#endif + +#ifndef NS_DESIGNATED_INITIALIZER +#if __has_attribute(objc_designated_initializer) +#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +#else +#define NS_DESIGNATED_INITIALIZER +#endif +#endif + +/** + `AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + + ## Subclassing Notes + + This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead. + + ## NSURLSession & NSURLSessionTask Delegate Methods + + `AFURLSessionManager` implements the following delegate methods: + + ### `NSURLSessionDelegate` + + - `URLSession:didBecomeInvalidWithError:` + - `URLSession:didReceiveChallenge:completionHandler:` + - `URLSessionDidFinishEventsForBackgroundURLSession:` + + ### `NSURLSessionTaskDelegate` + + - `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:` + - `URLSession:task:didReceiveChallenge:completionHandler:` + - `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` + - `URLSession:task:didCompleteWithError:` + + ### `NSURLSessionDataDelegate` + + - `URLSession:dataTask:didReceiveResponse:completionHandler:` + - `URLSession:dataTask:didBecomeDownloadTask:` + - `URLSession:dataTask:didReceiveData:` + - `URLSession:dataTask:willCacheResponse:completionHandler:` + + ### `NSURLSessionDownloadDelegate` + + - `URLSession:downloadTask:didFinishDownloadingToURL:` + - `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:` + - `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:` + + If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first. + + ## Network Reachability Monitoring + + Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details. + + ## NSCoding Caveats + + - Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`. + + ## NSCopying Caveats + + - `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original. + - Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied. + + @warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance. + */ + +NS_ASSUME_NONNULL_BEGIN + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || TARGET_OS_WATCH + +@interface AFURLSessionManager : NSObject + +/** + The managed session. + */ +@property (readonly, nonatomic, strong) NSURLSession *session; + +/** + The operation queue on which delegate callbacks are run. + */ +@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue; + +/** + Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`. + + @warning `responseSerializer` must not be `nil`. + */ +@property (nonatomic, strong) id responseSerializer; + +///------------------------------- +/// @name Managing Security Policy +///------------------------------- + +/** + The security policy used by created request operations to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified. + */ +@property (nonatomic, strong) AFSecurityPolicy *securityPolicy; + +#if !TARGET_OS_WATCH +///-------------------------------------- +/// @name Monitoring Network Reachability +///-------------------------------------- + +/** + The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default. + */ +@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager; +#endif + +///---------------------------- +/// @name Getting Session Tasks +///---------------------------- + +/** + The data, upload, and download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *tasks; + +/** + The data tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *dataTasks; + +/** + The upload tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *uploadTasks; + +/** + The download tasks currently run by the managed session. + */ +@property (readonly, nonatomic, strong) NSArray *downloadTasks; + +///------------------------------- +/// @name Managing Callback Queues +///------------------------------- + +/** + The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; +#else +@property (nonatomic, assign, nullable) dispatch_queue_t completionQueue; +#endif + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +#if OS_OBJECT_HAVE_OBJC_SUPPORT +@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; +#else +@property (nonatomic, assign, nullable) dispatch_group_t completionGroup; +#endif + +///--------------------------------- +/// @name Working Around System Bugs +///--------------------------------- + +/** + Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default. + + @bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again. + + @see https://github.com/AFNetworking/AFNetworking/issues/1675 + */ +@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions; + +///--------------------- +/// @name Initialization +///--------------------- + +/** + Creates and returns a manager for a session created with the specified configuration. This is the designated initializer. + + @param configuration The configuration used to create the managed session. + + @return A manager for a newly-created session. + */ +- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +/** + Invalidates the managed session, optionally canceling pending tasks. + + @param cancelPendingTasks Whether or not to cancel pending tasks. + */ +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks; + +///------------------------- +/// @name Running Data Tasks +///------------------------- + +/** + Creates an `NSURLSessionDataTask` with the specified request. + + @param request The HTTP request for the request. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(nullable void (^)(NSURLResponse *response, id __nullable responseObject, NSError * __nullable error))completionHandler; + +///--------------------------- +/// @name Running Upload Tasks +///--------------------------- + +/** + Creates an `NSURLSessionUploadTask` with the specified request for a local file. + + @param request The HTTP request for the request. + @param fileURL A URL to the local file to be uploaded. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + + @see `attemptsToRecreateUploadTasksForBackgroundSessions` + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(NSProgress * __nullable __autoreleasing * __nullable)progress + completionHandler:(nullable void (^)(NSURLResponse *response, id __nullable responseObject, NSError * __nullable error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body. + + @param request The HTTP request for the request. + @param bodyData A data object containing the HTTP body to be uploaded. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(nullable NSData *)bodyData + progress:(NSProgress * __nullable __autoreleasing * __nullable)progress + completionHandler:(nullable void (^)(NSURLResponse *response, id __nullable responseObject, NSError * __nullable error))completionHandler; + +/** + Creates an `NSURLSessionUploadTask` with the specified streaming request. + + @param request The HTTP request for the request. + @param progress A progress object monitoring the current upload progress. + @param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any. + */ +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(NSProgress * __nullable __autoreleasing * __nullable)progress + completionHandler:(nullable void (^)(NSURLResponse *response, id __nullable responseObject, NSError * __nullable error))completionHandler; + +///----------------------------- +/// @name Running Download Tasks +///----------------------------- + +/** + Creates an `NSURLSessionDownloadTask` with the specified request. + + @param request The HTTP request for the request. + @param progress A progress object monitoring the current download progress. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + + @warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(NSProgress * __nullable __autoreleasing * __nullable)progress + destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * __nullable filePath, NSError * __nullable error))completionHandler; + +/** + Creates an `NSURLSessionDownloadTask` with the specified resume data. + + @param resumeData The data used to resume downloading. + @param progress A progress object monitoring the current download progress. + @param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL. + @param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any. + */ +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(NSProgress * __nullable __autoreleasing * __nullable)progress + destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * __nullable filePath, NSError * __nullable error))completionHandler; + +///--------------------------------- +/// @name Getting Progress for Tasks +///--------------------------------- + +/** + Returns the upload progress of the specified task. + + @param uploadTask The session upload task. Must not be `nil`. + + @return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable. + */ +- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask; + +/** + Returns the download progress of the specified task. + + @param downloadTask The session download task. Must not be `nil`. + + @return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable. + */ +- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask; + +///----------------------------------------- +/// @name Setting Session Delegate Callbacks +///----------------------------------------- + +/** + Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`. + + @param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation. + */ +- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block; + +/** + Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`. + + @param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. + */ +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __nullable __autoreleasing * __nullable credential))block; + +///-------------------------------------- +/// @name Setting Task Delegate Callbacks +///-------------------------------------- + +/** + Sets a block to be executed when a task requires a new request body stream to send to the remote server, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:needNewBodyStream:`. + + @param block A block object to be executed when a task requires a new request body stream. + */ +- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block; + +/** + Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`. + + @param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response. + */ +- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block; + +/** + Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`. + + @param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge. + */ +- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __nullable __autoreleasing * __nullable credential))block; + +/** + Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. + + @param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread. + */ +- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block; + +/** + Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`. + + @param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task. + */ +- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * __nullable error))block; + +///------------------------------------------- +/// @name Setting Data Task Delegate Callbacks +///------------------------------------------- + +/** + Sets a block to be executed when a data task has received a response, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveResponse:completionHandler:`. + + @param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response. + */ +- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block; + +/** + Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`. + + @param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become. + */ +- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block; + +/** + Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block; + +/** + Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`. + + @param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response. + */ +- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block; + +/** + Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`. + + @param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session. + */ +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block; + +///----------------------------------------------- +/// @name Setting Download Task Delegate Callbacks +///----------------------------------------------- + +/** + Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`. + + @param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error. + */ +- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block; + +/** + Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`. + + @param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue. + */ +- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block; + +/** + Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. + + @param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded. + */ +- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block; + +@end + +#endif + +///-------------------- +/// @name Notifications +///-------------------- + +/** + Posted when a task begins executing. + + @deprecated Use `AFNetworkingTaskDidResumeNotification` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidStartNotification DEPRECATED_ATTRIBUTE; + +/** + Posted when a task resumes. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification; + +/** + Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. + + @deprecated Use `AFNetworkingTaskDidCompleteNotification` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishNotification DEPRECATED_ATTRIBUTE; + +/** + Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification; + +/** + Posted when a task suspends its execution. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification; + +/** + Posted when a session is invalidated. + */ +FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification; + +/** + Posted when a session download task encountered an error when moving the temporary download file to a specified destination. + */ +FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification; + +/** + The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. + + @deprecated Use `AFNetworkingTaskDidCompleteResponseDataKey` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishResponseDataKey DEPRECATED_ATTRIBUTE; + +/** + The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey; + +/** + The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. + + @deprecated Use `AFNetworkingTaskDidCompleteSerializedResponseKey` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishSerializedResponseKey DEPRECATED_ATTRIBUTE; + +/** + The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey; + +/** + The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. + + @deprecated Use `AFNetworkingTaskDidCompleteResponseSerializerKey` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishResponseSerializerKey DEPRECATED_ATTRIBUTE; + +/** + The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey; + +/** + The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. + + @deprecated Use `AFNetworkingTaskDidCompleteAssetPathKey` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishAssetPathKey DEPRECATED_ATTRIBUTE; + +/** + The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey; + +/** + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. + + @deprecated Use `AFNetworkingTaskDidCompleteErrorKey` instead. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidFinishErrorKey DEPRECATED_ATTRIBUTE; + +/** + Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists. + */ +FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey; + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m new file mode 100644 index 0000000..eb28959 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m @@ -0,0 +1,1171 @@ +// AFURLSessionManager.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFURLSessionManager.h" +#import + +#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) + +static dispatch_queue_t url_session_manager_creation_queue() { + static dispatch_queue_t af_url_session_manager_creation_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); + }); + + return af_url_session_manager_creation_queue; +} + +static dispatch_queue_t url_session_manager_processing_queue() { + static dispatch_queue_t af_url_session_manager_processing_queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); + }); + + return af_url_session_manager_processing_queue; +} + +static dispatch_group_t url_session_manager_completion_group() { + static dispatch_group_t af_url_session_manager_completion_group; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + af_url_session_manager_completion_group = dispatch_group_create(); + }); + + return af_url_session_manager_completion_group; +} + +NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume"; +NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete"; +NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend"; +NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate"; +NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error"; + +NSString * const AFNetworkingTaskDidStartNotification = @"com.alamofire.networking.task.resume"; // Deprecated +NSString * const AFNetworkingTaskDidFinishNotification = @"com.alamofire.networking.task.complete"; // Deprecated + +NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; +NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; +NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; +NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error"; +NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; + +NSString * const AFNetworkingTaskDidFinishSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; // Deprecated +NSString * const AFNetworkingTaskDidFinishResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; // Deprecated +NSString * const AFNetworkingTaskDidFinishResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; // Deprecated +NSString * const AFNetworkingTaskDidFinishErrorKey = @"com.alamofire.networking.task.complete.error"; // Deprecated +NSString * const AFNetworkingTaskDidFinishAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; // Deprecated + +static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock"; + +static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3; + +static void * AFTaskStateChangedContext = &AFTaskStateChangedContext; + +typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); + +typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request); +typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential); +typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session); + +typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task); +typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend); +typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error); + +typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response); +typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask); +typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data); +typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse); + +typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location); +typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite); +typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes); + +typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error); + +#pragma mark - + +@interface AFURLSessionManagerTaskDelegate : NSObject +@property (nonatomic, weak) AFURLSessionManager *manager; +@property (nonatomic, strong) NSMutableData *mutableData; +@property (nonatomic, strong) NSProgress *progress; +@property (nonatomic, copy) NSURL *downloadFileURL; +@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; +@end + +@implementation AFURLSessionManagerTaskDelegate + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.mutableData = [NSMutableData data]; + + self.progress = [NSProgress progressWithTotalUnitCount:0]; + + return self; +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(__unused NSURLSession *)session + task:(__unused NSURLSessionTask *)task + didSendBodyData:(__unused int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + self.progress.totalUnitCount = totalBytesExpectedToSend; + self.progress.completedUnitCount = totalBytesSent; +} + +- (void)URLSession:(__unused NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + __strong AFURLSessionManager *manager = self.manager; + + __block id responseObject = nil; + + __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer; + + //Performance Improvement from #2672 + NSData *data = nil; + if (self.mutableData) { + data = [self.mutableData copy]; + //We no longer need the reference, so nil it out to gain back some memory. + self.mutableData = nil; + } + + if (self.downloadFileURL) { + userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL; + } else if (data) { + userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data; + } + + if (error) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, error); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + } else { + dispatch_async(url_session_manager_processing_queue(), ^{ + NSError *serializationError = nil; + responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; + + if (self.downloadFileURL) { + responseObject = self.downloadFileURL; + } + + if (responseObject) { + userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject; + } + + if (serializationError) { + userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError; + } + + dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ + if (self.completionHandler) { + self.completionHandler(task.response, responseObject, serializationError); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; + }); + }); + }); + } +#pragma clang diagnostic pop +} + +#pragma mark - NSURLSessionDataTaskDelegate + +- (void)URLSession:(__unused NSURLSession *)session + dataTask:(__unused NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + [self.mutableData appendData:data]; +} + +#pragma mark - NSURLSessionDownloadTaskDelegate + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + NSError *fileManagerError = nil; + self.downloadFileURL = nil; + + if (self.downloadTaskDidFinishDownloading) { + self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (self.downloadFileURL) { + [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; + + if (fileManagerError) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; + } + } + } +} + +- (void)URLSession:(__unused NSURLSession *)session + downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask + didWriteData:(__unused int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + self.progress.totalUnitCount = totalBytesExpectedToWrite; + self.progress.completedUnitCount = totalBytesWritten; +} + +- (void)URLSession:(__unused NSURLSession *)session + downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes { + self.progress.totalUnitCount = expectedTotalBytes; + self.progress.completedUnitCount = fileOffset; +} + +@end + +#pragma mark - + +/** + * A workaround for issues related to key-value observing the `state` of an `NSURLSessionTask`. + * + * See: + * - https://github.com/AFNetworking/AFNetworking/issues/1477 + * - https://github.com/AFNetworking/AFNetworking/issues/2638 + * - https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + +static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) { + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +static inline BOOL af_addMethod(Class class, SEL selector, Method method) { + return class_addMethod(class, selector, method_getImplementation(method), method_getTypeEncoding(method)); +} + +static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume"; +static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend"; + +@interface _AFURLSessionTaskSwizzling : NSObject + +@end + +@implementation _AFURLSessionTaskSwizzling + ++ (void)load { + /** + WARNING: Trouble Ahead + https://github.com/AFNetworking/AFNetworking/pull/2702 + */ + + if (NSClassFromString(@"NSURLSessionTask")) { + /** + iOS 7 and iOS 8 differ in NSURLSessionTask implementation, which makes the next bit of code a bit tricky. + Many Unit Tests have been built to validate as much of this behavior has possible. + Here is what we know: + - NSURLSessionTasks are implemented with class clusters, meaning the class you request from the API isn't actually the type of class you will get back. + - Simply referencing `[NSURLSessionTask class]` will not work. You need to ask an `NSURLSession` to actually create an object, and grab the class from there. + - On iOS 7, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `__NSCFURLSessionTask`. + - On iOS 8, `localDataTask` is a `__NSCFLocalDataTask`, which inherits from `__NSCFLocalSessionTask`, which inherits from `NSURLSessionTask`. + - On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled. + - On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled. + - Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there. + + Some Assumptions: + - No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it. + - No background task classes override `resume` or `suspend` + + The current solution: + 1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task. + 2) Grab a pointer to the original implementation of `af_resume` + 3) Check to see if the current class has an implementation of resume. If so, continue to step 4. + 4) Grab the super class of the current class. + 5) Grab a pointer for the current class to the current implementation of `resume`. + 6) Grab a pointer for the super class to the current implementation of `resume`. + 7) If the current class implementation of `resume` is not equal to the super class implementation of `resume` AND the current implementation of `resume` is not equal to the original implementation of `af_resume`, THEN swizzle the methods + 8) Set the current class to the super class, and repeat steps 3-8 + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + NSURLSessionDataTask *localDataTask = [[NSURLSession sessionWithConfiguration:nil] dataTaskWithURL:nil]; +#pragma clang diagnostic pop + IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume))); + Class currentClass = [localDataTask class]; + + while (class_getInstanceMethod(currentClass, @selector(resume))) { + Class superClass = [currentClass superclass]; + IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume))); + IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume))); + if (classResumeIMP != superclassResumeIMP && + originalAFResumeIMP != classResumeIMP) { + [self swizzleResumeAndSuspendMethodForClass:currentClass]; + } + currentClass = [currentClass superclass]; + } + + [localDataTask cancel]; + } +} + ++ (void)swizzleResumeAndSuspendMethodForClass:(Class)class { + Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); + Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); + + if (af_addMethod(class, @selector(af_resume), afResumeMethod)) { + af_swizzleSelector(class, @selector(resume), @selector(af_resume)); + } + + if (af_addMethod(class, @selector(af_suspend), afSuspendMethod)) { + af_swizzleSelector(class, @selector(suspend), @selector(af_suspend)); + } +} + +- (NSURLSessionTaskState)state { + NSAssert(NO, @"State method should never be called in the actual dummy class"); + return NSURLSessionTaskStateCanceling; +} + +- (void)af_resume { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_resume]; + + if (state != NSURLSessionTaskStateRunning) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; + } +} + +- (void)af_suspend { + NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); + NSURLSessionTaskState state = [self state]; + [self af_suspend]; + + if (state != NSURLSessionTaskStateSuspended) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; + } +} +@end + +#pragma mark - + +@interface AFURLSessionManager () +@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; +@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; +@property (readwrite, nonatomic, strong) NSURLSession *session; +@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; +@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks; +@property (readwrite, nonatomic, strong) NSLock *lock; +@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid; +@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession; +@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge; +@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData; +@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData; +@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData; +@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume; +@end + +@implementation AFURLSessionManager + +- (instancetype)init { + return [self initWithSessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { + self = [super init]; + if (!self) { + return nil; + } + + if (!configuration) { + configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + } + + self.sessionConfiguration = configuration; + + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.maxConcurrentOperationCount = 1; + + self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; + + self.responseSerializer = [AFJSONResponseSerializer serializer]; + + self.securityPolicy = [AFSecurityPolicy defaultPolicy]; + +#if !TARGET_OS_WATCH + self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; +#endif + + self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; + + self.lock = [[NSLock alloc] init]; + self.lock.name = AFURLSessionManagerLockName; + + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + for (NSURLSessionDataTask *task in dataTasks) { + [self addDelegateForDataTask:task completionHandler:nil]; + } + + for (NSURLSessionUploadTask *uploadTask in uploadTasks) { + [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil]; + } + + for (NSURLSessionDownloadTask *downloadTask in downloadTasks) { + [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil]; + } + }]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:nil]; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - + +- (NSString *)taskDescriptionForSessionTasks { + return [NSString stringWithFormat:@"%p", self]; +} + +- (void)taskDidResume:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task]; + }); + } + } +} + +- (void)taskDidSuspend:(NSNotification *)notification { + NSURLSessionTask *task = notification.object; + if ([task respondsToSelector:@selector(taskDescription)]) { + if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task]; + }); + } + } +} + +#pragma mark - + +- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + AFURLSessionManagerTaskDelegate *delegate = nil; + [self.lock lock]; + delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; + [self.lock unlock]; + + return delegate; +} + +- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate + forTask:(NSURLSessionTask *)task +{ + NSParameterAssert(task); + NSParameterAssert(delegate); + + [self.lock lock]; + self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; + [self.lock unlock]; +} + +- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + dataTask.taskDescription = self.taskDescriptionForSessionTasks; + [self setDelegate:delegate forTask:dataTask]; +} + +- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + int64_t totalUnitCount = uploadTask.countOfBytesExpectedToSend; + if(totalUnitCount == NSURLSessionTransferSizeUnknown) { + NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"]; + if(contentLength) { + totalUnitCount = (int64_t)[contentLength longLongValue]; + } + } + + if (delegate.progress) { + delegate.progress.totalUnitCount = totalUnitCount; + } else { + delegate.progress = [NSProgress progressWithTotalUnitCount:totalUnitCount]; + } + + delegate.progress.pausingHandler = ^{ + [uploadTask suspend]; + }; + delegate.progress.cancellationHandler = ^{ + [uploadTask cancel]; + }; + + if (progress) { + *progress = delegate.progress; + } + + uploadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:uploadTask]; +} + +- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + if (destination) { + delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) { + return destination(location, task.response); + }; + } + + if (progress) { + *progress = delegate.progress; + } + + downloadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:downloadTask]; +} + +- (void)removeDelegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + [self.lock lock]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; + [self.lock unlock]; +} + +- (void)removeAllDelegates { + [self.lock lock]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeAllObjects]; + [self.lock unlock]; +} + +#pragma mark - + +- (NSArray *)tasksForKeyPath:(NSString *)keyPath { + __block NSArray *tasks = nil; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { + tasks = dataTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { + tasks = uploadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { + tasks = downloadTasks; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { + tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; + } + + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + return tasks; +} + +- (NSArray *)tasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)dataTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)uploadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +- (NSArray *)downloadTasks { + return [self tasksForKeyPath:NSStringFromSelector(_cmd)]; +} + +#pragma mark - + +- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks { + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelPendingTasks) { + [self.session invalidateAndCancel]; + } else { + [self.session finishTasksAndInvalidate]; + } + }); +} + +#pragma mark - + +- (void)setResponseSerializer:(id )responseSerializer { + NSParameterAssert(responseSerializer); + + _responseSerializer = responseSerializer; +} + +#pragma mark - + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionDataTask *dataTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + dataTask = [self.session dataTaskWithRequest:request]; + }); + + [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; + + return dataTask; +} + +#pragma mark - + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + }); + + if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) { + for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) { + uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL]; + } + } + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(NSData *)bodyData + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + }); + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + }); + + [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; + + return uploadTask; +} + +#pragma mark - + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithRequest:request]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(NSProgress * __autoreleasing *)progress + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + dispatch_sync(url_session_manager_creation_queue(), ^{ + downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +#pragma mark - + +- (NSProgress *)uploadProgressForTask:(NSURLSessionUploadTask *)uploadTask { + return [[self delegateForTask:uploadTask] progress]; +} + +- (NSProgress *)downloadProgressForTask:(NSURLSessionDownloadTask *)downloadTask { + return [[self delegateForTask:downloadTask] progress]; +} + +#pragma mark - + +- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block { + self.sessionDidBecomeInvalid = block; +} + +- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { + self.sessionDidReceiveAuthenticationChallenge = block; +} + +- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block { + self.didFinishEventsForBackgroundURLSession = block; +} + +#pragma mark - + +- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block { + self.taskNeedNewBodyStream = block; +} + +- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block { + self.taskWillPerformHTTPRedirection = block; +} + +- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block { + self.taskDidReceiveAuthenticationChallenge = block; +} + +- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block { + self.taskDidSendBodyData = block; +} + +- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block { + self.taskDidComplete = block; +} + +#pragma mark - + +- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block { + self.dataTaskDidReceiveResponse = block; +} + +- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block { + self.dataTaskDidBecomeDownloadTask = block; +} + +- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block { + self.dataTaskDidReceiveData = block; +} + +- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block { + self.dataTaskWillCacheResponse = block; +} + +#pragma mark - + +- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block { + self.downloadTaskDidFinishDownloading = block; +} + +- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block { + self.downloadTaskDidWriteData = block; +} + +- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block { + self.downloadTaskDidResume = block; +} + +#pragma mark - NSObject + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue]; +} + +- (BOOL)respondsToSelector:(SEL)selector { + if (selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) { + return self.taskWillPerformHTTPRedirection != nil; + } else if (selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) { + return self.dataTaskDidReceiveResponse != nil; + } else if (selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) { + return self.dataTaskWillCacheResponse != nil; + } else if (selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) { + return self.didFinishEventsForBackgroundURLSession != nil; + } + + return [[self class] instancesRespondToSelector:selector]; +} + +#pragma mark - NSURLSessionDelegate + +- (void)URLSession:(NSURLSession *)session +didBecomeInvalidWithError:(NSError *)error +{ + if (self.sessionDidBecomeInvalid) { + self.sessionDidBecomeInvalid(session, error); + } + + [self removeAllDelegates]; + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session]; +} + +- (void)URLSession:(NSURLSession *)session +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + __block NSURLCredential *credential = nil; + + if (self.sessionDidReceiveAuthenticationChallenge) { + disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + if (credential) { + disposition = NSURLSessionAuthChallengeUseCredential; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler +{ + NSURLRequest *redirectRequest = request; + + if (self.taskWillPerformHTTPRedirection) { + redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); + } + + if (completionHandler) { + completionHandler(redirectRequest); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + __block NSURLCredential *credential = nil; + + if (self.taskDidReceiveAuthenticationChallenge) { + disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + disposition = NSURLSessionAuthChallengeUseCredential; + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler +{ + NSInputStream *inputStream = nil; + + if (self.taskNeedNewBodyStream) { + inputStream = self.taskNeedNewBodyStream(session, task); + } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { + inputStream = [task.originalRequest.HTTPBodyStream copy]; + } + + if (completionHandler) { + completionHandler(inputStream); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend +{ + + int64_t totalUnitCount = totalBytesExpectedToSend; + if(totalUnitCount == NSURLSessionTransferSizeUnknown) { + NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; + if(contentLength) { + totalUnitCount = (int64_t) [contentLength longLongValue]; + } + } + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalUnitCount]; + + if (self.taskDidSendBodyData) { + self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didCompleteWithError:(NSError *)error +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + + // delegate may be nil when completing a task in the background + if (delegate) { + [delegate URLSession:session task:task didCompleteWithError:error]; + + [self removeDelegateForTask:task]; + } + + if (self.taskDidComplete) { + self.taskDidComplete(session, task, error); + } + +} + +#pragma mark - NSURLSessionDataDelegate + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler +{ + NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; + + if (self.dataTaskDidReceiveResponse) { + disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); + } + + if (completionHandler) { + completionHandler(disposition); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + if (delegate) { + [self removeDelegateForTask:dataTask]; + [self setDelegate:delegate forTask:downloadTask]; + } + + if (self.dataTaskDidBecomeDownloadTask) { + self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + didReceiveData:(NSData *)data +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; + [delegate URLSession:session dataTask:dataTask didReceiveData:data]; + + if (self.dataTaskDidReceiveData) { + self.dataTaskDidReceiveData(session, dataTask, data); + } +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler +{ + NSCachedURLResponse *cachedResponse = proposedResponse; + + if (self.dataTaskWillCacheResponse) { + cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); + } + + if (completionHandler) { + completionHandler(cachedResponse); + } +} + +- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { + if (self.didFinishEventsForBackgroundURLSession) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.didFinishEventsForBackgroundURLSession(session); + }); + } +} + +#pragma mark - NSURLSessionDownloadDelegate + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask +didFinishDownloadingToURL:(NSURL *)location +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + if (self.downloadTaskDidFinishDownloading) { + NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); + if (fileURL) { + delegate.downloadFileURL = fileURL; + NSError *error = nil; + [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; + if (error) { + [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; + } + + return; + } + } + + if (delegate) { + [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten +totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + [delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + + if (self.downloadTaskDidWriteData) { + self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } +} + +- (void)URLSession:(NSURLSession *)session + downloadTask:(NSURLSessionDownloadTask *)downloadTask + didResumeAtOffset:(int64_t)fileOffset +expectedTotalBytes:(int64_t)expectedTotalBytes +{ + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; + [delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes]; + + if (self.downloadTaskDidResume) { + self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (id)initWithCoder:(NSCoder *)decoder { + NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; + + self = [self initWithSessionConfiguration:configuration]; + if (!self) { + return nil; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/LICENSE b/Pods/AFNetworking/LICENSE new file mode 100644 index 0000000..91f125b --- /dev/null +++ b/Pods/AFNetworking/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/AFNetworking/README.md b/Pods/AFNetworking/README.md new file mode 100644 index 0000000..f25efe0 --- /dev/null +++ b/Pods/AFNetworking/README.md @@ -0,0 +1,394 @@ +

+ AFNetworking +

+ +[![Build Status](https://travis-ci.org/AFNetworking/AFNetworking.svg)](https://travis-ci.org/AFNetworking/AFNetworking) + +AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of the [Foundation URL Loading System](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html), extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. + +Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac. + +Choose AFNetworking for your next project, or migrate over your existing projects—you'll be happy you did! + +## How To Get Started + +- [Download AFNetworking](https://github.com/AFNetworking/AFNetworking/archive/master.zip) and try out the included Mac and iPhone example apps +- Read the ["Getting Started" guide](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking), [FAQ](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-FAQ), or [other articles on the Wiki](https://github.com/AFNetworking/AFNetworking/wiki) +- Check out the [documentation](http://cocoadocs.org/docsets/AFNetworking/) for a comprehensive look at all of the APIs available in AFNetworking +- Read the [AFNetworking 2.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-2.0-Migration-Guide) for an overview of the architectural changes from 1.0. + +## Communication + +- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). (Tag 'afnetworking') +- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/afnetworking). +- If you **found a bug**, _and can provide steps to reliably reproduce it_, open an issue. +- If you **have a feature request**, open an issue. +- If you **want to contribute**, submit a pull request. + +### Installation with CocoaPods + +[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like AFNetworking in your projects. See the ["Getting Started" guide for more information](https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking). + +#### Podfile + +```ruby +platform :ios, '7.0' +pod "AFNetworking", "~> 2.0" +``` + +## Requirements + +| AFNetworking Version | Minimum iOS Target | Minimum OS X Target | Notes | +|:--------------------:|:---------------------------:|:----------------------------:|:-------------------------------------------------------------------------:| +| 2.x | iOS 6 | OS X 10.8 | Xcode 5 is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. | +| [1.x](https://github.com/AFNetworking/AFNetworking/tree/1.x) | iOS 5 | Mac OS X 10.7 | | +| [0.10.x](https://github.com/AFNetworking/AFNetworking/tree/0.10.x) | iOS 4 | Mac OS X 10.6 | | + +(OS X projects must support [64-bit with modern Cocoa runtime](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html)). + +> Programming in Swift? Try [Alamofire](https://github.com/Alamofire/Alamofire) for a more conventional set of APIs. + +## Architecture + +### NSURLConnection + +- `AFURLConnectionOperation` +- `AFHTTPRequestOperation` +- `AFHTTPRequestOperationManager` + +### NSURLSession _(iOS 7 / Mac OS X 10.9)_ + +- `AFURLSessionManager` +- `AFHTTPSessionManager` + +### Serialization + +* `` + - `AFHTTPRequestSerializer` + - `AFJSONRequestSerializer` + - `AFPropertyListRequestSerializer` +* `` + - `AFHTTPResponseSerializer` + - `AFJSONResponseSerializer` + - `AFXMLParserResponseSerializer` + - `AFXMLDocumentResponseSerializer` _(Mac OS X)_ + - `AFPropertyListResponseSerializer` + - `AFImageResponseSerializer` + - `AFCompoundResponseSerializer` + +### Additional Functionality + +- `AFSecurityPolicy` +- `AFNetworkReachabilityManager` + +## Usage + +### HTTP Request Operation Manager + +`AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management. + +#### `GET` Request + +```objective-c +AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; +[manager GET:@"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSLog(@"JSON: %@", responseObject); +} failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"Error: %@", error); +}]; +``` + +#### `POST` URL-Form-Encoded Request + +```objective-c +AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; +NSDictionary *parameters = @{@"foo": @"bar"}; +[manager POST:@"http://example.com/resources.json" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSLog(@"JSON: %@", responseObject); +} failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"Error: %@", error); +}]; +``` + +#### `POST` Multi-Part Request + +```objective-c +AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; +NSDictionary *parameters = @{@"foo": @"bar"}; +NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"]; +[manager POST:@"http://example.com/resources.json" parameters:parameters constructingBodyWithBlock:^(id formData) { + [formData appendPartWithFileURL:filePath name:@"image" error:nil]; +} success:^(AFHTTPRequestOperation *operation, id responseObject) { + NSLog(@"Success: %@", responseObject); +} failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"Error: %@", error); +}]; +``` + +--- + +### AFURLSessionManager + +`AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to ``, ``, ``, and ``. + +#### Creating a Download Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { + NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; + return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]]; +} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { + NSLog(@"File downloaded to: %@", filePath); +}]; +[downloadTask resume]; +``` + +#### Creating an Upload Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"]; +NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"Success: %@ %@", response, responseObject); + } +}]; +[uploadTask resume]; +``` + +#### Creating an Upload Task for a Multi-Part Request, with Progress + +```objective-c +NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id formData) { + [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil]; + } error:nil]; + +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; +NSProgress *progress = nil; + +NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"%@ %@", response, responseObject); + } +}]; + +[uploadTask resume]; +``` + +#### Creating a Data Task + +```objective-c +NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; + +NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + +NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + NSLog(@"Error: %@", error); + } else { + NSLog(@"%@ %@", response, responseObject); + } +}]; +[dataTask resume]; +``` + +--- + +### Request Serialization + +Request serializers create requests from URL strings, encoding parameters as either a query string or HTTP body. + +```objective-c +NSString *URLString = @"http://example.com"; +NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]}; +``` + +#### Query String Parameter Encoding + +```objective-c +[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil]; +``` + + GET http://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3 + +#### URL Form Parameter Encoding + +```objective-c +[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters]; +``` + + POST http://example.com/ + Content-Type: application/x-www-form-urlencoded + + foo=bar&baz[]=1&baz[]=2&baz[]=3 + +#### JSON Parameter Encoding + +```objective-c +[[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters]; +``` + + POST http://example.com/ + Content-Type: application/json + + {"foo": "bar", "baz": [1,2,3]} + +--- + +### Network Reachability Manager + +`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. + +* Do not use Reachability to determine if the original request should be sent. + * You should try to send it. +* You can use Reachability to determine when a request should be automatically retried. + * Although it may still fail, a Reachability notification that the connectivity is available is a good time to retry something. +* Network reachability is a useful tool for determining why a request might have failed. + * After a network request has failed, telling the user they're offline is better than giving them a more technical but accurate error, such as "request timed out." + +See also [WWDC 2012 session 706, "Networking Best Practices."](https://developer.apple.com/videos/wwdc/2012/#706). + +#### Shared Network Reachability + +```objective-c +[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { + NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); +}]; + +[[AFNetworkReachabilityManager sharedManager] startMonitoring]; +``` + +#### HTTP Manager Reachability + +```objective-c +NSURL *baseURL = [NSURL URLWithString:@"http://example.com/"]; +AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; + +NSOperationQueue *operationQueue = manager.operationQueue; +[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { + switch (status) { + case AFNetworkReachabilityStatusReachableViaWWAN: + case AFNetworkReachabilityStatusReachableViaWiFi: + [operationQueue setSuspended:NO]; + break; + case AFNetworkReachabilityStatusNotReachable: + default: + [operationQueue setSuspended:YES]; + break; + } +}]; + +[manager.reachabilityManager startMonitoring]; +``` + +--- + +### Security Policy + +`AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. + +Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. + +#### Allowing Invalid SSL Certificates + +```objective-c +AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; +manager.securityPolicy.allowInvalidCertificates = YES; // not recommended for production +``` + +--- + +### AFHTTPRequestOperation + +`AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. + +Although `AFHTTPRequestOperationManager` is usually the best way to go about making requests, `AFHTTPRequestOperation` can be used by itself. + +#### `GET` with `AFHTTPRequestOperation` + +```objective-c +NSURL *URL = [NSURL URLWithString:@"http://example.com/resources/123.json"]; +NSURLRequest *request = [NSURLRequest requestWithURL:URL]; +AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request]; +op.responseSerializer = [AFJSONResponseSerializer serializer]; +[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + NSLog(@"JSON: %@", responseObject); +} failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSLog(@"Error: %@", error); +}]; +[[NSOperationQueue mainQueue] addOperation:op]; +``` + +#### Batch of Operations + +```objective-c +NSMutableArray *mutableOperations = [NSMutableArray array]; +for (NSURL *fileURL in filesToUpload) { + NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id formData) { + [formData appendPartWithFileURL:fileURL name:@"images[]" error:nil]; + }]; + + AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + + [mutableOperations addObject:operation]; +} + +NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) { + NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations); +} completionBlock:^(NSArray *operations) { + NSLog(@"All operations in batch complete"); +}]; +[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO]; +``` + +## Unit Tests + +AFNetworking includes a suite of unit tests within the Tests subdirectory. In order to run the unit tests, you must install the testing dependencies via [CocoaPods](http://cocoapods.org/): + + $ cd Tests + $ pod install + +Once testing dependencies are installed, you can execute the test suite via the 'iOS Tests' and 'OS X Tests' schemes within Xcode. + +### Running Tests from the Command Line + +Tests can also be run from the command line or within a continuous integration environment. The [`xcpretty`](https://github.com/mneorr/xcpretty) utility needs to be installed before running the tests from the command line: + + $ gem install xcpretty + +Once `xcpretty` is installed, you can execute the suite via `rake test`. + +## Credits + +AFNetworking is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). + +AFNetworking was originally created by [Scott Raymond](https://github.com/sco/) and [Mattt Thompson](https://github.com/mattt/) in the development of [Gowalla for iPhone](http://en.wikipedia.org/wiki/Gowalla). + +AFNetworking's logo was designed by [Alan Defibaugh](http://www.alandefibaugh.com/). + +And most of all, thanks to AFNetworking's [growing list of contributors](https://github.com/AFNetworking/AFNetworking/contributors). + +### Security Disclosure + +If you believe you have identified a security vulnerability with AFNetworking, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. + +## License + +AFNetworking is released under the MIT license. See LICENSE for details. diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 100644 index 0000000..3c7649b --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1,80 @@ +// AFNetworkActivityIndicatorManager.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + `AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero. + + You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code: + + [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES]; + + By setting `enabled` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself. + + See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information: + http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44 + */ +NS_EXTENSION_UNAVAILABLE_IOS("Use view controller based solutions where appropriate instead.") +@interface AFNetworkActivityIndicatorManager : NSObject + +/** + A Boolean value indicating whether the manager is enabled. + + If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO. + */ +@property (nonatomic, assign, getter = isEnabled) BOOL enabled; + +/** + A Boolean value indicating whether the network activity indicator is currently displayed in the status bar. + */ +@property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible; + +/** + Returns the shared network activity indicator manager object for the system. + + @return The systemwide network activity indicator manager. + */ ++ (instancetype)sharedManager; + +/** + Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator. + */ +- (void)incrementActivityCount; + +/** + Decrements the number of active network requests. If this number becomes zero after decrementing, this will stop animating the status bar network activity indicator. + */ +- (void)decrementActivityCount; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m new file mode 100644 index 0000000..cf13180 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m @@ -0,0 +1,170 @@ +// AFNetworkActivityIndicatorManager.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AFNetworkActivityIndicatorManager.h" + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17; + +static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { + if ([[notification object] isKindOfClass:[AFURLConnectionOperation class]]) { + return [(AFURLConnectionOperation *)[notification object] request]; + } + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + if ([[notification object] respondsToSelector:@selector(originalRequest)]) { + return [(NSURLSessionTask *)[notification object] originalRequest]; + } +#endif + + return nil; +} + +@interface AFNetworkActivityIndicatorManager () +@property (readwrite, nonatomic, assign) NSInteger activityCount; +@property (readwrite, nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer; +@property (readonly, nonatomic, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +- (void)updateNetworkActivityIndicatorVisibility; +- (void)updateNetworkActivityIndicatorVisibilityDelayed; +@end + +@implementation AFNetworkActivityIndicatorManager +@dynamic networkActivityIndicatorVisible; + ++ (instancetype)sharedManager { + static AFNetworkActivityIndicatorManager *_sharedManager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _sharedManager = [[self alloc] init]; + }); + + return _sharedManager; +} + ++ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible { + return [NSSet setWithObject:@"activityCount"]; +} + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingOperationDidStartNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidStart:) name:AFNetworkingTaskDidResumeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidSuspendNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkRequestDidFinish:) name:AFNetworkingTaskDidCompleteNotification object:nil]; +#endif + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_activityIndicatorVisibilityTimer invalidate]; +} + +- (void)updateNetworkActivityIndicatorVisibilityDelayed { + if (self.enabled) { + // Delay hiding of activity indicator for a short interval, to avoid flickering + if (![self isNetworkActivityIndicatorVisible]) { + [self.activityIndicatorVisibilityTimer invalidate]; + self.activityIndicatorVisibilityTimer = [NSTimer timerWithTimeInterval:kAFNetworkActivityIndicatorInvisibilityDelay target:self selector:@selector(updateNetworkActivityIndicatorVisibility) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.activityIndicatorVisibilityTimer forMode:NSRunLoopCommonModes]; + } else { + [self performSelectorOnMainThread:@selector(updateNetworkActivityIndicatorVisibility) withObject:nil waitUntilDone:NO modes:@[NSRunLoopCommonModes]]; + } + } +} + +- (BOOL)isNetworkActivityIndicatorVisible { + return self.activityCount > 0; +} + +- (void)updateNetworkActivityIndicatorVisibility { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]]; +} + +- (void)setActivityCount:(NSInteger)activityCount { + @synchronized(self) { + _activityCount = activityCount; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)incrementActivityCount { + [self willChangeValueForKey:@"activityCount"]; + @synchronized(self) { + _activityCount++; + } + [self didChangeValueForKey:@"activityCount"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)decrementActivityCount { + [self willChangeValueForKey:@"activityCount"]; + @synchronized(self) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + _activityCount = MAX(_activityCount - 1, 0); +#pragma clang diagnostic pop + } + [self didChangeValueForKey:@"activityCount"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateNetworkActivityIndicatorVisibilityDelayed]; + }); +} + +- (void)networkRequestDidStart:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self incrementActivityCount]; + } +} + +- (void)networkRequestDidFinish:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self decrementActivityCount]; + } +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 100644 index 0000000..0c8f9b5 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1,63 @@ +// UIActivityIndicatorView+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIActivityIndicatorView` class. The methods in this category provide support for automatically starting and stopping animation depending on the loading state of a request operation or session task. + */ +@interface UIActivityIndicatorView (AFNetworking) + +///---------------------------------- +/// @name Animating for Session Tasks +///---------------------------------- + +/** + Binds the animating state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task; +#endif + +///--------------------------------------- +/// @name Animating for Request Operations +///--------------------------------------- + +/** + Binds the animating state to the execution state of the specified operation. + + @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setAnimatingWithStateOfOperation:(nullable AFURLConnectionOperation *)operation; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m new file mode 100644 index 0000000..dd362b0 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m @@ -0,0 +1,171 @@ +// UIActivityIndicatorView+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIActivityIndicatorView+AFNetworking.h" +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +@interface AFActivityIndicatorViewNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView; +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; +#endif +- (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation; + +@end + +@implementation UIActivityIndicatorView (AFNetworking) + +- (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver { + AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); + if (notificationObserver == nil) { + notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self]; + objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return notificationObserver; +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setAnimatingWithStateOfTask:task]; +} +#endif + +- (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation { + [[self af_notificationObserver] setAnimatingWithStateOfOperation:operation]; +} + +@end + +@implementation AFActivityIndicatorViewNotificationObserver + +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView +{ + self = [super init]; + if (self) { + _activityIndicatorView = activityIndicatorView; + } + return self; +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { + if (task.state != NSURLSessionTaskStateCompleted) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" +#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" + if (task.state == NSURLSessionTaskStateRunning) { + [self.activityIndicatorView startAnimating]; + } else { + [self.activityIndicatorView stopAnimating]; + } +#pragma clang diagnostic pop + + [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingTaskDidSuspendNotification object:task]; + } + } +} +#endif + +#pragma mark - + +- (void)setAnimatingWithStateOfOperation:(AFURLConnectionOperation *)operation { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; + + if (operation) { + if (![operation isFinished]) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" +#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" + if ([operation isExecuting]) { + [self.activityIndicatorView startAnimating]; + } else { + [self.activityIndicatorView stopAnimating]; + } +#pragma clang diagnostic pop + + [notificationCenter addObserver:self selector:@selector(af_startAnimating) name:AFNetworkingOperationDidStartNotification object:operation]; + [notificationCenter addObserver:self selector:@selector(af_stopAnimating) name:AFNetworkingOperationDidFinishNotification object:operation]; + } + } +} + +#pragma mark - + +- (void)af_startAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" + [self.activityIndicatorView startAnimating]; +#pragma clang diagnostic pop + }); +} + +- (void)af_stopAnimating { + dispatch_async(dispatch_get_main_queue(), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" + [self.activityIndicatorView stopAnimating]; +#pragma clang diagnostic pop + }); +} + +#pragma mark - + +- (void)dealloc { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; +#endif + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h new file mode 100644 index 0000000..97f5622 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h @@ -0,0 +1,99 @@ +// UIAlertView+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIAlertView` class. The methods in this category provide support for automatically showing an alert if a session task or request operation finishes with an error. Alert title and message are filled from the corresponding `localizedDescription` & `localizedRecoverySuggestion` or `localizedFailureReason` of the error. + */ +@interface UIAlertView (AFNetworking) + +///------------------------------------- +/// @name Showing Alert for Session Task +///------------------------------------- + +/** + Shows an alert view with the error of the specified session task, if any. + + @param task The session task. + @param delegate The alert view delegate. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(nullable id)delegate NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extensions."); +#endif + +/** + Shows an alert view with the error of the specified session task, if any, with a custom cancel button title and other button titles. + + @param task The session task. + @param delegate The alert view delegate. + @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. + @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(nullable id)delegate + cancelButtonTitle:(nullable NSString *)cancelButtonTitle + otherButtonTitles:(nullable NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extensions."); +#endif + +///------------------------------------------ +/// @name Showing Alert for Request Operation +///------------------------------------------ + +/** + Shows an alert view with the error of the specified request operation, if any. + + @param operation The request operation. + @param delegate The alert view delegate. + */ ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(nullable id)delegate NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extensions."); + +/** + Shows an alert view with the error of the specified request operation, if any, with a custom cancel button title and other button titles. + + @param operation The request operation. + @param delegate The alert view delegate. + @param cancelButtonTitle The title of the cancel button or nil if there is no cancel button. Using this argument is equivalent to setting the cancel button index to the value returned by invoking addButtonWithTitle: specifying this title. + @param otherButtonTitles The title of another button. Using this argument is equivalent to invoking addButtonWithTitle: with this title to add more buttons. Too many buttons can cause the alert view to scroll. For guidelines on the best ways to use an alert in an app, see "Temporary Views". Titles of additional buttons to add to the receiver, terminated with `nil`. + */ ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(nullable id)delegate + cancelButtonTitle:(nullable NSString *)cancelButtonTitle + otherButtonTitles:(nullable NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION NS_EXTENSION_UNAVAILABLE_IOS("Not available in app extensions."); + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m new file mode 100644 index 0000000..0d1e9e7 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m @@ -0,0 +1,141 @@ +// UIAlertView+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIAlertView+AFNetworking.h" + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFURLConnectionOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static void AFGetAlertViewTitleAndMessageFromError(NSError *error, NSString * __autoreleasing *title, NSString * __autoreleasing *message) { + if (error.localizedDescription && (error.localizedRecoverySuggestion || error.localizedFailureReason)) { + *title = error.localizedDescription; + + if (error.localizedRecoverySuggestion) { + *message = error.localizedRecoverySuggestion; + } else { + *message = error.localizedFailureReason; + } + } else if (error.localizedDescription) { + *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); + *message = error.localizedDescription; + } else { + *title = NSLocalizedStringFromTable(@"Error", @"AFNetworking", @"Fallback Error Description"); + *message = [NSString stringWithFormat:NSLocalizedStringFromTable(@"%@ Error: %ld", @"AFNetworking", @"Fallback Error Failure Reason Format"), error.domain, (long)error.code]; + } +} + +@implementation UIAlertView (AFNetworking) + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate +{ + [self showAlertViewForTaskWithErrorOnCompletion:task delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; +} + ++ (void)showAlertViewForTaskWithErrorOnCompletion:(NSURLSessionTask *)task + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION +{ + NSMutableArray *mutableOtherTitles = [NSMutableArray array]; + va_list otherButtonTitleList; + va_start(otherButtonTitleList, otherButtonTitles); + { + for (NSString *otherButtonTitle = otherButtonTitles; otherButtonTitle != nil; otherButtonTitle = va_arg(otherButtonTitleList, NSString *)) { + [mutableOtherTitles addObject:otherButtonTitle]; + } + } + va_end(otherButtonTitleList); + + __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingTaskDidCompleteNotification object:task queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + NSError *error = notification.userInfo[AFNetworkingTaskDidCompleteErrorKey]; + if (error) { + NSString *title, *message; + AFGetAlertViewTitleAndMessageFromError(error, &title, &message); + + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for (NSString *otherButtonTitle in mutableOtherTitles) { + [alertView addButtonWithTitle:otherButtonTitle]; + } + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; + } + + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + }]; +} +#endif + +#pragma mark - + ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate +{ + [self showAlertViewForRequestOperationWithErrorOnCompletion:operation delegate:delegate cancelButtonTitle:NSLocalizedStringFromTable(@"Dismiss", @"AFNetworking", @"UIAlertView Cancel Button Title") otherButtonTitles:nil, nil]; +} + ++ (void)showAlertViewForRequestOperationWithErrorOnCompletion:(AFURLConnectionOperation *)operation + delegate:(id)delegate + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION +{ + NSMutableArray *mutableOtherTitles = [NSMutableArray array]; + va_list otherButtonTitleList; + va_start(otherButtonTitleList, otherButtonTitles); + { + for (NSString *otherButtonTitle = otherButtonTitles; otherButtonTitle != nil; otherButtonTitle = va_arg(otherButtonTitleList, NSString *)) { + [mutableOtherTitles addObject:otherButtonTitle]; + } + } + va_end(otherButtonTitleList); + + __block __weak id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingOperationDidFinishNotification object:operation queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + + if (notification.object && [notification.object isKindOfClass:[AFURLConnectionOperation class]]) { + NSError *error = [(AFURLConnectionOperation *)notification.object error]; + if (error) { + NSString *title, *message; + AFGetAlertViewTitleAndMessageFromError(error, &title, &message); + + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:nil delegate:delegate cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil]; + for (NSString *otherButtonTitle in mutableOtherTitles) { + [alertView addButtonWithTitle:otherButtonTitle]; + } + [alertView setTitle:title]; + [alertView setMessage:message]; + [alertView show]; + } + } + + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + }]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h new file mode 100644 index 0000000..7289bdb --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1,186 @@ +// UIButton+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol AFURLResponseSerialization, AFImageCache; + +/** + This category adds methods to the UIKit framework's `UIButton` class. The methods in this category provide support for loading remote images and background images asynchronously from a URL. + + @warning Compound values for control `state` (such as `UIControlStateHighlighted | UIControlStateDisabled`) are unsupported. + */ +@interface UIButton (AFNetworking) + +///---------------------------- +/// @name Accessing Image Cache +///---------------------------- + +/** + The image cache used to improve image loading performance on scroll views. By default, `UIButton` will use the `sharedImageCache` of `UIImageView`. + */ ++ (id )sharedImageCache; + +/** + Set the cache used for image loading. + + @param imageCache The image cache. + */ ++ (void)setSharedImageCache:(__nullable id )imageCache; + +///------------------------------------ +/// @name Accessing Response Serializer +///------------------------------------ + +/** + The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. + + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer + */ +@property (nonatomic, strong) id imageResponseSerializer; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + */ +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the button will not change its image until the image request finishes. + @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes two arguments: the server response and the image. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(nullable void (^)(NSError *error))failure; + + +///------------------------------- +/// @name Setting Background Image +///------------------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous background image request for the receiver will be cancelled. + + If the background image is cached locally, the background image is set immediately, otherwise the specified placeholder background image will be set immediately, and then the remote background image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it as the background image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + @param state The control state. + @param url The URL used for the background image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it as the image for the specified state once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the button before returning. If no success block is specified, the default behavior of setting the image with `setBackgroundImage:forState:` is applied. + + @param state The control state. + @param urlRequest The URL request used for the image request. + @param placeholderImage The background image to be set initially, until the background image request finishes. If `nil`, the button will not change its background image until the background image request finishes. + @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes two arguments: the server response and the image. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(nullable void (^)(NSError *error))failure; + + +///------------------------------ +/// @name Canceling Image Loading +///------------------------------ + +/** + Cancels any executing image operation for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelImageRequestOperationForState:(UIControlState)state; + +/** + Cancels any executing background image operation for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m new file mode 100644 index 0000000..e5c36a5 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m @@ -0,0 +1,293 @@ +// UIButton+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIButton+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFURLResponseSerialization.h" +#import "AFHTTPRequestOperation.h" + +#import "UIImageView+AFNetworking.h" + +@interface UIButton (_AFNetworking) +@end + +@implementation UIButton (_AFNetworking) + ++ (NSOperationQueue *)af_sharedImageRequestOperationQueue { + static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; + _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; + }); + + return _af_sharedImageRequestOperationQueue; +} + +#pragma mark - + +static char AFImageRequestOperationNormal; +static char AFImageRequestOperationHighlighted; +static char AFImageRequestOperationSelected; +static char AFImageRequestOperationDisabled; + +static const char * af_imageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFImageRequestOperationNormal; + } +} + +- (AFHTTPRequestOperation *)af_imageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_imageRequestOperationKeyForState(state)); +} + +- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_imageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +static char AFBackgroundImageRequestOperationNormal; +static char AFBackgroundImageRequestOperationHighlighted; +static char AFBackgroundImageRequestOperationSelected; +static char AFBackgroundImageRequestOperationDisabled; + +static const char * af_backgroundImageRequestOperationKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFBackgroundImageRequestOperationHighlighted; + case UIControlStateSelected: + return &AFBackgroundImageRequestOperationSelected; + case UIControlStateDisabled: + return &AFBackgroundImageRequestOperationDisabled; + case UIControlStateNormal: + default: + return &AFBackgroundImageRequestOperationNormal; + } +} + +- (AFHTTPRequestOperation *)af_backgroundImageRequestOperationForState:(UIControlState)state { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state)); +} + +- (void)af_setBackgroundImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_backgroundImageRequestOperationKeyForState(state), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIButton (AFNetworking) + ++ (id )sharedImageCache { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: [UIImageView sharedImageCache]; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageCache:(__nullable id )imageCache { + objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (id )imageResponseSerializer { + static id _af_defaultImageResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setImageResponseSerializer:(id )serializer { + objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(void (^)(NSError *error))failure +{ + [self cancelImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setImage:cachedImage forState:state]; + } + + [self af_setImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + imageRequestOperation.responseSerializer = self.imageResponseSerializer; + [imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setImageRequestOperation:imageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:imageRequestOperation]; + } +} + +#pragma mark - + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setBackgroundImageForState:state withURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(void (^)(NSError *error))failure +{ + [self cancelBackgroundImageRequestOperationForState:state]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setBackgroundImage:cachedImage forState:state]; + } + + [self af_setBackgroundImageRequestOperation:nil forState:state]; + } else { + if (placeholderImage) { + [self setBackgroundImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + AFHTTPRequestOperation *backgroundImageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + backgroundImageRequestOperation.responseSerializer = self.imageResponseSerializer; + [backgroundImageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (success) { + success(operation.request, operation.response, responseObject); + } else if (responseObject) { + [strongSelf setBackgroundImage:responseObject forState:state]; + } + } + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + if ([[urlRequest URL] isEqual:[operation.request URL]]) { + if (failure) { + failure(error); + } + } + }]; + + [self af_setBackgroundImageRequestOperation:backgroundImageRequestOperation forState:state]; + [[[self class] af_sharedImageRequestOperationQueue] addOperation:backgroundImageRequestOperation]; + } +} + +#pragma mark - + +- (void)cancelImageRequestOperationForState:(UIControlState)state { + [[self af_imageRequestOperationForState:state] cancel]; + [self af_setImageRequestOperation:nil forState:state]; +} + +- (void)cancelBackgroundImageRequestOperationForState:(UIControlState)state { + [[self af_backgroundImageRequestOperationForState:state] cancel]; + [self af_setBackgroundImageRequestOperation:nil forState:state]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h new file mode 100644 index 0000000..3292920 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h @@ -0,0 +1,35 @@ +// +// UIImage+AFNetworking.h +// +// +// Created by Paulo Ferreira on 08/07/15. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +@interface UIImage (AFNetworking) + ++ (UIImage*) safeImageWithData:(NSData*)data; + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h new file mode 100644 index 0000000..bf61915 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1,146 @@ +// UIImageView+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol AFURLResponseSerialization, AFImageCache; + +/** + This category adds methods to the UIKit framework's `UIImageView` class. The methods in this category provide support for loading remote images asynchronously from a URL. + */ +@interface UIImageView (AFNetworking) + +///---------------------------- +/// @name Accessing Image Cache +///---------------------------- + +/** + The image cache used to improve image loading performance on scroll views. By default, this is an `NSCache` subclass conforming to the `AFImageCache` protocol, which listens for notification warnings and evicts objects accordingly. +*/ ++ (id )sharedImageCache; + +/** + Set the cache used for image loading. + + @param imageCache The image cache. + */ ++ (void)setSharedImageCache:(__nullable id )imageCache; + +///------------------------------------ +/// @name Accessing Response Serializer +///------------------------------------ + +/** + The response serializer used to create an image representation from the server response and response data. By default, this is an instance of `AFImageResponseSerializer`. + + @discussion Subclasses of `AFImageResponseSerializer` could be used to perform post-processing, such as color correction, face detection, or other effects. See https://github.com/AFNetworking/AFCoreImageSerializer + */ +@property (nonatomic, strong) id imageResponseSerializer; + +///-------------------- +/// @name Setting Image +///-------------------- + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + */ +- (void)setImageWithURL:(NSURL *)url; + +/** + Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + By default, URL requests have a `Accept` header field value of "image / *", a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:` + + @param url The URL used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + */ +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(nullable UIImage *)placeholderImage; + +/** + Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled. + + If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished. + + If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied. + + @param urlRequest The URL request used for the image request. + @param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes. + @param success A block to be executed when the image request operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`. + @param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred. + */ +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(nullable UIImage *)placeholderImage + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, NSError *error))failure; + +/** + Cancels any executing image operation for the receiver, if one exists. + */ +- (void)cancelImageRequestOperation; + +@end + +#pragma mark - + +/** + The `AFImageCache` protocol is adopted by an object used to cache images loaded by the AFNetworking category on `UIImageView`. + */ +@protocol AFImageCache + +/** + Returns a cached image for the specified request, if available. + + @param request The image request. + + @return The cached image. + */ +- (nullable UIImage *)cachedImageForRequest:(NSURLRequest *)request; + +/** + Caches a particular image for the specified request. + + @param image The image to cache. + @param request The request to be used as a cache key. + */ +- (void)cacheImage:(UIImage *)image + forRequest:(NSURLRequest *)request; +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m new file mode 100644 index 0000000..2efc160 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m @@ -0,0 +1,215 @@ +// UIImageView+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIImageView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +@interface AFImageCache : NSCache +@end + +#pragma mark - + +@interface UIImageView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFHTTPRequestOperation *af_imageRequestOperation; +@end + +@implementation UIImageView (_AFNetworking) + ++ (NSOperationQueue *)af_sharedImageRequestOperationQueue { + static NSOperationQueue *_af_sharedImageRequestOperationQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_sharedImageRequestOperationQueue = [[NSOperationQueue alloc] init]; + _af_sharedImageRequestOperationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; + }); + + return _af_sharedImageRequestOperationQueue; +} + +- (AFHTTPRequestOperation *)af_imageRequestOperation { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_imageRequestOperation)); +} + +- (void)af_setImageRequestOperation:(AFHTTPRequestOperation *)imageRequestOperation { + objc_setAssociatedObject(self, @selector(af_imageRequestOperation), imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIImageView (AFNetworking) +@dynamic imageResponseSerializer; + ++ (id )sharedImageCache { + static AFImageCache *_af_defaultImageCache = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _af_defaultImageCache = [[AFImageCache alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * __unused notification) { + [_af_defaultImageCache removeAllObjects]; + }]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageCache)) ?: _af_defaultImageCache; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageCache:(__nullable id )imageCache { + objc_setAssociatedObject(self, @selector(sharedImageCache), imageCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (id )imageResponseSerializer { + static id _af_defaultImageResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultImageResponseSerializer = [AFImageResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(imageResponseSerializer)) ?: _af_defaultImageResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setImageResponseSerializer:(id )serializer { + objc_setAssociatedObject(self, @selector(imageResponseSerializer), serializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)setImageWithURL:(NSURL *)url { + [self setImageWithURL:url placeholderImage:nil]; +} + +- (void)setImageWithURL:(NSURL *)url + placeholderImage:(UIImage *)placeholderImage +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; + + [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; +} + +- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest + placeholderImage:(UIImage *)placeholderImage + success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, UIImage *image))success + failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * __nullable response, NSError *error))failure +{ + [self cancelImageRequestOperation]; + + UIImage *cachedImage = [[[self class] sharedImageCache] cachedImageForRequest:urlRequest]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + self.image = cachedImage; + } + + self.af_imageRequestOperation = nil; + } else { + if (placeholderImage) { + self.image = placeholderImage; + } + + __weak __typeof(self)weakSelf = self; + self.af_imageRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest]; + self.af_imageRequestOperation.responseSerializer = self.imageResponseSerializer; + [self.af_imageRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { + if (success) { + success(urlRequest, operation.response, responseObject); + } else if (responseObject) { + strongSelf.image = responseObject; + } + + if (operation == strongSelf.af_imageRequestOperation){ + strongSelf.af_imageRequestOperation = nil; + } + } + + [[[strongSelf class] sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[urlRequest URL] isEqual:[strongSelf.af_imageRequestOperation.request URL]]) { + if (failure) { + failure(urlRequest, operation.response, error); + } + + if (operation == strongSelf.af_imageRequestOperation){ + strongSelf.af_imageRequestOperation = nil; + } + } + }]; + + [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation]; + } +} + +- (void)cancelImageRequestOperation { + [self.af_imageRequestOperation cancel]; + self.af_imageRequestOperation = nil; +} + +@end + +#pragma mark - + +static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) { + return [[request URL] absoluteString]; +} + +@implementation AFImageCache + +- (UIImage *)cachedImageForRequest:(NSURLRequest *)request { + switch ([request cachePolicy]) { + case NSURLRequestReloadIgnoringCacheData: + case NSURLRequestReloadIgnoringLocalAndRemoteCacheData: + return nil; + default: + break; + } + + return [self objectForKey:AFImageCacheKeyFromURLRequest(request)]; +} + +- (void)cacheImage:(UIImage *)image + forRequest:(NSURLRequest *)request +{ + if (image && request) { + [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)]; + } +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h new file mode 100644 index 0000000..49850ed --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1,39 @@ +// UIKit+AFNetworking.h +// +// Copyright (c) 2013 AFNetworking (http://afnetworking.com/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if TARGET_OS_IOS +#import + +#ifndef _UIKIT_AFNETWORKING_ + #define _UIKIT_AFNETWORKING_ + + #import "AFNetworkActivityIndicatorManager.h" + + #import "UIActivityIndicatorView+AFNetworking.h" + #import "UIAlertView+AFNetworking.h" + #import "UIButton+AFNetworking.h" + #import "UIImageView+AFNetworking.h" + #import "UIProgressView+AFNetworking.h" + #import "UIRefreshControl+AFNetworking.h" + #import "UIWebView+AFNetworking.h" +#endif /* _UIKIT_AFNETWORKING_ */ +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h new file mode 100644 index 0000000..5c00d6d --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1,91 @@ +// UIProgressView+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIProgressView` class. The methods in this category provide support for binding the progress to the upload and download progress of a session task or request operation. + */ +@interface UIProgressView (AFNetworking) + +///------------------------------------ +/// @name Setting Session Task Progress +///------------------------------------ + +/** + Binds the progress to the upload progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated; +#endif + +/** + Binds the progress to the download progress of the specified session task. + + @param task The session task. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated; +#endif + +///------------------------------------ +/// @name Setting Session Task Progress +///------------------------------------ + +/** + Binds the progress to the upload progress of the specified request operation. + + @param operation The request operation. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated; + +/** + Binds the progress to the download progress of the specified request operation. + + @param operation The request operation. + @param animated `YES` if the change should be animated, `NO` if the change should happen immediately. + */ +- (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m new file mode 100644 index 0000000..ad2c280 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m @@ -0,0 +1,182 @@ +// UIProgressView+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIProgressView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFURLConnectionOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; +static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; + +@interface AFURLConnectionOperation (_UIProgressView) +@property (readwrite, nonatomic, copy) void (^uploadProgress)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +@property (readwrite, nonatomic, assign, setter = af_setUploadProgressAnimated:) BOOL af_uploadProgressAnimated; + +@property (readwrite, nonatomic, copy) void (^downloadProgress)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); +@property (readwrite, nonatomic, assign, setter = af_setDownloadProgressAnimated:) BOOL af_downloadProgressAnimated; +@end + +@implementation AFURLConnectionOperation (_UIProgressView) +@dynamic uploadProgress; // Implemented in AFURLConnectionOperation +@dynamic af_uploadProgressAnimated; + +@dynamic downloadProgress; // Implemented in AFURLConnectionOperation +@dynamic af_downloadProgressAnimated; +@end + +#pragma mark - + +@implementation UIProgressView (AFNetworking) + +- (BOOL)af_uploadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_uploadProgressAnimated)) boolValue]; +} + +- (void)af_setUploadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_uploadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)af_downloadProgressAnimated { + return [(NSNumber *)objc_getAssociatedObject(self, @selector(af_downloadProgressAnimated)) boolValue]; +} + +- (void)af_setDownloadProgressAnimated:(BOOL)animated { + objc_setAssociatedObject(self, @selector(af_downloadProgressAnimated), @(animated), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated +{ + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + [task addObserver:self forKeyPath:@"countOfBytesSent" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesSentContext]; + + [self af_setUploadProgressAnimated:animated]; +} + +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + animated:(BOOL)animated +{ + [task addObserver:self forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + [task addObserver:self forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext]; + + [self af_setDownloadProgressAnimated:animated]; +} +#endif + +#pragma mark - + +- (void)setProgressWithUploadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated +{ + __weak __typeof(self)weakSelf = self; + void (^original)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) = [operation.uploadProgress copy]; + [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { + if (original) { + original(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (totalBytesExpectedToWrite > 0) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf setProgress:(totalBytesWritten / (totalBytesExpectedToWrite * 1.0f)) animated:animated]; + } + }); + }]; +} + +- (void)setProgressWithDownloadProgressOfOperation:(AFURLConnectionOperation *)operation + animated:(BOOL)animated +{ + __weak __typeof(self)weakSelf = self; + void (^original)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) = [operation.downloadProgress copy]; + [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { + if (original) { + original(bytesRead, totalBytesRead, totalBytesExpectedToRead); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (totalBytesExpectedToRead > 0) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + [strongSelf setProgress:(totalBytesRead / (totalBytesExpectedToRead * 1.0f)) animated:animated]; + } + }); + }]; +} + +#pragma mark - NSKeyValueObserving + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(__unused NSDictionary *)change + context:(void *)context +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + if (context == AFTaskCountOfBytesSentContext || context == AFTaskCountOfBytesReceivedContext) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { + if ([object countOfBytesExpectedToSend] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesSent] / ([object countOfBytesExpectedToSend] * 1.0f) animated:self.af_uploadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { + if ([object countOfBytesExpectedToReceive] > 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setProgress:[object countOfBytesReceived] / ([object countOfBytesExpectedToReceive] * 1.0f) animated:self.af_downloadProgressAnimated]; + }); + } + } + + if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) { + if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) { + @try { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))]; + + if (context == AFTaskCountOfBytesSentContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; + } + + if (context == AFTaskCountOfBytesReceivedContext) { + [object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; + } + } + @catch (NSException * __unused exception) {} + } + } + } +#endif +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h new file mode 100644 index 0000000..a65e390 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1,68 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2014 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFURLConnectionOperation; + +/** + This category adds methods to the UIKit framework's `UIRefreshControl` class. The methods in this category provide support for automatically beginning and ending refreshing depending on the loading state of a request operation or session task. + */ +@interface UIRefreshControl (AFNetworking) + +///----------------------------------- +/// @name Refreshing for Session Tasks +///----------------------------------- + +/** + Binds the refreshing state to the state of the specified task. + + @param task The task. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; +#endif + +///---------------------------------------- +/// @name Refreshing for Request Operations +///---------------------------------------- + +/** + Binds the refreshing state to the execution state of the specified operation. + + @param operation The operation. If `nil`, automatic updating from any previously specified operation will be disabled. + */ +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m new file mode 100644 index 0000000..4c19245 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m @@ -0,0 +1,166 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2014 AFNetworking (http://afnetworking.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIRefreshControl+AFNetworking.h" +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +#import "AFURLSessionManager.h" +#endif + +@interface AFRefreshControlNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIRefreshControl *refreshControl; +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; +#endif +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation; + +@end + +@implementation UIRefreshControl (AFNetworking) + +- (AFRefreshControlNotificationObserver *)af_notificationObserver { + AFRefreshControlNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver)); + if (notificationObserver == nil) { + notificationObserver = [[AFRefreshControlNotificationObserver alloc] initWithActivityRefreshControl:self]; + objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return notificationObserver; +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setRefreshingWithStateOfTask:task]; +} +#endif + +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation { + [[self af_notificationObserver] setRefreshingWithStateOfOperation:operation]; +} + +@end + +@implementation AFRefreshControlNotificationObserver + +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl +{ + self = [super init]; + if (self) { + _refreshControl = refreshControl; + } + return self; +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + + if (task) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" +#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" + if (task.state == NSURLSessionTaskStateRunning) { + [self.refreshControl beginRefreshing]; + + [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingTaskDidResumeNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidCompleteNotification object:task]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingTaskDidSuspendNotification object:task]; + } else { + [self.refreshControl endRefreshing]; + } +#pragma clang diagnostic pop + } +} +#endif + +- (void)setRefreshingWithStateOfOperation:(AFURLConnectionOperation *)operation { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; + + if (operation) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" +#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak" + if (![operation isFinished]) { + if ([operation isExecuting]) { + [self.refreshControl beginRefreshing]; + } else { + [self.refreshControl endRefreshing]; + } + + [notificationCenter addObserver:self selector:@selector(af_beginRefreshing) name:AFNetworkingOperationDidStartNotification object:operation]; + [notificationCenter addObserver:self selector:@selector(af_endRefreshing) name:AFNetworkingOperationDidFinishNotification object:operation]; + } +#pragma clang diagnostic pop + } +} + +#pragma mark - + +- (void)af_beginRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" + [self.refreshControl beginRefreshing]; +#pragma clang diagnostic pop + }); +} + +- (void)af_endRefreshing { + dispatch_async(dispatch_get_main_queue(), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreceiver-is-weak" + [self.refreshControl endRefreshing]; +#pragma clang diagnostic pop + }); +} + +#pragma mark - + +- (void)dealloc { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; +#endif + + [notificationCenter removeObserver:self name:AFNetworkingOperationDidStartNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingOperationDidFinishNotification object:nil]; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h new file mode 100644 index 0000000..5d61d6a --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h @@ -0,0 +1,86 @@ +// UIWebView+AFNetworking.h +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFHTTPRequestSerializer, AFHTTPResponseSerializer; +@protocol AFURLRequestSerialization, AFURLResponseSerialization; + +/** + This category adds methods to the UIKit framework's `UIWebView` class. The methods in this category provide increased control over the request cycle, including progress monitoring and success / failure handling. + + @discussion When using these category methods, make sure to assign `delegate` for the web view, which implements `–webView:shouldStartLoadWithRequest:navigationType:` appropriately. This allows for tapped links to be loaded through AFNetworking, and can ensure that `canGoBack` & `canGoForward` update their values correctly. + */ +@interface UIWebView (AFNetworking) + +/** + The request serializer used to serialize requests made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPRequestSerializer`. + */ +@property (nonatomic, strong) AFHTTPRequestSerializer * requestSerializer; + +/** + The response serializer used to serialize responses made with the `-loadRequest:...` category methods. By default, this is an instance of `AFHTTPResponseSerializer`. + */ +@property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; + +/** + Asynchronously loads the specified request. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + @param success A block object to be executed when the request finishes loading successfully. This block returns the HTML string to be loaded by the web view, and takes two arguments: the response, and the response string. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + progress:(nullable void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(nullable NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(nullable void (^)(NSError *error))failure; + +/** + Asynchronously loads the data associated with a particular request with a specified MIME type and text encoding. + + @param request A URL request identifying the location of the content to load. This must not be `nil`. + @param MIMEType The MIME type of the content. Defaults to the content type of the response if not specified. + @param textEncodingName The IANA encoding name, as in `utf-8` or `utf-16`. Defaults to the response text encoding if not specified. + @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread. + @param success A block object to be executed when the request finishes loading successfully. This block returns the data to be loaded by the web view and takes two arguments: the response, and the downloaded data. + @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + MIMEType:(nullable NSString *)MIMEType + textEncodingName:(nullable NSString *)textEncodingName + progress:(nullable void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(nullable NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(nullable void (^)(NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m new file mode 100644 index 0000000..93eacaa --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m @@ -0,0 +1,159 @@ +// UIWebView+AFNetworking.m +// Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIWebView+AFNetworking.h" + +#import + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + +#import "AFHTTPRequestOperation.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" + +@interface UIWebView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setHTTPRequestOperation:) AFHTTPRequestOperation *af_HTTPRequestOperation; +@end + +@implementation UIWebView (_AFNetworking) + +- (AFHTTPRequestOperation *)af_HTTPRequestOperation { + return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, @selector(af_HTTPRequestOperation)); +} + +- (void)af_setHTTPRequestOperation:(AFHTTPRequestOperation *)operation { + objc_setAssociatedObject(self, @selector(af_HTTPRequestOperation), operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIWebView (AFNetworking) + +- (AFHTTPRequestSerializer *)requestSerializer { + static AFHTTPRequestSerializer *_af_defaultRequestSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultRequestSerializer = [AFHTTPRequestSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(requestSerializer)) ?: _af_defaultRequestSerializer; +#pragma clang diagnostic pop +} + +- (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { + objc_setAssociatedObject(self, @selector(requestSerializer), requestSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (AFHTTPResponseSerializer *)responseSerializer { + static AFHTTPResponseSerializer *_af_defaultResponseSerializer = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultResponseSerializer = [AFHTTPResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(responseSerializer)) ?: _af_defaultResponseSerializer; +#pragma clang diagnostic pop +} + +- (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { + objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +- (void)loadRequest:(NSURLRequest *)request + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSString * (^)(NSHTTPURLResponse *response, NSString *HTML))success + failure:(void (^)(NSError *error))failure +{ + [self loadRequest:request MIMEType:nil textEncodingName:nil progress:progress success:^NSData *(NSHTTPURLResponse *response, NSData *data) { + NSStringEncoding stringEncoding = NSUTF8StringEncoding; + if (response.textEncodingName) { + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (encoding != kCFStringEncodingInvalidId) { + stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding); + } + } + + NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding]; + if (success) { + string = success(response, string); + } + + return [string dataUsingEncoding:stringEncoding]; + } failure:failure]; +} + +- (void)loadRequest:(NSURLRequest *)request + MIMEType:(NSString *)MIMEType + textEncodingName:(NSString *)textEncodingName + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(void (^)(NSError *error))failure +{ + NSParameterAssert(request); + + if (self.af_HTTPRequestOperation) { + [self.af_HTTPRequestOperation cancel]; + } + + request = [self.requestSerializer requestBySerializingRequest:request withParameters:nil error:nil]; + + self.af_HTTPRequestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; + self.af_HTTPRequestOperation.responseSerializer = self.responseSerializer; + + __weak __typeof(self)weakSelf = self; + [self.af_HTTPRequestOperation setDownloadProgressBlock:progress]; + [self.af_HTTPRequestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id __unused responseObject) { + NSData *data = success ? success(operation.response, operation.responseData) : operation.responseData; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + __strong __typeof(weakSelf) strongSelf = weakSelf; + [strongSelf loadData:data MIMEType:(MIMEType ?: [operation.response MIMEType]) textEncodingName:(textEncodingName ?: [operation.response textEncodingName]) baseURL:[operation.response URL]]; + + if ([strongSelf.delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { + [strongSelf.delegate webViewDidFinishLoad:strongSelf]; + } + +#pragma clang diagnostic pop + } failure:^(AFHTTPRequestOperation * __unused operation, NSError *error) { + if (failure) { + failure(error); + } + }]; + + [self.af_HTTPRequestOperation start]; + + if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [self.delegate webViewDidStartLoad:self]; + } +} + +@end + +#endif diff --git a/Pods/Bolts/Bolts/Common/BFCancellationToken.h b/Pods/Bolts/Bolts/Common/BFCancellationToken.h new file mode 100644 index 0000000..90a20d7 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationToken.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +#import + +/*! + A block that will be called when a token is cancelled. + */ +typedef void(^BFCancellationBlock)(); + +/*! + The consumer view of a CancellationToken. + Propagates notification that operations should be canceled. + A BFCancellationToken has methods to inspect whether the token has been cancelled. + */ +@interface BFCancellationToken : NSObject + +/*! + Whether cancellation has been requested for this token source. + */ +@property (nonatomic, assign, readonly, getter=isCancellationRequested) BOOL cancellationRequested; + +/*! + Register a block to be notified when the token is cancelled. + If the token is already cancelled the delegate will be notified immediately. + */ +- (BFCancellationTokenRegistration *)registerCancellationObserverWithBlock:(BFCancellationBlock)block; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFCancellationToken.m b/Pods/Bolts/Bolts/Common/BFCancellationToken.m new file mode 100644 index 0000000..b5006d0 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationToken.m @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFCancellationToken.h" +#import "BFCancellationTokenRegistration.h" + +@interface BFCancellationToken () + +@property (atomic, assign, getter=isCancellationRequested) BOOL cancellationRequested; +@property (nonatomic, strong) NSMutableArray *registrations; +@property (nonatomic, strong) NSObject *lock; +@property (nonatomic) BOOL disposed; + +@end + +@interface BFCancellationTokenRegistration (BFCancellationToken) + ++ (instancetype)registrationWithToken:(BFCancellationToken *)token delegate:(BFCancellationBlock)delegate; + +- (void)notifyDelegate; + +@end + +@implementation BFCancellationToken + +#pragma mark - Initializer + +- (instancetype)init { + if (self = [super init]) { + _registrations = [NSMutableArray array]; + _lock = [NSObject new]; + } + return self; +} + +#pragma mark - Custom Setters/Getters + +- (BOOL)isCancellationRequested { + @synchronized(self.lock) { + [self throwIfDisposed]; + return _cancellationRequested; + } +} + +- (void)cancel { + NSArray *registrations; + @synchronized(self.lock) { + [self throwIfDisposed]; + if (_cancellationRequested) { + return; + } + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil]; + _cancellationRequested = YES; + registrations = [self.registrations copy]; + } + + [self notifyCancellation:registrations]; +} + +- (void)notifyCancellation:(NSArray *)registrations { + for (BFCancellationTokenRegistration *registration in registrations) { + [registration notifyDelegate]; + } +} + +- (BFCancellationTokenRegistration *)registerCancellationObserverWithBlock:(BFCancellationBlock)block { + @synchronized(self.lock) { + BFCancellationTokenRegistration *registration = [BFCancellationTokenRegistration registrationWithToken:self delegate:[block copy]]; + [self.registrations addObject:registration]; + + return registration; + } +} + +- (void)unregisterRegistration:(BFCancellationTokenRegistration *)registration { + @synchronized(self.lock) { + [self throwIfDisposed]; + [self.registrations removeObject:registration]; + } +} + +// Delay on a non-public method to prevent interference with a user calling performSelector or +// cancelPreviousPerformRequestsWithTarget on the public method +- (void)cancelPrivate { + [self cancel]; +} + +- (void)cancelAfterDelay:(int)millis { + [self throwIfDisposed]; + if (millis < -1) { + [NSException raise:NSInvalidArgumentException format:@"Delay must be >= -1"]; + } + + if (millis == 0) { + [self cancel]; + return; + } + + @synchronized(self.lock) { + [self throwIfDisposed]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil]; + if (self.cancellationRequested) { + return; + } + + if (millis != -1) { + double delay = (double)millis / 1000; + [self performSelector:@selector(cancelPrivate) withObject:nil afterDelay:delay]; + } + } +} + +- (void)dispose { + @synchronized(self.lock) { + if (self.disposed) { + return; + } + self.disposed = YES; + for (BFCancellationTokenRegistration *registration in self.registrations) { + [registration dispose]; + } + [self.registrations removeAllObjects]; + } +} + +- (void)throwIfDisposed { + if (self.disposed) { + [NSException raise:NSInternalInconsistencyException format:@"Object already disposed"]; + } +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.h b/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.h new file mode 100644 index 0000000..3e7b711 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +/*! + Represents the registration of a cancellation observer with a cancellation token. + Can be used to unregister the observer at a later time. + */ +@interface BFCancellationTokenRegistration : NSObject + +/*! + Removes the cancellation observer registered with the token + and releases all resources associated with this registration. + */ +- (void)dispose; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.m b/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.m new file mode 100644 index 0000000..9c8a7ae --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationTokenRegistration.m @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFCancellationTokenRegistration.h" + +#import "BFCancellationToken.h" + +@interface BFCancellationTokenRegistration () + +@property (nonatomic, weak) BFCancellationToken *token; +@property (nonatomic, strong) BFCancellationBlock cancellationObserverBlock; +@property (nonatomic, strong) NSObject *lock; +@property (nonatomic) BOOL disposed; + +@end + +@interface BFCancellationToken (BFCancellationTokenRegistration) + +- (void)unregisterRegistration:(BFCancellationTokenRegistration *)registration; + +@end + +@implementation BFCancellationTokenRegistration + ++ (instancetype)registrationWithToken:(BFCancellationToken *)token delegate:(BFCancellationBlock)delegate { + BFCancellationTokenRegistration *registration = [BFCancellationTokenRegistration new]; + registration.token = token; + registration.cancellationObserverBlock = delegate; + return registration; +} + +- (instancetype)init { + if (self = [super init]) { + _lock = [NSObject new]; + } + return self; +} + +- (void)dispose { + @synchronized(self.lock) { + if (self.disposed) { + return; + } + self.disposed = YES; + } + + BFCancellationToken *token = self.token; + if (token != nil) { + [token unregisterRegistration:self]; + self.token = nil; + } + self.cancellationObserverBlock = nil; +} + +- (void)notifyDelegate { + @synchronized(self.lock) { + [self throwIfDisposed]; + self.cancellationObserverBlock(); + } +} + +- (void)throwIfDisposed { + NSAssert(!self.disposed, @"Object already disposed"); +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.h b/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.h new file mode 100644 index 0000000..bd6e7a1 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@class BFCancellationToken; + +/*! + BFCancellationTokenSource represents the producer side of a CancellationToken. + Signals to a CancellationToken that it should be canceled. + It is a cancellation token that also has methods + for changing the state of a token by cancelling it. + */ +@interface BFCancellationTokenSource : NSObject + +/*! + Creates a new cancellation token source. + */ ++ (instancetype)cancellationTokenSource; + +/*! + The cancellation token associated with this CancellationTokenSource. + */ +@property (nonatomic, strong, readonly) BFCancellationToken *token; + +/*! + Whether cancellation has been requested for this token source. + */ +@property (nonatomic, assign, readonly, getter=isCancellationRequested) BOOL cancellationRequested; + +/*! + Cancels the token if it has not already been cancelled. + */ +- (void)cancel; + +/*! + Schedules a cancel operation on this CancellationTokenSource after the specified number of milliseconds. + @param millis The number of milliseconds to wait before completing the returned task. + If delay is `0` the cancel is executed immediately. If delay is `-1` any scheduled cancellation is stopped. + */ +- (void)cancelAfterDelay:(int)millis; + +/*! + Releases all resources associated with this token source, + including disposing of all registrations. + */ +- (void)dispose; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.m b/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.m new file mode 100644 index 0000000..947f725 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFCancellationTokenSource.m @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFCancellationTokenSource.h" + +#import "BFCancellationToken.h" + +@interface BFCancellationToken (BFCancellationTokenSource) + +- (void)cancel; +- (void)cancelAfterDelay:(int)millis; + +- (void)dispose; +- (void)throwIfDisposed; + +@end + +@implementation BFCancellationTokenSource + +#pragma mark - Initializer + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _token = [BFCancellationToken new]; + + return self; +} + ++ (instancetype)cancellationTokenSource { + return [BFCancellationTokenSource new]; +} + +#pragma mark - Custom Setters/Getters + +- (BOOL)isCancellationRequested { + return _token.isCancellationRequested; +} + +- (void)cancel { + [_token cancel]; +} + +- (void)cancelAfterDelay:(int)millis { + [_token cancelAfterDelay:millis]; +} + +- (void)dispose { + [_token dispose]; +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BFDefines.h b/Pods/Bolts/Bolts/Common/BFDefines.h new file mode 100644 index 0000000..cf7dcdf --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFDefines.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +#if __has_feature(objc_generics) || __has_extension(objc_generics) +# define BF_GENERIC(type) +#else +# define BF_GENERIC(type) +# define BFGenericType id +#endif diff --git a/Pods/Bolts/Bolts/Common/BFExecutor.h b/Pods/Bolts/Bolts/Common/BFExecutor.h new file mode 100644 index 0000000..02af9ba --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFExecutor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +/*! + An object that can run a given block. + */ +@interface BFExecutor : NSObject + +/*! + Returns a default executor, which runs continuations immediately until the call stack gets too + deep, then dispatches to a new GCD queue. + */ ++ (instancetype)defaultExecutor; + +/*! + Returns an executor that runs continuations on the thread where the previous task was completed. + */ ++ (instancetype)immediateExecutor; + +/*! + Returns an executor that runs continuations on the main thread. + */ ++ (instancetype)mainThreadExecutor; + +/*! + Returns a new executor that uses the given block to execute continuations. + @param block The block to use. + */ ++ (instancetype)executorWithBlock:(void(^)(void(^block)()))block; + +/*! + Returns a new executor that runs continuations on the given queue. + @param queue The instance of `dispatch_queue_t` to dispatch all continuations onto. + */ ++ (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue; + +/*! + Returns a new executor that runs continuations on the given queue. + @param queue The instance of `NSOperationQueue` to run all continuations on. + */ ++ (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue; + +/*! + Runs the given block using this executor's particular strategy. + @param block The block to execute. + */ +- (void)execute:(void(^)())block; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFExecutor.m b/Pods/Bolts/Bolts/Common/BFExecutor.m new file mode 100644 index 0000000..292e27c --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFExecutor.m @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFExecutor.h" + +@interface BFExecutor () + +@property (nonatomic, copy) void(^block)(void(^block)()); + +@end + +@implementation BFExecutor + +#pragma mark - Executor methods + ++ (instancetype)defaultExecutor { + static BFExecutor *defaultExecutor = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultExecutor = [self executorWithBlock:^void(void(^block)()) { + static const NSString *BFTaskDepthKey = @"BFTaskDepth"; + static const int BFTaskDefaultExecutorMaxDepth = 20; + + // We prefer to run everything possible immediately, so that there is callstack information + // when debugging. However, we don't want the stack to get too deep, so if the number of + // recursive calls to this method exceeds a certain depth, we dispatch to another GCD queue. + NSMutableDictionary *threadLocal = [[NSThread currentThread] threadDictionary]; + NSNumber *depth = threadLocal[BFTaskDepthKey]; + if (!depth) { + depth = @0; + } + if (depth.intValue > BFTaskDefaultExecutorMaxDepth) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); + } else { + NSNumber *previousDepth = depth; + threadLocal[BFTaskDepthKey] = @(previousDepth.intValue + 1); + @try { + block(); + } @finally { + threadLocal[BFTaskDepthKey] = previousDepth; + } + } + }]; + }); + return defaultExecutor; +} + ++ (instancetype)immediateExecutor { + static BFExecutor *immediateExecutor = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + immediateExecutor = [self executorWithBlock:^void(void(^block)()) { + block(); + }]; + }); + return immediateExecutor; +} + ++ (instancetype)mainThreadExecutor { + static BFExecutor *mainThreadExecutor = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mainThreadExecutor = [self executorWithBlock:^void(void(^block)()) { + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), block); + } else { + block(); + } + }]; + }); + return mainThreadExecutor; +} + ++ (instancetype)executorWithBlock:(void(^)(void(^block)()))block { + return [[self alloc] initWithBlock:block]; +} + ++ (instancetype)executorWithDispatchQueue:(dispatch_queue_t)queue { + return [self executorWithBlock:^void(void(^block)()) { + dispatch_async(queue, block); + }]; +} + ++ (instancetype)executorWithOperationQueue:(NSOperationQueue *)queue { + return [self executorWithBlock:^void(void(^block)()) { + [queue addOperation:[NSBlockOperation blockOperationWithBlock:block]]; + }]; +} + +#pragma mark - Initializer + +- (instancetype)initWithBlock:(void(^)(void(^block)()))block { + if (self = [super init]) { + _block = block; + } + return self; +} + +#pragma mark - Execution + +- (void)execute:(void(^)())block { + self.block(block); +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BFTask.h b/Pods/Bolts/Bolts/Common/BFTask.h new file mode 100644 index 0000000..827071d --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFTask.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +#import +#import + +/*! + Error domain used if there was multiple errors on . + */ +extern NSString *const BFTaskErrorDomain; + +/*! + An exception that is thrown if there was multiple exceptions on . + */ +extern NSString *const BFTaskMultipleExceptionsException; + +@class BFExecutor; +@class BFTask; + +/*! + The consumer view of a Task. A BFTask has methods to + inspect the state of the task, and to add continuations to + be run once the task is complete. + */ +@interface BFTask BF_GENERIC(__covariant BFGenericType) : NSObject + +/*! + A block that can act as a continuation for a task. + */ +typedef id(^BFContinuationBlock)(BFTask BF_GENERIC(BFGenericType) *task); + +/*! + Creates a task that is already completed with the given result. + @param result The result for the task. + */ ++ (instancetype)taskWithResult:(BFGenericType)result; + +/*! + Creates a task that is already completed with the given error. + @param error The error for the task. + */ ++ (instancetype)taskWithError:(NSError *)error; + +/*! + Creates a task that is already completed with the given exception. + @param exception The exception for the task. + */ ++ (instancetype)taskWithException:(NSException *)exception; + +/*! + Creates a task that is already cancelled. + */ ++ (instancetype)cancelledTask; + +/*! + Returns a task that will be completed (with result == nil) once + all of the input tasks have completed. + @param tasks An `NSArray` of the tasks to use as an input. + */ ++ (instancetype)taskForCompletionOfAllTasks:(NSArray *)tasks; + +/*! + Returns a task that will be completed once all of the input tasks have completed. + If all tasks complete successfully without being faulted or cancelled the result will be + an `NSArray` of all task results in the order they were provided. + @param tasks An `NSArray` of the tasks to use as an input. + */ ++ (instancetype)taskForCompletionOfAllTasksWithResults:(NSArray *)tasks; + +/*! + Returns a task that will be completed a certain amount of time in the future. + @param millis The approximate number of milliseconds to wait before the + task will be finished (with result == nil). + */ ++ (instancetype)taskWithDelay:(int)millis; + +/*! + Returns a task that will be completed a certain amount of time in the future. + @param millis The approximate number of milliseconds to wait before the + task will be finished (with result == nil). + @param token The cancellation token (optional). + */ ++ (instancetype)taskWithDelay:(int)millis + cancellationToken:(BFCancellationToken *)token; + +/*! + Returns a task that will be completed after the given block completes with + the specified executor. + @param executor A BFExecutor responsible for determining how the + continuation block will be run. + @param block The block to immediately schedule to run with the given executor. + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ ++ (instancetype)taskFromExecutor:(BFExecutor *)executor + withBlock:(id (^)())block; + +// Properties that will be set on the task once it is completed. + +/*! + The result of a successful task. + */ +@property (nonatomic, strong, readonly) BFGenericType result; + +/*! + The error of a failed task. + */ +@property (nonatomic, strong, readonly) NSError *error; + +/*! + The exception of a failed task. + */ +@property (nonatomic, strong, readonly) NSException *exception; + +/*! + Whether this task has been cancelled. + */ +@property (nonatomic, assign, readonly, getter=isCancelled) BOOL cancelled; + +/*! + Whether this task has completed due to an error or exception. + */ +@property (nonatomic, assign, readonly, getter=isFaulted) BOOL faulted; + +/*! + Whether this task has completed. + */ +@property (nonatomic, assign, readonly, getter=isCompleted) BOOL completed; + +/*! + Enqueues the given block to be run once this task is complete. + This method uses a default execution strategy. The block will be + run on the thread where the previous task completes, unless the + the stack depth is too deep, in which case it will be run on a + dispatch queue with default priority. + @param block The block to be run once this task is complete. + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithBlock:(BFContinuationBlock)block; + +/*! + Enqueues the given block to be run once this task is complete. + This method uses a default execution strategy. The block will be + run on the thread where the previous task completes, unless the + the stack depth is too deep, in which case it will be run on a + dispatch queue with default priority. + @param block The block to be run once this task is complete. + @param cancellationToken The cancellation token (optional). + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken; + +/*! + Enqueues the given block to be run once this task is complete. + @param executor A BFExecutor responsible for determining how the + continuation block will be run. + @param block The block to be run once this task is complete. + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithExecutor:(BFExecutor *)executor + withBlock:(BFContinuationBlock)block; +/*! + Enqueues the given block to be run once this task is complete. + @param executor A BFExecutor responsible for determining how the + continuation block will be run. + @param block The block to be run once this task is complete. + @param cancellationToken The cancellation token (optional). + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + his method will not be completed until that task is completed. + */ +- (instancetype)continueWithExecutor:(BFExecutor *)executor + block:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken; + +/*! + Identical to continueWithBlock:, except that the block is only run + if this task did not produce a cancellation, error, or exception. + If it did, then the failure will be propagated to the returned + task. + @param block The block to be run once this task is complete. + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithSuccessBlock:(BFContinuationBlock)block; + +/*! + Identical to continueWithBlock:, except that the block is only run + if this task did not produce a cancellation, error, or exception. + If it did, then the failure will be propagated to the returned + task. + @param block The block to be run once this task is complete. + @param cancellationToken The cancellation token (optional). + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithSuccessBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken; + +/*! + Identical to continueWithExecutor:withBlock:, except that the block + is only run if this task did not produce a cancellation, error, or + exception. If it did, then the failure will be propagated to the + returned task. + @param executor A BFExecutor responsible for determining how the + continuation block will be run. + @param block The block to be run once this task is complete. + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithExecutor:(BFExecutor *)executor + withSuccessBlock:(BFContinuationBlock)block; + +/*! + Identical to continueWithExecutor:withBlock:, except that the block + is only run if this task did not produce a cancellation, error, or + exception. If it did, then the failure will be propagated to the + returned task. + @param executor A BFExecutor responsible for determining how the + continuation block will be run. + @param block The block to be run once this task is complete. + @param cancellationToken The cancellation token (optional). + @returns A task that will be completed after block has run. + If block returns a BFTask, then the task returned from + this method will not be completed until that task is completed. + */ +- (instancetype)continueWithExecutor:(BFExecutor *)executor + successBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken; + +/*! + Waits until this operation is completed. + This method is inefficient and consumes a thread resource while + it's running. It should be avoided. This method logs a warning + message if it is used on the main thread. + */ +- (void)waitUntilFinished; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFTask.m b/Pods/Bolts/Bolts/Common/BFTask.m new file mode 100644 index 0000000..4985cfa --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFTask.m @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFTask.h" + +#import + +#import "Bolts.h" + +__attribute__ ((noinline)) void warnBlockingOperationOnMainThread() { + NSLog(@"Warning: A long-running operation is being executed on the main thread. \n" + " Break on warnBlockingOperationOnMainThread() to debug."); +} + +NSString *const BFTaskErrorDomain = @"bolts"; +NSString *const BFTaskMultipleExceptionsException = @"BFMultipleExceptionsException"; + +@interface BFTask () { + id _result; + NSError *_error; + NSException *_exception; +} + +@property (atomic, assign, readwrite, getter=isCancelled) BOOL cancelled; +@property (atomic, assign, readwrite, getter=isFaulted) BOOL faulted; +@property (atomic, assign, readwrite, getter=isCompleted) BOOL completed; + +@property (nonatomic, strong) NSObject *lock; +@property (nonatomic, strong) NSCondition *condition; +@property (nonatomic, strong) NSMutableArray *callbacks; + +@end + +@implementation BFTask + +#pragma mark - Initializer + +- (instancetype)init { + if (self = [super init]) { + _lock = [[NSObject alloc] init]; + _condition = [[NSCondition alloc] init]; + _callbacks = [NSMutableArray array]; + } + return self; +} + +#pragma mark - Task Class methods + ++ (instancetype)taskWithResult:(id)result { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + tcs.result = result; + return tcs.task; +} + ++ (instancetype)taskWithError:(NSError *)error { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + tcs.error = error; + return tcs.task; +} + ++ (instancetype)taskWithException:(NSException *)exception { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + tcs.exception = exception; + return tcs.task; +} + ++ (instancetype)cancelledTask { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + [tcs cancel]; + return tcs.task; +} + ++ (instancetype)taskForCompletionOfAllTasks:(NSArray *)tasks { + __block int32_t total = (int32_t)tasks.count; + if (total == 0) { + return [self taskWithResult:nil]; + } + + __block int32_t cancelled = 0; + NSObject *lock = [[NSObject alloc] init]; + NSMutableArray *errors = [NSMutableArray array]; + NSMutableArray *exceptions = [NSMutableArray array]; + + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + for (BFTask *task in tasks) { + [task continueWithBlock:^id(BFTask *task) { + if (task.exception) { + @synchronized (lock) { + [exceptions addObject:task.exception]; + } + } else if (task.error) { + @synchronized (lock) { + [errors addObject:task.error]; + } + } else if (task.cancelled) { + OSAtomicIncrement32(&cancelled); + } + + if (OSAtomicDecrement32(&total) == 0) { + if (exceptions.count > 0) { + if (exceptions.count == 1) { + tcs.exception = [exceptions firstObject]; + } else { + NSException *exception = + [NSException exceptionWithName:BFTaskMultipleExceptionsException + reason:@"There were multiple exceptions." + userInfo:@{ @"exceptions": exceptions }]; + tcs.exception = exception; + } + } else if (errors.count > 0) { + if (errors.count == 1) { + tcs.error = [errors firstObject]; + } else { + NSError *error = [NSError errorWithDomain:BFTaskErrorDomain + code:kBFMultipleErrorsError + userInfo:@{ @"errors": errors }]; + tcs.error = error; + } + } else if (cancelled > 0) { + [tcs cancel]; + } else { + tcs.result = nil; + } + } + return nil; + }]; + } + return tcs.task; +} + ++ (instancetype)taskForCompletionOfAllTasksWithResults:(NSArray *)tasks { + return [[self taskForCompletionOfAllTasks:tasks] continueWithSuccessBlock:^id(BFTask *task) { + return [tasks valueForKey:@"result"]; + }]; +} + ++ (instancetype)taskWithDelay:(int)millis { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC); + dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + tcs.result = nil; + }); + return tcs.task; +} + ++ (instancetype)taskWithDelay:(int)millis + cancellationToken:(BFCancellationToken *)token { + if (token.cancellationRequested) { + return [BFTask cancelledTask]; + } + + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, millis * NSEC_PER_MSEC); + dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + if (token.cancellationRequested) { + [tcs cancel]; + return; + } + tcs.result = nil; + }); + return tcs.task; +} + ++ (instancetype)taskFromExecutor:(BFExecutor *)executor + withBlock:(id (^)())block { + return [[self taskWithResult:nil] continueWithExecutor:executor withBlock:^id(BFTask *task) { + return block(); + }]; +} + +#pragma mark - Custom Setters/Getters + +- (id)result { + @synchronized(self.lock) { + return _result; + } +} + +- (void)setResult:(id)result { + if (![self trySetResult:result]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the result on a completed task."]; + } +} + +- (BOOL)trySetResult:(id)result { + @synchronized(self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + _result = result; + [self runContinuations]; + return YES; + } +} + +- (NSError *)error { + @synchronized(self.lock) { + return _error; + } +} + +- (void)setError:(NSError *)error { + if (![self trySetError:error]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the error on a completed task."]; + } +} + +- (BOOL)trySetError:(NSError *)error { + @synchronized(self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + self.faulted = YES; + _error = error; + [self runContinuations]; + return YES; + } +} + +- (NSException *)exception { + @synchronized(self.lock) { + return _exception; + } +} + +- (void)setException:(NSException *)exception { + if (![self trySetException:exception]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot set the exception on a completed task."]; + } +} + +- (BOOL)trySetException:(NSException *)exception { + @synchronized(self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + self.faulted = YES; + _exception = exception; + [self runContinuations]; + return YES; + } +} + +- (BOOL)isCancelled { + @synchronized(self.lock) { + return _cancelled; + } +} + +- (BOOL)isFaulted { + @synchronized(self.lock) { + return _faulted; + } +} + +- (void)cancel { + @synchronized(self.lock) { + if (![self trySetCancelled]) { + [NSException raise:NSInternalInconsistencyException + format:@"Cannot cancel a completed task."]; + } + } +} + +- (BOOL)trySetCancelled { + @synchronized(self.lock) { + if (self.completed) { + return NO; + } + self.completed = YES; + self.cancelled = YES; + [self runContinuations]; + return YES; + } +} + +- (BOOL)isCompleted { + @synchronized(self.lock) { + return _completed; + } +} + +- (void)setCompleted { + @synchronized(self.lock) { + _completed = YES; + } +} + +- (void)runContinuations { + @synchronized(self.lock) { + [self.condition lock]; + [self.condition broadcast]; + [self.condition unlock]; + for (void (^callback)() in self.callbacks) { + callback(); + } + [self.callbacks removeAllObjects]; + } +} + +#pragma mark - Chaining methods + +- (instancetype)continueWithExecutor:(BFExecutor *)executor + withBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:executor block:block cancellationToken:nil]; +} + +- (instancetype)continueWithExecutor:(BFExecutor *)executor + block:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + + // Capture all of the state that needs to used when the continuation is complete. + void (^wrappedBlock)() = ^() { + [executor execute:^{ + if (cancellationToken.cancellationRequested) { + [tcs cancel]; + return; + } + + id result = nil; + @try { + result = block(self); + } @catch (NSException *exception) { + tcs.exception = exception; + return; + } + + if ([result isKindOfClass:[BFTask class]]) { + + id (^setupWithTask) (BFTask *) = ^id(BFTask *task) { + if (cancellationToken.cancellationRequested || task.cancelled) { + [tcs cancel]; + } else if (task.exception) { + tcs.exception = task.exception; + } else if (task.error) { + tcs.error = task.error; + } else { + tcs.result = task.result; + } + return nil; + }; + + BFTask *resultTask = (BFTask *)result; + + if (resultTask.completed) { + setupWithTask(resultTask); + } else { + [resultTask continueWithBlock:setupWithTask]; + } + + } else { + tcs.result = result; + } + }]; + }; + + BOOL completed; + @synchronized(self.lock) { + completed = self.completed; + if (!completed) { + [self.callbacks addObject:[wrappedBlock copy]]; + } + } + if (completed) { + wrappedBlock(); + } + + return tcs.task; +} + +- (instancetype)continueWithBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:nil]; +} + +- (instancetype)continueWithBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken { + return [self continueWithExecutor:[BFExecutor defaultExecutor] block:block cancellationToken:cancellationToken]; +} + +- (instancetype)continueWithExecutor:(BFExecutor *)executor + withSuccessBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:executor successBlock:block cancellationToken:nil]; +} + +- (instancetype)continueWithExecutor:(BFExecutor *)executor + successBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + return [self continueWithExecutor:executor block:^id(BFTask *task) { + if (task.faulted || task.cancelled) { + return task; + } else { + return block(task); + } + } cancellationToken:cancellationToken]; +} + +- (instancetype)continueWithSuccessBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:nil]; +} + +- (instancetype)continueWithSuccessBlock:(BFContinuationBlock)block + cancellationToken:(BFCancellationToken *)cancellationToken { + return [self continueWithExecutor:[BFExecutor defaultExecutor] successBlock:block cancellationToken:cancellationToken]; +} + +#pragma mark - Syncing Task (Avoid it) + +- (void)warnOperationOnMainThread { + warnBlockingOperationOnMainThread(); +} + +- (void)waitUntilFinished { + if ([NSThread isMainThread]) { + [self warnOperationOnMainThread]; + } + + @synchronized(self.lock) { + if (self.completed) { + return; + } + [self.condition lock]; + } + [self.condition wait]; + [self.condition unlock]; +} + +#pragma mark - NSObject + +- (NSString *)description { + // Acquire the data from the locked properties + BOOL completed; + BOOL cancelled; + BOOL faulted; + + @synchronized(self.lock) { + completed = self.completed; + cancelled = self.cancelled; + faulted = self.faulted; + } + + // Description string includes status information and, if available, the + // result since in some ways this is what a promise actually "is". + return [NSString stringWithFormat:@"<%@: %p; completed = %@; cancelled = %@; faulted = %@;%@>", + NSStringFromClass([self class]), + self, + completed ? @"YES" : @"NO", + cancelled ? @"YES" : @"NO", + faulted ? @"YES" : @"NO", + completed ? [NSString stringWithFormat:@" result:%@", _result] : @""]; +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.h b/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.h new file mode 100644 index 0000000..23366c1 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +#import + +@class BFTask BF_GENERIC(BFGenericType); + +/*! + A BFTaskCompletionSource represents the producer side of tasks. + It is a task that also has methods for changing the state of the + task by settings its completion values. + */ +@interface BFTaskCompletionSource BF_GENERIC(__covariant BFGenericType) : NSObject + +/*! + Creates a new unfinished task. + */ ++ (instancetype)taskCompletionSource; + +/*! + The task associated with this TaskCompletionSource. + */ +@property (nonatomic, strong, readonly) BFTask BF_GENERIC(BFGenericType) *task; + +/*! + Completes the task by setting the result. + Attempting to set this for a completed task will raise an exception. + @param result The result of the task. + */ +- (void)setResult:(BFGenericType)result; + +/*! + Completes the task by setting the error. + Attempting to set this for a completed task will raise an exception. + @param error The error for the task. + */ +- (void)setError:(NSError *)error; + +/*! + Completes the task by setting an exception. + Attempting to set this for a completed task will raise an exception. + @param exception The exception for the task. + */ +- (void)setException:(NSException *)exception; + +/*! + Completes the task by marking it as cancelled. + Attempting to set this for a completed task will raise an exception. + */ +- (void)cancel; + +/*! + Sets the result of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetResult:(BFGenericType)result; + +/*! + Sets the error of the task if it wasn't already completed. + @param error The error for the task. + @returns whether the new value was set. + */ +- (BOOL)trySetError:(NSError *)error; + +/*! + Sets the exception of the task if it wasn't already completed. + @param exception The exception for the task. + @returns whether the new value was set. + */ +- (BOOL)trySetException:(NSException *)exception; + +/*! + Sets the cancellation state of the task if it wasn't already completed. + @returns whether the new value was set. + */ +- (BOOL)trySetCancelled; + +@end diff --git a/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.m b/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.m new file mode 100644 index 0000000..bd66835 --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BFTaskCompletionSource.m @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "BFTaskCompletionSource.h" + +#import "BFTask.h" + +@interface BFTaskCompletionSource () + +@property (nonatomic, strong, readwrite) BFTask *task; + +@end + +@interface BFTask (BFTaskCompletionSource) + +- (void)setResult:(id)result; +- (void)setError:(NSError *)error; +- (void)setException:(NSException *)exception; +- (void)cancel; +- (BOOL)trySetResult:(id)result; +- (BOOL)trySetError:(NSError *)error; +- (BOOL)trySetException:(NSException *)exception; +- (BOOL)trySetCancelled; + +@end + +@implementation BFTaskCompletionSource + +#pragma mark - Initializer + ++ (instancetype)taskCompletionSource { + return [[self alloc] init]; +} + +- (instancetype)init { + if (self = [super init]) { + _task = [[BFTask alloc] init]; + } + return self; +} + +#pragma mark - Custom Setters/Getters + +- (void)setResult:(id)result { + [self.task setResult:result]; +} + +- (void)setError:(NSError *)error { + [self.task setError:error]; +} + +- (void)setException:(NSException *)exception { + [self.task setException:exception]; +} + +- (void)cancel { + [self.task cancel]; +} + +- (BOOL)trySetResult:(id)result { + return [self.task trySetResult:result]; +} + +- (BOOL)trySetError:(NSError *)error { + return [self.task trySetError:error]; +} + +- (BOOL)trySetException:(NSException *)exception { + return [self.task trySetException:exception]; +} + +- (BOOL)trySetCancelled { + return [self.task trySetCancelled]; +} + +@end diff --git a/Pods/Bolts/Bolts/Common/Bolts.h b/Pods/Bolts/Bolts/Common/Bolts.h new file mode 100644 index 0000000..35d6c7c --- /dev/null +++ b/Pods/Bolts/Bolts/Common/Bolts.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import +#import +#import +#import +#import +#import +#import +#import + +#if __has_include() && TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV +#import +#import +#import +#import +#import +#import +#import +#import +#import +#endif + +/*! @abstract 80175001: There were multiple errors. */ +extern NSInteger const kBFMultipleErrorsError; + +@interface Bolts : NSObject + +/*! + Returns the version of the Bolts Framework as an NSString. + @returns The NSString representation of the current version. + */ ++ (NSString *)version; + +@end diff --git a/Pods/Bolts/Bolts/Common/Bolts.m b/Pods/Bolts/Bolts/Common/Bolts.m new file mode 100644 index 0000000..9a3e75c --- /dev/null +++ b/Pods/Bolts/Bolts/Common/Bolts.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "Bolts.h" + +NSInteger const kBFMultipleErrorsError = 80175001; + +@implementation Bolts + ++ (NSString *)version { + return BOLTS_VERSION; +} + +@end diff --git a/Pods/Bolts/Bolts/Common/BoltsVersion.h b/Pods/Bolts/Bolts/Common/BoltsVersion.h new file mode 100644 index 0000000..afb6fcb --- /dev/null +++ b/Pods/Bolts/Bolts/Common/BoltsVersion.h @@ -0,0 +1 @@ +#define BOLTS_VERSION @"1.3.0" diff --git a/Pods/Bolts/LICENSE b/Pods/Bolts/LICENSE new file mode 100644 index 0000000..e1a5831 --- /dev/null +++ b/Pods/Bolts/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For Bolts software + +Copyright (c) 2013-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Pods/Bolts/README.md b/Pods/Bolts/README.md new file mode 100644 index 0000000..003a2ea --- /dev/null +++ b/Pods/Bolts/README.md @@ -0,0 +1,682 @@ +Bolts +============ +[![Build Status](http://img.shields.io/travis/BoltsFramework/Bolts-iOS/master.svg?style=flat)](https://travis-ci.org/BoltsFramework/Bolts-iOS) +[![Coverage Status](https://codecov.io/github/BoltsFramework/Bolts-iOS/coverage.svg?branch=master)](https://codecov.io/github/BoltsFramework/Bolts-iOS?branch=master) +[![Pod Version](http://img.shields.io/cocoapods/v/Bolts.svg?style=flat)](http://cocoadocs.org/docsets/Bolts/) +[![Pod Platform](http://img.shields.io/cocoapods/p/Bolts.svg?style=flat)](http://cocoadocs.org/docsets/Bolts/) +[![Pod License](http://img.shields.io/cocoapods/l/Bolts.svg?style=flat)](https://github.com/BoltsFramework/Bolts-iOS/blob/master/LICENSE) +[![Dependency Status](https://www.versioneye.com/objective-c/bolts/1.1.1/badge.svg?style=flat)](https://www.versioneye.com/objective-c/bolts) +[![Reference Status](https://www.versioneye.com/objective-c/bolts/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/bolts/references) + +Bolts is a collection of low-level libraries designed to make developing mobile +apps easier. Bolts was designed by Parse and Facebook for our own internal use, +and we have decided to open source these libraries to make them available to +others. Using these libraries does not require using any Parse services. Nor +do they require having a Parse or Facebook developer account. + +Bolts includes: + +* "Tasks", which make organization of complex asynchronous code more manageable. A task is kind of like a JavaScript Promise, but available for iOS and Android. +* An implementation of the [App Links protocol](http://www.applinks.org), helping you link to content in other apps and handle incoming deep-links. + +For more information, see the [Bolts iOS API Reference](http://boltsframework.github.io/docs/ios/). + +# Tasks + +To build a truly responsive iOS application, you must keep long-running operations off of the UI thread, and be careful to avoid blocking anything the UI thread might be waiting on. This means you will need to execute various operations in the background. To make this easier, we've added a class called `BFTask`. A task represents the result of an asynchronous operation. Typically, a `BFTask` is returned from an asynchronous function and gives the ability to continue processing the result of the task. When a task is returned from a function, it's already begun doing its job. A task is not tied to a particular threading model: it represents the work being done, not where it is executing. Tasks have many advantages over other methods of asynchronous programming, such as callbacks. `BFTask` is not a replacement for `NSOperation` or GCD. In fact, they play well together. But tasks do fill in some gaps that those technologies don't address. +* `BFTask` takes care of managing dependencies for you. Unlike using `NSOperation` for dependency management, you don't have to declare all dependencies before starting a `BFTask`. For example, imagine you need to save a set of objects and each one may or may not require saving child objects. With an `NSOperation`, you would normally have to create operations for each of the child saves ahead of time. But you don't always know before you start the work whether that's going to be necessary. That can make managing dependencies with `NSOperation` very painful. Even in the best case, you have to create your dependencies before the operations that depend on them, which results in code that appears in a different order than it executes. With `BFTask`, you can decide during your operation's work whether there will be subtasks and return the other task in just those cases. +* `BFTasks` release their dependencies. `NSOperation` strongly retains its dependencies, so if you have a queue of ordered operations and sequence them using dependencies, you have a leak, because every operation gets retained forever. `BFTasks` release their callbacks as soon as they are run, so everything cleans up after itself. This can reduce memory use, and simplify memory management. +* `BFTasks` keep track of the state of finished tasks: It tracks whether there was a returned value, the task was cancelled, or if an error occurred. It also has convenience methods for propagating errors. With `NSOperation`, you have to build all of this stuff yourself. +* `BFTasks` don't depend on any particular threading model. So it's easy to have some tasks perform their work with an operation queue, while others perform work using blocks with GCD. These tasks can depend on each other seamlessly. +* Performing several tasks in a row will not create nested "pyramid" code as you would get when using only callbacks. +* `BFTasks` are fully composable, allowing you to perform branching, parallelism, and complex error handling, without the spaghetti code of having many named callbacks. +* You can arrange task-based code in the order that it executes, rather than having to split your logic across scattered callback functions. + +For the examples in this doc, assume there are async versions of some common Parse methods, called `saveAsync:` and `findAsync:` which return a `Task`. In a later section, we'll show how to define these functions yourself. + +## The `continueWithBlock` Method + +Every `BFTask` has a method named `continueWithBlock:` which takes a continuation block. A continuation is a block that will be executed when the task is complete. You can then inspect the task to check if it was successful and to get its result. + +```objective-c +// Objective-C +[[self saveAsync:obj] continueWithBlock:^id(BFTask *task) { + if (task.isCancelled) { + // the save was cancelled. + } else if (task.error) { + // the save failed. + } else { + // the object was saved successfully. + PFObject *object = task.result; + } + return nil; +}]; +``` + +```swift +// Swift +self.saveAsync(obj).continueWithBlock { + (task: BFTask!) -> BFTask in + if task.isCancelled() { + // the save was cancelled. + } else if task.error() { + // the save failed. + } else { + // the object was saved successfully. + var object = task.result() as PFObject + } +} +``` + +BFTasks use Objective-C blocks, so the syntax should be pretty straightforward. Let's look closer at the types involved with an example. + +```objective-c +// Objective-C +/** + * Gets an NSString asynchronously. + */ +- (BFTask *)getStringAsync { + // Let's suppose getNumberAsync returns a BFTask whose result is an NSNumber. + return [[self getNumberAsync] continueWithBlock:^id(BFTask *task) { + // This continuation block takes the NSNumber BFTask as input, + // and provides an NSString as output. + + NSNumber *number = task.result; + return [NSString stringWithFormat:@"%@", number]; + )]; +} +``` + +```swift +// Swift +/** + * Gets an NSString asynchronously. + */ +func getStringAsync() -> BFTask { + //Let's suppose getNumberAsync returns a BFTask whose result is an NSNumber. + return self.getNumberAsync().continueWithBlock { + (task: BFTask!) -> NSString in + // This continuation block takes the NSNumber BFTask as input, + // and provides an NSString as output. + + let number = task.result() as NSNumber + return NSString(format:"%@", number) + } +} +``` + +In many cases, you only want to do more work if the previous task was successful, and propagate any errors or cancellations to be dealt with later. To do this, use the `continueWithSuccessBlock:` method instead of `continueWithBlock:`. + +```objective-c +// Objective-C +[[self saveAsync:obj] continueWithSuccessBlock:^id(BFTask *task) { + // the object was saved successfully. + return nil; +}]; +``` + +```swift +// Swift +self.saveAsync(obj).continueWithSuccessBlock { + (task: BFTask!) -> AnyObject! in + // the object was saved successfully. + return nil +} +``` + +## Chaining Tasks Together + +BFTasks are a little bit magical, in that they let you chain them without nesting. If you return a BFTask from `continueWithBlock:`, then the task returned by `continueWithBlock:` will not be considered finished until the new task returned from the new continuation block. This lets you perform multiple actions without incurring the pyramid code you would get with callbacks. Likewise, you can return a `BFTask` from `continueWithSuccessBlock:`. So, return a `BFTask` to do more asynchronous work. + +```objective-c +// Objective-C +PFQuery *query = [PFQuery queryWithClassName:@"Student"]; +[query orderByDescending:@"gpa"]; +[[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *students = task.result; + PFObject *valedictorian = [students objectAtIndex:0]; + [valedictorian setObject:@YES forKey:@"valedictorian"]; + return [self saveAsync:valedictorian]; +}] continueWithSuccessBlock:^id(BFTask *task) { + PFObject *valedictorian = task.result; + return [self findAsync:query]; +}] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *students = task.result; + PFObject *salutatorian = [students objectAtIndex:1]; + [salutatorian setObject:@YES forKey:@"salutatorian"]; + return [self saveAsync:salutatorian]; +}] continueWithSuccessBlock:^id(BFTask *task) { + // Everything is done! + return nil; +}]; +``` + +```swift +// Swift +var query = PFQuery(className:"Student") +query.orderByDescending("gpa") +findAsync(query).continueWithSuccessBlock { + (task: BFTask!) -> BFTask in + let students = task.result() as NSArray + var valedictorian = students.objectAtIndex(0) as PFObject + valedictorian["valedictorian"] = true + return self.saveAsync(valedictorian) +}.continueWithSuccessBlock { + (task: BFTask!) -> BFTask in + var valedictorian = task.result() as PFObject + return self.findAsync(query) +}.continueWithSuccessBlock { + (task: BFTask!) -> BFTask in + let students = task.result() as NSArray + var salutatorian = students.objectAtIndex(1) as PFObject + salutatorian["salutatorian"] = true + return self.saveAsync(salutatorian) +}.continueWithSuccessBlock { + (task: BFTask!) -> AnyObject! in + // Everything is done! + return nil +} +``` + +## Error Handling + +By carefully choosing whether to call `continueWithBlock:` or `continueWithSuccessBlock:`, you can control how errors are propagated in your application. Using `continueWithBlock:` lets you handle errors by transforming them or dealing with them. You can think of failed tasks kind of like throwing an exception. In fact, if you throw an exception inside a continuation, the resulting task will be faulted with that exception. + +```objective-c +// Objective-C +PFQuery *query = [PFQuery queryWithClassName:@"Student"]; +[query orderByDescending:@"gpa"]; +[[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *students = task.result; + PFObject *valedictorian = [students objectAtIndex:0]; + [valedictorian setObject:@YES forKey:@"valedictorian"]; + // Force this callback to fail. + return [BFTask taskWithError:[NSError errorWithDomain:@"example.com" + code:-1 + userInfo:nil]]; +}] continueWithSuccessBlock:^id(BFTask *task) { + // Now this continuation will be skipped. + PFQuery *valedictorian = task.result; + return [self findAsync:query]; +}] continueWithBlock:^id(BFTask *task) { + if (task.error) { + // This error handler WILL be called. + // The error will be the NSError returned above. + // Let's handle the error by returning a new value. + // The task will be completed with nil as its value. + return nil; + } + // This will also be skipped. + NSArray *students = task.result; + PFObject *salutatorian = [students objectAtIndex:1]; + [salutatorian setObject:@YES forKey:@"salutatorian"]; + return [self saveAsync:salutatorian]; +}] continueWithSuccessBlock:^id(BFTask *task) { + // Everything is done! This gets called. + // The task's result is nil. + return nil; +}]; +``` + +```swift +// Swift +var query = PFQuery(className:"Student") +query.orderByDescending("gpa") +findAsync(query).continueWithSuccessBlock { + (task: BFTask!) -> BFTask in + let students = task.result() as NSArray + var valedictorian = students.objectAtIndex(0) as PFObject + valedictorian["valedictorian"] = true + //Force this callback to fail. + return BFTask(error:NSError(domain:"example.com", + code:-1, userInfo: nil)) +}.continueWithSuccessBlock { + (task: BFTask!) -> AnyObject! in + //Now this continuation will be skipped. + var valedictorian = task.result() as PFObject + return self.findAsync(query) +}.continueWithBlock { + (task: BFTask!) -> AnyObject! in + if task.error() { + // This error handler WILL be called. + // The error will be the NSError returned above. + // Let's handle the error by returning a new value. + // The task will be completed with nil as its value. + return nil + } + // This will also be skipped. + let students = task.result() as NSArray + var salutatorian = students.objectAtIndex(1) as PFObject + salutatorian["salutatorian"] = true + return self.saveAsync(salutatorian) +}.continueWithSuccessBlock { + (task: BFTask!) -> AnyObject! in + // Everything is done! This gets called. + // The tasks result is nil. + return nil +} +``` + +It's often convenient to have a long chain of success callbacks with only one error handler at the end. + +## Creating Tasks + +When you're getting started, you can just use the tasks returned from methods like `findAsync:` or `saveAsync:`. However, for more advanced scenarios, you may want to make your own tasks. To do that, you create a `BFTaskCompletionSource`. This object will let you create a new `BFTask`, and control whether it gets marked as finished or cancelled. After you create a `BFTask`, you'll need to call `setResult:`, `setError:`, or `cancel` to trigger its continuations. + +```objective-c +// Objective-C +- (BFTask *)successAsync { + BFTaskCompletionSource *successful = [BFTaskCompletionSource taskCompletionSource]; + [successful setResult:@"The good result."]; + return successful.task; +} + +- (BFTask *)failAsync { + BFTaskCompletionSource *failed = [BFTaskCompletionSource taskCompletionSource]; + [failed setError:[NSError errorWithDomain:@"example.com" code:-1 userInfo:nil]]; + return failed.task; +} +``` + +```swift +// Swift +func successAsync() -> BFTask { + var successful = BFTaskCompletionSource() + successful.setResult("The good result.") + return successful.task +} + +func failAsync() -> BFTask { + var failed = BFTaskCompletionSource() + failed.setError(NSError(domain:"example.com", code:-1, userInfo:nil)) + return failed.task +} +``` + +If you know the result of a task at the time it is created, there are some convenience methods you can use. + +```objective-c +// Objective-C +BFTask *successful = [BFTask taskWithResult:@"The good result."]; + +BFTask *failed = [BFTask taskWithError:anError]; +``` + +```swift +// Swift +let successful = BFTask(result:"The good result") + +let failed = BFTask(error:anError) +``` + +## Creating Async Methods + +With these tools, it's easy to make your own asynchronous functions that return tasks. For example, you can make a task-based version of `fetchAsync:` easily. + +```objective-c +// Objective-C +- (BFTask *) fetchAsync:(PFObject *)object { + BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource]; + [object fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) { + if (!error) { + [task setResult:object]; + } else { + [task setError:error]; + } + }]; + return task.task; +} +``` + +```swift +// Swift +func fetchAsync(object: PFObject) -> BFTask { + var task = BFTaskCompletionSource() + object.fetchInBackgroundWithBlock { + (object: PFObject?, error: NSError?) -> Void in + if error == nil { + task.setResult(object) + } else { + task.setError(error) + } + } + return task.task +} + +``` + +It's similarly easy to create `saveAsync:`, `findAsync:` or `deleteAsync:`. + +## Tasks in Series + +`BFTasks` are convenient when you want to do a series of tasks in a row, each one waiting for the previous to finish. For example, imagine you want to delete all of the comments on your blog. + +```objective-c +// Objective-C +PFQuery *query = [PFQuery queryWithClassName:@"Comments"]; +[query whereKey:@"post" equalTo:@123]; + +[[[self findAsync:query] continueWithBlock:^id(BFTask *task) { + NSArray *results = task.result; + + // Create a trivial completed task as a base case. + BFTask *task = [BFTask taskWithResult:nil]; + for (PFObject *result in results) { + // For each item, extend the task with a function to delete the item. + task = [task continueWithBlock:^id(BFTask *task) { + // Return a task that will be marked as completed when the delete is finished. + return [self deleteAsync:result]; + }]; + } + return task; +}] continueWithBlock:^id(BFTask *task) { + // Every comment was deleted. + return nil; +}]; +``` + +```swift +// Swift +var query = PFQuery(className:"Comments") +query.whereKey("post", equalTo:123) +findAsync(query).continueWithBlock { + (task: BFTask!) -> BFTask in + let results = task.result() as NSArray + + // Create a trivial completed task as a base case. + let task = BFTask(result:nil) + for result : PFObject in results { + // For each item, extend the task with a function to delete the item. + task = task.continueWithBlock { + (task: BFTask!) -> BFTask in + return self.deleteAsync(result) + } + } + return task +}.continueWithBlock { + (task: BFTask!) -> AnyObject! in + // Every comment was deleted. + return nil +} +``` + +## Tasks in Parallel + +You can also perform several tasks in parallel, using the `taskForCompletionOfAllTasks:` method. You can start multiple operations at once, and use `taskForCompletionOfAllTasks:` to create a new task that will be marked as completed when all of its input tasks are completed. The new task will be successful only if all of the passed-in tasks succeed. Performing operations in parallel will be faster than doing them serially, but may consume more system resources and bandwidth. + +```objective-c +// Objective-C +PFQuery *query = [PFQuery queryWithClassName:@"Comments"]; +[query whereKey:@"post" equalTo:@123]; + +[[[self findAsync:query] continueWithBlock:^id(BFTask *results) { + // Collect one task for each delete into an array. + NSMutableArray *tasks = [NSMutableArray array]; + for (PFObject *result in results) { + // Start this delete immediately and add its task to the list. + [tasks addObject:[self deleteAsync:result]]; + } + // Return a new task that will be marked as completed when all of the deletes are + // finished. + return [BFTask taskForCompletionOfAllTasks:tasks]; +}] continueWithBlock:^id(BFTask *task) { + // Every comment was deleted. + return nil; +}]; +``` + +```swift +// Swift +var query = PFQuery(className:"Comments") +query.whereKey("post", equalTo:123) + +findAsync(query).continueWithBlock { + (task: BFTask!) -> BFTask in + // Collect one task for each delete into an array. + var tasks = NSMutableArray.array() + var results = task.result() as NSArray + for result : PFObject! in results { + // Start this delete immediately and add its task to the list. + tasks.addObject(self.deleteAsync(result)) + } + // Return a new task that will be marked as completed when all of the deletes + // are finished. + return BFTask(forCompletionOfAllTasks:tasks) +}.continueWithBlock { + (task: BFTask!) -> AnyObject! in + // Every comment was deleted. + return nil +} +``` + +## Task Executors + +Both `continueWithBlock:` and `continueWithSuccessBlock:` methods have another form that takes an instance of `BFExecutor`. These are `continueWithExecutor:withBlock:` and `continueWithExecutor:withSuccessBlock:`. These methods allow you to control how the continuation is executed. The default executor will dispatch to GCD, but you can provide your own executor to schedule work onto a different thread. For example, if you want to continue with work on the UI thread: + +```objective-c +// Create a BFExecutor that uses the main thread. +BFExecutor *myExecutor = [BFExecutor executorWithBlock:^void(void(^block)()) { + dispatch_async(dispatch_get_main_queue(), block); +}]; + +// And use the Main Thread Executor like this. The executor applies only to the new +// continuation being passed into continueWithBlock. +[[self fetchAsync:object] continueWithExecutor:myExecutor withBlock:^id(BFTask *task) { + myTextView.text = [object objectForKey:@"name"]; +}]; +``` + +For common cases, such as dispatching on the main thread, we have provided default implementations of `BFExecutor`. These include `defaultExecutor`, `immediateExecutor`, `mainThreadExecutor`, `executorWithDispatchQueue:`, and `executorWithOperationQueue:`. For example: + +```objective-c +// Continue on the Main Thread, using a built-in executor. +[[self fetchAsync:object] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) { + myTextView.text = [object objectForKey:@"name"]; +}]; +``` + +## Task Cancellation + +It's generally bad design to keep track of the `BFTaskCompletionSource` for cancellation. A better model is to create a "cancellation token" at the top level, and pass that to each async function that you want to be part of the same "cancelable operation". Then, in your continuation blocks, you can check whether the cancellation token has been cancelled and bail out early by returning a `[BFTask cancelledTask]`. For example: + +```objective-c +- (void)doSomethingComplicatedAsync:(MYCancellationToken *)cancellationToken { + [[self doSomethingAsync:cancellationToken] continueWithBlock:^{ + if (cancellationToken.isCancelled) { + return [BFTask cancelledTask]; + } + // Do something that takes a while. + return result; + }]; +} + +// Somewhere else. +MYCancellationToken *cancellationToken = [[MYCancellationToken alloc] init]; +[obj doSomethingComplicatedAsync:cancellationToken]; + +// When you get bored... +[cancellationToken cancel]; +``` + +**Note:** The cancellation token implementation should be thread-safe. +We are likely to add some concept like this to Bolts at some point in the future. + +# App Links + +[App Links](http://www.applinks.org) provide a cross-platform mechanism that allows a developer to define and publish a deep-linking scheme for their content, allowing other apps to link directly to an experience optimized for the device they are running on. Whether you are building an app that receives incoming links or one that may link out to other apps' content, Bolts provides tools to simplify implementation of the [App Links protocol](http://www.applinks.org/documentation). + +## Handling an App Link + +The most common case will be making your app receive App Links. In-linking will allow your users to quickly access the richest, most native-feeling presentation of linked content on their devices. Bolts makes it easy to handle an inbound App Link (as well as general inbound deep-links) by providing utilities for processing an incoming URL. + +For example, you can use the `BFURL` utility class to parse an incoming URL in your `AppDelegate`: + +```objective-c +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { + BFURL *parsedUrl = [BFURL URLWithInboundURL:url sourceApplication:sourceApplication]; + + // Use the target URL from the App Link to locate content. + if ([parsedUrl.targetURL.pathComponents[1] isEqualToString:@"profiles"]) { + // Open a profile viewer. + } + + // You can also check the query string easily. + NSString *query = parsedUrl.targetQueryParameters[@"query"]; + + // Apps that have existing deep-linking support and map their App Links to existing + // deep-linking functionality may instead want to perform these operations on the input URL. + // Use the target URL from the App Link to locate content. + if ([parsedUrl.inputURL.pathComponents[1] isEqualToString:@"profiles"]) { + // Open a profile viewer. + } + + // You can also check the query string easily. + NSString *query = parsedUrl.inputQueryParameters[@"query"]; + + // Apps can easily check the Extras and App Link data from the App Link as well. + NSString *fbAccessToken = parsedUrl.appLinkExtras[@"fb_access_token"]; + NSDictionary *refererData = parsedUrl.appLinkExtras[@"referer"]; +} +``` + +## Navigating to a URL + +Following an App Link allows your app to provide the best user experience (as defined by the receiving app) when a user navigates to a link. Bolts makes this process simple, automating the steps required to follow a link: + +1. Resolve the App Link by getting the App Link metadata from the HTML at the URL specified. +2. Step through App Link targets relevant to the device being used, checking whether the app that can handle the target is present on the device. +3. If an app is present, build a URL with the appropriate al_applink_data specified and navigate to that URL. +4. Otherwise, open the browser with the original URL specified. + +In the simplest case, it takes just one line of code to navigate to a URL that may have an App Link: + +```objective-c +[BFAppLinkNavigation navigateToURLInBackground:url]; +``` + +### Adding App and Navigation Data + +Under most circumstances, the data that will need to be passed along to an app during a navigation will be contained in the URL itself, so that whether or not the app is actually installed on the device, users are taken to the correct content. Occasionally, however, apps will want to pass along data that is relevant for app-to-app navigation, or will want to augment the App Link protocol with information that might be used by the app to adjust how the app should behave (e.g. showing a link back to the referring app). + +If you want to take advantage of these features, you can break apart the navigation process. First, you must have an App Link to which you wish to navigate: + +```objective-c +[[BFAppLinkNavigation resolveAppLinkInBackground:url] continueWithSuccessBlock:^id(BFTask *task) { + BFAppLink *link = task.result; +}]; +``` + +Then, you can build an App Link request with any additional data you would like and navigate: + +```objective-c +BFAppLinkNavigation *navigation = [BFAppLinkNavigation navigationWithAppLink:link + extras:@{ @"access_token": @"t0kEn" } + appLinkData:@{ @"ref": @"12345" }]; +NSError *error = nil; +[navigation navigate:&error]; +``` + +### Resolving App Link Metadata + +Bolts allows for custom App Link resolution, which may be used as a performance optimization (e.g. caching the metadata) or as a mechanism to allow developers to use a centralized index for obtaining App Link metadata. A custom App Link resolver just needs to be able to take a URL and return a `BFAppLink` containing the ordered list of `BFAppLinkTarget`s that are applicable for this device. Bolts provides one of these out of the box that performs this resolution on the device using a hidden UIWebView. + +You can use any resolver that implements the `BFAppLinkResolving` protocol by using one of the overloads on `BFAppLinkNavigation`: + +```objective-c +[BFAppLinkNavigation navigateToURLInBackground:url + resolver:resolver]; +``` + +Alternatively, a you can swap out the default resolver to be used by the built-in APIs: + +```objective-c +[BFAppLinkNavigation setDefaultResolver:resolver]; +[BFAppLinkNavigation navigateToURLInBackground:url]; +``` + +## App Link Return-to-Referer View + +When an application is opened via an App Link, a banner allowing the user to "Touch to return to " should be displayed. The `BFAppLinkReturnToRefererView` provides this functionality. It will take an incoming App Link and parse the referer information to display the appropriate calling app name. + +```objective-c +- (void)viewDidLoad { + [super viewDidLoad]; + + // Perform other view initialization. + + self.returnToRefererController = [[BFAppLinkReturnToRefererController alloc] init]; + + // self.returnToRefererView is a BFAppLinkReturnToRefererView. + // You may initialize the view either by loading it from a NIB or programmatically. + self.returnToRefererController.view = self.returnToRefererView; + + // If you have a UINavigationController in the view, then the bar must be shown above it. + [self.returnToRefererController] +} +``` + +The following code assumes that the view controller has an `openedAppLinkURL` `NSURL` property that has already been populated with the URL used to open the app. You can then do something like this to show the view: + +```objective-c +- (void)viewWillAppear { + [super viewWillAppear]; + + // Show only if you have a back AppLink. + [self.returnToRefererController showViewForRefererURL:self.openedAppLinkURL]; +} +``` + +In a navigaton-controller view hierarchy, the banner should be displayed above the navigation bar, and `BFAppLinkReturnToRefererController` provides an `initForDisplayAboveNavController` method to assist with this. + +## Analytics + +Bolts introduces Measurement Event. App Links posts three different Measurement Event notifications to the application, which can be caught and integrated with existing analytics components in your application. + +* `al_nav_out` — Raised when your app switches out to an App Links URL. +* `al_nav_in` — Raised when your app opens an incoming App Links URL. +* `al_ref_back_out` — Raised when your app returns back the referrer app using the built-in top navigation back bar view. + +### Listen for App Links Measurement Events + +There are other analytics tools that are integrated with Bolts' App Links events, but you can also listen for these events yourself: + +```objective-c +[[NSNotificationCenter defaultCenter] addObserverForName:BFMeasurementEventNotificationName object:nil queue:nil usingBlock:^(NSNotification *note) { + NSDictionary *event = note.userInfo; + NSDictionary *eventData = event[BFMeasurementEventArgsKey]; + // Integrate to your logging/analytics component. +}]; +``` + +### App Links Event Fields + +App Links Measurement Events sends additional information from App Links Intents in flattened string key value pairs. Here are some of the useful fields for the three events. + +* `al_nav_in` + * `inputURL`: the URL that opens the app. + * `inputURLScheme`: the scheme of `inputURL`. + * `refererURL`: the URL that the referrer app added into `al_applink_data`: `referer_app_link`. + * `refererAppName`: the app name that the referrer app added to `al_applink_data`: `referer_app_link`. + * `sourceApplication`: the bundle of referrer application. + * `targetURL`: the `target_url` field in `al_applink_data`. + * `version`: App Links API version. + +* `al_nav_out` / `al_ref_back_out` + * `outputURL`: the URL used to open the other app (or browser). If there is an eligible app to open, this will be the custom scheme url/intent in `al_applink_data`. + * `outputURLScheme`: the scheme of `outputURL`. + * `sourceURL`: the URL of the page hosting App Links meta tags. + * `sourceURLHost`: the hostname of `sourceURL`. + * `success`: `“1”` to indicate success in opening the App Link in another app or browser; `“0”` to indicate failure to open the App Link. + * `type`: `“app”` for open in app, `“web”` for open in browser; `“fail”` when the success field is `“0”`. + * `version`: App Links API version. + +# Installation + +You can download the latest framework files from our [Releases page](https://github.com/BoltsFramework/Bolts-iOS/releases). + +Bolts is also available through [CocoaPods](http://cocoapods.org). To install it simply add the following line to your Podfile: + + pod 'Bolts' diff --git a/Pods/GoogleMaps/CHANGELOG b/Pods/GoogleMaps/CHANGELOG new file mode 100644 index 0000000..ab22e10 --- /dev/null +++ b/Pods/GoogleMaps/CHANGELOG @@ -0,0 +1,477 @@ +Version 1.10.4 - October 2015 +============================= +Resolved Issues: + - Fixed a crash on iOS 9 when dismissing the place picker without a selection. + - Fixed a crash when using both a GMSMapView and a UIWebView or WKWebView in the view + hierarchy at the same time. + - Recompiled with Xcode 7 to avoid raising failed to load optimized model log messages + on iOS 9 devices. + +Version 1.10.3 - September 2015 +=============================== +Features: + - Google logos have been updated. + +Resolved Issues: + - Framework now ships with the device version of bundles to pass Xcode 7 archive checks. + +Version 1.10.2 - August 2015 +============================ +Resolved Issues: + - Fixed a crash releasing a map view while in background. + - Resolved a conflict with apps using gtm-session-fetcher resumable downloads. + - Recompiled with Xcode 6.4 to avoid some bugs in Xcode 6.3 compiler. + - Updated GoogleMaps.bundle info.plist to avoid triggering new checks in + pre-submission verification. + +Version 1.10.1 - June 2015 +========================== +Resolved Issues: + - Fixed an issue where instantiating GMSPlacesClient triggered a request to enable Bluetooth. + - Miscellaneous improvements to the GMSPlacePicker UI. + +Version 1.10.0 - May 2015 +========================= +Major Feature: + - Places API is now bundled with the Google Maps SDK for iOS. + +Features: + - New allowScrollGesturesDuringRotateOrZoom property on GMSUISettings controls whether + the user can scroll by panning during multi-touch rotate or zoom gestures. + - GMSPanoramaView now supports being used via storyboard. + - GMSGeocoder now supports being used while the application is in the background. + - GMSServices sharedServices can now be called while application is in the background. Note + that if the first call to sharedServices is while application is in the background some + async initialization work will be deferred until the first time a map is shown where it will + be performed synchronously. + - GMSMapView/GMSPanoramaView init messages can now be handled while the application is in + background. This should remove the last case where GMSMapView/GMSPanoramaView could not + be used in the background. + - GMSMapView/GMSPanormaView delegate properties now support IBOutlet for easier use via + storyboard. + +Resolved Issues: + - mapView:didTapMyLocationButtonForMapView: is now correctly called even if no location is + available. + - GMSGroundOverlay now shows correctly when rotated if image aspect ratio doesn't match the + selected ground region. + - Fixed an issue resizing the map on iOS 8. + - Fixed a rare crash under fast camera changes. + - Map no longer hangs when adding a ground overlay with certain invalid bounds. + - Fixed a crash when texture memory is exhausted by markers. + - Correctly return the tapped GMSCircle to mapView:didTapOverlay: for tappable circles. + - mapView:idleAtCameraPosition: will now be called even if there is an ongoing update of the + my location dot. + +Notes: + - Due to an ABI change in the Xcode compiler, Xcode 6.3 is now the only supported version for + compiling against Google Maps SDK for iOS. + - The minimum target iOS version for Google Maps SDK for iOS is now 7.0. Version 6.0 is no + longer supported. + +Version 1.9.2 - February 2015 +============================= +Resolved Issues: + - Show correct characters for Myanmar place labels. + - Fixed small memory leak related to font registration. + - Fixed large memory leak in rare cases where My Location is enabled and the user rotates + the screen. + - Correctly show ground overlays defined by zoom level which extend across >180 degrees + of longitude. + - Allow selected marker to be set during mapView:didTapAtCoordinate:. + - Throw exception rather than crash when map services are initialized while application is + in background. + - Raise mapView:willMove: and mapView:idleAtCameraPosition: even for swipe motions which + last less than 30ms. + - Correctly handle animations starting while a gesture is decelerating. + - Always return an error from GMSPanoramaService callbacks if panorama is nil. + - Don't attempt to navigate to empty panorama if moveNearCoordinate: resolves to nil. + +Version 1.9.1 - December 2014 +============================= +Resolved Issues: + - Added workaround for userEmail private selector false positive. + - Improved handling of info windows for iPhone 6+ running applications in scaled mode. + +Version 1.9.0 - October 2014 +============================ +Features: + - Support for iOS 8 + - Support for iPhone 6/6+ + - Support for Swift + - UI elements have been updated for material design + +Resolved Issues: + - Fixed some memory reclamation issues + - Improved handling of application background state transition + +Notes: + ! In order to improve compatibility with Swift, two geometry library + functions have been renamed to avoid function overloading + The new names are GMSGeometryIsLocationOnPathTolerance and + GMSStyleSpansOffset + +Version 1.8.1 - May 2014 +======================== +Resolved Issues: + - Resolved GMSTileLayer not displaying + - Resolved a rare case where an app would crash when displaying polylines + while accessibility features are enabled + - mapView:willMove: is no longer called alongside a tap gesture + - Resolved symbol collisions with the Protocol Buffer library + +Version 1.8.0 - May 2014 +======================== +Resolved Issues: + - Resolved threading deadlock prominent on iPhone 4 running iOS 7.1 or later + - GMSMapView correctly releases some shared GL state previously causing + memory leak + - GMSPolyline no longer crashes in some cases where its path contained more + than 1024 segments + - The delegate method mapView:idleAtCameraPosition: is now only called once + all user gestures are complete + - The Google Maps SDK for iOS now includes fonts for languages currently + unsupported by the iOS system, such as Khmer + - These fonts may be safely removed from your GoogleMaps.framework if you + have no interest in these regions, but some text may render as "[?]" + +Version 1.7.2 - March 2014 +========================== +Resolved Issues: + - Heading will only appear on My Location dot when available + - Better reduction of colors on gradient or colored polylines at low zoom + - The search radius is now respected when retrieving a GMSPanorama object + via GMSPanoramaService and on GMSPanoramaView construction or move + - GMSPolyline is no longer grayscale on iOS 7.1 + +Version 1.7.0 - February 2014 +============================= +Features: + - Styled polylines: additional color options via GMSPolyline, including + gradients and colors per any number of polyline segments + * Each polyline may be drawn with many GMSStyleSpan instances, configuring + a unique color or gradient over an arbitrary number of segments + * Gradient or color may be specified via a GMSStrokeStyle + * GMSPath provides a helper category to determine distance along a path + * GMSStyleSpans helper to apply repeated styles along a polyline + - GMSGeocoder now provides structured addresses via GMSAddress, deprecating + GMSReverseGeocodeResult + - Added mutable version of GMSCameraPosition, GMSMutableCameraPosition + - Delegate method for user tapping the My Location button + - Added GMSMapPoint for linear interpolation between points in Mercator space + on the Earth + - My Location dot now shows compass arrow + - 3D building data at many places on the Earth + +Resolved Issues: + - GMSPolyline width is much closer to screen width + - GMSPolyline performance and memory improvements + - Reduced memory use of OpenGL textures + - Floor picker is positioned correctly when My Location button is disabled + - cameraForBounds:insets: on GMSMapView now correctly accounts for padding + +Notes: + ! To align with other Google Maps APIs, GMSMapView no longer provides helper + methods to retrieve previously added overlays, such as -markers, -polylines + and -groundOverlays + +Version 1.6.2 - January 2014 +============================ +Resolved Issues: + - Resolved a gesture bug effecting full-screen maps on iOS 7 + - Resolved an issue where overlays were sometimes not initially tappable + +Version 1.6.1 - December 2013 +============================= +Resolved Issues: + - Resolved a memory leak involving vector tiles + - Markers not immediately added to a GMSMapView no longer fail to appear + when configured at a later point + - GMSMapView/GMSPanoramaView will now continue to render while your + application is resigned + +Version 1.6.0 - November 2013 +============================= +Features: + - The Google Maps SDK for iOS now supports 64-bit architectures + - Added the ability to restrict min and max zoom on GMSMapView + - Added opacity on GMSTileLayer + - Added opacity on GMSMarker, which may be animated + ! Updated types within the SDK and used float or double instead of CGFloat + in cases where it was more appropriate + ! Core Animation on GMSMapView now requires model values to be set + +Resolved Issues: + - Marker info windows and tappable regions now rotate correctly with markers + - Padding on a GMSMapView is no longer clamped to its bounds (useful if + setting padding on an initially zero-sized map) + - Copyright information now animates alongside changing GMSMapView size or + padding + - Info windows are removed if their GMSMarker is removed from a GMSMapView + - My Location dot uses the last known information when enabled + - Resolved two rare race conditions that were causing crashes + - Resolved an issue where retain cycles were causing memory leaks on + GMSMapView and GMSPanoramaView + +Version 1.5.0 - September 2013 +============================== +Features: + ! This release officially supports iOS 7, and requires iOS 6.0 or later (iOS + 5.1 is no longer supported). + ! The 'animated' field on GMSMarker is now known as 'appearAnimation', and + may be set to kGMSMarkerAnimationNone (default) or kGMSMarkerAnimationPop + - The Google Maps SDK for iOS now ships with an armv7s slice + - New features for GMSMarker instances + * Markers can be made draggable using the draggable property, and new drag + delegate methods have been added to GMSMapViewDelegate + * Added GMSMarkerLayer, a custom CALayer subclass for GMSMarker that + supports animation of marker position and rotation + * Added support for markers that appear flat against the Earth's surface + * Added rotation property to rotate markers around their ground anchor + * The UIImage used by GMSMarker now supports the images and duration + properties, and will animate images with multiple frames + * The UIImage used by GMSMarker now supports alignmentRectInsets, and will + adjust groundAnchor, infoWindowAnchor, and the tappable region + - Added padding on GMSMapView, allowing you to indicate parts of the map that + may be obscured by other views; setting padding re-positions the standard + map controls, and the camera and camera updates will use the padded region + - GMSPanoramaView and GMSPanoramaService now support searching for panoramas + with custom radius + - Added cameraForBounds:insets: to GMSMapView, allowing construction of a + GMSCameraPosition for the map from a specified GMSCoordinateBounds + +Resolved Issues: + - My Location button now clips within GMSMapView + - Reduced memory usage of GMSMapView through less agressive tile caching + - Reduced the time taken to obtain GMSServices by moving some startup tasks + to a background thread; obtaining this object early in your application + (before creating a GMSMapView or other objects) may improve performance + - Polylines may now be drawn twice, as required, if they have very large + longitudinal span + - Resolved a rounding error with very small polygons far from latlng (0,0) + +Version 1.4.3 - August 2013 +=========================== +Resolved Issues: + - Resolved several causes of modifying markers that could cause 'ghost' + markers to appear + - Resolved excess texture use when modifying animated markers + +Version 1.4.2 - August 2013 +=========================== +Resolved Issues: + - Fixed a rare case where modifying an animated marker could cause 'ghost' + markers to appear + - Prioritized markers over other overlays for tappability + +Version 1.4.1 - August 2013 +=========================== +Features: + - Tappable markers inside GMSPanoramaView using the + panoramaView:didTapMarker: delegate method on GMSPanoramaViewDelegate + - Added GMSPanoramaLayer, a custom CALayer subclass for GMSPanoramaView that + supports animation of the panorama camera + - GMSPanoramaCamera supports custom field of view (FOV) + - Programmatic access to the floor picker allows you to enable or disable the + selector, and set which floor should be displayed + - GMSTileLayer now supports high DPI tiles, for use on a Retina device + - GMSMapView.camera is now observable via KVO + - Added fitBounds:withEdgeInsets: to GMSCameraUpdate + - The default behavior of a GMSMapView to consume all gestures within its + bounds may now be disabled via consumesGesturesInView + - Expanded GMSGeometryUtils to include additional helper methods + - GMSServices may be held by applications to maintain cache and connection to + Google; this can improve performance when creating and destroying many maps + - Improved visuals when resizing a GMSMapView via UIView animation methods + +Resolved Issues: + - Fixed crash bug during memory warning (related to indoor) + - Fixed crash bug with indoor maps on iOS 5.1 + - Performance improvements when using hundreds of GMSMarkers + - Reduced memory footprint of GMSMapView + - Touch target for GMSMarkers matches the size and shape of the marker when + the GMSMapView is tilted + - GMSMapView will no longer render a single frame of black in some cases + (noticable e.g., inside UISplitViewController on iPad) + - Street View imagery is now adjusted correctly for tilted base data + (e.g., data taken by a Street View car on a slope) + - Geodesic interpolation has been tweaked to be more correct + - Fixed incorrect GMSGroundOverlay sizing (regression in 1.4.0) + - fitBounds:withPadding: on GMSCameraUpdate now correctly applies padding to + all edges of the bounds; previously it used 1/2 padding on each edge + +Version 1.4.0 - July 2013 +========================= +Features: + - Support for Google Street View imagery, with coverage in 50+ countries + * Added GMSPanoramaView, a viewer for Street View imagery, that enables + both programmatic and user control + * GMSMarkers can be shared between GMSMapView and GMSPanoramaView + * GMSPanoramaService may be used to load panorama data ahead of display + - Indoor floor plans and a floor selector control will now be displayed when + available + - Updated map design inspired by the new Google Maps + - Info windows now show at 1:1 resolution on the screen regardless of tilt + - Additional delegate methods on GMSMapView - mapView:willMove: and + mapView:idleAtCameraPosition: - allow you to detect the start and + end of camera movement, respectively + - An improved look and feel for polylines and polygon stroke + - Added a zIndex property on all overlays; z-indexes are calculated in two + groups: GMSMarkers and all other overlays + - Added GMSGeometryUtils methods for heading, distance, offset etc. with + respect to points on the Earth + +Resolved Issues: + - Improved the tappability of GMSPolygon + - The compass now disappears when the map returns to zero bearing for any + reason, including animation + - Resolved crash issue when creating a zero-sized GMSPolygon + - Resolved an issue where active gestures could cause a GMSMapView to not + be released until deceleration completed + - Info windows no longer allow taps to pass through them + ! Accessibility elements on GMSMapView are now hidden by default; you can + enable via accessibilityElementsHidden + +Notes: + ! To align with other Google Maps APIs, GMSGroundOverlay no longer supports + the zoomLevel property. You can use the helper method + groundOverlayWithPosition:icon:zoomLevel: to migrate existing code + +Version 1.3.1 - June 2013 +========================= +Resolved Issues: + - Shows all tiles when animating across the antimeridian + - Performance improvements while zooming + - Touches are consumed more agressively by GMSMapView + - Fixed constructing a GMSMutablePath via pathFromEncodedPath: + - Restores OpenGL state correctly in GMSMapView in applications that also use + GLKView + +Version 1.3.0 - May 2013 +======================== +Features: + - Support for custom tile overlays (image-based) via GMSTileLayer + - Anti-aliasing for GMSPolyline and GMSPolygon stroke + - Support for 'invisible' base map tiles via kGMSTypeNone + - Basic support for CAAnimationGroup on GMSMapLayer + +Resolved Issues: + - Performance improvements with large numbers of overlays + - Resolved excessive memory use when device was locked/unlocked while an info + window was displayed + - Animations are stopped when a user performs a gesture + - Animations stop any active gesture (e.g., a pan) + - Resolved crash issue with setting/clearing My Location dot. + - GMSPolyline and GMSPolygon now support greater precision at high zoom + - GMSPolyline and GMSPolygon use the correct alpha values + - Touches are consumed by GMSMapView, allowing use within e.g. a scroll view + +Version 1.2.2 - April 2013 +========================== +Resolved Issues: + - Tappable regions for GMSMarker fixed. + - Overlays are no longer able to render on half pixels. + - Ground overlays appear underneath the My Location dot. + - GMSPolyline 'strokeColor' is no longer erroneously deallocated. + +Version 1.2.0 - April 2013 +========================== +Features: + ! Removed GMS...Options classes in favor of creating overlays directly + and setting their 'map' property + ! Map overlays (GMSMarker, GMSPolyline, others) now inherit from a shared + GMSOverlay class + ! GMSPolyline now has 'strokeWidth' and 'strokeColor' to match GMSPolygon, + rather than 'width' and 'stroke' + ! More helper methods on GMSCoordinateBounds, 'including' renamed to + 'includingCoordinate', added 'includingBounds' + - Added GMSPolygon and GMSCircle overlays + - A GMSMarker may be animated when added to a map + - Overlay types may now be subclassed + - GMSCameraUpdate to create camera update objects, including operations to + set a camera that presents a specified GMSCoordinateBounds + - GMSUISettings may be used to add a compass or My Location button (disabled + by default) + - Non-marker overlay types may be tapped (see GMSMapViewDelegate) + - Default marker changed to the Google Maps for iPhone marker + - Added markerImageWithColor: to create tinted versions of the default marker + - GMSMapLayer, the CALayer subclass for GMSMapView, now supports modification + of its camera properties, allowing for advanced animation effects + +Resolved Issues: + - visibleRegion now reports correctly sized region on Retina devices + - Double-tap to zoom now centers around tapped point + - Disabling pan via UISettings now prevents movement with zoom gestures + - GMSPolyline performance is improved for large polylines + - GMSMapView may be subclassed + - My Location dot appears underneath markers + - Performance improvements when using the My Location dot + - Grayscale polylines now render correctly + - Calling renderInContext: on the GMSMapView layer now renders correctly; + this allows for snapshots and UI effects + - The default behavior when a marker is tapped has been updated to also pan + the camera to the marker's position + - semaphore_wait_trap issue resolved + +Version 1.1.2 - March 2013 +========================== +Resolved Issues: + ! Updated the SDK to use libc++ instead of libstdc++ + - Improved support for including a GMSMapView and GLKView in the same app + +Version 1.1.1 - March 2013 +========================== +Features: + - Improved the messages that are logged to the console when a invalid key is + used or a connection error occurs + - Added multi-line snippet support for GMSMarker + +Resolved Issues: + - GMSMapView could return a nil camera + - Multiple GMSMapView instances no longer 'camera crosstalk.' + - The SDK contained unresolved external references + - A GMSMarker with an empty title and snippet no longer shows an empty + info window. + +Version 1.1.0 - February 2013 +============================= +Features: + ! The points of a GMSPolyline (and GMSPolylineOptions) are now specified as + a GMSPath and built via a GMSMutablePath, rather than addVertex: etc + - GMSPolyline may now be specified as geodesic + - animateToCameraPosition: method on GMSMapView + - GMSProjection provides containsCoordinate: and visibleRegion helpers + +Resolved Issues: + - GMSCameraPosition and animateToLocation: now clamp/wrap latitude/longitude + respectively; similarly, bearing is clamped to 0 <= bearing < 360 + - GMSGroundOverlay may be modified after creation + - The points of a GMSPoyline may be modified after creation + - GMSPolyline may cross the antimeridian + - Resolved a marker sorting issue + +Version 1.0.2 - January 2013 +============================ +Features: + ! GMSCamera (struct) has been dropped in favor of GMSCameraPosition * (objc + class), supports finer control of bearing and viewing angle + - Added GMSUISettings to control gesture availability + - Added GMSGroundOverlay/GMSGroundOverlayOptions for basic ground overlay + support + - Removed requirement to call startRendering/stopRendering + - Support for adding GMSMapView as a custom UIView in Interface Builder + - Improved texture memory handling + +Resolved Issues: + - Info windows now have highest tap priority + - Selected markers are automatically brought to front + - Polylines now render at constant size regardless of the zoom level + +Version 1.0.1 - December 2012 +============================= +Initial release alongside Google Maps for iOS. +Support for 3D maps, rotation, tilt, 3D buildings, markers, polylines, +satellite and terrain tiles, traffic data, and other features. + + +* Items denoted with an '!' may indicate a backwards incompatible change. diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/GoogleMaps b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/GoogleMaps new file mode 120000 index 0000000..17ed6fb --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/GoogleMaps @@ -0,0 +1 @@ +Versions/Current/GoogleMaps \ No newline at end of file diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Headers b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Modules b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Resources b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps new file mode 100644 index 0000000..b39df80 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h new file mode 100644 index 0000000..e9b1a87 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h @@ -0,0 +1,69 @@ +// +// GMSAddress.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +/** + * A result from a reverse geocode request, containing a human-readable address. This class is + * immutable and should be obtained via GMSGeocoder. + * + * Some of the fields may be nil, indicating they are not present. + */ +@interface GMSAddress : NSObject + +/** Location, or kLocationCoordinate2DInvalid if unknown. */ +@property(nonatomic, readonly) CLLocationCoordinate2D coordinate; + +/** Street number and name. */ +@property(nonatomic, copy, readonly) NSString *thoroughfare; + +/** Locality or city. */ +@property(nonatomic, copy, readonly) NSString *locality; + +/** Subdivision of locality, district or park. */ +@property(nonatomic, copy, readonly) NSString *subLocality; + +/** Region/State/Administrative area. */ +@property(nonatomic, copy, readonly) NSString *administrativeArea; + +/** Postal/Zip code. */ +@property(nonatomic, copy, readonly) NSString *postalCode; + +/** The country name. */ +@property(nonatomic, copy, readonly) NSString *country; + +/** An array of NSString containing formatted lines of the address. May be nil. */ +@property(nonatomic, copy, readonly) NSArray *lines; + +/** + * Returns the first line of the address. + * + * This method is obsolete and deprecated and will be removed in a future release. + * Use the lines property instead. + */ +- (NSString *)addressLine1 __GMS_AVAILABLE_BUT_DEPRECATED; + +/** + * Returns the second line of the address. + * + * This method is obsolete and deprecated and will be removed in a future release. + * Use the lines property instead. + */ +- (NSString *)addressLine2 __GMS_AVAILABLE_BUT_DEPRECATED; + +@end + +/** + * The former type of geocode results (pre-1.7). This remains here for migration and will be + * removed in future releases. + */ +@compatibility_alias GMSReverseGeocodeResult GMSAddress; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteFilter.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteFilter.h new file mode 100644 index 0000000..080ad00 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteFilter.h @@ -0,0 +1,62 @@ +// +// GMSAutocompleteFilter.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** + * The type filters that may be applied to an autocomplete request to restrict results to different + * types. + */ +typedef NS_ENUM(NSInteger, GMSPlacesAutocompleteTypeFilter) { + /** + * All results. + */ + kGMSPlacesAutocompleteTypeFilterNoFilter, + /** + * Geeocoding results, as opposed to business results. + */ + kGMSPlacesAutocompleteTypeFilterGeocode, + /** + * Geocoding results with a precise address. + */ + kGMSPlacesAutocompleteTypeFilterAddress, + /** + * Business results. + */ + kGMSPlacesAutocompleteTypeFilterEstablishment, + /** + * Results that match the following types: + * "locality", + * "sublocality" + * "postal_code", + * "country", + * "administrative_area_level_1", + * "administrative_area_level_2" + */ + kGMSPlacesAutocompleteTypeFilterRegion, + /** + * Results that match the following types: + * "locality", + * "administrative_area_level_3" + */ + kGMSPlacesAutocompleteTypeFilterCity, +}; + +/** + * This class represents a set of restrictions that may be applied to autocomplete requests. This + * allows customization of autocomplete suggestions to only those places that are of interest. + */ +@interface GMSAutocompleteFilter : NSObject + +/** + * The type filter applied to an autocomplete request to restrict results to different types. + * Default value is kGMSPlacesAutocompleteTypeFilterNoFilter. + */ +@property(nonatomic, assign) GMSPlacesAutocompleteTypeFilter type; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h new file mode 100644 index 0000000..5eb36cf --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h @@ -0,0 +1,29 @@ +// +// GMSAutocompleteMatchFragment.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + +/** + * This class represents a matched fragment of a string. This is a contiguous range of characters + * in a string, suitable for highlighting in an autocompletion UI. + */ +@interface GMSAutocompleteMatchFragment : NSObject + +/** + * The offset of the matched fragment. This is an index into a string. The character at this index + * is the first matched character. + */ +@property(nonatomic, readonly) NSUInteger offset; + +/** + * The length of the matched fragment. + */ +@property(nonatomic, readonly) NSUInteger length; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompletePrediction.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompletePrediction.h new file mode 100644 index 0000000..7ea0dbe --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompletePrediction.h @@ -0,0 +1,59 @@ +// +// GMSAutocompletePrediction.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + +/* + * Attribute name for match fragments in |GMSAutocompletePrediction| attributedFullText. + */ +extern NSString *const kGMSAutocompleteMatchAttribute; + +/** + * This class represents a prediction of a full query based on a partially typed string. + */ +@interface GMSAutocompletePrediction : NSObject + +/** + * The full description of the prediction as a NSAttributedString. E.g., "Sydney Opera House, + * Sydney, New South Wales, Australia". + * + * Every text range that matches the user input has a |kGMSAutocompleteMatchAttribute|. For + * example, you can make every match bold using enumerateAttribute: + *
+ *   UIFont *regularFont = [UIFont systemFontOfSize:[UIFont labelFontSize]];
+ *   UIFont *boldFont = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
+ *
+ *   NSMutableAttributedString *bolded = [prediction.attributedFullText mutableCopy];
+ *   [bolded enumerateAttribute:kGMSAutocompleteMatchAttribute
+ *                      inRange:NSMakeRange(0, bolded.length)
+ *                      options:0
+ *                   usingBlock:^(id value, NSRange range, BOOL *stop) {
+ *                     UIFont *font = (value == nil) ? regularFont : boldFont;
+ *                     [bolded addAttribute:NSFontAttributeName value:font range:range];
+ *                   }];
+ *
+ *   label.attributedText = bolded;
+ * 
+ */ +@property(nonatomic, copy, readonly) NSAttributedString *attributedFullText; + + +/** + * An optional property representing the place ID of the prediction, suitable for use in a place + * details request. + */ +@property(nonatomic, copy, readonly) NSString *placeID; + +/** + * The types of this autocomplete result. Types are NSStrings, valid values are any types + * documented at . + */ +@property(nonatomic, copy, readonly) NSArray *types; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h new file mode 100644 index 0000000..c10bc7b --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h @@ -0,0 +1,20 @@ +// +// GMSCALayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSCALayer is a superclass used by layers in the Google Maps SDK for iOS, + * such as GMSMapLayer and GMSPanoramaLayer. + * + * This is an implementation detail and it should not be instantiated directly. + */ +@interface GMSCALayer : CALayer +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h new file mode 100644 index 0000000..8ecdeae --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h @@ -0,0 +1,117 @@ +// +// GMSCameraPosition.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** +* An immutable class that aggregates all camera position parameters. + */ +@interface GMSCameraPosition : NSObject + +/** + * Location on the Earth towards which the camera points. + */ +@property(nonatomic, readonly) CLLocationCoordinate2D target; + +/** + * Zoom level. Zoom uses an exponentional scale, where zoom 0 represents the entire world as a + * 256 x 256 square. Each successive zoom level increases magnification by a factor of 2. So at + * zoom level 1, the world is 512x512, and at zoom level 2, the entire world is 1024x1024. + */ +@property(nonatomic, readonly) float zoom; + +/** + * Bearing of the camera, in degrees clockwise from true north. + */ +@property(nonatomic, readonly) CLLocationDirection bearing; + +/** + * The angle, in degrees, of the camera angle from the nadir (directly facing the Earth). 0 is + * straight down, 90 is parallel to the ground. Note that the maximum angle allowed is 45 degrees. + */ +@property(nonatomic, readonly) double viewingAngle; + +/** + * Designated initializer. Configures this GMSCameraPosition with all available camera properties. + * Building a GMSCameraPosition via this initializer (or by the following convenience constructors) + * will implicitly clamp camera values. + * + * @param target location on the earth which the camera points + * @param zoom the zoom level near the center of the screen + * @param bearing of the camera in degrees from true north + * @param viewingAngle in degrees, of the camera angle from the nadir + */ +- (id)initWithTarget:(CLLocationCoordinate2D)target + zoom:(float)zoom + bearing:(CLLocationDirection)bearing + viewingAngle:(double)viewingAngle; + +/** + * Convenience constructor for GMSCameraPosition for a particular target and zoom level. This will + * set the bearing and viewingAngle properties of this camera to zero defaults (i.e., directly + * facing the Earth's surface, with the top of the screen pointing north). + */ ++ (instancetype)cameraWithTarget:(CLLocationCoordinate2D)target zoom:(float)zoom; + +/** + * Convenience constructor for GMSCameraPosition, as per cameraWithTarget:zoom:. + */ ++ (instancetype)cameraWithLatitude:(CLLocationDegrees)latitude + longitude:(CLLocationDegrees)longitude + zoom:(float)zoom; + +/** + * Convenience constructor for GMSCameraPosition, with all camera properties as per + * initWithTarget:zoom:bearing:viewingAngle:. + */ ++ (instancetype)cameraWithTarget:(CLLocationCoordinate2D)target + zoom:(float)zoom + bearing:(CLLocationDirection)bearing + viewingAngle:(double)viewingAngle; + +/** + * Convenience constructor for GMSCameraPosition, with latitude/longitude and all other camera + * properties as per initWithTarget:zoom:bearing:viewingAngle:. + */ ++ (instancetype)cameraWithLatitude:(CLLocationDegrees)latitude + longitude:(CLLocationDegrees)longitude + zoom:(float)zoom + bearing:(CLLocationDirection)bearing + viewingAngle:(double)viewingAngle; + +/** + * Get the zoom level at which |meters| distance, at given |coord| on Earth, correspond to the + * specified number of screen |points|. + * + * For extremely large or small distances the returned zoom level may be smaller or larger than the + * minimum or maximum zoom level allowed on the camera. + * + * This helper method is useful for building camera positions that contain specific physical areas + * on Earth. + */ ++ (float)zoomAtCoordinate:(CLLocationCoordinate2D)coordinate + forMeters:(CLLocationDistance)meters + perPoints:(CGFloat)points; + +@end + +/** Mutable version of GMSCameraPosition. */ +@interface GMSMutableCameraPosition : GMSCameraPosition +@property(nonatomic, assign) CLLocationCoordinate2D target; +@property(nonatomic, assign) float zoom; +@property(nonatomic, assign) CLLocationDirection bearing; +@property(nonatomic, assign) double viewingAngle; +@end + +/** The maximum zoom (closest to the Earth's surface) permitted by the map camera. */ +FOUNDATION_EXTERN const float kGMSMaxZoomLevel; + +/** The minimum zoom (farthest from the Earth's surface) permitted by the map camera. */ +FOUNDATION_EXTERN const float kGMSMinZoomLevel; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h new file mode 100644 index 0000000..7145ff1 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h @@ -0,0 +1,106 @@ +// +// GMSCameraUpdate.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +@class GMSCameraPosition; +@class GMSCoordinateBounds; + +/** + * GMSCameraUpdate represents an update that may be applied to a GMSMapView. + * It encapsulates some logic for modifying the current camera. + * It should only be constructed using the factory helper methods below. + */ +@interface GMSCameraUpdate : NSObject + +/** + * Returns a GMSCameraUpdate that zooms in on the map. + * The zoom increment is 1.0. + */ ++ (GMSCameraUpdate *)zoomIn; + +/** + * Returns a GMSCameraUpdate that zooms out on the map. + * The zoom increment is -1.0. + */ ++ (GMSCameraUpdate *)zoomOut; + +/** + * Returns a GMSCameraUpdate that changes the zoom by the specified amount. + */ ++ (GMSCameraUpdate *)zoomBy:(float)delta; + +/** + * Returns a GMSCameraUpdate that sets the zoom to the specified amount. + */ ++ (GMSCameraUpdate *)zoomTo:(float)zoom; + +/** + * Returns a GMSCameraUpdate that sets the camera target to the specified + * coordinate. + */ ++ (GMSCameraUpdate *)setTarget:(CLLocationCoordinate2D)target; + +/** + * Returns a GMSCameraUpdate that sets the camera target and zoom to the + * specified values. + */ ++ (GMSCameraUpdate *)setTarget:(CLLocationCoordinate2D)target zoom:(float)zoom; + +/** + * Returns a GMSCameraUpdate that sets the camera to the specified + * GMSCameraPosition. + */ ++ (GMSCameraUpdate *)setCamera:(GMSCameraPosition *)camera; + +/** + * Returns a GMSCameraUpdate that transforms the camera such that the specified + * bounds are centered on screen at the greatest possible zoom level. The bounds + * will have a default padding of 64 points. + * + * The returned camera update will set the camera's bearing and tilt to their + * default zero values (i.e., facing north and looking directly at the Earth). + */ ++ (GMSCameraUpdate *)fitBounds:(GMSCoordinateBounds *)bounds; + +/** + * This is similar to fitBounds: but allows specifying the padding (in points) + * in order to inset the bounding box from the view's edges. + * If the requested |padding| is larger than the view size in either the + * vertical or horizontal direction the map will be maximally zoomed out. + */ ++ (GMSCameraUpdate *)fitBounds:(GMSCoordinateBounds *)bounds + withPadding:(CGFloat)padding; + +/** + * This is similar to fitBounds: but allows specifying edge insets + * in order to inset the bounding box from the view's edges. + * If the requested |edgeInsets| are larger than the view size in either the + * vertical or horizontal direction the map will be maximally zoomed out. + */ ++ (GMSCameraUpdate *)fitBounds:(GMSCoordinateBounds *)bounds + withEdgeInsets:(UIEdgeInsets)edgeInsets; + +/** + * Returns a GMSCameraUpdate that shifts the center of the view by the + * specified number of points in the x and y directions. + * X grows to the right, Y grows down. + */ ++ (GMSCameraUpdate *)scrollByX:(CGFloat)dX Y:(CGFloat)dY; + +/** + * Returns a GMSCameraUpdate that zooms with a focus point; the focus point + * stays fixed on screen. + */ ++ (GMSCameraUpdate *)zoomBy:(float)zoom atPoint:(CGPoint)point; + +@end + diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h new file mode 100644 index 0000000..b67e3bd --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h @@ -0,0 +1,49 @@ +// +// GMSCircle.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +/** + * A circle on the Earth's surface (spherical cap). + */ +@interface GMSCircle : GMSOverlay + +/** Position on Earth of circle center. */ +@property(nonatomic, assign) CLLocationCoordinate2D position; + +/** Radius of the circle in meters; must be positive. */ +@property(nonatomic, assign) CLLocationDistance radius; + +/** + * The width of the circle's outline in screen points. Defaults to 1. As per + * GMSPolygon, the width does not scale when the map is zoomed. + * Setting strokeWidth to 0 results in no stroke. + */ +@property(nonatomic, assign) CGFloat strokeWidth; + +/** The color of this circle's outline. The default value is black. */ +@property(nonatomic, strong) UIColor *strokeColor; + +/** + * The interior of the circle is painted with fillColor. + * The default value is nil, resulting in no fill. + */ +@property(nonatomic, strong) UIColor *fillColor; + +/** + * Convenience constructor for GMSCircle for a particular position and radius. + * Other properties will have default values. + */ ++ (instancetype)circleWithPosition:(CLLocationCoordinate2D)position + radius:(CLLocationDistance)radius; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds.h new file mode 100644 index 0000000..f7aae8a --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds.h @@ -0,0 +1,97 @@ +// +// GMSCoordinateBounds.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +@class GMSPath; + +/** + * GMSCoordinateBounds represents a rectangular bounding box on the Earth's + * surface. GMSCoordinateBounds is immutable and can't be modified after + * construction. + */ +@interface GMSCoordinateBounds : NSObject + +/** The North-East corner of these bounds. */ +@property(nonatomic, readonly) CLLocationCoordinate2D northEast; + +/** The South-West corner of these bounds. */ +@property(nonatomic, readonly) CLLocationCoordinate2D southWest; + +/** + * Returns NO if this bounds does not contain any points. + * For example, [[GMSCoordinateBounds alloc] init].valid == NO. + * When an invalid bounds is expanded with valid coordinates via + * includingCoordinate: or includingBounds:, the resulting bounds will be valid + * but contain only the new coordinates. + */ +@property(readonly, getter=isValid) BOOL valid; + +/** + * Inits the northEast and southWest bounds corresponding + * to the rectangular region defined by the two corners. + * + * It is ambiguous whether the longitude of the box + * extends from |coord1| to |coord2| or vice-versa; + * the box is constructed as the smaller of the two variants, eliminating the + * ambiguity. + */ +- (id)initWithCoordinate:(CLLocationCoordinate2D)coord1 + coordinate:(CLLocationCoordinate2D)coord2; + +/** + * Inits with bounds that encompass |region|. + */ +- (id)initWithRegion:(GMSVisibleRegion)region; + +/** + * Inits with bounds that encompass |path|. + */ +- (id)initWithPath:(GMSPath *)path; + +/** + * Returns a GMSCoordinateBounds representing + * the current bounds extended to include the passed-in coordinate. + * If the current bounds is invalid, the result is a valid bounds containing + * only |coordinate|. + */ +- (GMSCoordinateBounds *)includingCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Returns a GMSCoordinateBounds representing + * the current bounds extended to include the entire other bounds. + * If the current bounds is invalid, the result is a valid bounds equal + * to |other|. + */ +- (GMSCoordinateBounds *)includingBounds:(GMSCoordinateBounds *)other; + +/** + * Returns a GMSCoordinateBounds representing the current bounds extended to + * include |path|. + */ +- (GMSCoordinateBounds *)includingPath:(GMSPath *)path; + +/** + * Returns YES if |coordinate| is contained within this bounds. This includes + * points that lie exactly on the edge of the bounds. + */ +- (BOOL)containsCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Returns YES if |other| overlaps with this bounds. + * Two bounds are overlapping if there is at least one coordinate point + * contained by both. + */ +- (BOOL)intersectsBounds:(GMSCoordinateBounds *)other; + +@end + diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h new file mode 100644 index 0000000..6f3574b --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h @@ -0,0 +1,56 @@ +// +// GMSGeocoder.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +@class GMSReverseGeocodeResponse; + +/** GMSGeocoder error codes, embedded in NSError. */ +typedef NS_ENUM(NSInteger, GMSGeocoderErrorCode) { + kGMSGeocoderErrorInvalidCoordinate = 1, + kGMSGeocoderErrorInternal, +}; + +/** Handler that reports a reverse geocoding response, or error. */ +typedef void (^GMSReverseGeocodeCallback)(GMSReverseGeocodeResponse *, NSError *); + +/** + * Exposes a service for reverse geocoding. This maps Earth coordinates (latitude and longitude) to + * a collection of addresses near that coordinate. + */ +@interface GMSGeocoder : NSObject + +/* Convenience constructor for GMSGeocoder. */ ++ (GMSGeocoder *)geocoder; + +/** + * Reverse geocodes a coordinate on the Earth's surface. + * + * @param coordinate The coordinate to reverse geocode. + * @param handler The callback to invoke with the reverse geocode results. + * The callback will be invoked asynchronously from the main thread. + */ +- (void)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate + completionHandler:(GMSReverseGeocodeCallback)handler; + +@end + +/** A collection of results from a reverse geocode request. */ +@interface GMSReverseGeocodeResponse : NSObject + +/** Returns the first result, or nil if no results were available. */ +- (GMSAddress *)firstResult; + +/** Returns an array of all the results (contains GMSAddress), including the first result. */ +- (NSArray *)results; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h new file mode 100644 index 0000000..a50d6f6 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h @@ -0,0 +1,222 @@ +// +// GMSGeometryUtils.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** + * \defgroup GeometryUtils GMSGeometryUtils + * @{ + */ + +#import + +#import + +@class GMSPath; + +/** Average Earth radius in meters. */ +static const CLLocationDistance kGMSEarthRadius = 6371009.0; + +/** + * A point on the map. May represent a projected coordinate. x is in [-1, 1]. + * The axis direction is normal: y grows towards North, x grows towards East. + * (0, 0) is the center of the map. See GMSProject() and GMSUnproject(). + */ +typedef struct GMSMapPoint { + double x; + double y; +} GMSMapPoint; + +/** Projects |coordinate| to the map. |coordinate| must be valid. */ +FOUNDATION_EXPORT +GMSMapPoint GMSProject(CLLocationCoordinate2D coordinate); + +/** Unprojects |point| from the map. point.x must be in [-1, 1]. */ +FOUNDATION_EXPORT +CLLocationCoordinate2D GMSUnproject(GMSMapPoint point); + +/** + * Returns a linearly interpolated point on the segment [a, b], at the fraction + * |t| from |a|. |t|==0 corresponds to |a|, |t|==1 corresponds to |b|. + * The interpolation takes place along the short path between the points + * potentially crossing the date line. E.g. interpolating from San Francisco + * to Tokyo will pass north of Hawaii and cross the date line. + */ +FOUNDATION_EXPORT +GMSMapPoint GMSMapPointInterpolate(GMSMapPoint a, GMSMapPoint b, double t); + +/** + * Returns the length of the segment [a, b] in projected space. The length is + * computed along the short path between the points potentially crossing the + * date line. E.g. the distance between the points corresponding to + * San Francisco and Tokyo measures the segment that passes north of Hawaii + * crossing the date line. + */ +FOUNDATION_EXPORT +double GMSMapPointDistance(GMSMapPoint a, GMSMapPoint b); + +/** + * Returns whether |point| lies inside of path. The path is always cosidered + * closed, regardless of whether the last point equals the first or not. + * Inside is defined as not containing the South Pole -- the South Pole is + * always outside. + * |path| describes great circle segments if |geodesic| is YES, and rhumb + * (loxodromic) segments otherwise. + * If |point| is exactly equal to one of the vertices, the result is YES. + * A point that is not equal to a vertex is on one side or the other of any path + * segment -- it can never be "exactly on the border". + * See GMSGeometryIsLocationOnPath() for a border test with tolerance. + */ +FOUNDATION_EXPORT +BOOL GMSGeometryContainsLocation(CLLocationCoordinate2D point, GMSPath *path, + BOOL geodesic); + +/** + * Returns whether |point| lies on or near |path|, within the specified + * |tolerance| in meters. + * |path| is composed of great circle segments if |geodesic| is YES, and of + * rhumb (loxodromic) segments if |geodesic| is NO. + * See also GMSGeometryIsLocationOnPath(point, path, geodesic). + * + * The tolerance, in meters, is relative to the spherical radius of the Earth. + * If you need to work on a sphere of different radius, + * you may compute the equivalent tolerance from the desired tolerance on the + * sphere of radius R: tolerance = toleranceR * (RadiusEarth / R), + * with RadiusEarth==6371009. + */ +FOUNDATION_EXPORT +BOOL GMSGeometryIsLocationOnPathTolerance(CLLocationCoordinate2D point, + GMSPath *path, + BOOL geodesic, + CLLocationDistance tolerance); + +/** + * Same as GMSGeometryIsLocationOnPath(point, path, geodesic, tolerance), + * with a default tolerance of 0.1 meters. + */ +FOUNDATION_EXPORT +BOOL GMSGeometryIsLocationOnPath(CLLocationCoordinate2D point, + GMSPath *path, + BOOL geodesic); + +/** + * Returns the great circle distance between two coordinates, in meters, + * on Earth. + * This is the shortest distance between the two coordinates on the sphere. + * Both coordinates must be valid. + */ +FOUNDATION_EXPORT +CLLocationDistance GMSGeometryDistance(CLLocationCoordinate2D from, + CLLocationCoordinate2D to); + +/** + * Returns the great circle length of |path|, in meters, on Earth. + * This is the sum of GMSGeometryDistance() over the path segments. + * All the coordinates of the path must be valid. + */ +FOUNDATION_EXPORT +CLLocationDistance GMSGeometryLength(GMSPath *path); + +/** + * Returns the area of a geodesic polygon defined by |path| on Earth. + * The "inside" of the polygon is defined as not containing the South pole. + * If |path| is not closed, it is implicitly treated as a closed path + * nevertheless and the result is the same. + * All coordinates of the path must be valid. + * If any segment of the path is a pair of antipodal points, the + * result is undefined -- because two antipodal points do not form a + * unique great circle segment on the sphere. + * The polygon must be simple (not self-overlapping) and may be concave. + */ +FOUNDATION_EXPORT +double GMSGeometryArea(GMSPath *path); + +/** + * Returns the signed area of a geodesic polygon defined by |path| on Earth. + * The result has the same absolute value as GMSGeometryArea(); it is positive + * if the points of path are in counter-clockwise order, and negative otherwise. + * The same restrictions as on GMSGeometryArea() apply. + */ +FOUNDATION_EXPORT +double GMSGeometrySignedArea(GMSPath *path); + +/** + * Returns the initial heading (degrees clockwise of North) at |from| + * of the shortest path to |to|. + * Returns 0 if the two coordinates are the same. + * Both coordinates must be valid. + * The returned value is in the range [0, 360). + * + * To get the final heading at |to| one may use + * (GMSGeometryHeading(|to|, |from|) + 180) modulo 360. + */ +FOUNDATION_EXPORT +CLLocationDirection GMSGeometryHeading(CLLocationCoordinate2D from, + CLLocationCoordinate2D to); + +/** + * Returns the destination coordinate, when starting at |from| + * with initial |heading|, travelling |distance| meters along a great circle + * arc, on Earth. + * The resulting longitude is in the range [-180, 180). + * Both coordinates must be valid. + */ +FOUNDATION_EXPORT +CLLocationCoordinate2D GMSGeometryOffset(CLLocationCoordinate2D from, + CLLocationDistance distance, + CLLocationDirection heading); + +/** + * Returns the coordinate that lies the given |fraction| of the way between + * the |from| and |to| coordinates on the shortest path between the two. + * The resulting longitude is in the range [-180, 180). + */ +FOUNDATION_EXPORT +CLLocationCoordinate2D GMSGeometryInterpolate(CLLocationCoordinate2D from, + CLLocationCoordinate2D to, + double fraction); + + +/** + * Returns an NSArray of GMSStyleSpan constructed by repeated application of style and length + * information from |styles| and |lengths| along |path|. + * + * |path| the path along which the output spans are computed. + * |styles| an NSArray of GMSStrokeStyle. Wraps if consumed. Can't be empty. + * |lengths| an NSArray of NSNumber; each entry gives the length of the corresponding + * style from |styles|. Wraps if consumed. Can't be empty. + * |lengthKind| the interpretation of values from |lengths| (geodesic, rhumb or projected). + * + * Example: a polyline with alternating black and white spans: + * + *
+ * GMSMutablePath *path;
+ * NSArray *styles = @[[GMSStrokeStyle solidColor:[UIColor whiteColor]],
+ *                     [GMSStrokeStyle solidColor:[UIColor blackColor]]];
+ * NSArray *lengths = @[@100000, @50000];
+ * polyline.path = path;
+ * polyline.spans = GMSStyleSpans(path, styles, lengths, kGMSLengthRhumb);
+ * 
+ */ +FOUNDATION_EXPORT +NSArray *GMSStyleSpans(GMSPath *path, NSArray *styles, NSArray *lengths, GMSLengthKind lengthKind); + +/** + * Similar to GMSStyleSpans(path, styles, lengths, lengthKind) but additionally takes an initial + * length offset that will be skipped over relative to the |lengths| array. + * + * |lengthOffset| the length (e.g. in meters) that should be skipped initially from |lengths|. + */ +FOUNDATION_EXPORT +NSArray *GMSStyleSpansOffset(GMSPath *path, + NSArray *styles, + NSArray *lengths, + GMSLengthKind lengthKind, + double lengthOffset); + +/**@}*/ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h new file mode 100644 index 0000000..de88e0a --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h @@ -0,0 +1,75 @@ +// +// GMSGroundOverlay.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSCoordinateBounds; + +/** + * GMSGroundOverlay specifies the available options for a ground overlay that + * exists on the Earth's surface. Unlike a marker, the position of a ground + * overlay is specified explicitly and it does not face the camera. + */ +@interface GMSGroundOverlay : GMSOverlay + +/** + * The position of this GMSGroundOverlay, or more specifically, the physical + * position of its anchor. If this is changed, |bounds| will be moved around + * the new position. + */ +@property(nonatomic, assign) CLLocationCoordinate2D position; + +/** + * The anchor specifies where this GMSGroundOverlay is anchored to the Earth in + * relation to |bounds|. If this is modified, |position| will be set to the + * corresponding new position within |bounds|. + */ +@property(nonatomic, assign) CGPoint anchor; + +/** + * Icon to render within |bounds| on the Earth. If this is nil, the overlay will + * not be visible (unlike GMSMarker which has a default image). + */ +@property(nonatomic, strong) UIImage *icon; + +/** + * Bearing of this ground overlay, in degrees. The default value, zero, points + * this ground overlay up/down along the normal Y axis of the earth. + */ +@property(nonatomic, assign) CLLocationDirection bearing; + +/** + * The 2D bounds on the Earth in which |icon| is drawn. Changing this value + * will adjust |position| accordingly. + */ +@property(nonatomic, strong) GMSCoordinateBounds *bounds; + +/** + * Convenience constructor for GMSGroundOverlay for a particular |bounds| and + * |icon|. Will set |position| accordingly. + */ ++ (instancetype)groundOverlayWithBounds:(GMSCoordinateBounds *)bounds + icon:(UIImage *)icon; + +/** + * Constructs a GMSGroundOverlay that renders the given |icon| at |position|, + * as if the image's actual size matches camera pixels at |zoomLevel|. + */ ++ (instancetype)groundOverlayWithPosition:(CLLocationCoordinate2D)position + icon:(UIImage *)icon + zoomLevel:(CGFloat)zoomLevel; + +@end + +/** + * The default position of the ground anchor of a GMSGroundOverlay: the center + * point of the icon. + */ +FOUNDATION_EXTERN const CGPoint kGMSGroundOverlayDefaultAnchor; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h new file mode 100644 index 0000000..c217483 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h @@ -0,0 +1,35 @@ +// +// GMSIndoorBuilding.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + +#import + +/** + * Describes a building which contains levels. + */ +@interface GMSIndoorBuilding : NSObject + +/** + * Array of GMSIndoorLevel describing the levels which make up the building. + * The levels are in 'display order' from top to bottom. + */ +@property(nonatomic, strong, readonly) NSArray *levels; + +/** + * Index in the levels array of the default level. + */ +@property(nonatomic, assign, readonly) NSUInteger defaultLevelIndex; + +/** + * If YES, the building is entirely underground and supports being hidden. + */ +@property(nonatomic, assign, readonly, getter=isUnderground) BOOL underground; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h new file mode 100644 index 0000000..0094cf4 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h @@ -0,0 +1,61 @@ +// +// GMSIndoorDisplay.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSIndoorBuilding; +@class GMSIndoorLevel; + +/** Delegate for events on GMSIndoorDisplay. */ +@protocol GMSIndoorDisplayDelegate +@optional + +/** + * Raised when the activeBuilding has changed. The activeLevel will also have + * already been updated for the new building, but didChangeActiveLevel: will + * be raised after this method. + */ +- (void)didChangeActiveBuilding:(GMSIndoorBuilding *)building; + +/** + * Raised when the activeLevel has changed. This event is raised for all + * changes, including explicit setting of the property. + */ +- (void)didChangeActiveLevel:(GMSIndoorLevel *)level; + +@end + +/** + * Provides ability to observe or control the display of indoor level data. + * + * Like GMSMapView, GMSIndoorDisplay may only be used from the main thread. + */ +@interface GMSIndoorDisplay : NSObject + +/** GMSIndoorDisplay delegate */ +@property(nonatomic, weak) id delegate; + +/** + * Provides the currently focused building, will be nil if there is no + * building with indoor data currently under focus. + */ +@property(nonatomic, strong, readonly) GMSIndoorBuilding *activeBuilding; + +/** + * Provides and controls the active level for activeBuilding. Will be updated + * whenever activeBuilding changes, and may be set to any member of + * activeBuilding's levels property. May also be set to nil if the building is + * underground, to stop showing the building (the building will remain active). + * Will always be nil if activeBuilding is nil. + * Any attempt to set it to an invalid value will be ignored. + */ +@property(nonatomic, strong) GMSIndoorLevel *activeLevel; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h new file mode 100644 index 0000000..3fb3d21 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h @@ -0,0 +1,27 @@ +// +// GMSIndoorLevel.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + +#import + +/** + * Describes a single level in a building. + * Multiple buildings can share a level - in this case the level instances will + * compare as equal, even though the level numbers/names may be different. + */ +@interface GMSIndoorLevel : NSObject + +/** Localized display name for the level, e.g. "Ground floor". */ +@property(nonatomic, copy, readonly) NSString *name; + +/** Localized short display name for the level, e.g. "1". */ +@property(nonatomic, copy, readonly) NSString *shortName; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h new file mode 100644 index 0000000..6c8388b --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h @@ -0,0 +1,96 @@ +// +// GMSMapLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +#import + +/** + * The following layer properties and constants describe the camera properties + * that may be animated on the custom model layer of a GMSMapView with Core + * Animation. For simple camera control and animation, please see the helper + * methods in GMSMapView+Animation.h, and the camera object definition within + * GMSCameraPosition.h. + * + * Changing layer properties triggers an implicit animation, e.g.:- + * mapView_.layer.cameraBearing = 20; + * + * An explicit animation, replacing the implicit animation, may be added after + * changing the property, e.g.- + * CAMediaTimingFunction *curve = [CAMediaTimingFunction functionWithName: + * kCAMediaTimingFunctionEaseInEaseOut]; + * CABasicAnimation *animation = + * [CABasicAnimation animationWithKeyPath:kGMSLayerCameraBearingKey]; + * animation.duration = 2.0f; + * animation.timingFunction = curve; + * animation.toValue = @20; + * [mapView_.layer addAnimation:animation forKey:kGMSLayerCameraBearingKey]; + * + * To control several implicit animations, Core Animation's transaction support + * may be used, e.g.- + * [CATransaction begin]; + * [CATransaction setAnimationDuration:2.0f]; + * mapView_.layer.cameraBearing = 20; + * mapView_.layer.cameraViewingAngle = 30; + * [CATransaction commit]; + * + * Note that these properties are not view-based. Please see "Animating View + * and Layer Changes Together" in the View Programming Guide for iOS- + * http://developer.apple.com/library/ios/#documentation/windowsviews/conceptual/viewpg_iphoneos/AnimatingViews/AnimatingViews.html + */ + +/** + * kGMSLayerCameraLatitudeKey ranges from [-85, 85], and values outside this + * range will be clamped. + */ +extern NSString *const kGMSLayerCameraLatitudeKey; + +/** + * kGMSLayerCameraLongitudeKey ranges from [-180, 180), and values outside this + * range will be wrapped to within this range. + */ +extern NSString *const kGMSLayerCameraLongitudeKey; + +/** + * kGMSLayerCameraBearingKey ranges from [0, 360), and values are wrapped. + */ +extern NSString *const kGMSLayerCameraBearingKey; + +/** + * kGMSLayerCameraZoomLevelKey ranges from [kGMSMinZoomLevel, kGMSMaxZoomLevel], + * and values are clamped. + */ +extern NSString *const kGMSLayerCameraZoomLevelKey; + +/** + * kGMSLayerCameraViewingAngleKey ranges from zero (i.e., facing straight down) + * and to between 30 and 45 degrees towards the horizon, depending on the model + * zoom level. + */ +extern NSString *const kGMSLayerCameraViewingAngleKey; + +/** + * GMSMapLayer is a custom subclass of CALayer, provided as the layer class on + * GMSMapView. This layer should not be instantiated directly. It provides + * model access to the camera normally defined on GMSMapView. + * + * Modifying or animating these properties will typically interrupt any current + * gesture on GMSMapView, e.g., a user's pan or rotation. Similarly, if a user + * performs an enabled gesture during an animation, the animation will stop + * 'in-place' (at the current presentation value). + */ +@interface GMSMapLayer : GMSCALayer +@property(nonatomic, assign) CLLocationDegrees cameraLatitude; +@property(nonatomic, assign) CLLocationDegrees cameraLongitude; +@property(nonatomic, assign) CLLocationDirection cameraBearing; +@property(nonatomic, assign) float cameraZoomLevel; +@property(nonatomic, assign) double cameraViewingAngle; +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h new file mode 100644 index 0000000..7dd2fef --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h @@ -0,0 +1,57 @@ +// +// GMSMapView+Animation.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSMapView (Animation) offers several animation helper methods. + * + * During any animation, retrieving the camera position through the camera + * property on GMSMapView returns an intermediate immutable GMSCameraPosition. + * This camera position will typically represent the most recently drawn frame. + */ +@interface GMSMapView (Animation) + +/** Animates the camera of this map to |cameraPosition|. */ +- (void)animateToCameraPosition:(GMSCameraPosition *)cameraPosition; + +/** + * As animateToCameraPosition:, but changes only the location of the camera + * (i.e., from the current location to |location|). + */ +- (void)animateToLocation:(CLLocationCoordinate2D)location; + +/** + * As animateToCameraPosition:, but changes only the zoom level of the camera. + * This value is clamped by [kGMSMinZoomLevel, kGMSMaxZoomLevel]. + */ +- (void)animateToZoom:(float)zoom; + +/** + * As animateToCameraPosition:, but changes only the bearing of the camera (in + * degrees). Zero indicates true north. + */ +- (void)animateToBearing:(CLLocationDirection)bearing; + +/** + * As animateToCameraPosition:, but changes only the viewing angle of the camera + * (in degrees). This value will be clamped to a minimum of zero (i.e., facing + * straight down) and between 30 and 45 degrees towards the horizon, depending + * on the relative closeness to the earth. + */ +- (void)animateToViewingAngle:(double)viewingAngle; + +/** + * Applies |cameraUpdate| to the current camera, and then uses the result as + * per animateToCameraPosition:. + */ +- (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h new file mode 100644 index 0000000..f58023e --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h @@ -0,0 +1,378 @@ +// +// GMSMapView.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +#import +#import + +#ifndef __GMS_AVAILABLE_BUT_DEPRECATED +#define __GMS_AVAILABLE_BUT_DEPRECATED __deprecated +#endif + +@class GMSCameraPosition; +@class GMSCameraUpdate; +@class GMSCoordinateBounds; +@class GMSIndoorDisplay; +@class GMSMapLayer; +@class GMSMapView; +@class GMSMarker; +@class GMSOverlay; +@class GMSProjection; + +/** Delegate for events on GMSMapView. */ +@protocol GMSMapViewDelegate + +@optional + +/** + * Called before the camera on the map changes, either due to a gesture, + * animation (e.g., by a user tapping on the "My Location" button) or by being + * updated explicitly via the camera or a zero-length animation on layer. + * + * @param gesture If YES, this is occuring due to a user gesture. +*/ +- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture; + +/** + * Called repeatedly during any animations or gestures on the map (or once, if + * the camera is explicitly set). This may not be called for all intermediate + * camera positions. It is always called for the final position of an animation + * or gesture. + */ +- (void)mapView:(GMSMapView *)mapView + didChangeCameraPosition:(GMSCameraPosition *)position; + +/** + * Called when the map becomes idle, after any outstanding gestures or + * animations have completed (or after the camera has been explicitly set). + */ +- (void)mapView:(GMSMapView *)mapView + idleAtCameraPosition:(GMSCameraPosition *)position; + +/** + * Called after a tap gesture at a particular coordinate, but only if a marker + * was not tapped. This is called before deselecting any currently selected + * marker (the implicit action for tapping on the map). + */ +- (void)mapView:(GMSMapView *)mapView + didTapAtCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Called after a long-press gesture at a particular coordinate. + * + * @param mapView The map view that was pressed. + * @param coordinate The location that was pressed. + */ +- (void)mapView:(GMSMapView *)mapView + didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Called after a marker has been tapped. + * + * @param mapView The map view that was pressed. + * @param marker The marker that was pressed. + * @return YES if this delegate handled the tap event, which prevents the map + * from performing its default selection behavior, and NO if the map + * should continue with its default selection behavior. + */ +- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker; + +/** + * Called after a marker's info window has been tapped. + */ +- (void)mapView:(GMSMapView *)mapView + didTapInfoWindowOfMarker:(GMSMarker *)marker; + +/** + * Called after an overlay has been tapped. + * This method is not called for taps on markers. + * + * @param mapView The map view that was pressed. + * @param overlay The overlay that was pressed. + */ +- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay; + +/** + * Called when a marker is about to become selected, and provides an optional + * custom info window to use for that marker if this method returns a UIView. + * If you change this view after this method is called, those changes will not + * necessarily be reflected in the rendered version. + * + * The returned UIView must not have bounds greater than 500 points on either + * dimension. As there is only one info window shown at any time, the returned + * view may be reused between other info windows. + * + * Removing the marker from the map or changing the map's selected marker during + * this call results in undefined behavior. + * + * @return The custom info window for the specified marker, or nil for default + */ +- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker; + +/** + * Called when mapView:markerInfoWindow: returns nil. If this method returns a + * view, it will be placed within the default info window frame. If this method + * returns nil, then the default rendering will be used instead. + * + * @param mapView The map view that was pressed. + * @param marker The marker that was pressed. + * @return The custom view to disaply as contents in the info window, or null to + * use the default content rendering instead + */ + +- (UIView *)mapView:(GMSMapView *)mapView markerInfoContents:(GMSMarker *)marker; + +/** + * Called when dragging has been initiated on a marker. + */ +- (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker; + +/** + * Called after dragging of a marker ended. + */ +- (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker; + +/** + * Called while a marker is dragged. + */ +- (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker; + +/** + * Called when the My Location button is tapped. + * + * @return YES if the listener has consumed the event (i.e., the default behavior should not occur), + * NO otherwise (i.e., the default behavior should occur). The default behavior is for the + * camera to move such that it is centered on the user location. + */ +- (BOOL)didTapMyLocationButtonForMapView:(GMSMapView *)mapView; + +@end + +/** + * Display types for GMSMapView. + */ +typedef enum { + /** Basic maps. The default. */ + kGMSTypeNormal = 1, + + /** Satellite maps with no labels. */ + kGMSTypeSatellite, + + /** Terrain maps. */ + kGMSTypeTerrain, + + /** Satellite maps with a transparent label overview. */ + kGMSTypeHybrid, + + /** No maps, no labels. Display of traffic data is not supported. */ + kGMSTypeNone, + +} GMSMapViewType; + +/** + * This is the main class of the Google Maps SDK for iOS and is the entry point + * for all methods related to the map. + * + * The map should be instantiated via the convenience constructor + * [GMSMapView mapWithFrame:camera:]. It may also be created with the default + * [[GMSMapView alloc] initWithFrame:] method (wherein its camera will be set to + * a default location). + * + * GMSMapView can only be read and modified from the main thread, similar to all + * UIKit objects. Calling these methods from another thread will result in an + * exception or undefined behavior. + */ +@interface GMSMapView : UIView + +/** GMSMapView delegate. */ +@property(nonatomic, weak) IBOutlet id delegate; + +/** + * Controls the camera, which defines how the map is oriented. Modification of + * this property is instantaneous. + */ +@property(nonatomic, copy) GMSCameraPosition *camera; + +/** + * Returns a GMSProjection object that you can use to convert between screen + * coordinates and latitude/longitude coordinates. + * + * This is a snapshot of the current projection, and will not automatically + * update when the camera moves. It represents either the projection of the last + * drawn GMSMapView frame, or; where the camera has been explicitly set or the + * map just created, the upcoming frame. It will never be nil. + */ +@property(nonatomic, readonly) GMSProjection *projection; + +/** + * Controls whether the My Location dot and accuracy circle is enabled. + * Defaults to NO. + */ +@property(nonatomic, assign, getter=isMyLocationEnabled) BOOL myLocationEnabled; + +/** + * If My Location is enabled, reveals where the user location dot is being + * drawn. If it is disabled, or it is enabled but no location data is available, + * this will be nil. This property is observable using KVO. + */ +@property(nonatomic, strong, readonly) CLLocation *myLocation; + +/** + * The marker that is selected. Setting this property selects a particular + * marker, showing an info window on it. If this property is non-nil, setting + * it to nil deselects the marker, hiding the info window. This property is + * observable using KVO. + */ +@property(nonatomic, strong) GMSMarker *selectedMarker; + +/** + * Controls whether the map is drawing traffic data, if available. This is + * subject to the availability of traffic data. Defaults to NO. + */ +@property(nonatomic, assign, getter=isTrafficEnabled) BOOL trafficEnabled; + +/** + * Controls the type of map tiles that should be displayed. Defaults to + * kGMSTypeNormal. + */ +@property(nonatomic, assign) GMSMapViewType mapType; + +/** + * Minimum zoom (the farthest the camera may be zoomed out). Defaults to + * kGMSMinZoomLevel. Modified with -setMinZoom:maxZoom:. + */ +@property(nonatomic, assign, readonly) float minZoom; + +/** + * Maximum zoom (the closest the camera may be to the Earth). Defaults to + * kGMSMaxZoomLevel. Modified with -setMinZoom:maxZoom:. + */ +@property(nonatomic, assign, readonly) float maxZoom; + +/** + * If set, 3D buildings will be shown where available. Defaults to YES. + * + * This may be useful when adding a custom tile layer to the map, in order to + * make it clearer at high zoom levels. Changing this value will cause all + * tiles to be briefly invalidated. + */ +@property(nonatomic, assign, getter=isBuildingsEnabled) BOOL buildingsEnabled; + +/** + * Sets whether indoor maps are shown, where available. Defaults to YES. + * + * If this is set to NO, caches for indoor data may be purged and any floor + * currently selected by the end-user may be reset. + */ +@property(nonatomic, assign, getter=isIndoorEnabled) BOOL indoorEnabled; + +/** + * Gets the GMSIndoorDisplay instance which allows to observe or control + * aspects of indoor data display. + */ +@property(nonatomic, strong, readonly) GMSIndoorDisplay *indoorDisplay; + +/** + * Gets the GMSUISettings object, which controls user interface settings for the + * map. + */ +@property(nonatomic, strong, readonly) GMSUISettings *settings; + +/** + * Controls the 'visible' region of the view. By applying padding an area + * arround the edge of the view can be created which will contain map data + * but will not contain UI controls. + * + * If the padding is not balanced, the visual center of the view will move as + * appropriate. Padding will also affect the |projection| property so the + * visible region will not include the padding area. GMSCameraUpdate + * fitToBounds will ensure that both this padding and any padding requested + * will be taken into account. + * + * This property may be animated within a UIView-based animation block. + */ +@property(nonatomic, assign) UIEdgeInsets padding; + +/** + * Defaults to YES. If set to NO, GMSMapView will generate accessibility + * elements for overlay objects, such as GMSMarker and GMSPolyline. + * + * This property is as per the informal UIAcessibility protocol, except for the + * default value of YES. + */ +@property(nonatomic) BOOL accessibilityElementsHidden; + +/** + * Accessor for the custom CALayer type used for the layer. + */ +@property(nonatomic, readonly, retain) GMSMapLayer *layer; + +/** + * Builds and returns a GMSMapView, with a frame and camera target. + */ ++ (instancetype)mapWithFrame:(CGRect)frame camera:(GMSCameraPosition *)camera; + +/** + * Tells this map to power up its renderer. This is optional and idempotent. + * + * This method is obsolete and deprecated and will be removed in a future release. + */ +- (void)startRendering __GMS_AVAILABLE_BUT_DEPRECATED; + +/** + * Tells this map to power down its renderer. This is optional and idempotent. + * + * This method is obsolete and deprecated and will be removed in a future release. + */ +- (void)stopRendering __GMS_AVAILABLE_BUT_DEPRECATED; + +/** + * Clears all markup that has been added to the map, including markers, + * polylines and ground overlays. This will not clear the visible location dot + * or reset the current mapType. + */ +- (void)clear; + +/** + * Sets |minZoom| and |maxZoom|. This method expects the minimum to be less than + * or equal to the maximum, and will throw an exception with name + * NSRangeException otherwise. + */ +- (void)setMinZoom:(float)minZoom maxZoom:(float)maxZoom; + +/** + * Build a GMSCameraPosition that presents |bounds| with |padding|. The camera + * will have a zero bearing and tilt (i.e., facing north and looking directly at + * the Earth). This takes the frame and padding of this GMSMapView into account. + * + * If the bounds is nil or invalid this method will return a nil camera. + */ +- (GMSCameraPosition *)cameraForBounds:(GMSCoordinateBounds *)bounds + insets:(UIEdgeInsets)insets; + +/** + * Changes the camera according to |update|. + * The camera change is instantaneous (with no animation). + */ +- (void)moveCamera:(GMSCameraUpdate *)update; + +@end + +/** + * Accessibility identifier for the compass button. + */ +extern NSString *const kGMSAccessibilityCompass; + +/** + * Accessibility identifier for the "my location" button. + */ +extern NSString *const kGMSAccessibilityMyLocation; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h new file mode 100644 index 0000000..aaf0f99 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h @@ -0,0 +1,151 @@ +// +// GMSMarker.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSMarkerLayer; +@class GMSPanoramaView; +@class UIImage; + +/** + * Animation types for GMSMarker. + */ +typedef enum { + /** No animation (default). */ + kGMSMarkerAnimationNone = 0, + + /** The marker will pop from its groundAnchor when added. */ + kGMSMarkerAnimationPop, +} GMSMarkerAnimation; + +/** + * A marker is an icon placed at a particular point on the map's surface. A + * marker's icon is drawn oriented against the device's screen rather than the + * map's surface; i.e., it will not necessarily change orientation due to map + * rotations, tilting, or zooming. + */ +@interface GMSMarker : GMSOverlay + +/** Marker position. Animated. */ +@property(nonatomic, assign) CLLocationCoordinate2D position; + +/** Snippet text, shown beneath the title in the info window when selected. */ +@property(nonatomic, copy) NSString *snippet; + +/** + * Marker icon to render. If left nil, uses a default SDK place marker. + * + * Supports animated images, but each frame must be the same size or the + * behavior is undefined. + * + * Supports the use of alignmentRectInsets to specify a reduced tap area. This + * also redefines how anchors are specified. For an animated image the + * value for the animation is used, not the individual frames. + */ +@property(nonatomic, strong) UIImage *icon; + +/** + * The ground anchor specifies the point in the icon image that is anchored to + * the marker's position on the Earth's surface. This point is specified within + * the continuous space [0.0, 1.0] x [0.0, 1.0], where (0,0) is the top-left + * corner of the image, and (1,1) is the bottom-right corner. + * + * If the image has non-zero alignmentRectInsets, the top-left and bottom-right + * mentioned above refer to the inset section of the image. + */ +@property(nonatomic, assign) CGPoint groundAnchor; + +/** + * The info window anchor specifies the point in the icon image at which to + * anchor the info window, which will be displayed directly above this point. + * This point is specified within the same space as groundAnchor. + */ +@property(nonatomic, assign) CGPoint infoWindowAnchor; + +/** + * Controls the animation used when this marker is placed on a GMSMapView + * (default kGMSMarkerAnimationNone, no animation). + */ +@property(nonatomic, assign) GMSMarkerAnimation appearAnimation; + +/** + * Controls whether this marker can be dragged interactively (default NO). + */ +@property(nonatomic, assign, getter=isDraggable) BOOL draggable; + +/** + * Controls whether this marker should be flat against the Earth's surface (YES) + * or a billboard facing the camera (NO, default). + */ +@property(nonatomic, assign, getter=isFlat) BOOL flat; + +/** + * Sets the rotation of the marker in degrees clockwise about the marker's + * anchor point. The axis of rotation is perpendicular to the marker. A rotation + * of 0 corresponds to the default position of the marker. Animated. + * + * When the marker is flat on the map, the default position is north aligned and + * the rotation is such that the marker always remains flat on the map. When the + * marker is a billboard, the default position is pointing up and the rotation + * is such that the marker is always facing the camera. + */ +@property(nonatomic, assign) CLLocationDegrees rotation; + +/** + * Sets the opacity of the marker, between 0 (completely transparent) and 1 + * (default) inclusive. + */ +@property(nonatomic, assign) float opacity; + +/** + * Marker data. You can use this property to associate an arbitrary object with + * this marker. Google Maps SDK for iOS neither reads nor writes this property. + * + * Note that userData should not hold any strong references to any Maps + * objects, otherwise a loop may be created (preventing ARC from releasing + * objects). + */ +@property(nonatomic, strong) id userData; + +/** + * Provides the Core Animation layer for this GMSMarker. + */ +@property(nonatomic, strong, readonly) GMSMarkerLayer *layer; + +/** + * The |panoramaView| specifies which panorama view will attempt to show this + * marker. Note that if the marker's |position| is too far away from the + * |panoramaView|'s current panorama location, it will not be displayed as it + * will be too small. + * Can be set to nil to remove the marker from any current panorama view it + * is attached to. + * A marker can be shown on both a panorama and a map at the same time. + */ +@property(nonatomic, weak) GMSPanoramaView *panoramaView; + +/** Convenience constructor for a default marker. */ ++ (instancetype)markerWithPosition:(CLLocationCoordinate2D)position; + +/** Creates a tinted version of the default marker image for use as an icon. */ ++ (UIImage *)markerImageWithColor:(UIColor *)color; + +@end + +/** + * The default position of the ground anchor of a GMSMarker: the center bottom + * point of the marker icon. + */ +FOUNDATION_EXTERN const CGPoint kGMSMarkerDefaultGroundAnchor; + +/** + * The default position of the info window anchor of a GMSMarker: the center top + * point of the marker icon. + */ +FOUNDATION_EXTERN const CGPoint kGMSMarkerDefaultInfoWindowAnchor; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h new file mode 100644 index 0000000..91347b2 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h @@ -0,0 +1,42 @@ +// +// GMSMarkerLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +/** + * GMSMarkerLayer is a custom subclass of CALayer, available on a per-marker + * basis, that allows animation of several properties of its associated + * GMSMarker. + * + * Note that this CALayer is never actually rendered directly, as GMSMapView is + * provided entirely via an OpenGL layer. As such, adjustments or animations to + * 'default' properties of CALayer will not have any effect. + */ +@interface GMSMarkerLayer : CALayer + +/** Latitude, part of |position| on GMSMarker. */ +@property(nonatomic, assign) CLLocationDegrees latitude; + +/** Longitude, part of |position| on GMSMarker. */ +@property(nonatomic, assign) CLLocationDegrees longitude; + +/** Rotation, as per GMSMarker. */ +@property(nonatomic, assign) CLLocationDegrees rotation; + +/** Opacity, as per GMSMarker. */ +@property float opacity; + +@end + +extern NSString *const kGMSMarkerLayerLatitude; +extern NSString *const kGMSMarkerLayerLongitude; +extern NSString *const kGMSMarkerLayerRotation; +extern NSString *const kGMSMarkerLayerOpacity; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h new file mode 100644 index 0000000..4f0100c --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h @@ -0,0 +1,61 @@ +// +// GMSMutablePath.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import +#import + +/** + * GMSMutablePath is a dynamic (resizable) array of CLLocationCoordinate2D. All coordinates must be + * valid. GMSMutablePath is the mutable counterpart to the immutable GMSPath. + */ +@interface GMSMutablePath : GMSPath + +/** Adds |coord| at the end of the path. */ +- (void)addCoordinate:(CLLocationCoordinate2D)coord; + +/** Adds a new CLLocationCoordinate2D instance with the given lat/lng. */ +- (void)addLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude; + +/** + * Inserts |coord| at |index|. + * + * If this is smaller than the size of the path, shifts all coordinates forward by one. Otherwise, + * behaves as replaceCoordinateAtIndex:withCoordinate:. + */ +- (void)insertCoordinate:(CLLocationCoordinate2D)coord atIndex:(NSUInteger)index; + +/** + * Replace the coordinate at |index| with |coord|. If |index| is after the end, grows the array with + * an undefined coordinate. + */ +- (void)replaceCoordinateAtIndex:(NSUInteger)index + withCoordinate:(CLLocationCoordinate2D)coord; + +/** + * Remove entry at |index|. + * + * If |index| < count decrements size. If |index| >= count this is a silent + * no-op. + */ +- (void)removeCoordinateAtIndex:(NSUInteger)index; + +/** + * Removes the last coordinate of the path. + * + * If the array is non-empty decrements size. If the array is empty, this is a silent no-op. + */ +- (void)removeLastCoordinate; + +/** Removes all coordinates in this path. */ +- (void)removeAllCoordinates; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h new file mode 100644 index 0000000..9a975c4 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h @@ -0,0 +1,40 @@ +// +// GMSOrientation.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSOrientation is a tuple of heading and pitch used to control the viewing direction of a + * GMSPanoramaCamera. + */ +typedef struct { + /** The camera heading (horizontal angle) in degrees. */ + const CLLocationDirection heading; + + /** + * The camera pitch (vertical angle), in degrees from the horizon. The |pitch| range is [-90,90], + * although it is possible that not the full range is supported. + */ + const double pitch; +} GMSOrientation; + +#ifdef __cplusplus +extern "C" { +#endif + +/** Returns a GMSOrientation with the given |heading| and |pitch|. */ +inline GMSOrientation GMSOrientationMake(CLLocationDirection heading, double pitch) { + GMSOrientation orientation = {heading, pitch}; + return orientation; +} + +#ifdef __cplusplus +} +#endif diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h new file mode 100644 index 0000000..a404868 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h @@ -0,0 +1,56 @@ +// +// GMSOverlay.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSMapView; + +/** + * GMSOverlay is an abstract class that represents some overlay that may be + * attached to a specific GMSMapView. It may not be instantiated directly; + * instead, instances of concrete overlay types should be created directly + * (such as GMSMarker, GMSPolyline, and GMSPolygon). + * + * This supports the NSCopying protocol; [overlay_ copy] will return a copy + * of the overlay type, but with |map| set to nil. + */ +@interface GMSOverlay : NSObject + +/** + * Title, a short description of the overlay. Some overlays, such as markers, + * will display the title on the map. The title is also the default + * accessibility text. + */ +@property(nonatomic, copy) NSString *title; + +/** + * The map this overlay is on. Setting this property will add the overlay to the + * map. Setting it to nil removes this overlay from the map. An overlay may be + * active on at most one map at any given time. + */ +@property(nonatomic, weak) GMSMapView *map; + +/** + * If this overlay should cause tap notifications. Some overlays, such as + * markers, will default to being tappable. + */ +@property(nonatomic, assign, getter=isTappable) BOOL tappable; + +/** + * Higher |zIndex| value overlays will be drawn on top of lower |zIndex| + * value tile layers and overlays. Equal values result in undefined draw + * ordering. Markers are an exception that regardless of |zIndex|, they will + * always be drawn above tile layers and other non-marker overlays; they + * are effectively considered to be in a separate z-index group compared to + * other overlays. + */ +@property(nonatomic, assign) int zIndex; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h new file mode 100644 index 0000000..25a4e90 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h @@ -0,0 +1,28 @@ +// +// GMSPanorama.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSPanorama represents metadata for a specific panorama on the Earth. This class is not + * instantiable directly and is obtained via GMSPanoramaService or GMSPanoramaView. + */ +@interface GMSPanorama : NSObject + +/** The precise location of this panorama. */ +@property(nonatomic, readonly) CLLocationCoordinate2D coordinate; + +/** The ID of this panorama. Panoramas may change ID over time, so this should not be persisted */ +@property(nonatomic, copy, readonly) NSString *panoramaID; + +/** An array of GMSPanoramaLink describing the neighboring panoramas. */ +@property(nonatomic, copy, readonly) NSArray *links; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h new file mode 100644 index 0000000..bf02be4 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h @@ -0,0 +1,77 @@ +// +// GMSPanoramaCamera.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +/** + * GMSPanoramaCamera is used to control the viewing direction of a GMSPanoramaView. It does not + * contain information about which particular panorama should be displayed. + */ +@interface GMSPanoramaCamera : NSObject + +/** + * Designated initializer. Configures this GMSPanoramaCamera with |orientation|, |zoom| and |FOV|. + * These values will be clamped to acceptable ranges. + */ +- (id)initWithOrientation:(GMSOrientation)orientation zoom:(float)zoom FOV:(double)FOV; + +/** + * Convenience constructor specifying heading and pitch as part of |orientation|, plus |zoom| and + * default field of view (90 degrees). + */ ++ (instancetype)cameraWithOrientation:(GMSOrientation)orientation zoom:(float)zoom; + +/** + * Convenience constructor specifying |heading|, |pitch|, |zoom| with default field of view (90 + * degrees). + */ ++ (instancetype)cameraWithHeading:(CLLocationDirection)heading pitch:(double)pitch zoom:(float)zoom; + +/** + * Convenience constructor for GMSPanoramaCamera, specifying all camera properties with heading and + * pitch as part of |orientation|. + */ ++ (instancetype)cameraWithOrientation:(GMSOrientation)orientation zoom:(float)zoom FOV:(double)FOV; + +/** + * Convenience constructor for GMSPanoramaCamera, specifying all camera properties. + */ ++ (instancetype)cameraWithHeading:(CLLocationDirection)heading + pitch:(double)pitch + zoom:(float)zoom + FOV:(double)FOV; + +/** + * The field of view (FOV) encompassed by the larger dimension (width or height) of the view in + * degrees at zoom 1. This is clamped to the range [1, 160] degrees, and has a default value of 90. + * + * Lower FOV values produce a zooming in effect; larger FOV values produce an fisheye effect. + * + * Note: This is not the displayed FOV if zoom is anything other than 1. User zoom gestures + * control the zoom property, not this property. + */ +@property(nonatomic, assign, readonly) double FOV; + +/** + * Adjusts the visible region of the screen. A zoom of N will show the same area as the central + * width/N height/N area of what is shown at zoom 1. + * + * Zoom is clamped to the implementation defined range [1, 5]. + */ +@property(nonatomic, assign, readonly) float zoom; + +/** + * The camera orientation, which groups together heading and pitch. + */ +@property(nonatomic, assign, readonly) GMSOrientation orientation; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h new file mode 100644 index 0000000..e97aabd --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h @@ -0,0 +1,25 @@ +// +// GMSPanoramaCameraUpdate.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +@interface GMSPanoramaCameraUpdate : NSObject + +/** Returns an update that increments the camera heading with |deltaHeading|. */ ++ (GMSPanoramaCameraUpdate *)rotateBy:(CGFloat)deltaHeading; + +/** Returns an update that sets the camera heading to the given value. */ ++ (GMSPanoramaCameraUpdate *)setHeading:(CGFloat)heading; + +/** Returns an update that sets the camera pitch to the given value. */ ++ (GMSPanoramaCameraUpdate *)setPitch:(CGFloat)pitch; + +/** Returns an update that sets the camera zoom to the given value. */ ++ (GMSPanoramaCameraUpdate *)setZoom:(CGFloat)zoom; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h new file mode 100644 index 0000000..7dd0524 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h @@ -0,0 +1,37 @@ +// +// GMSPanoramaLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +#import + +/** kGMSLayerPanoramaHeadingKey ranges from [0, 360). */ +extern NSString *const kGMSLayerPanoramaHeadingKey; + +/** kGMSLayerPanoramaPitchKey ranges from [-90, 90]. */ +extern NSString *const kGMSLayerPanoramaPitchKey; + +/** kGMSLayerCameraZoomLevelKey ranges from [1, 5], default 1. */ +extern NSString *const kGMSLayerPanoramaZoomKey; + +/** kGMSLayerPanoramaFOVKey ranges from [1, 160] (in degrees), default 90. */ +extern NSString *const kGMSLayerPanoramaFOVKey; + +/** + * GMSPanoramaLayer is a custom subclass of CALayer, provided as the layer + * class on GMSPanoramaView. This layer should not be instantiated directly. + */ +@interface GMSPanoramaLayer : GMSCALayer +@property(nonatomic, assign) CLLocationDirection cameraHeading; +@property(nonatomic, assign) double cameraPitch; +@property(nonatomic, assign) float cameraZoom; +@property(nonatomic, assign) double cameraFOV; +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h new file mode 100644 index 0000000..f741d0e --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h @@ -0,0 +1,23 @@ +// +// GMSPanoramaLink.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** Links from a GMSPanorama to neighboring panoramas. */ +@interface GMSPanoramaLink : NSObject + +/** Angle of the neighboring panorama, clockwise from north in degrees. */ +@property(nonatomic, assign) CGFloat heading; + +/** + * Panorama ID for the neighboring panorama. + * Do not store this persistenly, it changes in time. + */ +@property(nonatomic, copy) NSString *panoramaID; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h new file mode 100644 index 0000000..0d72e61 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h @@ -0,0 +1,52 @@ +// +// GMSPanoramaService.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSPanorama; + +/** + * Callback for when a panorama metadata becomes available. + * If an error occured, |panorama| is nil and |error| is not nil. + * Otherwise, |panorama| is not nil and |error| is nil. + */ +typedef void (^GMSPanoramaCallback)(GMSPanorama *panorama, NSError *error); + +/** + * GMSPanoramaService can be used to request panorama metadata even when a + * GMSPanoramaView is not active. + * Get an instance like this: [[GMSPanoramaService alloc] init]. + */ +@interface GMSPanoramaService : NSObject + +/** + * Retrieves information about a panorama near the given |coordinate|. + * This is an asynchronous request, |callback| will be called with the result. + */ +- (void)requestPanoramaNearCoordinate:(CLLocationCoordinate2D)coordinate + callback:(GMSPanoramaCallback)callback; + +/** + * Similar to requestPanoramaNearCoordinate:callback: but allows specifying + * a search radius (meters) around |coordinate|. + */ +- (void)requestPanoramaNearCoordinate:(CLLocationCoordinate2D)coordinate + radius:(NSUInteger)radius + callback:(GMSPanoramaCallback)callback; + +/** + * Retrieves information about a panorama with the given |panoramaID|. + * |callback| will be called with the result. Only panoramaIDs obtained + * from the Google Maps SDK for iOS are supported. + */ +- (void)requestPanoramaWithID:(NSString *)panoramaID + callback:(GMSPanoramaCallback)callback; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h new file mode 100644 index 0000000..cf3895d --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h @@ -0,0 +1,248 @@ +// +// GMSPanoramaView.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import +#import + +@class GMSMarker; +@class GMSPanorama; +@class GMSPanoramaCamera; +@class GMSPanoramaCameraUpdate; +@class GMSPanoramaView; + +/** Delegate for events on GMSPanoramaView. */ +@protocol GMSPanoramaViewDelegate +@optional + +/** + * Called when starting a move to another panorama. + * This can be the result of interactive navigation to a neighbouring panorama. + * At the moment this method is called, the |view|.panorama is still + * pointing to the old panorama, as the new panorama identified by |panoID| + * is not yet resolved. panoramaView:didMoveToPanorama: will be called when the + * new panorama is ready. + */ +- (void)panoramaView:(GMSPanoramaView *)view + willMoveToPanoramaID:(NSString *)panoramaID; + +/** + * This is invoked every time the |view|.panorama property changes. + */ +- (void)panoramaView:(GMSPanoramaView *)view + didMoveToPanorama:(GMSPanorama *)panorama; + +/** + * Called when the panorama change was caused by invoking + * moveToPanoramaNearCoordinate:. The coordinate passed to that method will also + * be passed here. + */ +- (void)panoramaView:(GMSPanoramaView *)view + didMoveToPanorama:(GMSPanorama *)panorama + nearCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Called when moveNearCoordinate: produces an error. + */ +- (void)panoramaView:(GMSPanoramaView *)view + error:(NSError *)error + onMoveNearCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Called when moveToPanoramaID: produces an error. + */ +- (void)panoramaView:(GMSPanoramaView *)view + error:(NSError *)error + onMoveToPanoramaID:(NSString *)panoramaID; + +/** + * Called repeatedly during changes to the camera on GMSPanoramaView. This may + * not be called for all intermediate camera values, but is always called for + * the final position of the camera after an animation or gesture. + */ +- (void)panoramaView:(GMSPanoramaView *)panoramaView + didMoveCamera:(GMSPanoramaCamera *)camera; + +/** + * Called when a user has tapped on the GMSPanoramaView, but this tap was not + * consumed (taps may be consumed by e.g., tapping on a navigation arrow). + */ +- (void)panoramaView:(GMSPanoramaView *)panoramaView didTap:(CGPoint)point; + +/** + * Called after a marker has been tapped. May return YES to indicate the event + * has been fully handled and suppress any default behavior. + */ +- (BOOL)panoramaView:(GMSPanoramaView *)panoramaView + didTapMarker:(GMSMarker *)marker; + +@end + +/** + * A panorama is used to display Street View imagery. It should be constructed + * via [[GMSPanoramaView alloc] initWithFrame:], and configured + * post-initialization. + * + * All properties and methods should be accessed on the main thread, similar to + * all UIKit objects. The GMSPanoramaViewDelegate methods will also be called + * back only on the main thread. + * + * The backgroundColor of this view is shown while no panorama is visible, such + * as while it is loading or if the panorama is later set to nil. The alpha + * color of backgroundColor is not supported. + */ +@interface GMSPanoramaView : UIView + +/** + * The panorama to display; setting it will transition to a new panorama. This + * is animated, except for the initial panorama. + * + * Can be set to nil to clear the view. + */ +@property(nonatomic, strong) GMSPanorama *panorama; + +/** GMSPanoramaView delegate. */ +@property(nonatomic, weak) IBOutlet id delegate; + +/** + * Sets the preference for whether all gestures should be enabled (default) or + * disabled. + * This does not limit programmatic movement of the camera or control of the + * panorama. + */ +- (void)setAllGesturesEnabled:(BOOL)enabled; + +/** + * Controls whether orientation gestures are enabled (default) or disabled. If + * enabled, users may use gestures to change the orientation of the camera. + * This does not limit programmatic movement of the camera. + */ +@property(nonatomic, assign) BOOL orientationGestures; + +/** + * Controls whether zoom gestures are enabled (default) or disabled. If + * enabled, users may pinch to zoom the camera. + * This does not limit programmatic movement of the camera. + */ +@property(nonatomic, assign) BOOL zoomGestures; + +/** + * Controls whether navigation gestures are enabled (default) or disabled. If + * enabled, users may use a single tap on navigation links or double tap the + * view to change panoramas. + * This does not limit programmatic control of the panorama. + */ +@property(nonatomic, assign) BOOL navigationGestures; + +/** + * Controls whether the tappable navigation links are hidden or visible + * (default). + * Hidden navigation links cannot be tapped. + */ +@property(nonatomic, assign) BOOL navigationLinksHidden; + +/** + * Controls whether the street name overlays are hidden or visible (default). + */ +@property(nonatomic, assign) BOOL streetNamesHidden; + +/** + * Controls the panorama's camera. Setting a new camera here jumps to the new + * camera value, with no animation. + */ +@property(nonatomic, strong) GMSPanoramaCamera *camera; + +/** + * Accessor for the custom CALayer type used for the layer. + */ +@property(nonatomic, readonly, retain) GMSPanoramaLayer *layer; + +/** + * Animates the camera of this GMSPanoramaView to |camera|, over |duration| + * (specified in seconds). + */ +- (void)animateToCamera:(GMSPanoramaCamera *)camera + animationDuration:(NSTimeInterval)duration; + +/** + * Modifies the camera according to |cameraUpdate|, over |duration| (specified + * in seconds). + */ +- (void)updateCamera:(GMSPanoramaCameraUpdate *)cameraUpdate + animationDuration:(NSTimeInterval)duration; + +/** + * Requests a panorama near |coordinate|. + * Upon successful completion panoramaView:didMoveToPanorama: and + * panoramaView:didMoveToPanorama:nearCoordinate: will be sent to + * GMSPanoramaViewDelegate. + * On error panoramaView:error:onMoveNearCoordinate: will be sent. + * Repeated calls to moveNearCoordinate: result in the previous pending + * (incomplete) transitions being cancelled -- only the most recent of + * moveNearCoordinate: and moveToPanoramaId: will proceed and generate events. + */ +- (void)moveNearCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Similar to moveNearCoordinate: but allows specifying a search radius (meters) + * around |coordinate|. + */ +- (void)moveNearCoordinate:(CLLocationCoordinate2D)coordinate + radius:(NSUInteger)radius; + +/** + * Requests a panorama with |panoramaID|. + * Upon successful completion panoramaView:didMoveToPanorama: will be sent to + * GMSPanoramaViewDelegate. + * On error panoramaView:error:onMoveToPanoramaID: will be sent. + * Repeated calls to moveToPanoramaID: result in the previous pending + * (incomplete) transitions being cancelled -- only the most recent of + * moveNearCoordinate: and moveToPanoramaId: will proceed and generate events. + * Only panoramaIDs obtained from the Google Maps SDK for iOS are supported. + */ +- (void)moveToPanoramaID:(NSString *)panoramaID; + +/** + * For the current view, returns the screen point the |orientation| points + * through. This value may be outside the view for forward facing orientations + * which are far enough away from straight ahead. + * The result will contain NaNs for camera orientations which point away from + * the view, where the implied screen point would have had a negative distance + * from the camera in the direction of orientation. + */ +- (CGPoint)pointForOrientation:(GMSOrientation)orientation; + +/** + * Given a point for this view, returns the current camera orientation pointing + * through that screen location. At the center of this view, the returned + * GMSOrientation will be approximately equal to that of the current + * GMSPanoramaCamera. + */ +- (GMSOrientation)orientationForPoint:(CGPoint)point; + +/** + * Convenience constructor for GMSPanoramaView, which searches for and displays + * a GMSPanorama near |coordinate|. This performs a similar action to that of + * moveNearCoordinate:, and will call the same delegate methods. + */ ++ (instancetype)panoramaWithFrame:(CGRect)frame + nearCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Similar to panoramaWithFrame:nearCoordinate: but allows specifying a + * search radius (meters) around |coordinate|. + */ ++ (instancetype)panoramaWithFrame:(CGRect)frame + nearCoordinate:(CLLocationCoordinate2D)coordinate + radius:(NSUInteger)radius; + + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h new file mode 100644 index 0000000..6b84012 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h @@ -0,0 +1,103 @@ +// +// GMSPath.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSPath encapsulates an immutable array of CLLocationCooordinate2D. All the coordinates of a + * GMSPath must be valid. The mutable counterpart is GMSMutablePath. + */ +@interface GMSPath : NSObject + +/** Convenience constructor for an empty path. */ ++ (instancetype)path; + +/** Initializes a newly allocated path with the contents of another GMSPath. */ +- (id)initWithPath:(GMSPath *)path; + +/** Get size of path. */ +- (NSUInteger)count; + +/** Returns kCLLocationCoordinate2DInvalid if |index| >= count. */ +- (CLLocationCoordinate2D)coordinateAtIndex:(NSUInteger)index; + +/** + * Initializes a newly allocated path from |encodedPath|. This format is described at: + * https://developers.google.com/maps/documentation/utilities/polylinealgorithm + */ ++ (instancetype)pathFromEncodedPath:(NSString *)encodedPath; + +/** Returns an encoded string of the path in the format described above. */ +- (NSString *)encodedPath; + +/** + * Returns a new path obtained by adding |deltaLatitude| and |deltaLongitude| to each coordinate + * of the current path. Does not modify the current path. + */ +- (instancetype)pathOffsetByLatitude:(CLLocationDegrees)deltaLatitude + longitude:(CLLocationDegrees)deltaLongitude; + +@end + + +/** + * kGMSEquatorProjectedMeter may be useful when specifying lengths for segment in "projected" units. + * The value of kGMSEquatorProjectedMeter, 1/(pi * EarthRadius), represents the length of one meter + * at the equator in projected units. For example to specify a projected length that corresponds + * to 100km at the equator use 100000 * kGMSEquatorProjectedMeter. + * See [GMSPath segmentsForLength:kind:], [GMSPath lengthOfKind:] and kGMSLengthProjected. + */ +extern const double kGMSEquatorProjectedMeter; + +/** + * GMSLengthKind indicates the type of a length value, which can be geodesic (in meters), rhumb + * length (in meters) and projected length (in GMSMapPoint units). + */ +typedef enum { + /* + * Geodesic length, in meters, along geodesic segments. May be useful, for example, to specify + * lengths along the the trajectory of airplanes or ships. + */ + kGMSLengthGeodesic, + + /* + * Rhumb length, in meters, along rhumb (straight line) segments. May be useful, for example, to + * draw a scale bar on a map. The visual size of a segment of a given length depens on the + * latitude. + */ + kGMSLengthRhumb, + + /* + * Length in projected space, along rhumb segments. Projected length uses the same units as + * GMSMapPoint - the Earth equator circumference has length 2. It is possible to specify projected + * length in units corresponding to 1 meter at the equator by multiplying with + * kGMSEquatorProjectedMeter, equal to 1/(pi * EarthRadius). + * + * Projected length may be useful, for example, to specify segments with the same visual length + * regardless of latitude. + */ + kGMSLengthProjected +} GMSLengthKind; + + +@interface GMSPath (GMSPathLength) + +/** + * Returns the fractional number of segments along the path that correspond to |length|, + * interpreted according to |kind|. See GMSLengthKind. + */ +- (double)segmentsForLength:(CLLocationDistance)length kind:(GMSLengthKind)kind; + +/** + * Returns the length of the path, according to |kind|. See GMSLengthKind. + */ +- (CLLocationDistance)lengthOfKind:(GMSLengthKind)kind; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlace.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlace.h new file mode 100644 index 0000000..ca2ad4c --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlace.h @@ -0,0 +1,109 @@ +// +// GMSPlace.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import + +@class GMSPlaceUserData; + + +/** Describes the current open status of a place. */ +typedef NS_ENUM(NSInteger, GMSPlacesOpenNowStatus) { + /** The place is open now. */ + kGMSPlacesOpenNowStatusYes, + /** The place is not open now. */ + kGMSPlacesOpenNowStatusNo, + /** We don't know whether the place is open now. */ + kGMSPlacesOpenNowStatusUnknown, +}; + +typedef NS_ENUM(NSInteger, GMSPlacesPriceLevel) { + kGMSPlacesPriceLevelUnknown = -1, + kGMSPlacesPriceLevelFree = 0, + kGMSPlacesPriceLevelCheap = 1, + kGMSPlacesPriceLevelMedium = 2, + kGMSPlacesPriceLevelHigh = 3, + kGMSPlacesPriceLevelExpensive = 4, +}; + +/** + * Represents a particular physical place. A GMSPlace encapsulates information about a physical + * location, including its name, location, and any other information we might have about it. This + * class is immutable. + */ +@interface GMSPlace : NSObject + +/** Name of the place. */ +@property(nonatomic, copy, readonly) NSString *name; + +/** Place ID of this place. */ +@property(nonatomic, copy, readonly) NSString *placeID; + +/** + * Location of the place. The location is not necessarily the center of the Place, or any + * particular entry or exit point, but some arbitrarily chosen point within the geographic extent of + * the Place. + */ +@property(nonatomic, readonly) CLLocationCoordinate2D coordinate; + +/** + * Represents the open now status of the place at the time that the place was created. + */ +@property(nonatomic, readonly) GMSPlacesOpenNowStatus openNowStatus; + +/** + * Phone number of this place, in international format, i.e. including the country code prefixed + * with "+". For example, Google Sydney's phone number is "+61 2 9374 4000". + */ +@property(nonatomic, copy, readonly) NSString *phoneNumber; + +/** + * Address of the place as a simple string. + */ +@property(nonatomic, copy, readonly) NSString *formattedAddress; + +/** + * Five-star rating for this place based on user reviews. + * + * Ratings range from 1.0 to 5.0. 0.0 means we have no rating for this place (e.g. because not + * enough users have reviewed this place). + */ +@property(nonatomic, readonly) float rating; + +/** + * Price level for this place, as integers from 0 to 4. + * + * e.g. A value of 4 means this place is "$$$$" (expensive). A value of 0 means free (such as a + * museum with free admission). + */ +@property(nonatomic, readonly) GMSPlacesPriceLevel priceLevel; + +/** + * The types of this place. Types are NSStrings, valid values are any types documented at + * . + */ +@property(nonatomic, copy, readonly) NSArray *types; + +/** Website for this place. */ +@property(nonatomic, copy, readonly) NSURL *website; + +/** + * The data provider attribution string for this place. + * + * These are provided as a NSAttributedString, which may contain hyperlinks to the website of each + * provider. + * + * In general, these must be shown to the user if data from this GMSPlace is shown, as described in + * the Places API Terms of Service. + */ +@property(nonatomic, copy, readonly) NSAttributedString *attributions; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihood.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihood.h new file mode 100644 index 0000000..e7a059a --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihood.h @@ -0,0 +1,35 @@ +// +// GMSPlaceLikelihood.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + +@class GMSPlace; + +/** + * Represents a |GMSPlace| and the relative likelihood of the place being the best match within the + * list of returned places for a single request. For more information about place likelihoods, see + * |GMSPlaceLikelihoodList|. + */ +@interface GMSPlaceLikelihood : NSObject + +/** + * The place contained in this place likelihood. + */ +@property(nonatomic, strong, readonly) GMSPlace *place; + +/** + * Returns a value from 0.0 to 1.0 indicating the confidence that the user is at this place. The + * larger the value the more confident we are of the place returned. For example, a likelihood of + * 0.75 means that the user is at least 75% likely to be at this place. + */ +@property(nonatomic, assign, readonly) double likelihood; + +- (instancetype)initWithPlace:(GMSPlace *)place likelihood:(double)likelihood; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h new file mode 100644 index 0000000..1baadf1 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h @@ -0,0 +1,34 @@ +// +// GMSPlaceLikelihoodList.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** + * Represents a list of places with an associated likelihood for the place being the correct place. + * For example, the Places service may be uncertain what the true Place is, but think it 55% likely + * to be PlaceA, and 35% likely to be PlaceB. The corresponding likelihood list has two members, one + * with likelihood 0.55 and the other with likelihood 0.35. The likelihoods are not guaranteed to be + * correct, and in a given place likelihood list they may not sum to 1.0. + */ +@interface GMSPlaceLikelihoodList : NSObject + +/** An array of |GMSPlaceLikelihood|s containing the likelihoods in the list. */ +@property(nonatomic, copy) NSArray *likelihoods; + +/** + * The data provider attribution strings for the likelihood list. + * + * These are provided as a NSAttributedString, which may contain hyperlinks to the website of each + * provider. + * + * In general, these must be shown to the user if data from this likelihood list is shown, as + * described in the Places API Terms of Service. + */ +@property(nonatomic, copy, readonly) NSAttributedString *attributions; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePicker.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePicker.h new file mode 100644 index 0000000..2edfb89 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePicker.h @@ -0,0 +1,72 @@ +// +// GMSPlacePicker.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + + +/* Error domain used for Place Picker errors. */ +extern NSString * const kGMSPlacePickerErrorDomain; + +/* Error codes for |kGMSPlacePickerErrorDomain|. */ +typedef NS_ENUM(NSInteger, GMSPlacePickerErrorCode) { + /** + * Something unknown went wrong. + */ + kGMSPlacePickerUnknownError = -1, + /** + * An internal error occurred in the Places API library. + */ + kGMSPlacePickerInternalError = -2, + /** + * An invalid GMSPlacePickerConfig was used. + */ + kGMSPlacePickerInvalidConfig = -3, + /** + * Attempted to perform simultaneous place picking operations. + */ + kGMSPlacePickerOverlappingCalls = -4, +}; + +/** + * The Place Picker is a dialog that allows the user to pick a |GMSPlace| using an interactive map + * and other tools. Users can select the place they're at or nearby. + */ +@interface GMSPlacePicker : NSObject + +/** + * The configuration of the place picker, as passed in at initialization. + */ +@property(nonatomic, readonly, copy) GMSPlacePickerConfig *config; + +/** + * Initializes the place picker with a given configuration. This does not start the process of + * picking a place. + */ +- (instancetype)initWithConfig:(GMSPlacePickerConfig *)config; + +/** + * Prompt the user to pick a place. The place picker is a full-screen window that appears on + * [UIScreen mainScreen]. The place picker takes over the screen until the user cancels the + * operation or picks a place. The supplied callback will be invoked with the chosen place, or nil + * if no place was chosen. + * + * This method should be called on the main thread. The callback will also be invoked on the main + * thread. + * + * It is not possible to have multiple place picking operations active at the same time. If this is + * attempted, the second callback will be invoked with an error. + * + * A reference to the place picker must be retained for the duration of the place picking operation. + * If the retain count of the place picker object becomes 0, the picking operation will be cancelled + * and the callback will not be invoked. + */ +- (void)pickPlaceWithCallback:(GMSPlaceResultCallback)callback; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePickerConfig.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePickerConfig.h new file mode 100644 index 0000000..bf9da34 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePickerConfig.h @@ -0,0 +1,31 @@ +// +// GMSPlacePickerConfig.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + + +/** + * Configuration object used to change the behaviour of the place picker. + */ +@interface GMSPlacePickerConfig : NSObject + +/** + * The initial viewport that the place picker map should show. If this is nil, a sensible default + * will be chosen based on the user's location. + */ +@property(nonatomic, strong, readonly) GMSCoordinateBounds *viewport; + +/** + * Initialize the configuration. + */ +- (instancetype)initWithViewport:(GMSCoordinateBounds *)viewport; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceTypes.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceTypes.h new file mode 100644 index 0000000..574a715 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceTypes.h @@ -0,0 +1,137 @@ +// +// GMSPlaceTypes.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + + + +extern NSString *const kGMSPlaceTypeAccounting; +extern NSString *const kGMSPlaceTypeAdministrativeAreaLevel1; +extern NSString *const kGMSPlaceTypeAdministrativeAreaLevel2; +extern NSString *const kGMSPlaceTypeAdministrativeAreaLevel3; +extern NSString *const kGMSPlaceTypeAirport; +extern NSString *const kGMSPlaceTypeAmusementPark; +extern NSString *const kGMSPlaceTypeAquarium; +extern NSString *const kGMSPlaceTypeArtGallery; +extern NSString *const kGMSPlaceTypeAtm; +extern NSString *const kGMSPlaceTypeBakery; +extern NSString *const kGMSPlaceTypeBank; +extern NSString *const kGMSPlaceTypeBar; +extern NSString *const kGMSPlaceTypeBeautySalon; +extern NSString *const kGMSPlaceTypeBicycleStore; +extern NSString *const kGMSPlaceTypeBookStore; +extern NSString *const kGMSPlaceTypeBowlingAlley; +extern NSString *const kGMSPlaceTypeBusStation; +extern NSString *const kGMSPlaceTypeCafe; +extern NSString *const kGMSPlaceTypeCampground; +extern NSString *const kGMSPlaceTypeCarDealer; +extern NSString *const kGMSPlaceTypeCarRental; +extern NSString *const kGMSPlaceTypeCarRepair; +extern NSString *const kGMSPlaceTypeCarWash; +extern NSString *const kGMSPlaceTypeCasino; +extern NSString *const kGMSPlaceTypeCemetery; +extern NSString *const kGMSPlaceTypeChurch; +extern NSString *const kGMSPlaceTypeCityHall; +extern NSString *const kGMSPlaceTypeClothingStore; +extern NSString *const kGMSPlaceTypeColloquialArea; +extern NSString *const kGMSPlaceTypeConvenienceStore; +extern NSString *const kGMSPlaceTypeCountry; +extern NSString *const kGMSPlaceTypeCourthouse; +extern NSString *const kGMSPlaceTypeDentist; +extern NSString *const kGMSPlaceTypeDepartmentStore; +extern NSString *const kGMSPlaceTypeDoctor; +extern NSString *const kGMSPlaceTypeElectrician; +extern NSString *const kGMSPlaceTypeElectronicsStore; +extern NSString *const kGMSPlaceTypeEmbassy; +extern NSString *const kGMSPlaceTypeEstablishment; +extern NSString *const kGMSPlaceTypeFinance; +extern NSString *const kGMSPlaceTypeFireStation; +extern NSString *const kGMSPlaceTypeFloor; +extern NSString *const kGMSPlaceTypeFlorist; +extern NSString *const kGMSPlaceTypeFood; +extern NSString *const kGMSPlaceTypeFuneralHome; +extern NSString *const kGMSPlaceTypeFurnitureStore; +extern NSString *const kGMSPlaceTypeGasStation; +extern NSString *const kGMSPlaceTypeGeneralContractor; +extern NSString *const kGMSPlaceTypeGeocode; +extern NSString *const kGMSPlaceTypeGroceryOrSupermarket; +extern NSString *const kGMSPlaceTypeGym; +extern NSString *const kGMSPlaceTypeHairCare; +extern NSString *const kGMSPlaceTypeHardwareStore; +extern NSString *const kGMSPlaceTypeHealth; +extern NSString *const kGMSPlaceTypeHinduTemple; +extern NSString *const kGMSPlaceTypeHomeGoodsStore; +extern NSString *const kGMSPlaceTypeHospital; +extern NSString *const kGMSPlaceTypeInsuranceAgency; +extern NSString *const kGMSPlaceTypeIntersection; +extern NSString *const kGMSPlaceTypeJewelryStore; +extern NSString *const kGMSPlaceTypeLaundry; +extern NSString *const kGMSPlaceTypeLawyer; +extern NSString *const kGMSPlaceTypeLibrary; +extern NSString *const kGMSPlaceTypeLiquorStore; +extern NSString *const kGMSPlaceTypeLocalGovernmentOffice; +extern NSString *const kGMSPlaceTypeLocality; +extern NSString *const kGMSPlaceTypeLocksmith; +extern NSString *const kGMSPlaceTypeLodging; +extern NSString *const kGMSPlaceTypeMealDelivery; +extern NSString *const kGMSPlaceTypeMealTakeaway; +extern NSString *const kGMSPlaceTypeMosque; +extern NSString *const kGMSPlaceTypeMovieRental; +extern NSString *const kGMSPlaceTypeMovieTheater; +extern NSString *const kGMSPlaceTypeMovingCompany; +extern NSString *const kGMSPlaceTypeMuseum; +extern NSString *const kGMSPlaceTypeNaturalFeature; +extern NSString *const kGMSPlaceTypeNeighborhood; +extern NSString *const kGMSPlaceTypeNightClub; +extern NSString *const kGMSPlaceTypePainter; +extern NSString *const kGMSPlaceTypePark; +extern NSString *const kGMSPlaceTypeParking; +extern NSString *const kGMSPlaceTypePetStore; +extern NSString *const kGMSPlaceTypePharmacy; +extern NSString *const kGMSPlaceTypePhysiotherapist; +extern NSString *const kGMSPlaceTypePlaceOfWorship; +extern NSString *const kGMSPlaceTypePlumber; +extern NSString *const kGMSPlaceTypePointOfInterest; +extern NSString *const kGMSPlaceTypePolice; +extern NSString *const kGMSPlaceTypePolitical; +extern NSString *const kGMSPlaceTypePostBox; +extern NSString *const kGMSPlaceTypePostOffice; +extern NSString *const kGMSPlaceTypePostalCode; +extern NSString *const kGMSPlaceTypePostalCodePrefix; +extern NSString *const kGMSPlaceTypePostalTown; +extern NSString *const kGMSPlaceTypePremise; +extern NSString *const kGMSPlaceTypeRealEstateAgency; +extern NSString *const kGMSPlaceTypeRestaurant; +extern NSString *const kGMSPlaceTypeRoofingContractor; +extern NSString *const kGMSPlaceTypeRoom; +extern NSString *const kGMSPlaceTypeRoute; +extern NSString *const kGMSPlaceTypeRvPark; +extern NSString *const kGMSPlaceTypeSchool; +extern NSString *const kGMSPlaceTypeShoeStore; +extern NSString *const kGMSPlaceTypeShoppingMall; +extern NSString *const kGMSPlaceTypeSpa; +extern NSString *const kGMSPlaceTypeStadium; +extern NSString *const kGMSPlaceTypeStorage; +extern NSString *const kGMSPlaceTypeStore; +extern NSString *const kGMSPlaceTypeStreetAddress; +extern NSString *const kGMSPlaceTypeSublocality; +extern NSString *const kGMSPlaceTypeSublocalityLevel1; +extern NSString *const kGMSPlaceTypeSublocalityLevel2; +extern NSString *const kGMSPlaceTypeSublocalityLevel3; +extern NSString *const kGMSPlaceTypeSublocalityLevel4; +extern NSString *const kGMSPlaceTypeSublocalityLevel5; +extern NSString *const kGMSPlaceTypeSubpremise; +extern NSString *const kGMSPlaceTypeSubwayStation; +extern NSString *const kGMSPlaceTypeSynagogue; +extern NSString *const kGMSPlaceTypeTaxiStand; +extern NSString *const kGMSPlaceTypeTrainStation; +extern NSString *const kGMSPlaceTypeTransitStation; +extern NSString *const kGMSPlaceTypeTravelAgency; +extern NSString *const kGMSPlaceTypeUniversity; +extern NSString *const kGMSPlaceTypeVeterinaryCare; +extern NSString *const kGMSPlaceTypeZoo; diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesClient.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesClient.h new file mode 100644 index 0000000..a7ef2ae --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesClient.h @@ -0,0 +1,192 @@ +// +// GMSPlacesClient.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +#import +#import +#import + + +@class GMSAutocompleteFilter; +@class GMSPlaceLikelihoodList; + +GMS_ASSUME_NONNULL_BEGIN + +/* Error domain used for Places API errors. */ +extern NSString * const kGMSPlacesErrorDomain; + +/* Error codes for |kGMSPlacesErrorDomain|. */ +typedef NS_ENUM(NSInteger, GMSPlacesErrorCode) { + /** + * Something went wrong with the connection to the Places API server. + */ + kGMSPlacesNetworkError = -1, + /** + * The Places API server returned a response that we couldn't understand. + */ + kGMSPlacesServerError = -2, + /** + * An internal error occurred in the Places API library. + */ + kGMSPlacesInternalError = -3, + /** + * Operation failed due to an invalid (malformed or missing) API key. + *

+ * See the developer's guide + * for information on creating and using an API key. + */ + kGMSPlacesKeyInvalid = -4, + /** + * Operation failed due to an expired API key. + *

+ * See the developer's guide + * for information on creating and using an API key. + */ + kGMSPlacesKeyExpired = -5, + /** + * Operation failed due to exceeding the quota usage limit. + *

+ * See the developer's guide + * for information on usage limits and how to request a higher limit. + */ + kGMSPlacesUsageLimitExceeded = -6, + /** + * Operation failed due to exceeding the usage rate limit for the API key. + *

+ * This status code shouldn't be returned during normal usage of the API. It relates to usage of + * the API that far exceeds normal request levels. + */ + kGMSPlacesRateLimitExceeded = -7, + /** + * Operation failed due to exceeding the per-device usage rate limit. + *

+ * This status code shouldn't be returned during normal usage of the API. It relates to usage of + * the API that far exceeds normal request levels. + */ + kGMSPlacesDeviceRateLimitExceeded = -8, + /** + * The Places API is not enabled. + *

+ * See the developer's guide for how + * to enable the Google Places API for iOS. + */ + kGMSPlacesAccessNotConfigured = -9, + /** + * The application's bundle identifier does not match one of the allowed iOS applications for the + * API key. + *

+ * See the developer's guide + * for how to configure bundle restrictions on API keys. + */ + kGMSPlacesIncorrectBundleIdentifier = -10 +}; + +/** + * @relates GMSPlacesClient + * Callback type for receiving place details lookups. If an error occurred, + * |result| will be nil and |error| will contain information about the error. + * @param result The |GMSPlace| that was returned. + * @param error The error that occured, if any. + */ +typedef void (^GMSPlaceResultCallback)( + GMSPlace * GMS_NULLABLE_PTR result, + NSError * GMS_NULLABLE_PTR error); + +/** + * @relates GMSPlacesClient + * Callback type for receiving place likelihood lists. If an error occurred, |likelihoodList| will + * be nil and |error| will contain information about the error. + * @param likelihoodList The list of place likelihoods. + * @param error The error that occured, if any. + */ +typedef void (^GMSPlaceLikelihoodListCallback)( + GMSPlaceLikelihoodList * GMS_NULLABLE_PTR likelihoodList, + NSError * GMS_NULLABLE_PTR error); + +/** + * @relates GMSPlacesClient + * Callback type for receiving autocompletion results. |results| is an array of + * GMSAutocompletePredictions representing candidate completions of the query. + * @param results An array of |GMSAutocompletePrediction|s. + * @param error The error that occured, if any. + */ +typedef void (^GMSAutocompletePredictionsCallback)( + NSArray * GMS_NULLABLE_PTR results, + NSError * GMS_NULLABLE_PTR error); + +/** + * Main interface to the Places API. Used for searching and getting details about places. This class + * should be accessed through the [GMSPlacesClient sharedClient] method. + * + * GMSPlacesClient methods should only be called from the main thread. Calling these methods from + * another thread will result in an exception or undefined behavior. Unless otherwise specified, all + * callbacks will be invoked on the main thread. + */ +@interface GMSPlacesClient : NSObject + +/** + * Provides the shared instance of GMSPlacesClient for the Google Maps SDK for iOS, + * creating it if necessary. + * + * If your application often uses methods of GMSPlacesClient it may want to hold + * onto this object directly, as otherwise your connection to Google may be restarted + * on a regular basis. + */ ++ (instancetype)sharedClient; + +/** + * Report that the device is at a particular place. + */ +- (void)reportDeviceAtPlaceWithID:(NSString *)placeID; + +/** + * Get details for a place. This method is non-blocking. + * @param placeID The place ID to lookup. + * @param callback The callback to invoke with the lookup result. + */ +- (void)lookUpPlaceID:(NSString *)placeID callback:(GMSPlaceResultCallback)callback; + +/** + * Returns an estimate of the place where the device is currently known to be located. + * + * Generates a place likelihood list based on the device's last estimated location. The supplied + * callback will be invoked with this likelihood list upon success and an NSError upon an error. + * @param callback The callback to invoke with the place likelihood list. + */ +- (void)currentPlaceWithCallback:(GMSPlaceLikelihoodListCallback)callback; + +/** + * Autocompletes a given text query. Results may optionally be biased towards a certain location. + * The supplied callback will be invoked with an array of autocompletion predictions upon success + * and an NSError upon an error. + * @param query The partial text to autocomplete. + * @param bounds The bounds used to bias the results. This is not a hard restrict - places may still + * be returned outside of these bounds. This parameter may be nil. + * @param filter The filter to apply to the results. This parameter may be nil. + * @param callback The callback to invoke with the predictions. + */ +- (void)autocompleteQuery:(NSString *)query + bounds:(GMSCoordinateBounds * GMS_NULLABLE_PTR)bounds + filter:(GMSAutocompleteFilter * GMS_NULLABLE_PTR)filter + callback:(GMSAutocompletePredictionsCallback)callback; + +/** + * Add a place. The |place| must have all its fields set, except that website or phoneNumber may be + * nil. + * @param place The details of the place to be added. + * @param callback The callback to invoke with the place that was added. + */ +- (void)addPlace:(GMSUserAddedPlace *)place + callback:(GMSPlaceResultCallback)callback; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesMacros.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesMacros.h new file mode 100644 index 0000000..76206be --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesMacros.h @@ -0,0 +1,23 @@ +// +// GMSPlacesMacros.h +// Google Maps SDK for iOS +// +// Copyright 2015 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#if !__has_feature(nullability) \ + || !defined(NS_ASSUME_NONNULL_BEGIN) \ + || !defined(NS_ASSUME_NONNULL_END) +#define GMS_ASSUME_NONNULL_BEGIN +#define GMS_ASSUME_NONNULL_END +#define GMS_NULLABLE +#define GMS_NULLABLE_PTR +#else +#define GMS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN +#define GMS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END +#define GMS_NULLABLE nullable +#define GMS_NULLABLE_PTR __nullable +#endif diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h new file mode 100644 index 0000000..7bc115b --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h @@ -0,0 +1,43 @@ +// +// GMSPolygon.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSPath; + +/** + * GMSPolygon defines a polygon that appears on the map. A polygon (like a polyline) defines a + * series of connected coordinates in an ordered sequence; additionally, polygons form a closed loop + * and define a filled region. + */ +@interface GMSPolygon : GMSOverlay + +/** The path that describes this polygon. The coordinates composing the path must be valid. */ +@property(nonatomic, copy) GMSPath *path; + +/** The width of the polygon outline in screen points. Defaults to 1. */ +@property(nonatomic, assign) CGFloat strokeWidth; + +/** The color of the polygon outline. Defaults to nil. */ +@property(nonatomic, strong) UIColor *strokeColor; + +/** The fill color. Defaults to blueColor. */ +@property(nonatomic, strong) UIColor *fillColor; + +/** Whether this polygon should be rendered with geodesic correction. */ +@property(nonatomic, assign) BOOL geodesic; + +/** + * Convenience constructor for GMSPolygon for a particular path. Other properties will have default + * values. + */ ++ (instancetype)polygonWithPath:(GMSPath *)path; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h new file mode 100644 index 0000000..704e4f0 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h @@ -0,0 +1,102 @@ +// +// GMSPolyline.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class GMSPath; + +/** Describes the drawing style for one-dimensional entities such as polylines. */ +@interface GMSStrokeStyle : NSObject + +/** Creates a solid color stroke style. */ ++ (instancetype)solidColor:(UIColor *)color; + +/** Creates a gradient stroke style interpolating from |fromColor| to |toColor|. */ ++ (instancetype)gradientFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor; + +@end + + +/** Describes the style for some region of a polyline. */ +@interface GMSStyleSpan : NSObject + +/** + * Factory returning a solid color span of length one segment. Equivalent to + * [GMSStyleSpan spanWithStyle:[GMSStrokeStyle solidColor:|color|] segments:1]. + */ ++ (instancetype)spanWithColor:(UIColor *)color; + +/** + * Factory returning a solid color span with a given number of segments. Equivalent to + * [GMSStyleSpan spanWithStyle:[GMSStrokeStyle solidColor:|color|] segments:|segments|]. + */ ++ (instancetype)spanWithColor:(UIColor *)color segments:(double)segments; + +/** + * Factory returning a span with the given |style| of length one segment. Equivalent to + * [GMSStyleSpan spanWithStyle:|style| segments:1]. + */ ++ (instancetype)spanWithStyle:(GMSStrokeStyle *)style; + +/** + * Factory returning a span with the given |style| and length in number of segments. + * |segments| must be greater than 0 (i.e. can't be 0). + */ ++ (instancetype)spanWithStyle:(GMSStrokeStyle *)style segments:(double)segments; + +/** The style of this span. */ +@property(nonatomic, readonly) GMSStrokeStyle *style; + +/** The length of this span in number of segments. */ +@property(nonatomic, readonly) double segments; + +@end + + +/** + * GMSPolyline specifies the available options for a polyline that exists on the Earth's surface. + * It is drawn as a physical line between the points specified in |path|. + */ +@interface GMSPolyline : GMSOverlay + +/** + * The path that describes this polyline. + */ +@property(nonatomic, copy) GMSPath *path; + +/** + * The width of the line in screen points. Defaults to 1. + */ +@property(nonatomic, assign) CGFloat strokeWidth; + +/** + * The UIColor used to render the polyline. Defaults to [UIColor blueColor]. + */ +@property(nonatomic, strong) UIColor *strokeColor; + +/** Whether this line should be rendered with geodesic correction. */ +@property(nonatomic, assign) BOOL geodesic; + +/** + * Convenience constructor for GMSPolyline for a particular path. Other properties will have + * default values. + */ ++ (instancetype)polylineWithPath:(GMSPath *)path; + +/** + * An array containing GMSStyleSpan, the spans used to render this polyline. + * + * If this array contains fewer segments than the polyline itself, the final segment will be applied + * over the remaining length. If this array is unset or empty, then |strokeColor| is used for the + * entire line instead. + */ +@property(nonatomic, copy) NSArray *spans; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h new file mode 100644 index 0000000..7596dba --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h @@ -0,0 +1,75 @@ +// +// GMSProjection.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSVisibleRegion contains the four points defining the polygon that is visible in a map's camera. + * + * This polygon can be a trapezoid instead of a rectangle, because a camera can have tilt. If the + * camera is directly over the center of the camera, the shape is rectangular, but if the camera is + * tilted, the shape will appear to be a trapezoid whose smallest side is closest to the point of + * view. + */ +typedef struct { + + /** Bottom left corner of the camera. */ + CLLocationCoordinate2D nearLeft; + + /** Bottom right corner of the camera. */ + CLLocationCoordinate2D nearRight; + + /** Far left corner of the camera. */ + CLLocationCoordinate2D farLeft; + + /** Far right corner of the camera. */ + CLLocationCoordinate2D farRight; +} GMSVisibleRegion; + +/** + * Defines a mapping between Earth coordinates (CLLocationCoordinate2D) and coordinates in the map's + * view (CGPoint). A projection is constant and immutable, in that the mapping it embodies never + * changes. The mapping is not necessarily linear. + * + * Passing invalid Earth coordinates (i.e., per CLLocationCoordinate2DIsValid) to this object may + * result in undefined behavior. + * + * This class should not be instantiated directly, instead, obtained via projection on GMSMapView. + */ +@interface GMSProjection : NSObject + +/** Maps an Earth coordinate to a point coordinate in the map's view. */ +- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate; + +/** Maps a point coordinate in the map's view to an Earth coordinate. */ +- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point; + +/** + * Converts a distance in meters to content size. This is only accurate for small Earth distances, + * as it uses CGFloat for screen distances. + */ +- (CGFloat)pointsForMeters:(CLLocationDistance)meters + atCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Returns whether a given coordinate (lat/lng) is contained within the projection. + */ +- (BOOL)containsCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Returns the region (four location coordinates) that is visible according to the projection. If + * padding was set on GMSMapView, this region takes the padding into account. + * + * The visible region can be non-rectangular. The result is undefined if the projection includes + * points that do not map to anywhere on the map (e.g., camera sees outer space). + */ +- (GMSVisibleRegion)visibleRegion; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h new file mode 100644 index 0000000..1829236 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h @@ -0,0 +1,51 @@ +// +// GMSServices.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** Service class for the Google Maps SDK for iOS. */ +@interface GMSServices : NSObject + +/** + * Provides the shared instance of GMSServices for the Google Maps SDK for iOS, + * creating it if necessary. Classes such as GMSMapView and GMSPanoramaView will + * hold this instance to provide their connection to Google. + * + * This is an opaque object. If your application often creates and destroys view + * or service classes provided by the Google Maps SDK for iOS, it may be useful + * to hold onto this object directly, as otherwise your connection to Google + * may be restarted on a regular basis. It also may be useful to take this + * object in advance of the first map creation, to reduce initial map creation + * performance cost. + * + * This method will throw an exception if provideAPIKey: has not been called. + */ ++ (id)sharedServices; + +/** + * Provides your API key to the Google Maps SDK for iOS. This key is generated + * for your application via the Google APIs Console, and is paired with your + * application's bundle ID to identify it. This should be called exactly once + * by your application, e.g., in application: didFinishLaunchingWithOptions:. + * + * @return YES if the APIKey was successfully provided + */ ++ (BOOL)provideAPIKey:(NSString *)APIKey; + +/** + * Returns the open source software license information for Google Maps SDK for + * iOS. This information must be made available within your application. + */ ++ (NSString *)openSourceLicenseInfo; + +/** + * Returns the version for this release of the Google Maps SDK for iOS. + */ ++ (NSString *)SDKVersion; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h new file mode 100644 index 0000000..c0671d9 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h @@ -0,0 +1,29 @@ +// +// GMSSyncTileLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +/** + * GMSSyncTileLayer is an abstract subclass of GMSTileLayer that provides a sync + * interface to generate image tile data. + */ +@interface GMSSyncTileLayer : GMSTileLayer + +/** + * As per requestTileForX:y:zoom:receiver: on GMSTileLayer, but provides a + * synchronous interface to return tiles. This method may block or otherwise + * perform work, and is not called on the main thread. + * + * Calls to this method may also be made from multiple threads so + * implementations must be threadsafe. + */ +- (UIImage *)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h new file mode 100644 index 0000000..20bf77b --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h @@ -0,0 +1,103 @@ +// +// GMSTileLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +@class GMSMapView; + +/** + * Stub tile that is used to indicate that no tile exists for a specific tile + * coordinate. May be returned by tileForX:y:zoom: on GMSTileProvider. + */ +FOUNDATION_EXTERN UIImage *const kGMSTileLayerNoTile; + +/** + * GMSTileReceiver is provided to GMSTileLayer when a tile request is made, + * allowing the callback to be later (or immediately) invoked. + */ +@protocol GMSTileReceiver +- (void)receiveTileWithX:(NSUInteger)x + y:(NSUInteger)y + zoom:(NSUInteger)zoom + image:(UIImage *)image; +@end + +/** + * GMSTileLayer is an abstract class that allows overlaying of custom image + * tiles on a specified GMSMapView. It may not be initialized directly, and + * subclasses must implement the tileForX:y:zoom: method to return tiles. + * + * At zoom level 0 the whole world is a square covered by a single tile, + * and the coordinates |x| and |y| are both 0 for that tile. At zoom level 1, + * the world is covered by 4 tiles with |x| and |y| being 0 or 1, and so on. + */ +@interface GMSTileLayer : NSObject + +/** + * requestTileForX:y:zoom:receiver: generates image tiles for GMSTileOverlay. + * It must be overridden by subclasses. The tile for the given |x|, |y| and + * |zoom| _must_ be later passed to |receiver|. + * + * Specify kGMSTileLayerNoTile if no tile is available for this location; or + * nil if a transient error occured and a tile may be available later. + * + * Calls to this method will be made on the main thread. See GMSSyncTileLayer + * for a base class that implements a blocking tile layer that does not run on + * your application's main thread. + */ +- (void)requestTileForX:(NSUInteger)x + y:(NSUInteger)y + zoom:(NSUInteger)zoom + receiver:(id)receiver; + +/** + * Clears the cache so that all tiles will be requested again. + */ +- (void)clearTileCache; + +/** + * The map this GMSTileOverlay is displayed on. Setting this property will add + * the layer to the map. Setting it to nil removes this layer from the map. A + * layer may be active on at most one map at any given time. + */ +@property(nonatomic, weak) GMSMapView *map; + +/** + * Higher |zIndex| value tile layers will be drawn on top of lower |zIndex| + * value tile layers and overlays. Equal values result in undefined draw + * ordering. + */ +@property(nonatomic, assign) int zIndex; + +/** + * Specifies the number of pixels (not points) that the returned tile images + * will prefer to display as. For best results, this should be the edge + * length of your custom tiles. Defaults to 256, which is the traditional + * size of Google Maps tiles. + * + * Values less than the equivalent of 128 points (e.g. 256 pixels on retina + * devices) may not perform well and are not recommended. + * + * As an example, an application developer may wish to provide retina tiles + * (512 pixel edge length) on retina devices, to keep the same number of tiles + * per view as the default value of 256 would give on a non-retina device. + */ +@property(nonatomic, assign) NSInteger tileSize; + +/** + * Specifies the opacity of the tile layer. This provides a multiplier for + * the alpha channel of tile images. + */ +@property(nonatomic, assign) float opacity; + +/** + * Specifies whether the tiles should fade in. Default YES. + */ +@property(nonatomic, assign) BOOL fadeIn; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h new file mode 100644 index 0000000..6d05c97 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h @@ -0,0 +1,94 @@ +// +// GMSUISettings.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +/** Settings for the user interface of a GMSMapView. */ +@interface GMSUISettings : NSObject + +/** + * Sets the preference for whether all gestures should be enabled (default) or + * disabled. This doesn't restrict users from tapping any on screen buttons to + * move the camera (e.g., compass or zoom controls), nor does it restrict + * programmatic movements and animation. + */ +- (void)setAllGesturesEnabled:(BOOL)enabled; + +/** + * Controls whether scroll gestures are enabled (default) or disabled. If + * enabled, users may drag to pan the camera. This does not limit programmatic + * movement of the camera. + */ +@property(nonatomic, assign) BOOL scrollGestures; + +/** + * Controls whether zoom gestures are enabled (default) or disabled. If + * enabled, users may double tap/two-finger tap or pinch to zoom the camera. + * This does not limit programmatic movement of the camera. + */ +@property(nonatomic, assign) BOOL zoomGestures; + +/** + * Controls whether tilt gestures are enabled (default) or disabled. If enabled, + * users may use a two-finger vertical down or up swipe to tilt the camera. This + * does not limit programmatic control of the camera's viewingAngle. + */ +@property(nonatomic, assign) BOOL tiltGestures; + +/** + * Controls whether rotate gestures are enabled (default) or disabled. If + * enabled, users may use a two-finger rotate gesture to rotate the camera. This + * does not limit programmatic control of the camera's bearing. + */ +@property(nonatomic, assign) BOOL rotateGestures; + +/** + * Controls whether gestures by users are completely consumed by the GMSMapView + * when gestures are enabled (default YES). This prevents these gestures from + * being received by parent views. + * + * When the GMSMapView is contained by a UIScrollView (or other scrollable area), + * this means that gestures on the map will not be additional consumed as scroll + * gestures. However, disabling this (set to NO) may be userful to support + * complex view hierarchies or requirements. + */ +@property(nonatomic, assign) BOOL consumesGesturesInView; + +/** + * Enables or disables the compass. The compass is an icon on the map that + * indicates the direction of north on the map. + * + * If enabled, it is only shown when the camera is rotated away from its default + * orientation (bearing of 0). When a user taps the compass, the camera orients + * itself to its default orientation and fades away shortly after. If disabled, + * the compass will never be displayed. + */ +@property(nonatomic, assign) BOOL compassButton; + +/** + * Enables or disables the My Location button. This is a button visible on the + * map that, when tapped by users, will center the map on the current user + * location. + */ +@property(nonatomic, assign) BOOL myLocationButton; + +/** + * Enables (default) or disables the indoor floor picker. If enabled, it is only + * visible when the view is focused on a building with indoor floor data. + * If disabled, the selected floor can still be controlled programatically via + * the indoorDisplay mapView property. + */ +@property(nonatomic, assign) BOOL indoorPicker; + +/** + * Controls whether rotate and zoom gestures can be performed off-center and scrolled around + * (default YES). + */ +@property(nonatomic, assign) BOOL allowScrollGesturesDuringRotateOrZoom; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h new file mode 100644 index 0000000..a3c6e87 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h @@ -0,0 +1,50 @@ +// +// GMSURLTileLayer.h +// Google Maps SDK for iOS +// +// Copyright 2013 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import + +@class NSURL; + +/** + * |GMSTileURLConstructor| is a block taking |x|, |y| and |zoom| + * and returning an NSURL, or nil to indicate no tile for that location. + */ +typedef NSURL *(^GMSTileURLConstructor)(NSUInteger x, NSUInteger y, NSUInteger zoom); + +/** + * GMSURLTileProvider fetches tiles based on the URLs returned from a + * GMSTileURLConstructor. For example: + *

+ *   GMSTileURLConstructor constructor = ^(NSUInteger x, NSUInteger y, NSUInteger zoom) {
+ *     NSString *URLStr =
+ *         [NSString stringWithFormat:@"https://example.com/%d/%d/%d.png", x, y, zoom];
+ *     return [NSURL URLWithString:URLStr];
+ *   };
+ *   GMSTileLayer *layer =
+ *       [GMSURLTileLayer tileLayerWithURLConstructor:constructor];
+ *   layer.userAgent = @"SDK user agent";
+ *   layer.map = map;
+ * 
+ * + * GMSURLTileProvider may not be subclassed and should only be created via its + * convenience constructor. + */ +@interface GMSURLTileLayer : GMSTileLayer + +/** Convenience constructor. |constructor| must be non-nil. */ ++ (instancetype)tileLayerWithURLConstructor:(GMSTileURLConstructor)constructor; + +/** + * Specify the user agent to describe your application. If this is nil (the + * default), the default iOS user agent is used for HTTP requests. + */ +@property(nonatomic, copy) NSString *userAgent; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUserAddedPlace.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUserAddedPlace.h new file mode 100644 index 0000000..c9f3472 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUserAddedPlace.h @@ -0,0 +1,40 @@ +// +// GMSUserAddedPlace.h +// Google Maps SDK for iOS +// +// Copyright 2014 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import + +/** + * Represents a place constructed by a user, suitable for adding to Google's collection of places. + * + * All properties must be set before passing to GMSPlacesClient.addPlace, except that either website + * _or_ phoneNumber may be nil. + */ +@interface GMSUserAddedPlace : NSObject + +/** Name of the place. */ +@property(nonatomic, copy) NSString *name; + +/** Address of the place. */ +@property(nonatomic, copy) NSString *address; + +/** Location of the place. */ +@property(nonatomic, assign) CLLocationCoordinate2D coordinate; + +/** Phone number of the place. */ +@property(nonatomic, copy) NSString *phoneNumber; + +/** List of types of the place as an array of NSStrings, like the GMSPlace.types property. */ +@property(nonatomic, copy) NSArray *types; + +/** The website for the place. */ +@property(nonatomic, copy) NSString *website; + +@end diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h new file mode 100644 index 0000000..bb86c01 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h @@ -0,0 +1,60 @@ +// +// GoogleMaps.h +// Google Maps SDK for iOS +// +// Copyright 2012 Google Inc. +// +// Usage of this SDK is subject to the Google Maps/Google Earth APIs Terms of +// Service: https://developers.google.com/maps/terms +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..c85090a --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1 @@ +framework module GoogleMaps { umbrella header "GoogleMaps.h" export * module * { export * } } diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.mom b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.mom new file mode 100644 index 0000000..89db31a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.mom differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.omo b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.omo new file mode 100644 index 0000000..6455250 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.omo differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/VersionInfo.plist b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/VersionInfo.plist new file mode 100644 index 0000000..47b2dcd Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/VersionInfo.plist differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/DroidSansMerged-Regular.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/DroidSansMerged-Regular.ttf new file mode 100644 index 0000000..2aca5f5 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/DroidSansMerged-Regular.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png new file mode 100644 index 0000000..8d7409e Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png new file mode 100644 index 0000000..3491ab4 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png new file mode 100644 index 0000000..8a35660 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist new file mode 100644 index 0000000..3e9ffc8 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Black.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Black.ttf new file mode 100644 index 0000000..cb905bc Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Black.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BlackItalic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BlackItalic.ttf new file mode 100644 index 0000000..3ebdc7d Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BlackItalic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Bold.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Bold.ttf new file mode 100644 index 0000000..68822ca Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Bold.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BoldItalic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BoldItalic.ttf new file mode 100644 index 0000000..aebf8eb Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BoldItalic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Italic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Italic.ttf new file mode 100644 index 0000000..2041cbc Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Italic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Light.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Light.ttf new file mode 100644 index 0000000..aa45340 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Light.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-LightItalic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-LightItalic.ttf new file mode 100644 index 0000000..a85444f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-LightItalic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Medium.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Medium.ttf new file mode 100644 index 0000000..a3c1a1f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Medium.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-MediumItalic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-MediumItalic.ttf new file mode 100644 index 0000000..b828205 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-MediumItalic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Regular.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Regular.ttf new file mode 100644 index 0000000..0e58508 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Regular.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Thin.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Thin.ttf new file mode 100644 index 0000000..8779333 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Thin.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-ThinItalic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..b79cb26 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-ThinItalic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Italic.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Italic.ttf new file mode 100644 index 0000000..d2b611f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Italic.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Regular.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Regular.ttf new file mode 100644 index 0000000..b9fc49c Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Regular.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Siemreap.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Siemreap.ttf new file mode 100644 index 0000000..a2c8dff Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Siemreap.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Tharlon-Regular.ttf b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Tharlon-Regular.ttf new file mode 100644 index 0000000..4717d70 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Tharlon-Regular.ttf differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings new file mode 100644 index 0000000..de9f9e8 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png new file mode 100644 index 0000000..847575a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png new file mode 100644 index 0000000..84e76a3 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png new file mode 100644 index 0000000..b89372f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png new file mode 100644 index 0000000..a0b07bb Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png new file mode 100644 index 0000000..c03e1e9 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png new file mode 100644 index 0000000..e32568d Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png new file mode 100644 index 0000000..c5465b7 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings new file mode 100644 index 0000000..83af8e3 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings new file mode 100644 index 0000000..aacf12c Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings new file mode 100644 index 0000000..d9d96ac Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png new file mode 100644 index 0000000..cb77f83 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings new file mode 100644 index 0000000..ffa8a58 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings new file mode 100644 index 0000000..457e994 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings new file mode 100644 index 0000000..4ee0314 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings new file mode 100644 index 0000000..91e4ccb Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings new file mode 100644 index 0000000..50636cb Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings new file mode 100644 index 0000000..bb6c09a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings new file mode 100644 index 0000000..f8b7c87 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings new file mode 100644 index 0000000..1288e8a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings new file mode 100644 index 0000000..3d840fa Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings new file mode 100644 index 0000000..9c6b00b Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png new file mode 100644 index 0000000..6e0663e Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png new file mode 100644 index 0000000..f1d5caf Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png new file mode 100644 index 0000000..4bd8c8f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png new file mode 100644 index 0000000..b2cf321 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png new file mode 100644 index 0000000..91cf55f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings new file mode 100644 index 0000000..61faea9 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings new file mode 100644 index 0000000..0afe1f2 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings new file mode 100644 index 0000000..54a532b Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings new file mode 100644 index 0000000..9f4c41c Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings new file mode 100644 index 0000000..3eee646 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings new file mode 100644 index 0000000..31a4197 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/no.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/no.lproj/GMSCore.strings new file mode 100644 index 0000000..dad08a4 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/no.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings new file mode 100644 index 0000000..f59c162 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png new file mode 100644 index 0000000..badf109 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings new file mode 100644 index 0000000..d08bfc1 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings new file mode 100644 index 0000000..f98aac2 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings new file mode 100644 index 0000000..576d222 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_1-1.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_1-1.png new file mode 100644 index 0000000..46b0843 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_1-1.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png new file mode 100644 index 0000000..357d1df Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png new file mode 100644 index 0000000..35f58cf Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_2-1.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_2-1.png new file mode 100644 index 0000000..df77f65 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_2-1.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png new file mode 100644 index 0000000..5162343 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_32-8.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_32-8.png new file mode 100644 index 0000000..ed0424b Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_32-8.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png new file mode 100644 index 0000000..a44a743 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png new file mode 100644 index 0000000..46915dc Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png new file mode 100644 index 0000000..be12717 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings new file mode 100644 index 0000000..c488955 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings new file mode 100644 index 0000000..3188adf Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings new file mode 100644 index 0000000..530d064 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings new file mode 100644 index 0000000..b544aa9 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings new file mode 100644 index 0000000..2878e9a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings new file mode 100644 index 0000000..249e3d6 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings new file mode 100644 index 0000000..6539316 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark.png new file mode 100644 index 0000000..be3a8ab Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@2x.png new file mode 100644 index 0000000..4bae5d5 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@3x.png new file mode 100644 index 0000000..ef7290b Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_dark@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light.png new file mode 100644 index 0000000..10624db Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@2x.png new file mode 100644 index 0000000..36112a0 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@3x.png new file mode 100644 index 0000000..6ad6233 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/watermark_light@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings new file mode 100644 index 0000000..dd4b0a5 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings new file mode 100644 index 0000000..6ec7c70 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist new file mode 100644 index 0000000..e6936d1 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active.png new file mode 100644 index 0000000..cbcf301 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@2x.png new file mode 100644 index 0000000..5f8306a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@3x.png new file mode 100644 index 0000000..170c60f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped.png new file mode 100644 index 0000000..1e1bcf6 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@2x.png new file mode 100644 index 0000000..143e144 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@3x.png new file mode 100644 index 0000000..1205603 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/active_grouped@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back.png new file mode 100644 index 0000000..2765dbc Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@2x.png new file mode 100644 index 0000000..a9fcb27 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@3x.png new file mode 100644 index 0000000..add6f20 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/back@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png new file mode 100644 index 0000000..0f8db09 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png new file mode 100644 index 0000000..8ece32c Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png new file mode 100644 index 0000000..dfdc21a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png new file mode 100644 index 0000000..4ed47e4 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png new file mode 100644 index 0000000..475f4b8 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png new file mode 100644 index 0000000..fc7e633 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg.png new file mode 100644 index 0000000..2ae75f6 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@2x.png new file mode 100644 index 0000000..3638007 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@3x.png new file mode 100644 index 0000000..8d7ae8f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/card_bg@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close.png new file mode 100644 index 0000000..9e693f1 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@2x.png new file mode 100644 index 0000000..8b9caea Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@3x.png new file mode 100644 index 0000000..6edea38 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/close@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png new file mode 100644 index 0000000..f51c3a4 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png new file mode 100644 index 0000000..59dbd92 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png new file mode 100644 index 0000000..829ea5f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/en.lproj/InfoPlist.strings b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..3967e06 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/en.lproj/InfoPlist.strings differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back.png new file mode 100644 index 0000000..4c72bcf Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back@2x.png new file mode 100644 index 0000000..95bc759 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_nav_back@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon.png new file mode 100644 index 0000000..7e6193a Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon@2x.png new file mode 100644 index 0000000..61e1e74 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/place_picker_search_icon@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons.png new file mode 100644 index 0000000..c6a7bec Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@2x.png new file mode 100644 index 0000000..f0a5e9f Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@3x.png new file mode 100644 index 0000000..02f0826 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/red_icons@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons.png new file mode 100644 index 0000000..49b4e9d Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@2x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@2x.png new file mode 100644 index 0000000..a42707e Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@2x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@3x.png b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@3x.png new file mode 100644 index 0000000..5838eb0 Binary files /dev/null and b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/white_icons@3x.png differ diff --git a/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/Current b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/Pods/GoogleMaps/Frameworks/GoogleMaps.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/GoogleMaps.framework b/Pods/GoogleMaps/GoogleMapsSDKDemos/GoogleMaps.framework new file mode 120000 index 0000000..4e52fac --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/GoogleMaps.framework @@ -0,0 +1 @@ +../Frameworks/GoogleMaps.framework \ No newline at end of file diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/README.GoogleMapsSDKDemos b/Pods/GoogleMaps/GoogleMapsSDKDemos/README.GoogleMapsSDKDemos new file mode 100644 index 0000000..e39aa06 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/README.GoogleMapsSDKDemos @@ -0,0 +1,22 @@ +GoogleMapsSDKDemos contains a demo application showcasing various features of +the Google Maps SDK for iOS. + +Before starting, please note that these demos are directed towards a technical +audience. You'll also need Xcode 6.3 or later, with the iOS SDK 7.0 or later. + +If you're new to the SDK, please read the Introduction section of the Google +Maps SDK for iOS documentation- + https://developers.google.com/maps/documentation/ios + +Once you've read the Introduction page, follow the first couple of steps on the +"Getting Started" page. Specifically; + + * Obtain an API key for the demo application, and specify the bundle ID of + this demo application as an an 'allowed iOS app'. By default, the bundle ID + is "com.example.SDKDemos". + + * Open the project in Xcode, and update `SDKDemoAPIKey.h` with this key. + +If you'd like to add a new sample, add a new subclass of `ViewController` and +add it to the samples definitions inside the `Samples.m`. + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos.gyp b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos.gyp new file mode 100644 index 0000000..fdc89bc --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos.gyp @@ -0,0 +1,60 @@ +{ + 'xcode_settings': { + 'SDKROOT': 'iphoneos', + }, + 'targets': [ + { + 'target_name': 'SDKDemos', + 'type': 'executable', + 'mac_bundle': 1, + 'mac_framework_dirs': [ + '$(SRCROOT)', + ], + 'link_settings': { + 'mac_bundle_resources': [ + '"; }; + 031103FAE4E394EA78895993 /* SDKDemoMasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDKDemoMasterViewController.m; sourceTree = ""; }; + 0372FAEB924D0ADD012FBDFE /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; + 0416087FE83DCD914CF69FA9 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 043F8041B5649C26BCE75231 /* Samples.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Samples.m; sourceTree = ""; }; + 09F0DD5C27A815E1A9720747 /* TrafficMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrafficMapViewController.h; sourceTree = ""; }; + 0B6999063F311237F673B8B0 /* IndoorViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IndoorViewController.m; sourceTree = ""; }; + 0BA108F4D0C3BDA34CE54180 /* GestureControlViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GestureControlViewController.m; sourceTree = ""; }; + 0F33C1C90D61AD0E2B502215 /* sdkdemos_icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sdkdemos_icon-72@2x.png"; sourceTree = ""; }; + 124B199908A64B1EE958F3CA /* x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "x29@2x.png"; sourceTree = ""; }; + 13CBFFCFE30583D29D77B5A1 /* MarkerInfoWindowViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerInfoWindowViewController.h; sourceTree = ""; }; + 14532E524E38739F32328ECE /* Samples+Places.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Samples+Places.h"; sourceTree = ""; }; + 14A1E50F98F1EA56590AA120 /* step3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step3@2x.png"; sourceTree = ""; }; + 15F93E8BE938118367B428AA /* MarkersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkersViewController.h; sourceTree = ""; }; + 193951C11D1549E7701FFC40 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; }; + 1A597E83DFFF77F1EBD76B64 /* museum-exhibits.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = "museum-exhibits.json"; sourceTree = ""; }; + 1BA8114B0A4CDC672F1CBD6F /* GeocoderViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeocoderViewController.h; sourceTree = ""; }; + 1F1D873627D847B5C1B099FC /* MarkerEventsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerEventsViewController.m; sourceTree = ""; }; + 2AB2A95CCD24024671158AD9 /* PanoramaViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PanoramaViewController.h; sourceTree = ""; }; + 2E95ACF5DD497E15A0B746DF /* glow-marker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "glow-marker.png"; sourceTree = ""; }; + 31DE8BA62A492FBC67E923AD /* sdkdemos_icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sdkdemos_icon-72.png"; sourceTree = ""; }; + 31F229C07CCFB0CFEBD70F68 /* MapLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapLayerViewController.m; sourceTree = ""; }; + 3401A5B1795F7B7056A7F491 /* arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = arrow.png; sourceTree = ""; }; + 3449C03C22DABB720D80FF72 /* GestureControlViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GestureControlViewController.h; sourceTree = ""; }; + 359EF8FC1D5600228CFFBF4D /* step6.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step6.png; sourceTree = ""; }; + 3628691A5936DDD1821A622B /* step7.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step7.png; sourceTree = ""; }; + 394AF35F597FB89E9D6A1E3D /* MapZoomViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapZoomViewController.h; sourceTree = ""; }; + 3AAAD985B0FF3AF55E964A33 /* IndoorMuseumNavigationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IndoorMuseumNavigationViewController.m; sourceTree = ""; }; + 3BD92F5F6AB10EDD9098A9DC /* GeocoderViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GeocoderViewController.m; sourceTree = ""; }; + 3C1A74AB0EFA8C78AF23E564 /* step5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step5.png; sourceTree = ""; }; + 3F1ABBF9B2E96BC488485119 /* track.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = track.json; sourceTree = ""; }; + 4152CBF14080E0C84DFD6613 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 425802FD822D22CC110119BA /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 429F95111FD1F8B8A1C16865 /* PolylinesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PolylinesViewController.m; sourceTree = ""; }; + 45580B3C558B48DD3359F2A1 /* MapZoomViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapZoomViewController.m; sourceTree = ""; }; + 460EBDFBDFFE13DB517214A5 /* BasicMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BasicMapViewController.h; sourceTree = ""; }; + 46FF553F45838251496245F9 /* arrow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "arrow@2x.png"; sourceTree = ""; }; + 487E4999CFF757EA18C569D8 /* x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = x29.png; sourceTree = ""; }; + 48C5F2798147CFFA14B5B7DD /* SDKDemoPlacePickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoPlacePickerViewController.h; sourceTree = ""; }; + 4BA4E139EA9FAA0D6A639CE6 /* BasicMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BasicMapViewController.m; sourceTree = ""; }; + 4C29D1A0DA47D1691C6CE0A5 /* argentina-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "argentina-large.png"; sourceTree = ""; }; + 4C3CBB9D63F29447E8C3C06F /* step2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step2@2x.png"; sourceTree = ""; }; + 4D35CE548040C1EF3B8BD581 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 4F353004786E58271D76683E /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = ""; }; + 54743FFF10003AA647D1654F /* australia-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "australia-large.png"; sourceTree = ""; }; + 550E6B03BFE321336D066223 /* spitfire@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "spitfire@2x.png"; sourceTree = ""; }; + 58B0EFEC1C69A989B6E84744 /* h1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = h1.png; sourceTree = ""; }; + 5977F6DB8B9BD6609BE664B0 /* TileLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TileLayerViewController.h; sourceTree = ""; }; + 59940AD9F86687DEA94EDE8C /* CameraViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraViewController.h; sourceTree = ""; }; + 5AA08AB86BAE0FBBB8B3705F /* boat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = boat.png; sourceTree = ""; }; + 5EC5186C29703467E4C2ED80 /* SDKDemoAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDKDemoAppDelegate.m; sourceTree = ""; }; + 5F3C1FEA265C2BC44CFFB4BD /* MapTypesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapTypesViewController.m; sourceTree = ""; }; + 62CBE24F5F8C7DF19AE8452C /* step4@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step4@2x.png"; sourceTree = ""; }; + 63B21B38E81405568CD36449 /* AnimatedCurrentLocationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimatedCurrentLocationViewController.h; sourceTree = ""; }; + 64888C52E2E272FEFE7C18AA /* StructuredGeocoderViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StructuredGeocoderViewController.m; sourceTree = ""; }; + 656D0083D388D030EA0C9E3E /* SDKDemos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SDKDemos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6571C6FD38FB80E9657C6CF4 /* PolylinesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolylinesViewController.h; sourceTree = ""; }; + 6A37ED9FA16A22EFC586E32E /* MyLocationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyLocationViewController.m; sourceTree = ""; }; + 6D2F332D6F2FB20FE11DFB6E /* step3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step3.png; sourceTree = ""; }; + 6DEE18F2AE053B9A1DB19C5C /* TileLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TileLayerViewController.m; sourceTree = ""; }; + 72FF2F70BA1B0073BE1F375F /* newark_nj_1922.jpg */ = {isa = PBXFileReference; lastKnownFileType = text; path = newark_nj_1922.jpg; sourceTree = ""; }; + 735DFE797020C7F09B21F773 /* CustomIndoorViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomIndoorViewController.h; sourceTree = ""; }; + 7385EE81D06D3C1BD826B7C5 /* step5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step5@2x.png"; sourceTree = ""; }; + 74A9950FBE5A377D1EBB2230 /* SDKDemoMasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoMasterViewController.h; sourceTree = ""; }; + 74F5EF3ED006A948BCC2BF82 /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = usr/lib/libobjc.dylib; sourceTree = SDKROOT; }; + 75E000124A1962A304CEAAB8 /* aeroplane.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = aeroplane.png; sourceTree = ""; }; + 778A178288F1E9593A03C6DB /* Samples.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Samples.h; sourceTree = ""; }; + 778FAE1359A6570015F495EA /* GroundOverlayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GroundOverlayViewController.h; sourceTree = ""; }; + 77EA8532CD15A6B5E800415E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + 78C6F7D665C6DD4328894967 /* TrafficMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TrafficMapViewController.m; sourceTree = ""; }; + 799CC7F35C34DAA014613DDF /* aeroplane@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "aeroplane@2x.png"; sourceTree = ""; }; + 79A9E14215E011A6DFD2B0BA /* GradientPolylinesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GradientPolylinesViewController.h; sourceTree = ""; }; + 7B47995B2A94F831E8B7DD0F /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; + 7B4AF795B18672B900DE3568 /* MarkerEventsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerEventsViewController.h; sourceTree = ""; }; + 7BBAEFAF2820EDE4EBA5D943 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + 7BFD80C8485C48F2C90C85A8 /* GradientPolylinesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GradientPolylinesViewController.m; sourceTree = ""; }; + 7EC4132AB51E1500FF16E4C5 /* voyager.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voyager.png; sourceTree = ""; }; + 81B8039817FFB9A398E1158B /* DoubleMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DoubleMapViewController.h; sourceTree = ""; }; + 837C86EA9AC87A3A5EA16145 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 842A2FB43838968D313B1783 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; + 85109740028667AB1646AC2D /* glow-marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "glow-marker@2x.png"; sourceTree = ""; }; + 85288BF143D609A7145A7846 /* popup_santa.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = popup_santa.png; sourceTree = ""; }; + 88F50C2BAD0B000697EBECCE /* boat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "boat@2x.png"; sourceTree = ""; }; + 8B3EA327A271CA6A64D117A6 /* FitBoundsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FitBoundsViewController.m; sourceTree = ""; }; + 8EB73A280D8FE0A1D2C054C8 /* bulgaria-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bulgaria-large.png"; sourceTree = ""; }; + 8F59372BD761533A2A6B470A /* GoogleMaps.bundle */ = {isa = PBXFileReference; lastKnownFileType = wrapper.cfbundle; path = GoogleMaps.bundle; sourceTree = ""; }; + 8F6ED0C99E84A5FE46148309 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; }; + 90258FFF395A5E25CCEC3AB0 /* PolygonsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PolygonsViewController.m; sourceTree = ""; }; + 9285758C5E07413BA20C8840 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 933F14F944559A5D5538023D /* AnimatedCurrentLocationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AnimatedCurrentLocationViewController.m; sourceTree = ""; }; + 959C749EEFBCC25DCDCC1416 /* Launch.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Launch.xib; sourceTree = ""; }; + 97284C2589969381B1F2BF65 /* botswana.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = botswana.png; sourceTree = ""; }; + 9820B8516BCB7CD9A6553720 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 989BBAF7DF1CD21CA2F9CC0E /* FixedPanoramaViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FixedPanoramaViewController.h; sourceTree = ""; }; + 98A5F219881F5A5612905ABC /* step4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step4.png; sourceTree = ""; }; + 9A2729CB24AF1A2D245CD886 /* CameraViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraViewController.m; sourceTree = ""; }; + 9AB08565AAF1A341BC7D2A61 /* MarkerLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerLayerViewController.m; sourceTree = ""; }; + 9C2D5258F1365BCD57029138 /* MarkersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkersViewController.m; sourceTree = ""; }; + A38AE667ACADB5627CF67973 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; + A48DE20E9EF0EFE5F9656B51 /* SDKDemoAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoAppDelegate.h; sourceTree = ""; }; + A56AF9AA60B75EF16CE3EF8C /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = ""; }; + A578CA16809EBB3047B3C573 /* DoubleMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DoubleMapViewController.m; sourceTree = ""; }; + A849C0D00CED5DEF33AC6844 /* MapLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapLayerViewController.h; sourceTree = ""; }; + AAA8B2B19FA46102744D14EF /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + AB330C8620876E40BEC85968 /* voyager@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "voyager@2x.png"; sourceTree = ""; }; + ABAA14F70E24A8E44326BBAE /* FixedPanoramaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FixedPanoramaViewController.m; sourceTree = ""; }; + AC4F4FF0F15305FE7DB327D5 /* MyLocationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyLocationViewController.h; sourceTree = ""; }; + ADB6F5B2F34C4461E2AFF686 /* CustomMarkersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomMarkersViewController.h; sourceTree = ""; }; + ADDE981D53FECD067A463663 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + B1FA4EC4A89767E10B655CB0 /* MarkerLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerLayerViewController.h; sourceTree = ""; }; + B247AC68273A6B9F29ED6CDC /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; + B6D2F25F8F4B1159C707B4A2 /* IndoorViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IndoorViewController.h; sourceTree = ""; }; + B8B2C467E3382D3E31EA92E1 /* SDKDemoPlacePickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDKDemoPlacePickerViewController.m; sourceTree = ""; }; + BA7783E4FDFA7455B4EF6174 /* PolygonsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolygonsViewController.h; sourceTree = ""; }; + BAF511F09C737A22E0A76E59 /* bulgaria.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bulgaria.png; sourceTree = ""; }; + BD36A58B321268BB8E267874 /* CustomMarkersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomMarkersViewController.m; sourceTree = ""; }; + BD7A194DAB9B544294D7A2D0 /* GroundOverlayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GroundOverlayViewController.m; sourceTree = ""; }; + C07CD02A92489C92FC822C3A /* SDKDemoAPIKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoAPIKey.h; sourceTree = ""; }; + C4D94F429262C3A09166D73F /* australia-large@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "australia-large@2x.png"; sourceTree = ""; }; + C7AB12C7756274D40E649762 /* step7@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step7@2x.png"; sourceTree = ""; }; + C9926C6844E111CB23EF2E80 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + CD99D73AD66B03243C670572 /* sdkdemos_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sdkdemos_icon.png; sourceTree = ""; }; + D03EB3995F5EF92504D8E44F /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = ""; }; + D05C1274204C92F13ABD8599 /* step6@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step6@2x.png"; sourceTree = ""; }; + D1B2675ECA97CB1F5D0F5F88 /* MapTypesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapTypesViewController.h; sourceTree = ""; }; + D2325AC9E493AAC86F87681E /* GoogleMaps.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GoogleMaps.framework; sourceTree = ""; }; + D2DFE1BF52A3A8AFFF379322 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + D619B3D989A533F358F1D077 /* botswana-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "botswana-large.png"; sourceTree = ""; }; + D6A64F2A232BF392091BFB79 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; + D7B0E6B7E378BF2301E42C6A /* StructuredGeocoderViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StructuredGeocoderViewController.h; sourceTree = ""; }; + D81C6119C4C7DE07FB42F86D /* popup_santa@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "popup_santa@2x.png"; sourceTree = ""; }; + D83F65E0A08406E055A906B0 /* step2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step2.png; sourceTree = ""; }; + D8C04CF121E2CAC61D549125 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; + DABC6731995E50EBF60E82DA /* VisibleRegionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VisibleRegionViewController.m; sourceTree = ""; }; + DBBB2837C3A8ADB3D5191304 /* PanoramaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PanoramaViewController.m; sourceTree = ""; }; + DC53293C94A672B4C62AD559 /* step1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step1@2x.png"; sourceTree = ""; }; + DC8661CA82A046ED2AF5B71F /* MarkerInfoWindowViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerInfoWindowViewController.m; sourceTree = ""; }; + E0D4EAD84FB1F606F6016E0B /* VisibleRegionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VisibleRegionViewController.h; sourceTree = ""; }; + E4B1CC9979AFE466B59A9AC4 /* IndoorMuseumNavigationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IndoorMuseumNavigationViewController.h; sourceTree = ""; }; + E8CF929DF33D172F36F845E3 /* step8.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step8.png; sourceTree = ""; }; + EA55E63E3ECB92756B8A8854 /* australia.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = australia.png; sourceTree = ""; }; + EB1CD2C01411A35969C872BA /* CustomIndoorViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomIndoorViewController.m; sourceTree = ""; }; + EBDAAD248E528C9D828A2878 /* argentina.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = argentina.png; sourceTree = ""; }; + EBDF69733C905396E3568053 /* Samples+Places.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Samples+Places.m"; sourceTree = ""; }; + EF3CF28401B594ADE426E656 /* h1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "h1@2x.png"; sourceTree = ""; }; + FA940308742FE4A8E2ACB6DB /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = ""; }; + FAF66F7D7BC732C1A5A896B0 /* SDKDemos.gyp */ = {isa = PBXFileReference; explicitFileType = sourcecode; path = SDKDemos.gyp; sourceTree = ""; }; + FCCC58E7443B387046306EEF /* step8@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step8@2x.png"; sourceTree = ""; }; + FD181A33530788C065AA2D90 /* sdkdemos_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sdkdemos_icon@2x.png"; sourceTree = ""; }; + FD40AB7623BD1C9A81EA9239 /* FitBoundsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FitBoundsViewController.h; sourceTree = ""; }; + FE2B3D4C04A8916BC8027A72 /* spitfire.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = spitfire.png; sourceTree = ""; }; + FE7C2DA207359F759D081156 /* step1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step1.png; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9F427D7DE56744A968901ED9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF3DA502EBB043CA9CBCC44C /* GoogleMaps.framework in Frameworks */, + 939D9DD972B3C9CEF6268682 /* libobjc.dylib in Frameworks */, + 7151879999E96F2B5F8EC9AD /* CoreFoundation.framework in Frameworks */, + 541DAAB4C469032E7585D5D6 /* Foundation.framework in Frameworks */, + 586C3C40DC409C135B0AA19A /* CoreGraphics.framework in Frameworks */, + 93326C0FF4EE9F4BC32A072C /* UIKit.framework in Frameworks */, + 0B225B8CEA118D7190C02F8A /* AVFoundation.framework in Frameworks */, + D9993D35B97D8FF8F5F99A76 /* CoreData.framework in Frameworks */, + AB2D25C2602C7E82EA483032 /* CoreLocation.framework in Frameworks */, + D256FE4FDF4FDC2AF346BD7B /* CoreText.framework in Frameworks */, + 495DCD4434C57715950F77B4 /* GLKit.framework in Frameworks */, + 9CD7E5647D53C860A29AEF86 /* ImageIO.framework in Frameworks */, + A223A4F133AD8F8474895B2D /* Security.framework in Frameworks */, + BB82297CC4CD1B229302A5EB /* CoreBluetooth.framework in Frameworks */, + 2F8454DDC3837DA90C5E9114 /* Accelerate.framework in Frameworks */, + 6FC1A0CD8C09A8A7A8C394AA /* libc++.dylib in Frameworks */, + 1BC4F897035B4D58120062D4 /* libicucore.dylib in Frameworks */, + 7677FF7FF78329D9A3205687 /* libz.dylib in Frameworks */, + 9A09C9C77E8CEC959473BBB4 /* OpenGLES.framework in Frameworks */, + B29088B5D465871C06402A9A /* QuartzCore.framework in Frameworks */, + 0C50E0E757CCCCAFCC64FBB4 /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1CC686768E6E7BE8890097AC /* PlacesSamples */ = { + isa = PBXGroup; + children = ( + 48C5F2798147CFFA14B5B7DD /* SDKDemoPlacePickerViewController.h */, + B8B2C467E3382D3E31EA92E1 /* SDKDemoPlacePickerViewController.m */, + 14532E524E38739F32328ECE /* Samples+Places.h */, + EBDF69733C905396E3568053 /* Samples+Places.m */, + ); + path = PlacesSamples; + sourceTree = ""; + }; + 2DBE6964ECA9681577AD08E9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 837C86EA9AC87A3A5EA16145 /* AVFoundation.framework */, + D6A64F2A232BF392091BFB79 /* Accelerate.framework */, + ADDE981D53FECD067A463663 /* CoreBluetooth.framework */, + 9820B8516BCB7CD9A6553720 /* CoreData.framework */, + D2DFE1BF52A3A8AFFF379322 /* CoreFoundation.framework */, + 0416087FE83DCD914CF69FA9 /* CoreGraphics.framework */, + A38AE667ACADB5627CF67973 /* CoreLocation.framework */, + D8C04CF121E2CAC61D549125 /* CoreText.framework */, + C9926C6844E111CB23EF2E80 /* Foundation.framework */, + 7B47995B2A94F831E8B7DD0F /* GLKit.framework */, + D2325AC9E493AAC86F87681E /* GoogleMaps.framework */, + B247AC68273A6B9F29ED6CDC /* ImageIO.framework */, + 7BBAEFAF2820EDE4EBA5D943 /* OpenGLES.framework */, + 4D35CE548040C1EF3B8BD581 /* QuartzCore.framework */, + 425802FD822D22CC110119BA /* Security.framework */, + 4152CBF14080E0C84DFD6613 /* SystemConfiguration.framework */, + 9285758C5E07413BA20C8840 /* UIKit.framework */, + 193951C11D1549E7701FFC40 /* libc++.dylib */, + 8F6ED0C99E84A5FE46148309 /* libicucore.dylib */, + 74F5EF3ED006A948BCC2BF82 /* libobjc.dylib */, + AAA8B2B19FA46102744D14EF /* libz.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4B066C2469FD81496EBDEF55 /* Resources */ = { + isa = PBXGroup; + children = ( + 69EBDE721FDFDCCDBB9DCC7A /* Images */, + 5F82E697DB805D8F5C7C7129 /* Museum-Icons */, + 959C749EEFBCC25DCDCC1416 /* Launch.xib */, + 75E000124A1962A304CEAAB8 /* aeroplane.png */, + 799CC7F35C34DAA014613DDF /* aeroplane@2x.png */, + 4C29D1A0DA47D1691C6CE0A5 /* argentina-large.png */, + EBDAAD248E528C9D828A2878 /* argentina.png */, + 3401A5B1795F7B7056A7F491 /* arrow.png */, + 46FF553F45838251496245F9 /* arrow@2x.png */, + 54743FFF10003AA647D1654F /* australia-large.png */, + C4D94F429262C3A09166D73F /* australia-large@2x.png */, + EA55E63E3ECB92756B8A8854 /* australia.png */, + 5AA08AB86BAE0FBBB8B3705F /* boat.png */, + 88F50C2BAD0B000697EBECCE /* boat@2x.png */, + D619B3D989A533F358F1D077 /* botswana-large.png */, + 97284C2589969381B1F2BF65 /* botswana.png */, + 8EB73A280D8FE0A1D2C054C8 /* bulgaria-large.png */, + BAF511F09C737A22E0A76E59 /* bulgaria.png */, + 2E95ACF5DD497E15A0B746DF /* glow-marker.png */, + 85109740028667AB1646AC2D /* glow-marker@2x.png */, + 1A597E83DFFF77F1EBD76B64 /* museum-exhibits.json */, + 72FF2F70BA1B0073BE1F375F /* newark_nj_1922.jpg */, + 85288BF143D609A7145A7846 /* popup_santa.png */, + D81C6119C4C7DE07FB42F86D /* popup_santa@2x.png */, + FE7C2DA207359F759D081156 /* step1.png */, + DC53293C94A672B4C62AD559 /* step1@2x.png */, + D83F65E0A08406E055A906B0 /* step2.png */, + 4C3CBB9D63F29447E8C3C06F /* step2@2x.png */, + 6D2F332D6F2FB20FE11DFB6E /* step3.png */, + 14A1E50F98F1EA56590AA120 /* step3@2x.png */, + 98A5F219881F5A5612905ABC /* step4.png */, + 62CBE24F5F8C7DF19AE8452C /* step4@2x.png */, + 3C1A74AB0EFA8C78AF23E564 /* step5.png */, + 7385EE81D06D3C1BD826B7C5 /* step5@2x.png */, + 359EF8FC1D5600228CFFBF4D /* step6.png */, + D05C1274204C92F13ABD8599 /* step6@2x.png */, + 3628691A5936DDD1821A622B /* step7.png */, + C7AB12C7756274D40E649762 /* step7@2x.png */, + E8CF929DF33D172F36F845E3 /* step8.png */, + FCCC58E7443B387046306EEF /* step8@2x.png */, + 3F1ABBF9B2E96BC488485119 /* track.json */, + ); + path = Resources; + sourceTree = ""; + }; + 55584DEF78F797697AA2FD6D /* GoogleMaps.framework/Resources */ = { + isa = PBXGroup; + children = ( + 8F59372BD761533A2A6B470A /* GoogleMaps.bundle */, + ); + path = GoogleMaps.framework/Resources; + sourceTree = ""; + }; + 5F82E697DB805D8F5C7C7129 /* Museum-Icons */ = { + isa = PBXGroup; + children = ( + 58B0EFEC1C69A989B6E84744 /* h1.png */, + EF3CF28401B594ADE426E656 /* h1@2x.png */, + FE2B3D4C04A8916BC8027A72 /* spitfire.png */, + 550E6B03BFE321336D066223 /* spitfire@2x.png */, + 7EC4132AB51E1500FF16E4C5 /* voyager.png */, + AB330C8620876E40BEC85968 /* voyager@2x.png */, + 487E4999CFF757EA18C569D8 /* x29.png */, + 124B199908A64B1EE958F3CA /* x29@2x.png */, + ); + path = "Museum-Icons"; + sourceTree = ""; + }; + 69EBDE721FDFDCCDBB9DCC7A /* Images */ = { + isa = PBXGroup; + children = ( + 77EA8532CD15A6B5E800415E /* Default-568h@2x.png */, + FA940308742FE4A8E2ACB6DB /* Default-Landscape@2x~ipad.png */, + 4F353004786E58271D76683E /* Default-Landscape~ipad.png */, + D03EB3995F5EF92504D8E44F /* Default-Portrait@2x~ipad.png */, + A56AF9AA60B75EF16CE3EF8C /* Default-Portrait~ipad.png */, + 842A2FB43838968D313B1783 /* Default.png */, + 0372FAEB924D0ADD012FBDFE /* Default@2x.png */, + 31DE8BA62A492FBC67E923AD /* sdkdemos_icon-72.png */, + 0F33C1C90D61AD0E2B502215 /* sdkdemos_icon-72@2x.png */, + CD99D73AD66B03243C670572 /* sdkdemos_icon.png */, + FD181A33530788C065AA2D90 /* sdkdemos_icon@2x.png */, + ); + path = Images; + sourceTree = ""; + }; + 6B58453C62AFCD886F0EED66 /* Source */ = { + isa = PBXGroup; + children = ( + 55584DEF78F797697AA2FD6D /* GoogleMaps.framework/Resources */, + 99979DB0F5D442647B9411F8 /* SDKDemos */, + ); + name = Source; + sourceTree = ""; + }; + 7F4C35C83CCF9EE02F5CB56A /* Samples */ = { + isa = PBXGroup; + children = ( + 63B21B38E81405568CD36449 /* AnimatedCurrentLocationViewController.h */, + 933F14F944559A5D5538023D /* AnimatedCurrentLocationViewController.m */, + 460EBDFBDFFE13DB517214A5 /* BasicMapViewController.h */, + 4BA4E139EA9FAA0D6A639CE6 /* BasicMapViewController.m */, + 59940AD9F86687DEA94EDE8C /* CameraViewController.h */, + 9A2729CB24AF1A2D245CD886 /* CameraViewController.m */, + 735DFE797020C7F09B21F773 /* CustomIndoorViewController.h */, + EB1CD2C01411A35969C872BA /* CustomIndoorViewController.m */, + ADB6F5B2F34C4461E2AFF686 /* CustomMarkersViewController.h */, + BD36A58B321268BB8E267874 /* CustomMarkersViewController.m */, + 81B8039817FFB9A398E1158B /* DoubleMapViewController.h */, + A578CA16809EBB3047B3C573 /* DoubleMapViewController.m */, + FD40AB7623BD1C9A81EA9239 /* FitBoundsViewController.h */, + 8B3EA327A271CA6A64D117A6 /* FitBoundsViewController.m */, + 989BBAF7DF1CD21CA2F9CC0E /* FixedPanoramaViewController.h */, + ABAA14F70E24A8E44326BBAE /* FixedPanoramaViewController.m */, + 1BA8114B0A4CDC672F1CBD6F /* GeocoderViewController.h */, + 3BD92F5F6AB10EDD9098A9DC /* GeocoderViewController.m */, + 3449C03C22DABB720D80FF72 /* GestureControlViewController.h */, + 0BA108F4D0C3BDA34CE54180 /* GestureControlViewController.m */, + 79A9E14215E011A6DFD2B0BA /* GradientPolylinesViewController.h */, + 7BFD80C8485C48F2C90C85A8 /* GradientPolylinesViewController.m */, + 778FAE1359A6570015F495EA /* GroundOverlayViewController.h */, + BD7A194DAB9B544294D7A2D0 /* GroundOverlayViewController.m */, + E4B1CC9979AFE466B59A9AC4 /* IndoorMuseumNavigationViewController.h */, + 3AAAD985B0FF3AF55E964A33 /* IndoorMuseumNavigationViewController.m */, + B6D2F25F8F4B1159C707B4A2 /* IndoorViewController.h */, + 0B6999063F311237F673B8B0 /* IndoorViewController.m */, + A849C0D00CED5DEF33AC6844 /* MapLayerViewController.h */, + 31F229C07CCFB0CFEBD70F68 /* MapLayerViewController.m */, + D1B2675ECA97CB1F5D0F5F88 /* MapTypesViewController.h */, + 5F3C1FEA265C2BC44CFFB4BD /* MapTypesViewController.m */, + 394AF35F597FB89E9D6A1E3D /* MapZoomViewController.h */, + 45580B3C558B48DD3359F2A1 /* MapZoomViewController.m */, + 7B4AF795B18672B900DE3568 /* MarkerEventsViewController.h */, + 1F1D873627D847B5C1B099FC /* MarkerEventsViewController.m */, + 13CBFFCFE30583D29D77B5A1 /* MarkerInfoWindowViewController.h */, + DC8661CA82A046ED2AF5B71F /* MarkerInfoWindowViewController.m */, + B1FA4EC4A89767E10B655CB0 /* MarkerLayerViewController.h */, + 9AB08565AAF1A341BC7D2A61 /* MarkerLayerViewController.m */, + 15F93E8BE938118367B428AA /* MarkersViewController.h */, + 9C2D5258F1365BCD57029138 /* MarkersViewController.m */, + AC4F4FF0F15305FE7DB327D5 /* MyLocationViewController.h */, + 6A37ED9FA16A22EFC586E32E /* MyLocationViewController.m */, + 2AB2A95CCD24024671158AD9 /* PanoramaViewController.h */, + DBBB2837C3A8ADB3D5191304 /* PanoramaViewController.m */, + BA7783E4FDFA7455B4EF6174 /* PolygonsViewController.h */, + 90258FFF395A5E25CCEC3AB0 /* PolygonsViewController.m */, + 6571C6FD38FB80E9657C6CF4 /* PolylinesViewController.h */, + 429F95111FD1F8B8A1C16865 /* PolylinesViewController.m */, + 778A178288F1E9593A03C6DB /* Samples.h */, + 043F8041B5649C26BCE75231 /* Samples.m */, + D7B0E6B7E378BF2301E42C6A /* StructuredGeocoderViewController.h */, + 64888C52E2E272FEFE7C18AA /* StructuredGeocoderViewController.m */, + 5977F6DB8B9BD6609BE664B0 /* TileLayerViewController.h */, + 6DEE18F2AE053B9A1DB19C5C /* TileLayerViewController.m */, + 09F0DD5C27A815E1A9720747 /* TrafficMapViewController.h */, + 78C6F7D665C6DD4328894967 /* TrafficMapViewController.m */, + E0D4EAD84FB1F606F6016E0B /* VisibleRegionViewController.h */, + DABC6731995E50EBF60E82DA /* VisibleRegionViewController.m */, + ); + path = Samples; + sourceTree = ""; + }; + 99979DB0F5D442647B9411F8 /* SDKDemos */ = { + isa = PBXGroup; + children = ( + 1CC686768E6E7BE8890097AC /* PlacesSamples */, + 4B066C2469FD81496EBDEF55 /* Resources */, + 7F4C35C83CCF9EE02F5CB56A /* Samples */, + C07CD02A92489C92FC822C3A /* SDKDemoAPIKey.h */, + A48DE20E9EF0EFE5F9656B51 /* SDKDemoAppDelegate.h */, + 5EC5186C29703467E4C2ED80 /* SDKDemoAppDelegate.m */, + 74A9950FBE5A377D1EBB2230 /* SDKDemoMasterViewController.h */, + 031103FAE4E394EA78895993 /* SDKDemoMasterViewController.m */, + 006739C6096E6B69D83F4A03 /* main.m */, + ); + path = SDKDemos; + sourceTree = ""; + }; + A2C8628B55F950FE20E7137C = { + isa = PBXGroup; + children = ( + 6B58453C62AFCD886F0EED66 /* Source */, + 2DBE6964ECA9681577AD08E9 /* Frameworks */, + E432E4A5C2DA557A55565E67 /* Products */, + D7938F561BAB3DB37E038EF7 /* Build */, + ); + sourceTree = ""; + }; + D7938F561BAB3DB37E038EF7 /* Build */ = { + isa = PBXGroup; + children = ( + FAF66F7D7BC732C1A5A896B0 /* SDKDemos.gyp */, + ); + name = Build; + sourceTree = ""; + }; + E432E4A5C2DA557A55565E67 /* Products */ = { + isa = PBXGroup; + children = ( + 656D0083D388D030EA0C9E3E /* SDKDemos.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F0A34C824BF91068571FB9C9 /* SDKDemos */ = { + isa = PBXNativeTarget; + buildConfigurationList = 258F0E920477D8ADA8B68139 /* Build configuration list for PBXNativeTarget "SDKDemos" */; + buildPhases = ( + ADA7B6829517252313848C86 /* Resources */, + 2F23BF35CD9D8A7EBB7A3277 /* Sources */, + 9F427D7DE56744A968901ED9 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SDKDemos; + productName = SDKDemos; + productReference = 656D0083D388D030EA0C9E3E /* SDKDemos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C0152C353C1C1C999BE34696 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + }; + buildConfigurationList = 3203C92569C9F50F18471A03 /* Build configuration list for PBXProject "SDKDemos" */; + compatibilityVersion = "Xcode 3.2"; + hasScannedForEncodings = 1; + mainGroup = A2C8628B55F950FE20E7137C; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F0A34C824BF91068571FB9C9 /* SDKDemos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + ADA7B6829517252313848C86 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F17C0B74A739BF14D241F16B /* aeroplane.png in Resources */, + C230F93CE1D3114716A8BB21 /* aeroplane@2x.png in Resources */, + 800859CC04D2A160A19CBEF7 /* argentina-large.png in Resources */, + 526533CE6B1C0ED1B688F483 /* argentina.png in Resources */, + 1E50BD7CDD9844B9D59D100E /* arrow.png in Resources */, + 59FCB7DFF4C3A3F9D7D09CEF /* arrow@2x.png in Resources */, + 9931F73B835246208F13740C /* australia-large.png in Resources */, + 67D720CC13969DD22BFA987A /* australia-large@2x.png in Resources */, + 8E8CDF0B6C238C4A12355BA6 /* australia.png in Resources */, + 07143A1B00A26FB01E24A850 /* boat.png in Resources */, + 1C19BC7BF5541FECFAC6D1E7 /* boat@2x.png in Resources */, + 742CEB5206972B2A9DF79010 /* botswana-large.png in Resources */, + 0248CC2CFECAE929CBA46D32 /* botswana.png in Resources */, + 7D0AC180E00091E865619B3A /* bulgaria-large.png in Resources */, + FF90C83B482F1E9B6B3CEDE9 /* bulgaria.png in Resources */, + 15B014D5EE4D4B8A9E5F1FAD /* glow-marker.png in Resources */, + 41A1EB571CD2C902160F70BA /* glow-marker@2x.png in Resources */, + ABB9D9368C8C9AA0F4E98A5B /* Default-568h@2x.png in Resources */, + 3008E4FF586C0C4CCE5414A0 /* Default-Landscape@2x~ipad.png in Resources */, + 8D032C0D459BAA28785D4D82 /* Default-Landscape~ipad.png in Resources */, + FF5B48D04F4CA6E5E78737D6 /* Default-Portrait@2x~ipad.png in Resources */, + 158A3EEE3E90A6CDE07C8D3E /* Default-Portrait~ipad.png in Resources */, + 0804F89A761192E17C4E14A0 /* Default.png in Resources */, + 4A32F95C94EB31838B53D74D /* Default@2x.png in Resources */, + D86BD487B00D457841F89438 /* sdkdemos_icon-72.png in Resources */, + 14AEFDCDCBCBA9BD4D739777 /* sdkdemos_icon-72@2x.png in Resources */, + B45757B5CE9CEDE5C085AC17 /* sdkdemos_icon.png in Resources */, + EDBD021F4894885DCBCF392F /* sdkdemos_icon@2x.png in Resources */, + DD3DCD76A940EBD4E58E76F3 /* h1.png in Resources */, + 8711D42833BA78FA57565605 /* h1@2x.png in Resources */, + 37D656EEA738FCF3B8FAAE89 /* spitfire.png in Resources */, + EB83CCF71D1F521D867360E6 /* spitfire@2x.png in Resources */, + ED6DEEBDC0298BF1AC19F0B0 /* voyager.png in Resources */, + B65254ACFCCA101144AEA425 /* voyager@2x.png in Resources */, + C6C66578EE0A3295A2392185 /* x29.png in Resources */, + E5CEE60985676D8CC6AC0FE8 /* x29@2x.png in Resources */, + C9B58772C3374D132D54B79E /* popup_santa.png in Resources */, + D8114F384AA6E0242578FAC3 /* popup_santa@2x.png in Resources */, + B3D44230385773BFD2CB22E7 /* step1.png in Resources */, + 150069CF910745504A7E3F7F /* step1@2x.png in Resources */, + 439219410CB49A6BB29EA6AA /* step2.png in Resources */, + EBDEB695207B4D4D9F3C677F /* step2@2x.png in Resources */, + DAD13235132F0A7B9A7D9A86 /* step3.png in Resources */, + 740C876D089202BF76EFF6D7 /* step3@2x.png in Resources */, + 0966341B5DECEEB70F824FB4 /* step4.png in Resources */, + 6E44FC6890A0979EED438270 /* step4@2x.png in Resources */, + 0A9073D3525470D2441215B2 /* step5.png in Resources */, + F844A584365DC27FDFC38368 /* step5@2x.png in Resources */, + 7E32939A0E72D31989291E78 /* step6.png in Resources */, + FAA88DA5B6CB123CB79FAB1E /* step6@2x.png in Resources */, + D5B79AD1DE6AFB7CA050796F /* step7.png in Resources */, + 696DC26814A711ED1FB8ACC8 /* step7@2x.png in Resources */, + 7BDA92EDDAB1F6D3BD5E3D33 /* step8.png in Resources */, + A1C1CC6329A61E8AC8DEF45E /* step8@2x.png in Resources */, + 099C7BEF376638CC84DE5779 /* newark_nj_1922.jpg in Resources */, + 81EE689DC035CAF1D9C479FD /* museum-exhibits.json in Resources */, + 93179278EF404CB0AB3AD109 /* track.json in Resources */, + F8DEB16CA5084A02112ACF8E /* Launch.xib in Resources */, + 6C86DB77A73CAEE2C48E6668 /* GoogleMaps.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2F23BF35CD9D8A7EBB7A3277 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13F27F941CFF1FF90A26E9FB /* main.m in Sources */, + 984203D4FC972F6775785F60 /* Samples+Places.m in Sources */, + 59B1403950BAF2A027E7AD06 /* SDKDemoPlacePickerViewController.m in Sources */, + 8305DE85D47096FF5E6B2C7F /* AnimatedCurrentLocationViewController.m in Sources */, + 1E848AC8B09FCD0B029B2515 /* BasicMapViewController.m in Sources */, + 4DAF1D27C71D97CEC4D35A40 /* CameraViewController.m in Sources */, + B5C57041E4F43FACD9D7855D /* CustomIndoorViewController.m in Sources */, + 3E0BB6C829DA62BCB801089E /* CustomMarkersViewController.m in Sources */, + C38470C15BFF9DAA51DB24CE /* DoubleMapViewController.m in Sources */, + 077F9A9BF20CCC7396BA038D /* FitBoundsViewController.m in Sources */, + F776F3C2E208EE9378C970C9 /* FixedPanoramaViewController.m in Sources */, + EB1E651BF9DC2712EDB689E2 /* GeocoderViewController.m in Sources */, + 5AFC6A9EF7E5334274F2003B /* GestureControlViewController.m in Sources */, + 73B795E621B99087515948DB /* GradientPolylinesViewController.m in Sources */, + BFAF2B58B5447DC7F6700608 /* GroundOverlayViewController.m in Sources */, + 5E25A0B2F58A1DC71D3D095E /* IndoorMuseumNavigationViewController.m in Sources */, + D81F4D7B477B99779B2E26BB /* IndoorViewController.m in Sources */, + D9E9F4337CCE7C06D731C309 /* MapLayerViewController.m in Sources */, + B62B3CD1C9493C6DC10D0F6A /* MapTypesViewController.m in Sources */, + 2351AA58A2D1B916CE6FC02F /* MapZoomViewController.m in Sources */, + 22E010704EB4D09C613E3D02 /* MarkerEventsViewController.m in Sources */, + 7CA30F964585636F7722EF50 /* MarkerInfoWindowViewController.m in Sources */, + 0AB2EA98279BC321EA4EFC30 /* MarkerLayerViewController.m in Sources */, + FB3CCF78441EF08378478AAA /* MarkersViewController.m in Sources */, + 35F868C56688C0541E7C08D6 /* MyLocationViewController.m in Sources */, + 96A9D65C45A6AC6A72B7A83D /* PanoramaViewController.m in Sources */, + 58ADE659AEBDB89F90AA0006 /* PolygonsViewController.m in Sources */, + E471D0D1812EBBED5E1BFC3E /* PolylinesViewController.m in Sources */, + C8FAEF11B1140F995C4AF50E /* Samples.m in Sources */, + A211BD2291B236DFBDB6F703 /* StructuredGeocoderViewController.m in Sources */, + BCDA5CF64C63BF71C5805642 /* TileLayerViewController.m in Sources */, + 317AF024CEE06AB3E759BE77 /* TrafficMapViewController.m in Sources */, + 55C349437FEF9D735F70C826 /* VisibleRegionViewController.m in Sources */, + 2B53A795F430861957A94EB2 /* SDKDemoAppDelegate.m in Sources */, + F67ADB09D12D294ABF56944E /* SDKDemoMasterViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 182F5544C35981299E88F8A3 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)"; + INFOPLIST_FILE = "./SDKDemos/SDKDemo-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + LIBRARY_SEARCH_PATHS = ( + ., + "$(SDKROOT)/System/Library/Frameworks", + ); + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = SDKDemos; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)"; + USE_HEADERMAP = NO; + VALID_ARCHS = "i386 armv7"; + WRAPPER_PREFIX = ""; + }; + name = Default; + }; + A37B5359681FA0FDEE47E9A5 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + INTERMEDIATE_DIR = "$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)"; + SDKROOT = iphoneos; + SHARED_INTERMEDIATE_DIR = "$(SYMROOT)/DerivedSources/$(CONFIGURATION)"; + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 258F0E920477D8ADA8B68139 /* Build configuration list for PBXNativeTarget "SDKDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 182F5544C35981299E88F8A3 /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; + 3203C92569C9F50F18471A03 /* Build configuration list for PBXProject "SDKDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A37B5359681FA0FDEE47E9A5 /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = C0152C353C1C1C999BE34696 /* Project object */; +} diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.h new file mode 100644 index 0000000..4f6afc2 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.h @@ -0,0 +1,5 @@ +#import + +@interface SDKDemoPlacePickerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.m new file mode 100644 index 0000000..5a8823a --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.m @@ -0,0 +1,62 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.h" + +#import "SDKDemos/SDKDemoAPIKey.h" + + +@implementation SDKDemoPlacePickerViewController { + GMSPlacePicker *_placePicker; +} + +- (instancetype)init { + if ((self = [super init])) { + CLLocationCoordinate2D southWestSydney = CLLocationCoordinate2DMake(-33.8659, 151.1953); + CLLocationCoordinate2D northEastSydney = CLLocationCoordinate2DMake(-33.8645, 151.1969); + GMSCoordinateBounds *sydneyBounds = + [[GMSCoordinateBounds alloc] initWithCoordinate:southWestSydney coordinate:northEastSydney]; + GMSPlacePickerConfig *config = + [[GMSPlacePickerConfig alloc] initWithViewport:sydneyBounds]; + _placePicker = [[GMSPlacePicker alloc] initWithConfig:config]; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + UITextView *textView = [[UITextView alloc] initWithFrame:self.view.bounds]; + textView.delegate = self; + textView.editable = NO; + [self.view addSubview:textView]; + __weak UITextView *weakResultView = textView; + [_placePicker pickPlaceWithCallback:^(GMSPlace *place, NSError *error) { + UITextView *resultView = weakResultView; + if (resultView == nil) { + return; + } + if (place) { + NSMutableAttributedString *text = + [[NSMutableAttributedString alloc] initWithString:[place description]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]]; + [text appendAttributedString:place.attributions]; + resultView.attributedText = text; + } else if (error) { + resultView.text = + [NSString stringWithFormat:@"Place picking failed with error: %@", error]; + } else { + resultView.text = @"Place picking cancelled."; + } + }]; +} + +#pragma mark - UITextViewDelegate + +- (BOOL)textView:(UITextView *)textView + shouldInteractWithURL:(NSURL *)url + inRange:(NSRange)characterRange { + // Make links clickable. + return YES; +} +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.h new file mode 100644 index 0000000..376a8ec --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.h @@ -0,0 +1,7 @@ +#import "SDKDemos/Samples/Samples.h" + +@interface Samples (Places) + ++ (NSArray *)placesDemos; + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.m new file mode 100644 index 0000000..6f4d194 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/PlacesSamples/Samples+Places.m @@ -0,0 +1,19 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/PlacesSamples/Samples+Places.h" + +#import "SDKDemos/PlacesSamples/SDKDemoPlacePickerViewController.h" + +@implementation Samples (Places) + ++ (NSArray *)placesDemos { + return @[ + [Samples newDemo:[SDKDemoPlacePickerViewController class] + withTitle:@"Places API Place Picker" + andDescription:nil], + ]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-568h@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-568h@2x.png new file mode 100644 index 0000000..8443cbe Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-568h@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape@2x~ipad.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape@2x~ipad.png new file mode 100644 index 0000000..3efbab7 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape@2x~ipad.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape~ipad.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape~ipad.png new file mode 100644 index 0000000..4cfca97 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Landscape~ipad.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait@2x~ipad.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait@2x~ipad.png new file mode 100644 index 0000000..66789fb Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait@2x~ipad.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait~ipad.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait~ipad.png new file mode 100644 index 0000000..0a62073 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default-Portrait~ipad.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default.png new file mode 100644 index 0000000..bcbeb8c Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default@2x.png new file mode 100644 index 0000000..a631978 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/Default@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72.png new file mode 100644 index 0000000..689b3a2 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72@2x.png new file mode 100644 index 0000000..3abcd45 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon-72@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon.png new file mode 100644 index 0000000..6e98095 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon@2x.png new file mode 100644 index 0000000..0979478 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Images/sdkdemos_icon@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Launch.xib b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Launch.xib new file mode 100644 index 0000000..c9334a5 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Launch.xib @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1.png new file mode 100644 index 0000000..9859ded Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1@2x.png new file mode 100644 index 0000000..0eb550f Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/h1@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire.png new file mode 100644 index 0000000..ed82a1a Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire@2x.png new file mode 100644 index 0000000..883152c Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/spitfire@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager.png new file mode 100644 index 0000000..2f83887 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager@2x.png new file mode 100644 index 0000000..f796311 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/voyager@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29.png new file mode 100644 index 0000000..5c84651 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29@2x.png new file mode 100644 index 0000000..7fb4758 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/Museum-Icons/x29@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane.png new file mode 100644 index 0000000..0ca6d73 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane@2x.png new file mode 100644 index 0000000..013d570 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/aeroplane@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ar.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ar.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ar.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina-large.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina-large.png new file mode 100644 index 0000000..50ee6b2 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina-large.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina.png new file mode 100644 index 0000000..23637d1 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/argentina.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow.png new file mode 100644 index 0000000..43a0465 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow@2x.png new file mode 100644 index 0000000..318efd5 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/arrow@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large.png new file mode 100644 index 0000000..098821d Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large@2x.png new file mode 100644 index 0000000..8d28a75 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia-large@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia.png new file mode 100644 index 0000000..b2e7c40 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/australia.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat.png new file mode 100644 index 0000000..0c6c08b Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat@2x.png new file mode 100644 index 0000000..609863f Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/boat@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana-large.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana-large.png new file mode 100644 index 0000000..ee171c8 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana-large.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana.png new file mode 100644 index 0000000..1e34013 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/botswana.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria-large.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria-large.png new file mode 100644 index 0000000..ab22b29 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria-large.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria.png new file mode 100644 index 0000000..bffb2af Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/bulgaria.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ca.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ca.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ca.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/cs.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/cs.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/da.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/da.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/da.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/de.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/de.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/de.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/el.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/el.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/el.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en_GB.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en_GB.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/en_GB.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/es.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/es.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/es.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fi.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fi.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fr.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/fr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker.png new file mode 100644 index 0000000..1a4b884 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker@2x.png new file mode 100644 index 0000000..f061e16 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/glow-marker@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/he.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/he.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/he.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hr.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hu.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/hu.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/id.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/id.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/id.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/it.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/it.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/it.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ja.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ja.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ko.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ko.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ko.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ms.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ms.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ms.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/museum-exhibits.json b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/museum-exhibits.json new file mode 100644 index 0000000..231334b --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/museum-exhibits.json @@ -0,0 +1,30 @@ +[ + { + "key": "h1", + "name": "Hughes H-1", + "lat": 38.8879, + "lng": -77.02085, + "level": "1", + }, + { + "key": "voyager", + "name": "Rutan Voyager", + "lat": 38.8880, + "lng": -77.0199, + "level": "1", + }, + { + "key": "spitfire", + "name": "Supermarine Spitfire", + "lat": 38.8879, + "lng": -77.0208, + "level": "2", + }, + { + "key": "x29", + "name": "Grumman X-29", + "lat": 38.88845, + "lng": -77.01875, + "level": "2", + } +] \ No newline at end of file diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nb.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nb.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nb.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/newark_nj_1922.jpg b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/newark_nj_1922.jpg new file mode 100644 index 0000000..1f4ae59 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/newark_nj_1922.jpg differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nl.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/nl.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pl.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pl.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa.png new file mode 100644 index 0000000..50b8f59 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa@2x.png new file mode 100644 index 0000000..4bdd7a3 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/popup_santa@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt_PT.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt_PT.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/pt_PT.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ro.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ro.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ro.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ru.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/ru.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sk.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sk.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sk.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1.png new file mode 100644 index 0000000..172e9fd Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1@2x.png new file mode 100644 index 0000000..aceedc6 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step1@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2.png new file mode 100644 index 0000000..c1c762e Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2@2x.png new file mode 100644 index 0000000..1457248 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step2@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3.png new file mode 100644 index 0000000..2dcc088 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3@2x.png new file mode 100644 index 0000000..7e0544c Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step3@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4.png new file mode 100644 index 0000000..a1e8155 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4@2x.png new file mode 100644 index 0000000..6bcc25b Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step4@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5.png new file mode 100644 index 0000000..4ff116e Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5@2x.png new file mode 100644 index 0000000..ec16e0d Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step5@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6.png new file mode 100644 index 0000000..09d9b37 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6@2x.png new file mode 100644 index 0000000..e759f9d Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step6@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7.png new file mode 100644 index 0000000..60fbf74 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7@2x.png new file mode 100644 index 0000000..2719641 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step7@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8.png new file mode 100644 index 0000000..f51cccc Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8@2x.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8@2x.png new file mode 100644 index 0000000..81a6196 Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/step8@2x.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sv.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sv.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/sv.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/th.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/th.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/th.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/tr.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/tr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/track.json b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/track.json new file mode 100644 index 0000000..1d49290 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/track.json @@ -0,0 +1 @@ +[{"lat": "44.145331", "lng": "9.661942", "elevation": "173.8000030517578", "time": "2013-09-20T08:40:00.855Z"}, {"lat": "44.145157", "lng": "9.661917", "elevation": "177.3000030517578", "time": "2013-09-20T08:40:01.824Z"}, {"lat": "44.14505", "lng": "9.662049", "elevation": "170.60000610351563", "time": "2013-09-20T08:40:02.945Z"}, {"lat": "44.145", "lng": "9.662165", "elevation": "156.5", "time": "2013-09-20T08:40:03.828Z"}, {"lat": "44.144918", "lng": "9.662227", "elevation": "130.6999969482422", "time": "2013-09-20T08:40:04.823Z"}, {"lat": "44.144945", "lng": "9.662122", "elevation": "149.5", "time": "2013-09-20T08:40:06.123Z"}, {"lat": "44.14503", "lng": "9.662141", "elevation": "152.89999389648438", "time": "2013-09-20T08:40:07.122Z"}, {"lat": "44.144943", "lng": "9.662169", "elevation": "155.3000030517578", "time": "2013-09-20T08:40:19.117Z"}, {"lat": "44.144937", "lng": "9.66217", "elevation": "155.5", "time": "2013-09-20T08:40:20.157Z"}, {"lat": "44.144933", "lng": "9.662171", "elevation": "154.8000030517578", "time": "2013-09-20T08:40:22.132Z"}, {"lat": "44.144933", "lng": "9.662173", "elevation": "155.0", "time": "2013-09-20T08:40:23.141Z"}, {"lat": "44.144937", "lng": "9.662186", "elevation": "155.8000030517578", "time": "2013-09-20T08:40:45.224Z"}, {"lat": "44.144934", "lng": "9.66219", "elevation": "158.5", "time": "2013-09-20T08:40:46.191Z"}, {"lat": "44.144911", "lng": "9.662248", "elevation": "161.6999969482422", "time": "2013-09-20T08:40:59.133Z"}, {"lat": "44.144911", "lng": "9.662249", "elevation": "161.8000030517578", "time": "2013-09-20T08:41:00.124Z"}, {"lat": "44.14491", "lng": "9.662258", "elevation": "161.6999969482422", "time": "2013-09-20T08:41:09.127Z"}, {"lat": "44.144907", "lng": "9.662263", "elevation": "162.0", "time": "2013-09-20T08:41:10.185Z"}, {"lat": "44.144884", "lng": "9.662378", "elevation": "161.3000030517578", "time": "2013-09-20T08:41:17.137Z"}, {"lat": "44.144879", "lng": "9.662397", "elevation": "161.1999969482422", "time": "2013-09-20T08:41:18.211Z"}, {"lat": "44.144874", "lng": "9.662517", "elevation": "163.0", "time": "2013-09-20T08:41:26.217Z"}, {"lat": "44.144877", "lng": "9.66253", "elevation": "163.39999389648438", "time": "2013-09-20T08:41:27.220Z"}, {"lat": "44.144812", "lng": "9.662617", "elevation": "166.8000030517578", "time": "2013-09-20T08:41:36.137Z"}, {"lat": "44.144806", "lng": "9.662625", "elevation": "166.89999389648438", "time": "2013-09-20T08:41:37.146Z"}, {"lat": "44.14477", "lng": "9.662604", "elevation": "167.10000610351563", "time": "2013-09-20T08:41:49.143Z"}, {"lat": "44.14477", "lng": "9.662607", "elevation": "167.1999969482422", "time": "2013-09-20T08:41:50.138Z"}, {"lat": "44.144763", "lng": "9.662619", "elevation": "168.0", "time": "2013-09-20T08:41:58.146Z"}, {"lat": "44.14476", "lng": "9.662618", "elevation": "168.3000030517578", "time": "2013-09-20T08:41:59.133Z"}, {"lat": "44.144755", "lng": "9.662616", "elevation": "168.5", "time": "2013-09-20T08:42:01.147Z"}, {"lat": "44.144755", "lng": "9.662616", "elevation": "168.6999969482422", "time": "2013-09-20T08:42:02.133Z"}, {"lat": "44.144754", "lng": "9.662623", "elevation": "169.8000030517578", "time": "2013-09-20T08:43:18.202Z"}, {"lat": "44.144753", "lng": "9.662633", "elevation": "169.39999389648438", "time": "2013-09-20T08:43:19.274Z"}, {"lat": "44.144768", "lng": "9.662683", "elevation": "173.8000030517578", "time": "2013-09-20T08:43:28.140Z"}, {"lat": "44.144768", "lng": "9.662684", "elevation": "174.0", "time": "2013-09-20T08:43:29.177Z"}, {"lat": "44.144764", "lng": "9.662687", "elevation": "172.89999389648438", "time": "2013-09-20T08:43:33.140Z"}, {"lat": "44.144761", "lng": "9.662692", "elevation": "173.3000030517578", "time": "2013-09-20T08:43:34.147Z"}, {"lat": "44.144755", "lng": "9.662699", "elevation": "173.1999969482422", "time": "2013-09-20T08:43:37.220Z"}, {"lat": "44.144754", "lng": "9.6627", "elevation": "173.1999969482422", "time": "2013-09-20T08:43:38.164Z"}, {"lat": "44.144755", "lng": "9.662702", "elevation": "173.3000030517578", "time": "2013-09-20T08:43:43.148Z"}, {"lat": "44.144756", "lng": "9.662709", "elevation": "172.6999969482422", "time": "2013-09-20T08:43:44.141Z"}, {"lat": "44.144716", "lng": "9.662816", "elevation": "179.5", "time": "2013-09-20T08:43:51.157Z"}, {"lat": "44.144717", "lng": "9.662831", "elevation": "180.8000030517578", "time": "2013-09-20T08:43:52.141Z"}, {"lat": "44.1447", "lng": "9.662945", "elevation": "182.3000030517578", "time": "2013-09-20T08:44:01.165Z"}, {"lat": "44.144696", "lng": "9.662956", "elevation": "181.89999389648438", "time": "2013-09-20T08:44:02.153Z"}, {"lat": "44.144679", "lng": "9.662965", "elevation": "181.6999969482422", "time": "2013-09-20T08:44:08.135Z"}, {"lat": "44.144679", "lng": "9.662966", "elevation": "181.60000610351563", "time": "2013-09-20T08:44:09.139Z"}, {"lat": "44.14469", "lng": "9.66299", "elevation": "183.1999969482422", "time": "2013-09-20T08:44:26.146Z"}, {"lat": "44.144687", "lng": "9.662998", "elevation": "182.89999389648438", "time": "2013-09-20T08:44:27.145Z"}, {"lat": "44.144661", "lng": "9.663117", "elevation": "193.1999969482422", "time": "2013-09-20T08:44:38.177Z"}, {"lat": "44.144658", "lng": "9.66312", "elevation": "193.1999969482422", "time": "2013-09-20T08:44:39.232Z"}, {"lat": "44.144581", "lng": "9.663173", "elevation": "199.3000030517578", "time": "2013-09-20T08:44:51.156Z"}, {"lat": "44.144572", "lng": "9.66319", "elevation": "199.39999389648438", "time": "2013-09-20T08:44:52.153Z"}, {"lat": "44.144518", "lng": "9.663271", "elevation": "201.1999969482422", "time": "2013-09-20T08:44:57.156Z"}, {"lat": "44.144506", "lng": "9.663276", "elevation": "202.5", "time": "2013-09-20T08:44:58.141Z"}, {"lat": "44.144498", "lng": "9.663277", "elevation": "202.3000030517578", "time": "2013-09-20T08:45:02.212Z"}, {"lat": "44.144506", "lng": "9.663277", "elevation": "201.8000030517578", "time": "2013-09-20T08:45:03.249Z"}, {"lat": "44.144513", "lng": "9.66328", "elevation": "201.1999969482422", "time": "2013-09-20T08:45:04.186Z"}, {"lat": "44.144526", "lng": "9.663302", "elevation": "199.5", "time": "2013-09-20T08:45:09.163Z"}, {"lat": "44.144526", "lng": "9.663298", "elevation": "199.89999389648438", "time": "2013-09-20T08:45:10.157Z"}, {"lat": "44.144527", "lng": "9.663291", "elevation": "200.6999969482422", "time": "2013-09-20T08:45:11.229Z"}, {"lat": "44.144527", "lng": "9.663281", "elevation": "201.8000030517578", "time": "2013-09-20T08:45:12.229Z"}, {"lat": "44.144522", "lng": "9.663257", "elevation": "202.0", "time": "2013-09-20T08:45:17.165Z"}, {"lat": "44.14452", "lng": "9.663259", "elevation": "201.60000610351563", "time": "2013-09-20T08:45:18.220Z"}, {"lat": "44.144511", "lng": "9.663258", "elevation": "202.0", "time": "2013-09-20T08:45:27.262Z"}, {"lat": "44.144503", "lng": "9.663259", "elevation": "200.39999389648438", "time": "2013-09-20T08:45:28.141Z"}, {"lat": "44.144419", "lng": "9.663262", "elevation": "198.3000030517578", "time": "2013-09-20T08:45:33.164Z"}, {"lat": "44.144404", "lng": "9.663262", "elevation": "197.3000030517578", "time": "2013-09-20T08:45:34.204Z"}, {"lat": "44.144364", "lng": "9.663282", "elevation": "198.3000030517578", "time": "2013-09-20T08:45:42.142Z"}, {"lat": "44.144366", "lng": "9.663283", "elevation": "198.10000610351563", "time": "2013-09-20T08:45:43.149Z"}, {"lat": "44.144362", "lng": "9.663275", "elevation": "199.3000030517578", "time": "2013-09-20T08:46:03.152Z"}, {"lat": "44.144358", "lng": "9.663284", "elevation": "199.1999969482422", "time": "2013-09-20T08:46:04.142Z"}, {"lat": "44.144319", "lng": "9.663392", "elevation": "201.60000610351563", "time": "2013-09-20T08:46:12.160Z"}, {"lat": "44.144313", "lng": "9.663404", "elevation": "201.0", "time": "2013-09-20T08:46:13.153Z"}, {"lat": "44.144264", "lng": "9.663501", "elevation": "204.89999389648438", "time": "2013-09-20T08:46:20.144Z"}, {"lat": "44.144256", "lng": "9.663513", "elevation": "206.60000610351563", "time": "2013-09-20T08:46:21.170Z"}, {"lat": "44.144207", "lng": "9.663617", "elevation": "207.89999389648438", "time": "2013-09-20T08:46:31.257Z"}, {"lat": "44.144203", "lng": "9.663625", "elevation": "208.6999969482422", "time": "2013-09-20T08:46:32.221Z"}, {"lat": "44.144194", "lng": "9.6637", "elevation": "210.10000610351563", "time": "2013-09-20T08:46:44.148Z"}, {"lat": "44.144195", "lng": "9.663701", "elevation": "210.0", "time": "2013-09-20T08:46:45.162Z"}, {"lat": "44.144193", "lng": "9.663706", "elevation": "210.0", "time": "2013-09-20T08:47:02.176Z"}, {"lat": "44.144194", "lng": "9.663712", "elevation": "209.39999389648438", "time": "2013-09-20T08:47:03.180Z"}, {"lat": "44.144242", "lng": "9.663813", "elevation": "205.8000030517578", "time": "2013-09-20T08:47:19.246Z"}, {"lat": "44.144247", "lng": "9.663822", "elevation": "205.1999969482422", "time": "2013-09-20T08:47:20.183Z"}, {"lat": "44.144316", "lng": "9.663899", "elevation": "202.10000610351563", "time": "2013-09-20T08:47:34.231Z"}, {"lat": "44.14432", "lng": "9.663909", "elevation": "201.89999389648438", "time": "2013-09-20T08:47:35.229Z"}, {"lat": "44.144355", "lng": "9.66397", "elevation": "205.89999389648438", "time": "2013-09-20T08:47:43.176Z"}, {"lat": "44.144354", "lng": "9.663968", "elevation": "205.8000030517578", "time": "2013-09-20T08:47:44.172Z"}, {"lat": "44.144359", "lng": "9.663989", "elevation": "207.8000030517578", "time": "2013-09-20T08:47:53.213Z"}, {"lat": "44.14436", "lng": "9.663996", "elevation": "207.89999389648438", "time": "2013-09-20T08:47:54.162Z"}, {"lat": "44.144404", "lng": "9.664094", "elevation": "210.10000610351563", "time": "2013-09-20T08:48:01.203Z"}, {"lat": "44.14441", "lng": "9.664112", "elevation": "209.89999389648438", "time": "2013-09-20T08:48:02.167Z"}, {"lat": "44.144445", "lng": "9.664217", "elevation": "208.39999389648438", "time": "2013-09-20T08:48:09.225Z"}, {"lat": "44.14445", "lng": "9.664226", "elevation": "207.39999389648438", "time": "2013-09-20T08:48:10.169Z"}, {"lat": "44.14451", "lng": "9.664318", "elevation": "207.6999969482422", "time": "2013-09-20T08:48:19.190Z"}, {"lat": "44.144516", "lng": "9.664334", "elevation": "206.0", "time": "2013-09-20T08:48:20.177Z"}, {"lat": "44.144565", "lng": "9.664426", "elevation": "205.0", "time": "2013-09-20T08:48:27.171Z"}, {"lat": "44.144574", "lng": "9.664434", "elevation": "205.10000610351563", "time": "2013-09-20T08:48:28.180Z"}, {"lat": "44.144609", "lng": "9.664543", "elevation": "206.6999969482422", "time": "2013-09-20T08:48:40.184Z"}, {"lat": "44.14461", "lng": "9.664554", "elevation": "206.39999389648438", "time": "2013-09-20T08:48:41.182Z"}, {"lat": "44.144638", "lng": "9.664672", "elevation": "205.10000610351563", "time": "2013-09-20T08:48:51.188Z"}, {"lat": "44.144642", "lng": "9.664682", "elevation": "205.60000610351563", "time": "2013-09-20T08:48:52.230Z"}, {"lat": "44.144682", "lng": "9.664781", "elevation": "205.8000030517578", "time": "2013-09-20T08:49:02.254Z"}, {"lat": "44.144687", "lng": "9.664793", "elevation": "206.0", "time": "2013-09-20T08:49:03.262Z"}, {"lat": "44.144653", "lng": "9.664906", "elevation": "206.60000610351563", "time": "2013-09-20T08:49:15.287Z"}, {"lat": "44.14465", "lng": "9.664912", "elevation": "207.10000610351563", "time": "2013-09-20T08:49:16.261Z"}, {"lat": "44.144651", "lng": "9.664916", "elevation": "205.89999389648438", "time": "2013-09-20T08:49:18.271Z"}, {"lat": "44.144656", "lng": "9.664914", "elevation": "205.89999389648438", "time": "2013-09-20T08:49:19.343Z"}, {"lat": "44.144661", "lng": "9.664911", "elevation": "206.0", "time": "2013-09-20T08:49:20.304Z"}, {"lat": "44.144685", "lng": "9.664912", "elevation": "205.89999389648438", "time": "2013-09-20T08:49:28.388Z"}, {"lat": "44.144686", "lng": "9.664914", "elevation": "206.0", "time": "2013-09-20T08:49:29.371Z"}, {"lat": "44.144687", "lng": "9.66492", "elevation": "205.89999389648438", "time": "2013-09-20T08:49:35.323Z"}, {"lat": "44.144691", "lng": "9.664926", "elevation": "205.39999389648438", "time": "2013-09-20T08:49:36.247Z"}, {"lat": "44.144753", "lng": "9.665007", "elevation": "203.6999969482422", "time": "2013-09-20T08:49:42.194Z"}, {"lat": "44.144764", "lng": "9.665024", "elevation": "203.89999389648438", "time": "2013-09-20T08:49:43.371Z"}, {"lat": "44.144819", "lng": "9.66512", "elevation": "204.10000610351563", "time": "2013-09-20T08:49:51.386Z"}, {"lat": "44.14482", "lng": "9.665126", "elevation": "204.3000030517578", "time": "2013-09-20T08:49:52.321Z"}, {"lat": "44.144856", "lng": "9.665239", "elevation": "205.89999389648438", "time": "2013-09-20T08:50:03.402Z"}, {"lat": "44.144859", "lng": "9.665241", "elevation": "205.60000610351563", "time": "2013-09-20T08:50:04.370Z"}, {"lat": "44.144862", "lng": "9.665246", "elevation": "205.5", "time": "2013-09-20T08:50:07.377Z"}, {"lat": "44.144862", "lng": "9.665247", "elevation": "205.5", "time": "2013-09-20T08:50:08.322Z"}, {"lat": "44.144864", "lng": "9.665254", "elevation": "206.1999969482422", "time": "2013-09-20T08:50:17.332Z"}, {"lat": "44.144867", "lng": "9.665261", "elevation": "206.10000610351563", "time": "2013-09-20T08:50:18.349Z"}, {"lat": "44.144931", "lng": "9.665342", "elevation": "207.6999969482422", "time": "2013-09-20T08:50:23.347Z"}, {"lat": "44.144945", "lng": "9.66536", "elevation": "208.0", "time": "2013-09-20T08:50:24.325Z"}, {"lat": "44.144995", "lng": "9.665457", "elevation": "206.39999389648438", "time": "2013-09-20T08:50:30.244Z"}, {"lat": "44.144997", "lng": "9.665466", "elevation": "206.3000030517578", "time": "2013-09-20T08:50:31.187Z"}, {"lat": "44.144991", "lng": "9.6655", "elevation": "206.1999969482422", "time": "2013-09-20T08:50:41.277Z"}, {"lat": "44.144991", "lng": "9.665502", "elevation": "205.8000030517578", "time": "2013-09-20T08:50:42.244Z"}, {"lat": "44.144995", "lng": "9.665519", "elevation": "204.1999969482422", "time": "2013-09-20T08:50:54.344Z"}, {"lat": "44.144995", "lng": "9.665528", "elevation": "204.1999969482422", "time": "2013-09-20T08:50:55.360Z"}, {"lat": "44.144992", "lng": "9.665644", "elevation": "206.8000030517578", "time": "2013-09-20T08:51:02.176Z"}, {"lat": "44.14499", "lng": "9.665659", "elevation": "206.6999969482422", "time": "2013-09-20T08:51:03.176Z"}, {"lat": "44.145013", "lng": "9.665772", "elevation": "204.60000610351563", "time": "2013-09-20T08:51:12.336Z"}, {"lat": "44.145022", "lng": "9.665786", "elevation": "204.10000610351563", "time": "2013-09-20T08:51:13.305Z"}, {"lat": "44.14507", "lng": "9.665875", "elevation": "204.3000030517578", "time": "2013-09-20T08:51:20.280Z"}, {"lat": "44.145072", "lng": "9.665891", "elevation": "202.89999389648438", "time": "2013-09-20T08:51:21.363Z"}, {"lat": "44.145067", "lng": "9.666001", "elevation": "197.8000030517578", "time": "2013-09-20T08:51:37.323Z"}, {"lat": "44.145074", "lng": "9.666025", "elevation": "197.5", "time": "2013-09-20T08:51:38.322Z"}, {"lat": "44.145099", "lng": "9.666122", "elevation": "196.3000030517578", "time": "2013-09-20T08:51:41.330Z"}, {"lat": "44.145112", "lng": "9.666149", "elevation": "196.3000030517578", "time": "2013-09-20T08:51:42.355Z"}, {"lat": "44.14516", "lng": "9.666228", "elevation": "197.6999969482422", "time": "2013-09-20T08:51:46.256Z"}, {"lat": "44.14517", "lng": "9.666247", "elevation": "197.3000030517578", "time": "2013-09-20T08:51:47.227Z"}, {"lat": "44.145223", "lng": "9.666331", "elevation": "199.89999389648438", "time": "2013-09-20T08:51:54.211Z"}, {"lat": "44.145231", "lng": "9.666343", "elevation": "201.0", "time": "2013-09-20T08:51:55.178Z"}, {"lat": "44.145287", "lng": "9.666436", "elevation": "202.60000610351563", "time": "2013-09-20T08:52:02.194Z"}, {"lat": "44.145294", "lng": "9.666447", "elevation": "202.89999389648438", "time": "2013-09-20T08:52:03.228Z"}, {"lat": "44.145377", "lng": "9.666465", "elevation": "201.0", "time": "2013-09-20T08:52:13.181Z"}, {"lat": "44.145386", "lng": "9.666461", "elevation": "201.10000610351563", "time": "2013-09-20T08:52:14.212Z"}, {"lat": "44.14542", "lng": "9.666575", "elevation": "199.1999969482422", "time": "2013-09-20T08:52:29.348Z"}, {"lat": "44.145421", "lng": "9.666594", "elevation": "199.0", "time": "2013-09-20T08:52:30.327Z"}, {"lat": "44.145417", "lng": "9.666709", "elevation": "195.39999389648438", "time": "2013-09-20T08:52:36.199Z"}, {"lat": "44.145418", "lng": "9.666721", "elevation": "196.10000610351563", "time": "2013-09-20T08:52:37.197Z"}, {"lat": "44.145423", "lng": "9.666843", "elevation": "195.6999969482422", "time": "2013-09-20T08:52:49.192Z"}, {"lat": "44.145426", "lng": "9.666855", "elevation": "195.10000610351563", "time": "2013-09-20T08:52:50.233Z"}, {"lat": "44.145455", "lng": "9.666967", "elevation": "194.1999969482422", "time": "2013-09-20T08:52:58.191Z"}, {"lat": "44.145459", "lng": "9.66698", "elevation": "194.0", "time": "2013-09-20T08:52:59.184Z"}, {"lat": "44.145496", "lng": "9.667082", "elevation": "191.10000610351563", "time": "2013-09-20T08:53:09.183Z"}, {"lat": "44.1455", "lng": "9.667098", "elevation": "191.1999969482422", "time": "2013-09-20T08:53:10.200Z"}, {"lat": "44.145552", "lng": "9.667184", "elevation": "191.8000030517578", "time": "2013-09-20T08:53:16.329Z"}, {"lat": "44.145557", "lng": "9.667196", "elevation": "191.8000030517578", "time": "2013-09-20T08:53:17.356Z"}, {"lat": "44.145562", "lng": "9.667214", "elevation": "189.60000610351563", "time": "2013-09-20T08:53:22.291Z"}, {"lat": "44.14556", "lng": "9.667212", "elevation": "189.6999969482422", "time": "2013-09-20T08:53:23.241Z"}, {"lat": "44.145553", "lng": "9.66721", "elevation": "188.6999969482422", "time": "2013-09-20T08:53:50.175Z"}, {"lat": "44.145559", "lng": "9.66721", "elevation": "189.1999969482422", "time": "2013-09-20T08:53:51.175Z"}, {"lat": "44.145641", "lng": "9.667257", "elevation": "192.10000610351563", "time": "2013-09-20T08:53:58.197Z"}, {"lat": "44.14565", "lng": "9.667267", "elevation": "192.5", "time": "2013-09-20T08:53:59.181Z"}, {"lat": "44.145691", "lng": "9.66735", "elevation": "193.1999969482422", "time": "2013-09-20T08:54:05.205Z"}, {"lat": "44.145695", "lng": "9.667379", "elevation": "193.39999389648438", "time": "2013-09-20T08:54:06.190Z"}, {"lat": "44.145706", "lng": "9.66749", "elevation": "194.60000610351563", "time": "2013-09-20T08:54:09.182Z"}, {"lat": "44.145712", "lng": "9.667534", "elevation": "195.3000030517578", "time": "2013-09-20T08:54:10.213Z"}, {"lat": "44.145739", "lng": "9.667573", "elevation": "194.89999389648438", "time": "2013-09-20T08:54:19.207Z"}, {"lat": "44.145739", "lng": "9.667574", "elevation": "194.0", "time": "2013-09-20T08:54:20.196Z"}, {"lat": "44.14574", "lng": "9.667582", "elevation": "195.39999389648438", "time": "2013-09-20T08:54:22.213Z"}, {"lat": "44.145741", "lng": "9.667587", "elevation": "194.5", "time": "2013-09-20T08:54:23.191Z"}, {"lat": "44.145733", "lng": "9.667644", "elevation": "198.1999969482422", "time": "2013-09-20T08:54:32.207Z"}, {"lat": "44.145733", "lng": "9.667643", "elevation": "198.89999389648438", "time": "2013-09-20T08:54:33.214Z"}, {"lat": "44.145739", "lng": "9.667633", "elevation": "198.10000610351563", "time": "2013-09-20T08:54:42.192Z"}, {"lat": "44.145741", "lng": "9.667637", "elevation": "198.39999389648438", "time": "2013-09-20T08:54:43.214Z"}, {"lat": "44.145724", "lng": "9.667754", "elevation": "199.8000030517578", "time": "2013-09-20T08:54:52.188Z"}, {"lat": "44.145723", "lng": "9.667775", "elevation": "198.5", "time": "2013-09-20T08:54:53.202Z"}, {"lat": "44.145703", "lng": "9.667889", "elevation": "197.0", "time": "2013-09-20T08:55:07.208Z"}, {"lat": "44.145707", "lng": "9.667901", "elevation": "196.6999969482422", "time": "2013-09-20T08:55:08.242Z"}, {"lat": "44.14571", "lng": "9.667922", "elevation": "195.6999969482422", "time": "2013-09-20T08:55:13.217Z"}, {"lat": "44.145707", "lng": "9.667921", "elevation": "196.6999969482422", "time": "2013-09-20T08:55:14.251Z"}, {"lat": "44.145704", "lng": "9.66792", "elevation": "196.1999969482422", "time": "2013-09-20T08:55:15.210Z"}, {"lat": "44.1457", "lng": "9.667919", "elevation": "196.60000610351563", "time": "2013-09-20T08:55:16.230Z"}, {"lat": "44.145617", "lng": "9.667918", "elevation": "196.3000030517578", "time": "2013-09-20T08:55:29.211Z"}, {"lat": "44.145603", "lng": "9.667908", "elevation": "197.39999389648438", "time": "2013-09-20T08:55:30.197Z"}, {"lat": "44.145516", "lng": "9.667888", "elevation": "197.10000610351563", "time": "2013-09-20T08:55:37.203Z"}, {"lat": "44.145508", "lng": "9.667883", "elevation": "198.60000610351563", "time": "2013-09-20T08:55:38.212Z"}, {"lat": "44.14545", "lng": "9.667852", "elevation": "196.8000030517578", "time": "2013-09-20T08:55:56.193Z"}, {"lat": "44.14545", "lng": "9.667852", "elevation": "197.0", "time": "2013-09-20T08:55:57.198Z"}, {"lat": "44.145443", "lng": "9.667863", "elevation": "195.6999969482422", "time": "2013-09-20T08:56:10.210Z"}, {"lat": "44.145437", "lng": "9.667863", "elevation": "198.1999969482422", "time": "2013-09-20T08:56:11.230Z"}, {"lat": "44.145349", "lng": "9.667869", "elevation": "197.10000610351563", "time": "2013-09-20T08:56:18.200Z"}, {"lat": "44.145335", "lng": "9.66787", "elevation": "198.0", "time": "2013-09-20T08:56:19.231Z"}, {"lat": "44.145254", "lng": "9.667841", "elevation": "193.89999389648438", "time": "2013-09-20T08:56:25.279Z"}, {"lat": "44.145241", "lng": "9.667831", "elevation": "192.6999969482422", "time": "2013-09-20T08:56:26.230Z"}, {"lat": "44.145155", "lng": "9.667803", "elevation": "194.10000610351563", "time": "2013-09-20T08:56:32.207Z"}, {"lat": "44.145141", "lng": "9.667805", "elevation": "194.3000030517578", "time": "2013-09-20T08:56:33.233Z"}, {"lat": "44.145086", "lng": "9.667807", "elevation": "191.8000030517578", "time": "2013-09-20T08:56:46.216Z"}, {"lat": "44.145085", "lng": "9.667808", "elevation": "191.8000030517578", "time": "2013-09-20T08:56:47.207Z"}, {"lat": "44.145082", "lng": "9.667807", "elevation": "192.1999969482422", "time": "2013-09-20T08:56:48.217Z"}, {"lat": "44.145076", "lng": "9.667807", "elevation": "192.39999389648438", "time": "2013-09-20T08:56:49.217Z"}, {"lat": "44.144992", "lng": "9.667778", "elevation": "194.0", "time": "2013-09-20T08:56:55.208Z"}, {"lat": "44.144977", "lng": "9.667771", "elevation": "194.10000610351563", "time": "2013-09-20T08:56:56.234Z"}, {"lat": "44.1449", "lng": "9.66773", "elevation": "195.39999389648438", "time": "2013-09-20T08:57:02.217Z"}, {"lat": "44.144888", "lng": "9.667724", "elevation": "196.10000610351563", "time": "2013-09-20T08:57:03.267Z"}, {"lat": "44.144801", "lng": "9.667719", "elevation": "193.3000030517578", "time": "2013-09-20T08:57:15.224Z"}, {"lat": "44.144792", "lng": "9.667717", "elevation": "193.10000610351563", "time": "2013-09-20T08:57:16.310Z"}, {"lat": "44.144702", "lng": "9.667699", "elevation": "189.5", "time": "2013-09-20T08:57:30.220Z"}, {"lat": "44.144698", "lng": "9.667704", "elevation": "189.5", "time": "2013-09-20T08:57:31.220Z"}, {"lat": "44.144612", "lng": "9.667714", "elevation": "184.1999969482422", "time": "2013-09-20T08:57:41.244Z"}, {"lat": "44.144597", "lng": "9.667713", "elevation": "184.39999389648438", "time": "2013-09-20T08:57:42.215Z"}, {"lat": "44.144547", "lng": "9.667816", "elevation": "194.1999969482422", "time": "2013-09-20T08:57:57.230Z"}, {"lat": "44.144544", "lng": "9.667823", "elevation": "195.39999389648438", "time": "2013-09-20T08:57:58.256Z"}, {"lat": "44.144581", "lng": "9.667931", "elevation": "200.8000030517578", "time": "2013-09-20T08:58:12.304Z"}, {"lat": "44.144579", "lng": "9.667938", "elevation": "201.10000610351563", "time": "2013-09-20T08:58:13.264Z"}, {"lat": "44.144543", "lng": "9.668047", "elevation": "200.6999969482422", "time": "2013-09-20T08:58:22.288Z"}, {"lat": "44.144541", "lng": "9.668063", "elevation": "201.10000610351563", "time": "2013-09-20T08:58:23.381Z"}, {"lat": "44.144542", "lng": "9.668181", "elevation": "200.39999389648438", "time": "2013-09-20T08:58:32.226Z"}, {"lat": "44.144542", "lng": "9.66819", "elevation": "201.89999389648438", "time": "2013-09-20T08:58:33.213Z"}, {"lat": "44.144476", "lng": "9.668256", "elevation": "198.6999969482422", "time": "2013-09-20T08:58:44.323Z"}, {"lat": "44.14447", "lng": "9.668272", "elevation": "199.3000030517578", "time": "2013-09-20T08:58:45.291Z"}, {"lat": "44.144473", "lng": "9.668395", "elevation": "207.10000610351563", "time": "2013-09-20T08:58:59.284Z"}, {"lat": "44.144475", "lng": "9.668399", "elevation": "207.5", "time": "2013-09-20T08:59:00.355Z"}, {"lat": "44.144447", "lng": "9.668515", "elevation": "205.5", "time": "2013-09-20T08:59:12.285Z"}, {"lat": "44.144445", "lng": "9.668528", "elevation": "205.8000030517578", "time": "2013-09-20T08:59:13.231Z"}, {"lat": "44.144438", "lng": "9.668644", "elevation": "205.3000030517578", "time": "2013-09-20T08:59:25.359Z"}, {"lat": "44.144429", "lng": "9.668653", "elevation": "205.3000030517578", "time": "2013-09-20T08:59:26.367Z"}, {"lat": "44.144408", "lng": "9.668772", "elevation": "207.5", "time": "2013-09-20T08:59:39.319Z"}, {"lat": "44.144411", "lng": "9.668783", "elevation": "208.10000610351563", "time": "2013-09-20T08:59:40.365Z"}, {"lat": "44.144481", "lng": "9.668861", "elevation": "211.1999969482422", "time": "2013-09-20T08:59:52.223Z"}, {"lat": "44.144485", "lng": "9.66887", "elevation": "211.39999389648438", "time": "2013-09-20T08:59:53.240Z"}, {"lat": "44.144481", "lng": "9.668992", "elevation": "210.39999389648438", "time": "2013-09-20T09:00:04.345Z"}, {"lat": "44.144482", "lng": "9.669003", "elevation": "210.60000610351563", "time": "2013-09-20T09:00:05.306Z"}, {"lat": "44.144454", "lng": "9.66906", "elevation": "210.39999389648438", "time": "2013-09-20T09:00:15.349Z"}, {"lat": "44.144453", "lng": "9.66906", "elevation": "210.3000030517578", "time": "2013-09-20T09:00:16.373Z"}, {"lat": "44.144451", "lng": "9.669059", "elevation": "210.1999969482422", "time": "2013-09-20T09:00:17.328Z"}, {"lat": "44.144447", "lng": "9.669058", "elevation": "210.1999969482422", "time": "2013-09-20T09:00:18.393Z"}, {"lat": "44.144438", "lng": "9.669054", "elevation": "210.10000610351563", "time": "2013-09-20T09:00:22.266Z"}, {"lat": "44.144438", "lng": "9.669054", "elevation": "210.0", "time": "2013-09-20T09:00:23.234Z"}, {"lat": "44.144439", "lng": "9.669063", "elevation": "210.1999969482422", "time": "2013-09-20T09:00:41.226Z"}, {"lat": "44.144439", "lng": "9.669074", "elevation": "210.60000610351563", "time": "2013-09-20T09:00:42.241Z"}, {"lat": "44.144431", "lng": "9.669184", "elevation": "213.1999969482422", "time": "2013-09-20T09:00:48.323Z"}, {"lat": "44.144428", "lng": "9.669204", "elevation": "213.6999969482422", "time": "2013-09-20T09:00:49.323Z"}, {"lat": "44.144437", "lng": "9.669318", "elevation": "212.39999389648438", "time": "2013-09-20T09:00:54.282Z"}, {"lat": "44.14444", "lng": "9.669341", "elevation": "212.0", "time": "2013-09-20T09:00:55.227Z"}, {"lat": "44.144402", "lng": "9.669447", "elevation": "211.3000030517578", "time": "2013-09-20T09:01:02.394Z"}, {"lat": "44.144399", "lng": "9.669458", "elevation": "211.3000030517578", "time": "2013-09-20T09:01:03.344Z"}, {"lat": "44.144371", "lng": "9.669565", "elevation": "213.89999389648438", "time": "2013-09-20T09:01:13.236Z"}, {"lat": "44.144368", "lng": "9.669583", "elevation": "214.8000030517578", "time": "2013-09-20T09:01:14.244Z"}, {"lat": "44.144391", "lng": "9.669694", "elevation": "215.0", "time": "2013-09-20T09:01:21.336Z"}, {"lat": "44.144397", "lng": "9.669703", "elevation": "214.6999969482422", "time": "2013-09-20T09:01:22.334Z"}, {"lat": "44.144386", "lng": "9.66982", "elevation": "215.1999969482422", "time": "2013-09-20T09:01:31.282Z"}, {"lat": "44.144379", "lng": "9.669826", "elevation": "216.3000030517578", "time": "2013-09-20T09:01:32.321Z"}, {"lat": "44.144351", "lng": "9.669856", "elevation": "217.0", "time": "2013-09-20T09:01:41.344Z"}, {"lat": "44.144351", "lng": "9.669857", "elevation": "216.6999969482422", "time": "2013-09-20T09:01:42.295Z"}, {"lat": "44.144337", "lng": "9.66986", "elevation": "214.39999389648438", "time": "2013-09-20T09:01:55.249Z"}, {"lat": "44.144331", "lng": "9.669859", "elevation": "213.3000030517578", "time": "2013-09-20T09:01:56.251Z"}, {"lat": "44.144244", "lng": "9.669859", "elevation": "210.6999969482422", "time": "2013-09-20T09:02:04.326Z"}, {"lat": "44.144229", "lng": "9.669855", "elevation": "209.6999969482422", "time": "2013-09-20T09:02:05.266Z"}, {"lat": "44.144145", "lng": "9.669813", "elevation": "210.3000030517578", "time": "2013-09-20T09:02:12.254Z"}, {"lat": "44.144133", "lng": "9.669806", "elevation": "211.60000610351563", "time": "2013-09-20T09:02:13.307Z"}, {"lat": "44.144084", "lng": "9.669726", "elevation": "211.8000030517578", "time": "2013-09-20T09:02:18.230Z"}, {"lat": "44.144075", "lng": "9.669703", "elevation": "211.60000610351563", "time": "2013-09-20T09:02:19.260Z"}, {"lat": "44.144018", "lng": "9.669627", "elevation": "213.5", "time": "2013-09-20T09:02:24.285Z"}, {"lat": "44.144005", "lng": "9.669616", "elevation": "214.3000030517578", "time": "2013-09-20T09:02:25.259Z"}, {"lat": "44.143925", "lng": "9.669578", "elevation": "214.0", "time": "2013-09-20T09:02:30.279Z"}, {"lat": "44.143911", "lng": "9.669575", "elevation": "213.5", "time": "2013-09-20T09:02:31.326Z"}, {"lat": "44.143833", "lng": "9.669571", "elevation": "214.3000030517578", "time": "2013-09-20T09:02:37.235Z"}, {"lat": "44.143818", "lng": "9.669571", "elevation": "214.10000610351563", "time": "2013-09-20T09:02:38.282Z"}, {"lat": "44.143755", "lng": "9.669483", "elevation": "213.1999969482422", "time": "2013-09-20T09:02:46.284Z"}, {"lat": "44.143748", "lng": "9.669477", "elevation": "212.3000030517578", "time": "2013-09-20T09:02:47.326Z"}, {"lat": "44.143667", "lng": "9.669444", "elevation": "211.1999969482422", "time": "2013-09-20T09:02:57.271Z"}, {"lat": "44.143659", "lng": "9.669438", "elevation": "211.39999389648438", "time": "2013-09-20T09:02:58.247Z"}, {"lat": "44.143598", "lng": "9.669348", "elevation": "216.1999969482422", "time": "2013-09-20T09:03:06.234Z"}, {"lat": "44.143589", "lng": "9.669334", "elevation": "217.0", "time": "2013-09-20T09:03:07.235Z"}, {"lat": "44.143531", "lng": "9.669243", "elevation": "218.8000030517578", "time": "2013-09-20T09:03:13.238Z"}, {"lat": "44.143523", "lng": "9.66923", "elevation": "219.0", "time": "2013-09-20T09:03:14.235Z"}, {"lat": "44.143485", "lng": "9.669128", "elevation": "219.1999969482422", "time": "2013-09-20T09:03:22.241Z"}, {"lat": "44.143479", "lng": "9.66912", "elevation": "219.10000610351563", "time": "2013-09-20T09:03:23.255Z"}, {"lat": "44.143393", "lng": "9.669141", "elevation": "220.3000030517578", "time": "2013-09-20T09:03:42.332Z"}, {"lat": "44.143389", "lng": "9.66914", "elevation": "220.8000030517578", "time": "2013-09-20T09:03:43.343Z"}, {"lat": "44.143316", "lng": "9.669112", "elevation": "224.60000610351563", "time": "2013-09-20T09:03:57.267Z"}, {"lat": "44.143317", "lng": "9.669111", "elevation": "224.6999969482422", "time": "2013-09-20T09:03:58.315Z"}, {"lat": "44.143317", "lng": "9.669118", "elevation": "224.10000610351563", "time": "2013-09-20T09:04:03.241Z"}, {"lat": "44.143314", "lng": "9.669121", "elevation": "224.1999969482422", "time": "2013-09-20T09:04:04.306Z"}, {"lat": "44.143311", "lng": "9.669125", "elevation": "224.6999969482422", "time": "2013-09-20T09:04:06.251Z"}, {"lat": "44.14331", "lng": "9.669126", "elevation": "225.10000610351563", "time": "2013-09-20T09:04:07.261Z"}, {"lat": "44.143303", "lng": "9.66912", "elevation": "225.39999389648438", "time": "2013-09-20T09:04:14.248Z"}, {"lat": "44.1433", "lng": "9.669122", "elevation": "224.1999969482422", "time": "2013-09-20T09:04:15.253Z"}, {"lat": "44.143214", "lng": "9.669147", "elevation": "221.60000610351563", "time": "2013-09-20T09:04:23.285Z"}, {"lat": "44.143201", "lng": "9.669156", "elevation": "220.5", "time": "2013-09-20T09:04:24.292Z"}, {"lat": "44.143132", "lng": "9.669228", "elevation": "218.8000030517578", "time": "2013-09-20T09:04:31.331Z"}, {"lat": "44.143125", "lng": "9.669245", "elevation": "219.1999969482422", "time": "2013-09-20T09:04:32.334Z"}, {"lat": "44.143048", "lng": "9.669309", "elevation": "216.0", "time": "2013-09-20T09:04:40.320Z"}, {"lat": "44.143039", "lng": "9.669316", "elevation": "217.39999389648438", "time": "2013-09-20T09:04:41.273Z"}, {"lat": "44.14297", "lng": "9.669391", "elevation": "220.1999969482422", "time": "2013-09-20T09:04:52.254Z"}, {"lat": "44.142966", "lng": "9.669397", "elevation": "220.3000030517578", "time": "2013-09-20T09:04:53.262Z"}, {"lat": "44.14292", "lng": "9.669493", "elevation": "231.0", "time": "2013-09-20T09:05:08.249Z"}, {"lat": "44.142916", "lng": "9.669504", "elevation": "231.6999969482422", "time": "2013-09-20T09:05:09.270Z"}, {"lat": "44.142854", "lng": "9.669583", "elevation": "229.3000030517578", "time": "2013-09-20T09:05:17.264Z"}, {"lat": "44.142843", "lng": "9.669591", "elevation": "229.0", "time": "2013-09-20T09:05:18.267Z"}, {"lat": "44.142811", "lng": "9.669699", "elevation": "229.1999969482422", "time": "2013-09-20T09:05:38.291Z"}, {"lat": "44.142812", "lng": "9.6697", "elevation": "229.39999389648438", "time": "2013-09-20T09:05:39.265Z"}, {"lat": "44.142807", "lng": "9.669704", "elevation": "229.10000610351563", "time": "2013-09-20T09:05:53.343Z"}, {"lat": "44.142802", "lng": "9.66971", "elevation": "228.60000610351563", "time": "2013-09-20T09:05:54.266Z"}, {"lat": "44.142739", "lng": "9.669788", "elevation": "226.89999389648438", "time": "2013-09-20T09:06:00.365Z"}, {"lat": "44.142725", "lng": "9.669803", "elevation": "225.60000610351563", "time": "2013-09-20T09:06:01.348Z"}, {"lat": "44.142665", "lng": "9.669875", "elevation": "224.6999969482422", "time": "2013-09-20T09:06:06.260Z"}, {"lat": "44.142658", "lng": "9.669893", "elevation": "225.39999389648438", "time": "2013-09-20T09:06:07.262Z"}, {"lat": "44.142614", "lng": "9.669987", "elevation": "223.60000610351563", "time": "2013-09-20T09:06:12.262Z"}, {"lat": "44.1426", "lng": "9.670006", "elevation": "223.5", "time": "2013-09-20T09:06:13.255Z"}, {"lat": "44.142532", "lng": "9.670084", "elevation": "221.60000610351563", "time": "2013-09-20T09:06:18.269Z"}, {"lat": "44.142521", "lng": "9.670096", "elevation": "221.0", "time": "2013-09-20T09:06:19.292Z"}, {"lat": "44.142444", "lng": "9.670138", "elevation": "220.39999389648438", "time": "2013-09-20T09:06:28.263Z"}, {"lat": "44.142433", "lng": "9.670138", "elevation": "219.5", "time": "2013-09-20T09:06:29.275Z"}, {"lat": "44.142349", "lng": "9.67018", "elevation": "215.10000610351563", "time": "2013-09-20T09:06:37.272Z"}, {"lat": "44.14234", "lng": "9.670191", "elevation": "215.0", "time": "2013-09-20T09:06:38.255Z"}, {"lat": "44.142282", "lng": "9.670284", "elevation": "212.60000610351563", "time": "2013-09-20T09:06:47.259Z"}, {"lat": "44.142278", "lng": "9.670289", "elevation": "211.6999969482422", "time": "2013-09-20T09:06:48.281Z"}, {"lat": "44.142205", "lng": "9.670358", "elevation": "212.3000030517578", "time": "2013-09-20T09:06:58.282Z"}, {"lat": "44.142197", "lng": "9.670374", "elevation": "212.0", "time": "2013-09-20T09:06:59.264Z"}, {"lat": "44.142145", "lng": "9.670464", "elevation": "211.60000610351563", "time": "2013-09-20T09:07:07.267Z"}, {"lat": "44.142132", "lng": "9.670468", "elevation": "211.89999389648438", "time": "2013-09-20T09:07:08.264Z"}, {"lat": "44.142055", "lng": "9.670509", "elevation": "208.5", "time": "2013-09-20T09:07:19.283Z"}, {"lat": "44.142045", "lng": "9.670511", "elevation": "207.8000030517578", "time": "2013-09-20T09:07:20.310Z"}, {"lat": "44.141963", "lng": "9.670544", "elevation": "205.1999969482422", "time": "2013-09-20T09:07:28.260Z"}, {"lat": "44.141956", "lng": "9.670557", "elevation": "204.89999389648438", "time": "2013-09-20T09:07:29.274Z"}, {"lat": "44.141916", "lng": "9.670662", "elevation": "203.6999969482422", "time": "2013-09-20T09:07:37.269Z"}, {"lat": "44.141911", "lng": "9.670673", "elevation": "204.0", "time": "2013-09-20T09:07:38.292Z"}, {"lat": "44.141832", "lng": "9.670726", "elevation": "203.5", "time": "2013-09-20T09:07:56.270Z"}, {"lat": "44.141829", "lng": "9.670736", "elevation": "203.89999389648438", "time": "2013-09-20T09:07:57.278Z"}, {"lat": "44.141775", "lng": "9.67069", "elevation": "208.89999389648438", "time": "2013-09-20T09:08:14.343Z"}, {"lat": "44.141775", "lng": "9.670689", "elevation": "208.6999969482422", "time": "2013-09-20T09:08:15.377Z"}, {"lat": "44.141772", "lng": "9.670694", "elevation": "208.89999389648438", "time": "2013-09-20T09:08:29.334Z"}, {"lat": "44.141767", "lng": "9.6707", "elevation": "209.8000030517578", "time": "2013-09-20T09:08:30.313Z"}, {"lat": "44.141695", "lng": "9.670774", "elevation": "209.0", "time": "2013-09-20T09:08:36.290Z"}, {"lat": "44.141682", "lng": "9.670793", "elevation": "206.3000030517578", "time": "2013-09-20T09:08:37.303Z"}, {"lat": "44.141676", "lng": "9.670903", "elevation": "206.3000030517578", "time": "2013-09-20T09:08:42.272Z"}, {"lat": "44.141676", "lng": "9.670921", "elevation": "206.5", "time": "2013-09-20T09:08:43.264Z"}, {"lat": "44.141665", "lng": "9.671042", "elevation": "209.10000610351563", "time": "2013-09-20T09:08:51.284Z"}, {"lat": "44.141658", "lng": "9.671052", "elevation": "208.60000610351563", "time": "2013-09-20T09:08:52.281Z"}, {"lat": "44.141598", "lng": "9.671141", "elevation": "207.5", "time": "2013-09-20T09:09:01.265Z"}, {"lat": "44.141586", "lng": "9.671158", "elevation": "207.8000030517578", "time": "2013-09-20T09:09:02.296Z"}, {"lat": "44.141525", "lng": "9.671237", "elevation": "208.8000030517578", "time": "2013-09-20T09:09:07.275Z"}, {"lat": "44.141513", "lng": "9.67125", "elevation": "209.1999969482422", "time": "2013-09-20T09:09:08.265Z"}, {"lat": "44.141455", "lng": "9.671324", "elevation": "210.39999389648438", "time": "2013-09-20T09:09:13.267Z"}, {"lat": "44.141449", "lng": "9.671346", "elevation": "210.89999389648438", "time": "2013-09-20T09:09:14.305Z"}, {"lat": "44.141409", "lng": "9.671437", "elevation": "212.5", "time": "2013-09-20T09:09:19.330Z"}, {"lat": "44.141397", "lng": "9.67145", "elevation": "212.10000610351563", "time": "2013-09-20T09:09:20.267Z"}, {"lat": "44.141331", "lng": "9.671521", "elevation": "211.39999389648438", "time": "2013-09-20T09:09:27.267Z"}, {"lat": "44.141326", "lng": "9.671536", "elevation": "211.3000030517578", "time": "2013-09-20T09:09:28.285Z"}, {"lat": "44.141303", "lng": "9.671647", "elevation": "212.6999969482422", "time": "2013-09-20T09:09:34.347Z"}, {"lat": "44.141298", "lng": "9.671664", "elevation": "212.60000610351563", "time": "2013-09-20T09:09:35.362Z"}, {"lat": "44.141252", "lng": "9.671752", "elevation": "211.3000030517578", "time": "2013-09-20T09:09:41.277Z"}, {"lat": "44.141247", "lng": "9.671769", "elevation": "211.1999969482422", "time": "2013-09-20T09:09:42.325Z"}, {"lat": "44.141196", "lng": "9.671864", "elevation": "213.8000030517578", "time": "2013-09-20T09:09:48.287Z"}, {"lat": "44.141177", "lng": "9.671877", "elevation": "213.3000030517578", "time": "2013-09-20T09:09:49.287Z"}, {"lat": "44.141107", "lng": "9.671917", "elevation": "214.3000030517578", "time": "2013-09-20T09:09:53.318Z"}, {"lat": "44.141094", "lng": "9.671927", "elevation": "214.10000610351563", "time": "2013-09-20T09:09:54.286Z"}, {"lat": "44.141035", "lng": "9.672006", "elevation": "213.6999969482422", "time": "2013-09-20T09:10:00.393Z"}, {"lat": "44.141027", "lng": "9.672018", "elevation": "212.60000610351563", "time": "2013-09-20T09:10:01.294Z"}, {"lat": "44.14094", "lng": "9.672043", "elevation": "213.1999969482422", "time": "2013-09-20T09:10:07.286Z"}, {"lat": "44.140927", "lng": "9.672048", "elevation": "214.39999389648438", "time": "2013-09-20T09:10:08.301Z"}, {"lat": "44.140852", "lng": "9.672109", "elevation": "217.5", "time": "2013-09-20T09:10:15.367Z"}, {"lat": "44.140845", "lng": "9.672117", "elevation": "217.0", "time": "2013-09-20T09:10:16.345Z"}, {"lat": "44.140788", "lng": "9.672193", "elevation": "215.60000610351563", "time": "2013-09-20T09:10:28.273Z"}, {"lat": "44.140779", "lng": "9.672203", "elevation": "216.1999969482422", "time": "2013-09-20T09:10:29.281Z"}, {"lat": "44.140702", "lng": "9.672256", "elevation": "214.89999389648438", "time": "2013-09-20T09:10:45.297Z"}, {"lat": "44.140699", "lng": "9.672262", "elevation": "214.1999969482422", "time": "2013-09-20T09:10:46.331Z"}, {"lat": "44.140637", "lng": "9.672337", "elevation": "216.10000610351563", "time": "2013-09-20T09:10:57.306Z"}, {"lat": "44.140626", "lng": "9.672344", "elevation": "216.1999969482422", "time": "2013-09-20T09:10:58.274Z"}, {"lat": "44.140567", "lng": "9.67243", "elevation": "216.10000610351563", "time": "2013-09-20T09:11:05.308Z"}, {"lat": "44.140564", "lng": "9.672447", "elevation": "216.8000030517578", "time": "2013-09-20T09:11:06.283Z"}, {"lat": "44.140541", "lng": "9.672555", "elevation": "218.5", "time": "2013-09-20T09:11:12.293Z"}, {"lat": "44.140535", "lng": "9.672568", "elevation": "218.89999389648438", "time": "2013-09-20T09:11:13.309Z"}, {"lat": "44.14053", "lng": "9.672691", "elevation": "218.0", "time": "2013-09-20T09:11:23.294Z"}, {"lat": "44.140535", "lng": "9.672708", "elevation": "216.89999389648438", "time": "2013-09-20T09:11:24.308Z"}, {"lat": "44.140551", "lng": "9.672822", "elevation": "221.10000610351563", "time": "2013-09-20T09:11:36.304Z"}, {"lat": "44.140552", "lng": "9.672832", "elevation": "221.5", "time": "2013-09-20T09:11:37.287Z"}, {"lat": "44.140572", "lng": "9.672943", "elevation": "219.8000030517578", "time": "2013-09-20T09:11:47.311Z"}, {"lat": "44.140574", "lng": "9.672955", "elevation": "219.3000030517578", "time": "2013-09-20T09:11:48.303Z"}, {"lat": "44.140561", "lng": "9.673054", "elevation": "218.5", "time": "2013-09-20T09:12:00.309Z"}, {"lat": "44.140562", "lng": "9.673054", "elevation": "218.6999969482422", "time": "2013-09-20T09:12:01.281Z"}, {"lat": "44.140573", "lng": "9.673073", "elevation": "217.6999969482422", "time": "2013-09-20T09:12:42.300Z"}, {"lat": "44.140572", "lng": "9.673081", "elevation": "216.60000610351563", "time": "2013-09-20T09:12:43.380Z"}, {"lat": "44.140562", "lng": "9.673203", "elevation": "212.5", "time": "2013-09-20T09:12:50.300Z"}, {"lat": "44.140571", "lng": "9.673217", "elevation": "212.39999389648438", "time": "2013-09-20T09:12:51.357Z"}, {"lat": "44.140627", "lng": "9.673314", "elevation": "209.8000030517578", "time": "2013-09-20T09:12:59.363Z"}, {"lat": "44.140628", "lng": "9.673317", "elevation": "210.10000610351563", "time": "2013-09-20T09:13:00.451Z"}, {"lat": "44.140604", "lng": "9.673426", "elevation": "208.89999389648438", "time": "2013-09-20T09:13:09.349Z"}, {"lat": "44.140605", "lng": "9.673443", "elevation": "206.89999389648438", "time": "2013-09-20T09:13:10.332Z"}, {"lat": "44.140649", "lng": "9.67355", "elevation": "204.89999389648438", "time": "2013-09-20T09:13:18.320Z"}, {"lat": "44.140649", "lng": "9.673571", "elevation": "204.8000030517578", "time": "2013-09-20T09:13:19.320Z"}, {"lat": "44.140614", "lng": "9.673678", "elevation": "208.0", "time": "2013-09-20T09:13:24.313Z"}, {"lat": "44.140609", "lng": "9.673696", "elevation": "208.5", "time": "2013-09-20T09:13:25.307Z"}, {"lat": "44.140609", "lng": "9.673815", "elevation": "209.1999969482422", "time": "2013-09-20T09:13:33.316Z"}, {"lat": "44.140604", "lng": "9.673838", "elevation": "208.8000030517578", "time": "2013-09-20T09:13:34.347Z"}, {"lat": "44.140612", "lng": "9.673959", "elevation": "205.10000610351563", "time": "2013-09-20T09:13:41.294Z"}, {"lat": "44.140623", "lng": "9.673962", "elevation": "205.5", "time": "2013-09-20T09:13:42.294Z"}, {"lat": "44.140633", "lng": "9.67408", "elevation": "206.60000610351563", "time": "2013-09-20T09:13:56.327Z"}, {"lat": "44.140625", "lng": "9.674094", "elevation": "205.3000030517578", "time": "2013-09-20T09:13:57.367Z"}, {"lat": "44.14059", "lng": "9.674206", "elevation": "204.5", "time": "2013-09-20T09:14:04.403Z"}, {"lat": "44.140588", "lng": "9.674225", "elevation": "204.89999389648438", "time": "2013-09-20T09:14:05.377Z"}, {"lat": "44.140568", "lng": "9.674345", "elevation": "206.0", "time": "2013-09-20T09:14:19.314Z"}, {"lat": "44.140567", "lng": "9.674357", "elevation": "206.39999389648438", "time": "2013-09-20T09:14:20.324Z"}, {"lat": "44.140591", "lng": "9.674469", "elevation": "207.1999969482422", "time": "2013-09-20T09:14:28.305Z"}, {"lat": "44.140591", "lng": "9.674481", "elevation": "206.89999389648438", "time": "2013-09-20T09:14:29.331Z"}, {"lat": "44.140605", "lng": "9.674598", "elevation": "209.60000610351563", "time": "2013-09-20T09:14:41.301Z"}, {"lat": "44.140609", "lng": "9.674611", "elevation": "210.60000610351563", "time": "2013-09-20T09:14:42.300Z"}, {"lat": "44.140588", "lng": "9.674654", "elevation": "211.3000030517578", "time": "2013-09-20T09:14:52.377Z"}, {"lat": "44.140587", "lng": "9.674654", "elevation": "211.10000610351563", "time": "2013-09-20T09:14:53.377Z"}, {"lat": "44.140596", "lng": "9.674662", "elevation": "210.3000030517578", "time": "2013-09-20T09:15:08.326Z"}, {"lat": "44.140596", "lng": "9.674669", "elevation": "210.10000610351563", "time": "2013-09-20T09:15:09.303Z"}, {"lat": "44.140624", "lng": "9.674769", "elevation": "205.1999969482422", "time": "2013-09-20T09:15:15.295Z"}, {"lat": "44.140634", "lng": "9.67479", "elevation": "204.60000610351563", "time": "2013-09-20T09:15:16.295Z"}, {"lat": "44.140666", "lng": "9.67489", "elevation": "206.10000610351563", "time": "2013-09-20T09:15:21.336Z"}, {"lat": "44.140673", "lng": "9.67491", "elevation": "207.39999389648438", "time": "2013-09-20T09:15:22.319Z"}, {"lat": "44.140681", "lng": "9.67502", "elevation": "207.6999969482422", "time": "2013-09-20T09:15:26.308Z"}, {"lat": "44.140676", "lng": "9.675045", "elevation": "209.3000030517578", "time": "2013-09-20T09:15:27.294Z"}, {"lat": "44.140648", "lng": "9.675161", "elevation": "210.10000610351563", "time": "2013-09-20T09:15:33.304Z"}, {"lat": "44.140649", "lng": "9.675166", "elevation": "210.0", "time": "2013-09-20T09:15:34.317Z"}, {"lat": "44.140668", "lng": "9.675164", "elevation": "210.0", "time": "2013-09-20T09:15:41.295Z"}, {"lat": "44.140669", "lng": "9.675165", "elevation": "210.0", "time": "2013-09-20T09:15:42.312Z"}, {"lat": "44.140668", "lng": "9.675169", "elevation": "209.1999969482422", "time": "2013-09-20T09:16:01.315Z"}, {"lat": "44.140663", "lng": "9.675176", "elevation": "208.1999969482422", "time": "2013-09-20T09:16:02.333Z"}, {"lat": "44.140639", "lng": "9.675219", "elevation": "207.10000610351563", "time": "2013-09-20T09:16:06.354Z"}, {"lat": "44.140631", "lng": "9.675516", "elevation": "195.5", "time": "2013-09-20T09:16:40.254Z"}, {"lat": "44.1406", "lng": "9.675602", "elevation": "206.89999389648438", "time": "2013-09-20T09:16:44.253Z"}, {"lat": "44.140593", "lng": "9.675632", "elevation": "209.0", "time": "2013-09-20T09:16:45.231Z"}, {"lat": "44.140526", "lng": "9.675678", "elevation": "207.89999389648438", "time": "2013-09-20T09:16:50.222Z"}, {"lat": "44.140505", "lng": "9.675667", "elevation": "207.3000030517578", "time": "2013-09-20T09:16:51.262Z"}, {"lat": "44.140521", "lng": "9.675644", "elevation": "205.60000610351563", "time": "2013-09-20T09:16:59.234Z"}, {"lat": "44.140522", "lng": "9.675644", "elevation": "205.6999969482422", "time": "2013-09-20T09:17:00.270Z"}, {"lat": "44.140515", "lng": "9.675657", "elevation": "206.0", "time": "2013-09-20T09:17:55.237Z"}, {"lat": "44.140517", "lng": "9.675663", "elevation": "207.5", "time": "2013-09-20T09:17:56.239Z"}, {"lat": "44.14057", "lng": "9.675754", "elevation": "209.39999389648438", "time": "2013-09-20T09:18:03.245Z"}, {"lat": "44.14058", "lng": "9.675765", "elevation": "209.1999969482422", "time": "2013-09-20T09:18:04.238Z"}, {"lat": "44.140586", "lng": "9.675823", "elevation": "211.5", "time": "2013-09-20T09:18:16.245Z"}, {"lat": "44.140586", "lng": "9.675825", "elevation": "211.60000610351563", "time": "2013-09-20T09:18:17.255Z"}, {"lat": "44.140592", "lng": "9.675829", "elevation": "211.6999969482422", "time": "2013-09-20T09:18:27.265Z"}, {"lat": "44.140593", "lng": "9.675839", "elevation": "212.6999969482422", "time": "2013-09-20T09:18:28.239Z"}, {"lat": "44.140558", "lng": "9.675943", "elevation": "219.39999389648438", "time": "2013-09-20T09:18:35.254Z"}, {"lat": "44.140548", "lng": "9.675955", "elevation": "220.1999969482422", "time": "2013-09-20T09:18:36.272Z"}, {"lat": "44.140505", "lng": "9.676063", "elevation": "222.6999969482422", "time": "2013-09-20T09:18:41.262Z"}, {"lat": "44.140501", "lng": "9.676086", "elevation": "223.3000030517578", "time": "2013-09-20T09:18:42.242Z"}, {"lat": "44.140424", "lng": "9.676136", "elevation": "223.60000610351563", "time": "2013-09-20T09:18:48.329Z"}, {"lat": "44.140408", "lng": "9.676142", "elevation": "223.60000610351563", "time": "2013-09-20T09:18:49.258Z"}, {"lat": "44.14036", "lng": "9.676245", "elevation": "221.0", "time": "2013-09-20T09:18:57.256Z"}, {"lat": "44.140359", "lng": "9.67626", "elevation": "219.8000030517578", "time": "2013-09-20T09:18:58.264Z"}, {"lat": "44.140321", "lng": "9.676372", "elevation": "220.60000610351563", "time": "2013-09-20T09:19:08.242Z"}, {"lat": "44.140317", "lng": "9.676377", "elevation": "221.0", "time": "2013-09-20T09:19:09.241Z"}, {"lat": "44.140239", "lng": "9.676438", "elevation": "218.89999389648438", "time": "2013-09-20T09:19:25.242Z"}, {"lat": "44.140236", "lng": "9.676448", "elevation": "220.5", "time": "2013-09-20T09:19:26.252Z"}, {"lat": "44.140213", "lng": "9.676558", "elevation": "213.5", "time": "2013-09-20T09:19:36.244Z"}, {"lat": "44.140218", "lng": "9.67658", "elevation": "212.3000030517578", "time": "2013-09-20T09:19:37.252Z"}, {"lat": "44.140208", "lng": "9.676694", "elevation": "208.10000610351563", "time": "2013-09-20T09:19:44.317Z"}, {"lat": "44.140202", "lng": "9.676706", "elevation": "207.1999969482422", "time": "2013-09-20T09:19:45.245Z"}, {"lat": "44.140178", "lng": "9.67682", "elevation": "206.60000610351563", "time": "2013-09-20T09:19:53.350Z"}, {"lat": "44.140177", "lng": "9.676834", "elevation": "207.3000030517578", "time": "2013-09-20T09:19:54.269Z"}, {"lat": "44.140178", "lng": "9.676951", "elevation": "204.5", "time": "2013-09-20T09:20:07.247Z"}, {"lat": "44.140184", "lng": "9.676964", "elevation": "204.10000610351563", "time": "2013-09-20T09:20:08.249Z"}, {"lat": "44.140158", "lng": "9.677077", "elevation": "206.10000610351563", "time": "2013-09-20T09:20:17.254Z"}, {"lat": "44.140146", "lng": "9.677089", "elevation": "206.60000610351563", "time": "2013-09-20T09:20:18.257Z"}, {"lat": "44.140076", "lng": "9.677164", "elevation": "208.10000610351563", "time": "2013-09-20T09:20:24.263Z"}, {"lat": "44.140066", "lng": "9.677187", "elevation": "207.3000030517578", "time": "2013-09-20T09:20:25.240Z"}, {"lat": "44.140011", "lng": "9.677273", "elevation": "208.39999389648438", "time": "2013-09-20T09:20:31.239Z"}, {"lat": "44.140003", "lng": "9.677287", "elevation": "208.3000030517578", "time": "2013-09-20T09:20:32.354Z"}, {"lat": "44.139937", "lng": "9.677346", "elevation": "209.39999389648438", "time": "2013-09-20T09:20:38.400Z"}, {"lat": "44.139926", "lng": "9.677353", "elevation": "210.0", "time": "2013-09-20T09:20:39.352Z"}, {"lat": "44.139849", "lng": "9.677395", "elevation": "208.39999389648438", "time": "2013-09-20T09:20:46.251Z"}, {"lat": "44.139841", "lng": "9.677402", "elevation": "210.39999389648438", "time": "2013-09-20T09:20:47.272Z"}, {"lat": "44.13977", "lng": "9.677457", "elevation": "211.60000610351563", "time": "2013-09-20T09:20:54.275Z"}, {"lat": "44.139763", "lng": "9.677472", "elevation": "212.0", "time": "2013-09-20T09:20:55.243Z"}, {"lat": "44.139711", "lng": "9.677569", "elevation": "214.0", "time": "2013-09-20T09:21:04.245Z"}, {"lat": "44.139705", "lng": "9.677577", "elevation": "213.60000610351563", "time": "2013-09-20T09:21:05.251Z"}, {"lat": "44.139645", "lng": "9.677534", "elevation": "217.1999969482422", "time": "2013-09-20T09:21:20.247Z"}, {"lat": "44.139644", "lng": "9.677533", "elevation": "217.1999969482422", "time": "2013-09-20T09:21:21.252Z"}, {"lat": "44.139635", "lng": "9.677543", "elevation": "216.89999389648438", "time": "2013-09-20T09:21:30.277Z"}, {"lat": "44.139631", "lng": "9.677547", "elevation": "217.10000610351563", "time": "2013-09-20T09:21:31.253Z"}, {"lat": "44.139551", "lng": "9.677589", "elevation": "218.8000030517578", "time": "2013-09-20T09:21:35.259Z"}, {"lat": "44.139524", "lng": "9.67759", "elevation": "218.60000610351563", "time": "2013-09-20T09:21:36.248Z"}, {"lat": "44.139454", "lng": "9.677585", "elevation": "219.10000610351563", "time": "2013-09-20T09:21:39.283Z"}, {"lat": "44.139433", "lng": "9.677585", "elevation": "218.89999389648438", "time": "2013-09-20T09:21:40.253Z"}, {"lat": "44.13935", "lng": "9.677614", "elevation": "219.1999969482422", "time": "2013-09-20T09:21:44.252Z"}, {"lat": "44.139334", "lng": "9.677625", "elevation": "218.60000610351563", "time": "2013-09-20T09:21:45.262Z"}, {"lat": "44.139269", "lng": "9.677702", "elevation": "216.5", "time": "2013-09-20T09:21:49.284Z"}, {"lat": "44.139256", "lng": "9.677719", "elevation": "214.89999389648438", "time": "2013-09-20T09:21:50.277Z"}, {"lat": "44.13917", "lng": "9.677756", "elevation": "220.89999389648438", "time": "2013-09-20T09:21:57.255Z"}, {"lat": "44.139157", "lng": "9.677764", "elevation": "221.5", "time": "2013-09-20T09:21:58.270Z"}, {"lat": "44.13907", "lng": "9.677779", "elevation": "221.5", "time": "2013-09-20T09:22:06.319Z"}, {"lat": "44.139058", "lng": "9.677776", "elevation": "220.6999969482422", "time": "2013-09-20T09:22:07.350Z"}, {"lat": "44.139001", "lng": "9.677871", "elevation": "224.0", "time": "2013-09-20T09:22:15.265Z"}, {"lat": "44.138985", "lng": "9.677885", "elevation": "225.0", "time": "2013-09-20T09:22:16.321Z"}, {"lat": "44.138915", "lng": "9.677936", "elevation": "227.10000610351563", "time": "2013-09-20T09:22:20.255Z"}, {"lat": "44.138896", "lng": "9.677943", "elevation": "227.39999389648438", "time": "2013-09-20T09:22:21.280Z"}, {"lat": "44.138807", "lng": "9.677963", "elevation": "228.60000610351563", "time": "2013-09-20T09:22:27.258Z"}, {"lat": "44.138795", "lng": "9.677966", "elevation": "228.5", "time": "2013-09-20T09:22:28.288Z"}, {"lat": "44.13872", "lng": "9.677989", "elevation": "229.60000610351563", "time": "2013-09-20T09:22:33.282Z"}, {"lat": "44.138702", "lng": "9.677992", "elevation": "232.39999389648438", "time": "2013-09-20T09:22:34.279Z"}, {"lat": "44.138622", "lng": "9.677985", "elevation": "231.39999389648438", "time": "2013-09-20T09:22:38.282Z"}, {"lat": "44.138604", "lng": "9.677982", "elevation": "232.10000610351563", "time": "2013-09-20T09:22:39.282Z"}, {"lat": "44.138516", "lng": "9.677967", "elevation": "234.60000610351563", "time": "2013-09-20T09:22:45.267Z"}, {"lat": "44.138508", "lng": "9.677969", "elevation": "236.3000030517578", "time": "2013-09-20T09:22:46.285Z"}, {"lat": "44.138439", "lng": "9.678037", "elevation": "238.89999389648438", "time": "2013-09-20T09:22:54.343Z"}, {"lat": "44.138428", "lng": "9.678039", "elevation": "240.1999969482422", "time": "2013-09-20T09:22:55.326Z"}, {"lat": "44.138345", "lng": "9.678053", "elevation": "235.39999389648438", "time": "2013-09-20T09:23:07.297Z"}, {"lat": "44.138338", "lng": "9.678057", "elevation": "236.39999389648438", "time": "2013-09-20T09:23:08.282Z"}, {"lat": "44.138254", "lng": "9.678067", "elevation": "240.89999389648438", "time": "2013-09-20T09:23:17.285Z"}, {"lat": "44.138247", "lng": "9.678071", "elevation": "240.8000030517578", "time": "2013-09-20T09:23:18.359Z"}, {"lat": "44.138243", "lng": "9.678189", "elevation": "224.6999969482422", "time": "2013-09-20T09:23:38.272Z"}, {"lat": "44.138243", "lng": "9.678197", "elevation": "224.5", "time": "2013-09-20T09:23:39.280Z"}, {"lat": "44.138248", "lng": "9.678206", "elevation": "223.8000030517578", "time": "2013-09-20T09:23:43.366Z"}, {"lat": "44.138248", "lng": "9.678205", "elevation": "224.8000030517578", "time": "2013-09-20T09:23:44.326Z"}, {"lat": "44.13825", "lng": "9.678205", "elevation": "223.10000610351563", "time": "2013-09-20T09:23:46.278Z"}, {"lat": "44.138254", "lng": "9.678206", "elevation": "222.89999389648438", "time": "2013-09-20T09:23:47.294Z"}, {"lat": "44.138273", "lng": "9.678225", "elevation": "219.39999389648438", "time": "2013-09-20T09:23:55.289Z"}, {"lat": "44.138273", "lng": "9.678226", "elevation": "219.3000030517578", "time": "2013-09-20T09:23:56.295Z"}, {"lat": "44.138272", "lng": "9.678229", "elevation": "219.0", "time": "2013-09-20T09:23:59.366Z"}, {"lat": "44.138272", "lng": "9.678235", "elevation": "216.6999969482422", "time": "2013-09-20T09:24:00.358Z"}, {"lat": "44.13827", "lng": "9.678347", "elevation": "205.6999969482422", "time": "2013-09-20T09:24:06.311Z"}, {"lat": "44.138268", "lng": "9.678371", "elevation": "204.8000030517578", "time": "2013-09-20T09:24:07.289Z"}, {"lat": "44.138286", "lng": "9.678438", "elevation": "204.8000030517578", "time": "2013-09-20T09:24:17.283Z"}, {"lat": "44.138286", "lng": "9.678439", "elevation": "204.5", "time": "2013-09-20T09:24:18.321Z"}, {"lat": "44.138282", "lng": "9.678456", "elevation": "202.89999389648438", "time": "2013-09-20T09:24:32.284Z"}, {"lat": "44.13828", "lng": "9.678465", "elevation": "202.3000030517578", "time": "2013-09-20T09:24:33.275Z"}, {"lat": "44.138262", "lng": "9.678573", "elevation": "197.1999969482422", "time": "2013-09-20T09:24:39.282Z"}, {"lat": "44.138254", "lng": "9.678589", "elevation": "197.6999969482422", "time": "2013-09-20T09:24:40.350Z"}, {"lat": "44.1382", "lng": "9.678683", "elevation": "196.39999389648438", "time": "2013-09-20T09:24:52.299Z"}, {"lat": "44.138196", "lng": "9.678692", "elevation": "195.0", "time": "2013-09-20T09:24:53.310Z"}, {"lat": "44.138166", "lng": "9.678792", "elevation": "196.10000610351563", "time": "2013-09-20T09:25:00.368Z"}, {"lat": "44.138159", "lng": "9.678807", "elevation": "196.3000030517578", "time": "2013-09-20T09:25:01.268Z"}, {"lat": "44.138194", "lng": "9.678917", "elevation": "198.6999969482422", "time": "2013-09-20T09:25:12.278Z"}, {"lat": "44.138197", "lng": "9.678928", "elevation": "198.8000030517578", "time": "2013-09-20T09:25:13.270Z"}, {"lat": "44.138226", "lng": "9.678961", "elevation": "197.39999389648438", "time": "2013-09-20T09:25:22.286Z"}, {"lat": "44.138225", "lng": "9.678964", "elevation": "197.1999969482422", "time": "2013-09-20T09:25:23.295Z"}, {"lat": "44.13823", "lng": "9.678985", "elevation": "194.39999389648438", "time": "2013-09-20T09:25:41.296Z"}, {"lat": "44.138232", "lng": "9.678992", "elevation": "194.6999969482422", "time": "2013-09-20T09:25:42.279Z"}, {"lat": "44.138286", "lng": "9.679026", "elevation": "190.8000030517578", "time": "2013-09-20T09:25:53.328Z"}, {"lat": "44.138286", "lng": "9.679026", "elevation": "190.5", "time": "2013-09-20T09:25:54.291Z"}, {"lat": "44.138286", "lng": "9.679034", "elevation": "191.0", "time": "2013-09-20T09:25:58.330Z"}, {"lat": "44.138288", "lng": "9.679045", "elevation": "190.1999969482422", "time": "2013-09-20T09:25:59.291Z"}, {"lat": "44.138312", "lng": "9.679158", "elevation": "185.89999389648438", "time": "2013-09-20T09:26:07.275Z"}, {"lat": "44.138313", "lng": "9.679171", "elevation": "184.6999969482422", "time": "2013-09-20T09:26:08.298Z"}, {"lat": "44.138306", "lng": "9.679292", "elevation": "183.10000610351563", "time": "2013-09-20T09:26:21.294Z"}, {"lat": "44.138303", "lng": "9.679298", "elevation": "183.39999389648438", "time": "2013-09-20T09:26:22.282Z"}, {"lat": "44.138285", "lng": "9.679396", "elevation": "176.60000610351563", "time": "2013-09-20T09:26:40.299Z"}, {"lat": "44.138285", "lng": "9.679395", "elevation": "176.6999969482422", "time": "2013-09-20T09:26:41.292Z"}, {"lat": "44.138283", "lng": "9.679398", "elevation": "176.60000610351563", "time": "2013-09-20T09:26:43.291Z"}, {"lat": "44.138279", "lng": "9.679403", "elevation": "176.3000030517578", "time": "2013-09-20T09:26:44.301Z"}, {"lat": "44.138229", "lng": "9.679493", "elevation": "172.5", "time": "2013-09-20T09:26:55.358Z"}, {"lat": "44.138228", "lng": "9.679506", "elevation": "172.89999389648438", "time": "2013-09-20T09:26:56.326Z"}, {"lat": "44.138213", "lng": "9.679581", "elevation": "172.60000610351563", "time": "2013-09-20T09:27:06.325Z"}, {"lat": "44.138215", "lng": "9.679582", "elevation": "172.6999969482422", "time": "2013-09-20T09:27:07.294Z"}, {"lat": "44.138199", "lng": "9.679572", "elevation": "172.0", "time": "2013-09-20T09:27:23.288Z"}, {"lat": "44.138195", "lng": "9.679574", "elevation": "172.1999969482422", "time": "2013-09-20T09:27:24.287Z"}, {"lat": "44.138114", "lng": "9.679621", "elevation": "169.5", "time": "2013-09-20T09:27:32.315Z"}, {"lat": "44.138106", "lng": "9.679629", "elevation": "170.60000610351563", "time": "2013-09-20T09:27:33.290Z"}, {"lat": "44.138108", "lng": "9.67968", "elevation": "168.1999969482422", "time": "2013-09-20T09:27:41.305Z"}, {"lat": "44.138107", "lng": "9.679679", "elevation": "168.1999969482422", "time": "2013-09-20T09:27:42.305Z"}, {"lat": "44.138102", "lng": "9.679695", "elevation": "165.39999389648438", "time": "2013-09-20T09:27:54.314Z"}, {"lat": "44.138099", "lng": "9.6797", "elevation": "165.5", "time": "2013-09-20T09:27:55.296Z"}, {"lat": "44.138052", "lng": "9.679789", "elevation": "166.0", "time": "2013-09-20T09:28:04.304Z"}, {"lat": "44.138043", "lng": "9.679801", "elevation": "166.60000610351563", "time": "2013-09-20T09:28:05.322Z"}, {"lat": "44.137982", "lng": "9.679877", "elevation": "163.5", "time": "2013-09-20T09:28:14.289Z"}, {"lat": "44.137973", "lng": "9.679881", "elevation": "163.6999969482422", "time": "2013-09-20T09:28:15.376Z"}, {"lat": "44.137898", "lng": "9.679945", "elevation": "163.1999969482422", "time": "2013-09-20T09:28:22.307Z"}, {"lat": "44.137892", "lng": "9.679957", "elevation": "163.10000610351563", "time": "2013-09-20T09:28:23.299Z"}, {"lat": "44.137811", "lng": "9.680002", "elevation": "161.0", "time": "2013-09-20T09:28:33.301Z"}, {"lat": "44.137806", "lng": "9.680006", "elevation": "161.6999969482422", "time": "2013-09-20T09:28:34.301Z"}, {"lat": "44.137785", "lng": "9.680024", "elevation": "160.6999969482422", "time": "2013-09-20T09:28:40.318Z"}, {"lat": "44.137789", "lng": "9.680023", "elevation": "161.10000610351563", "time": "2013-09-20T09:28:41.318Z"}, {"lat": "44.137792", "lng": "9.680034", "elevation": "162.5", "time": "2013-09-20T09:28:49.325Z"}, {"lat": "44.137787", "lng": "9.680036", "elevation": "162.1999969482422", "time": "2013-09-20T09:28:50.318Z"}, {"lat": "44.137728", "lng": "9.680109", "elevation": "156.8000030517578", "time": "2013-09-20T09:28:58.302Z"}, {"lat": "44.137718", "lng": "9.680118", "elevation": "155.6999969482422", "time": "2013-09-20T09:28:59.309Z"}, {"lat": "44.13766", "lng": "9.680206", "elevation": "153.60000610351563", "time": "2013-09-20T09:29:07.310Z"}, {"lat": "44.137658", "lng": "9.680217", "elevation": "152.39999389648438", "time": "2013-09-20T09:29:08.311Z"}, {"lat": "44.137629", "lng": "9.68033", "elevation": "151.39999389648438", "time": "2013-09-20T09:29:25.294Z"}, {"lat": "44.137629", "lng": "9.680338", "elevation": "151.1999969482422", "time": "2013-09-20T09:29:26.322Z"}, {"lat": "44.137663", "lng": "9.680357", "elevation": "148.1999969482422", "time": "2013-09-20T09:29:35.304Z"}, {"lat": "44.137662", "lng": "9.680357", "elevation": "148.1999969482422", "time": "2013-09-20T09:29:36.298Z"}, {"lat": "44.137657", "lng": "9.680357", "elevation": "147.8000030517578", "time": "2013-09-20T09:29:38.260Z"}, {"lat": "44.137652", "lng": "9.680356", "elevation": "147.89999389648438", "time": "2013-09-20T09:29:38.294Z"}, {"lat": "44.137566", "lng": "9.680328", "elevation": "147.89999389648438", "time": "2013-09-20T09:29:49.322Z"}, {"lat": "44.137562", "lng": "9.680335", "elevation": "148.3000030517578", "time": "2013-09-20T09:29:50.307Z"}, {"lat": "44.137504", "lng": "9.680421", "elevation": "146.0", "time": "2013-09-20T09:30:01.319Z"}, {"lat": "44.137495", "lng": "9.680429", "elevation": "146.8000030517578", "time": "2013-09-20T09:30:02.322Z"}, {"lat": "44.137471", "lng": "9.68045", "elevation": "142.1999969482422", "time": "2013-09-20T09:30:08.322Z"}, {"lat": "44.137475", "lng": "9.680451", "elevation": "142.39999389648438", "time": "2013-09-20T09:30:09.305Z"}, {"lat": "44.13748", "lng": "9.68045", "elevation": "142.60000610351563", "time": "2013-09-20T09:30:10.322Z"}, {"lat": "44.137481", "lng": "9.68045", "elevation": "142.60000610351563", "time": "2013-09-20T09:30:11.308Z"}, {"lat": "44.137477", "lng": "9.680446", "elevation": "142.8000030517578", "time": "2013-09-20T09:30:13.300Z"}, {"lat": "44.137471", "lng": "9.680443", "elevation": "145.0", "time": "2013-09-20T09:30:14.308Z"}, {"lat": "44.137448", "lng": "9.680447", "elevation": "144.60000610351563", "time": "2013-09-20T09:30:35.325Z"}, {"lat": "44.137448", "lng": "9.680446", "elevation": "144.5", "time": "2013-09-20T09:30:36.319Z"}, {"lat": "44.137441", "lng": "9.680445", "elevation": "144.10000610351563", "time": "2013-09-20T09:30:41.373Z"}, {"lat": "44.137436", "lng": "9.680444", "elevation": "143.6999969482422", "time": "2013-09-20T09:30:42.333Z"}, {"lat": "44.137362", "lng": "9.680377", "elevation": "140.89999389648438", "time": "2013-09-20T09:30:58.336Z"}, {"lat": "44.137354", "lng": "9.680371", "elevation": "141.89999389648438", "time": "2013-09-20T09:30:59.326Z"}, {"lat": "44.137318", "lng": "9.680375", "elevation": "141.3000030517578", "time": "2013-09-20T09:31:07.407Z"}, {"lat": "44.137319", "lng": "9.680376", "elevation": "141.1999969482422", "time": "2013-09-20T09:31:08.446Z"}, {"lat": "44.137316", "lng": "9.680386", "elevation": "140.6999969482422", "time": "2013-09-20T09:31:12.376Z"}, {"lat": "44.137313", "lng": "9.680391", "elevation": "140.1999969482422", "time": "2013-09-20T09:31:13.304Z"}, {"lat": "44.137241", "lng": "9.680448", "elevation": "140.5", "time": "2013-09-20T09:31:22.335Z"}, {"lat": "44.137234", "lng": "9.680452", "elevation": "140.10000610351563", "time": "2013-09-20T09:31:23.426Z"}, {"lat": "44.137146", "lng": "9.680462", "elevation": "139.60000610351563", "time": "2013-09-20T09:31:38.435Z"}, {"lat": "44.137138", "lng": "9.680468", "elevation": "137.39999389648438", "time": "2013-09-20T09:31:39.355Z"}, {"lat": "44.137067", "lng": "9.680541", "elevation": "134.8000030517578", "time": "2013-09-20T09:31:48.350Z"}, {"lat": "44.137056", "lng": "9.680546", "elevation": "135.1999969482422", "time": "2013-09-20T09:31:48.378Z"}, {"lat": "44.136989", "lng": "9.680622", "elevation": "132.10000610351563", "time": "2013-09-20T09:31:58.072Z"}, {"lat": "44.136983", "lng": "9.680626", "elevation": "131.8000030517578", "time": "2013-09-20T09:31:58.411Z"}, {"lat": "44.13691", "lng": "9.680685", "elevation": "122.5", "time": "2013-09-20T09:32:14.311Z"}, {"lat": "44.1369", "lng": "9.680693", "elevation": "122.0999984741211", "time": "2013-09-20T09:32:15.324Z"}, {"lat": "44.136825", "lng": "9.680753", "elevation": "122.30000305175781", "time": "2013-09-20T09:32:24.311Z"}, {"lat": "44.13682", "lng": "9.680757", "elevation": "122.4000015258789", "time": "2013-09-20T09:32:25.318Z"}, {"lat": "44.13679", "lng": "9.680869", "elevation": "118.9000015258789", "time": "2013-09-20T09:32:35.310Z"}, {"lat": "44.136793", "lng": "9.680883", "elevation": "117.69999694824219", "time": "2013-09-20T09:32:36.325Z"}, {"lat": "44.136808", "lng": "9.680865", "elevation": "119.30000305175781", "time": "2013-09-20T09:32:43.487Z"}, {"lat": "44.136807", "lng": "9.680862", "elevation": "119.4000015258789", "time": "2013-09-20T09:32:44.399Z"}, {"lat": "44.136809", "lng": "9.680865", "elevation": "118.0", "time": "2013-09-20T09:32:46.322Z"}, {"lat": "44.136816", "lng": "9.680872", "elevation": "116.0", "time": "2013-09-20T09:32:47.375Z"}, {"lat": "44.136808", "lng": "9.680989", "elevation": "115.19999694824219", "time": "2013-09-20T09:33:01.335Z"}, {"lat": "44.136796", "lng": "9.680997", "elevation": "115.80000305175781", "time": "2013-09-20T09:33:02.329Z"}, {"lat": "44.136744", "lng": "9.681017", "elevation": "122.0", "time": "2013-09-20T09:33:15.387Z"}, {"lat": "44.136743", "lng": "9.681018", "elevation": "122.0", "time": "2013-09-20T09:33:16.440Z"}, {"lat": "44.136747", "lng": "9.681026", "elevation": "122.0", "time": "2013-09-20T09:33:34.434Z"}, {"lat": "44.136746", "lng": "9.681032", "elevation": "121.69999694824219", "time": "2013-09-20T09:33:35.447Z"}, {"lat": "44.136743", "lng": "9.681041", "elevation": "121.80000305175781", "time": "2013-09-20T09:33:38.809Z"}, {"lat": "44.136743", "lng": "9.681042", "elevation": "121.5999984741211", "time": "2013-09-20T09:33:39.348Z"}, {"lat": "44.136738", "lng": "9.681053", "elevation": "121.0999984741211", "time": "2013-09-20T09:33:55.372Z"}, {"lat": "44.136735", "lng": "9.68106", "elevation": "120.5", "time": "2013-09-20T09:33:56.339Z"}, {"lat": "44.13669", "lng": "9.681147", "elevation": "117.0", "time": "2013-09-20T09:34:09.499Z"}, {"lat": "44.136683", "lng": "9.681162", "elevation": "117.0", "time": "2013-09-20T09:34:10.421Z"}, {"lat": "44.136632", "lng": "9.681262", "elevation": "109.9000015258789", "time": "2013-09-20T09:34:18.314Z"}, {"lat": "44.13663", "lng": "9.681274", "elevation": "110.19999694824219", "time": "2013-09-20T09:34:18.356Z"}, {"lat": "44.136596", "lng": "9.681383", "elevation": "107.5", "time": "2013-09-20T09:34:43.441Z"}, {"lat": "44.136597", "lng": "9.681385", "elevation": "106.80000305175781", "time": "2013-09-20T09:34:43.473Z"}, {"lat": "44.136606", "lng": "9.681394", "elevation": "104.80000305175781", "time": "2013-09-20T09:34:48.412Z"}, {"lat": "44.136609", "lng": "9.681397", "elevation": "102.30000305175781", "time": "2013-09-20T09:34:48.444Z"}, {"lat": "44.13664", "lng": "9.681501", "elevation": "102.80000305175781", "time": "2013-09-20T09:35:01.438Z"}, {"lat": "44.13664", "lng": "9.6815", "elevation": "102.9000015258789", "time": "2013-09-20T09:35:02.371Z"}, {"lat": "44.13665", "lng": "9.681504", "elevation": "103.0", "time": "2013-09-20T09:35:31.443Z"}, {"lat": "44.13665", "lng": "9.681513", "elevation": "101.4000015258789", "time": "2013-09-20T09:35:32.341Z"}, {"lat": "44.13667", "lng": "9.681625", "elevation": "97.5", "time": "2013-09-20T09:35:38.483Z"}, {"lat": "44.136679", "lng": "9.681646", "elevation": "97.30000305175781", "time": "2013-09-20T09:35:39.341Z"}, {"lat": "44.13673", "lng": "9.681748", "elevation": "92.80000305175781", "time": "2013-09-20T09:35:50.359Z"}, {"lat": "44.136739", "lng": "9.681759", "elevation": "92.5999984741211", "time": "2013-09-20T09:35:51.437Z"}, {"lat": "44.136706", "lng": "9.681826", "elevation": "94.80000305175781", "time": "2013-09-20T09:36:08.146Z"}, {"lat": "44.136707", "lng": "9.681825", "elevation": "94.5", "time": "2013-09-20T09:36:08.390Z"}, {"lat": "44.136707", "lng": "9.681837", "elevation": "94.5", "time": "2013-09-20T09:36:39.352Z"}, {"lat": "44.136704", "lng": "9.681846", "elevation": "94.19999694824219", "time": "2013-09-20T09:36:40.378Z"}, {"lat": "44.136751", "lng": "9.68195", "elevation": "92.9000015258789", "time": "2013-09-20T09:36:48.422Z"}, {"lat": "44.136759", "lng": "9.681955", "elevation": "92.80000305175781", "time": "2013-09-20T09:36:49.442Z"}, {"lat": "44.136748", "lng": "9.682016", "elevation": "91.69999694824219", "time": "2013-09-20T09:37:00.429Z"}, {"lat": "44.136748", "lng": "9.682016", "elevation": "91.80000305175781", "time": "2013-09-20T09:37:01.458Z"}, {"lat": "44.136742", "lng": "9.682022", "elevation": "90.19999694824219", "time": "2013-09-20T09:37:20.330Z"}, {"lat": "44.136735", "lng": "9.68226", "elevation": "91.80000305175781", "time": "2013-09-20T09:37:38.350Z"}, {"lat": "44.136665", "lng": "9.682325", "elevation": "95.69999694824219", "time": "2013-09-20T09:37:52.361Z"}, {"lat": "44.136658", "lng": "9.682328", "elevation": "96.5", "time": "2013-09-20T09:37:53.354Z"}, {"lat": "44.136652", "lng": "9.682356", "elevation": "96.80000305175781", "time": "2013-09-20T09:38:01.402Z"}, {"lat": "44.136653", "lng": "9.682355", "elevation": "96.5", "time": "2013-09-20T09:38:02.353Z"}, {"lat": "44.136645", "lng": "9.682335", "elevation": "96.80000305175781", "time": "2013-09-20T09:38:21.350Z"}, {"lat": "44.136642", "lng": "9.682327", "elevation": "96.9000015258789", "time": "2013-09-20T09:38:22.352Z"}, {"lat": "44.136583", "lng": "9.682238", "elevation": "96.9000015258789", "time": "2013-09-20T09:38:29.385Z"}, {"lat": "44.136569", "lng": "9.682232", "elevation": "97.4000015258789", "time": "2013-09-20T09:38:30.362Z"}, {"lat": "44.136498", "lng": "9.682229", "elevation": "98.5", "time": "2013-09-20T09:38:34.355Z"}, {"lat": "44.136478", "lng": "9.68223", "elevation": "98.5999984741211", "time": "2013-09-20T09:38:35.346Z"}, {"lat": "44.136424", "lng": "9.682296", "elevation": "96.19999694824219", "time": "2013-09-20T09:38:50.350Z"}, {"lat": "44.136424", "lng": "9.682297", "elevation": "96.0999984741211", "time": "2013-09-20T09:38:51.347Z"}, {"lat": "44.136423", "lng": "9.682303", "elevation": "97.5999984741211", "time": "2013-09-20T09:40:53.088Z"}, {"lat": "44.136423", "lng": "9.682312", "elevation": "98.19999694824219", "time": "2013-09-20T09:40:53.412Z"}, {"lat": "44.1364", "lng": "9.682417", "elevation": "94.5", "time": "2013-09-20T09:40:58.346Z"}, {"lat": "44.136392", "lng": "9.682442", "elevation": "93.69999694824219", "time": "2013-09-20T09:40:59.357Z"}, {"lat": "44.136363", "lng": "9.682493", "elevation": "92.9000015258789", "time": "2013-09-20T09:41:02.357Z"}, {"lat": "44.136197", "lng": "9.682553", "elevation": "95.30000305175781", "time": "2013-09-20T09:41:56.360Z"}, {"lat": "44.136195", "lng": "9.682557", "elevation": "95.19999694824219", "time": "2013-09-20T09:41:57.381Z"}, {"lat": "44.136195", "lng": "9.682557", "elevation": "95.19999694824219", "time": "2013-09-20T09:41:58.201Z"}, {"lat": "44.136191", "lng": "9.682562", "elevation": "95.0999984741211", "time": "2013-09-20T09:41:58.415Z"}, {"lat": "44.136141", "lng": "9.682635", "elevation": "95.5", "time": "2013-09-20T09:42:01.418Z"}, {"lat": "44.136117", "lng": "9.68266", "elevation": "95.30000305175781", "time": "2013-09-20T09:42:02.411Z"}, {"lat": "44.136062", "lng": "9.682709", "elevation": "95.30000305175781", "time": "2013-09-20T09:42:04.388Z"}, {"lat": "44.136034", "lng": "9.682733", "elevation": "94.30000305175781", "time": "2013-09-20T09:42:05.350Z"}, {"lat": "44.135954", "lng": "9.682785", "elevation": "94.30000305175781", "time": "2013-09-20T09:42:08.350Z"}, {"lat": "44.135928", "lng": "9.682794", "elevation": "94.19999694824219", "time": "2013-09-20T09:42:09.434Z"}, {"lat": "44.135858", "lng": "9.682798", "elevation": "95.80000305175781", "time": "2013-09-20T09:42:17.378Z"}, {"lat": "44.135863", "lng": "9.682797", "elevation": "95.5999984741211", "time": "2013-09-20T09:42:18.394Z"}, {"lat": "44.135866", "lng": "9.682803", "elevation": "95.19999694824219", "time": "2013-09-20T09:42:27.370Z"}, {"lat": "44.135862", "lng": "9.682808", "elevation": "93.5999984741211", "time": "2013-09-20T09:42:28.402Z"}, {"lat": "44.135818", "lng": "9.682786", "elevation": "88.5999984741211", "time": "2013-09-20T09:42:38.527Z"}, {"lat": "44.135817", "lng": "9.682786", "elevation": "88.5999984741211", "time": "2013-09-20T09:42:39.437Z"}, {"lat": "44.135806", "lng": "9.682802", "elevation": "87.69999694824219", "time": "2013-09-20T09:42:49.407Z"}, {"lat": "44.1358", "lng": "9.682803", "elevation": "87.19999694824219", "time": "2013-09-20T09:42:50.384Z"}, {"lat": "44.135722", "lng": "9.682807", "elevation": "87.69999694824219", "time": "2013-09-20T09:42:55.448Z"}, {"lat": "44.135702", "lng": "9.682807", "elevation": "87.4000015258789", "time": "2013-09-20T09:42:56.430Z"}, {"lat": "44.13562", "lng": "9.682857", "elevation": "82.0", "time": "2013-09-20T09:43:03.346Z"}, {"lat": "44.135619", "lng": "9.682872", "elevation": "81.5", "time": "2013-09-20T09:43:03.379Z"}, {"lat": "44.13562", "lng": "9.682894", "elevation": "81.69999694824219", "time": "2013-09-20T09:43:08.423Z"}, {"lat": "44.135619", "lng": "9.68289", "elevation": "82.0", "time": "2013-09-20T09:43:09.374Z"}, {"lat": "44.135616", "lng": "9.682886", "elevation": "82.19999694824219", "time": "2013-09-20T09:43:10.438Z"}, {"lat": "44.135614", "lng": "9.682881", "elevation": "82.19999694824219", "time": "2013-09-20T09:43:11.446Z"}, {"lat": "44.135599", "lng": "9.682876", "elevation": "82.0999984741211", "time": "2013-09-20T09:43:17.377Z"}, {"lat": "44.135598", "lng": "9.682876", "elevation": "82.0999984741211", "time": "2013-09-20T09:43:18.486Z"}, {"lat": "44.135599", "lng": "9.682874", "elevation": "82.30000305175781", "time": "2013-09-20T09:44:03.157Z"}, {"lat": "44.135593", "lng": "9.682876", "elevation": "82.30000305175781", "time": "2013-09-20T09:44:03.443Z"}, {"lat": "44.135517", "lng": "9.682874", "elevation": "82.30000305175781", "time": "2013-09-20T09:44:09.412Z"}, {"lat": "44.1355", "lng": "9.682868", "elevation": "82.19999694824219", "time": "2013-09-20T09:44:10.395Z"}, {"lat": "44.135417", "lng": "9.682881", "elevation": "84.5999984741211", "time": "2013-09-20T09:44:14.495Z"}, {"lat": "44.135402", "lng": "9.682891", "elevation": "85.0", "time": "2013-09-20T09:44:15.483Z"}, {"lat": "44.135328", "lng": "9.682879", "elevation": "83.30000305175781", "time": "2013-09-20T09:44:28.361Z"}, {"lat": "44.135333", "lng": "9.682882", "elevation": "83.30000305175781", "time": "2013-09-20T09:44:29.374Z"}, {"lat": "44.135338", "lng": "9.682885", "elevation": "83.30000305175781", "time": "2013-09-20T09:44:30.412Z"}, {"lat": "44.135343", "lng": "9.682889", "elevation": "83.30000305175781", "time": "2013-09-20T09:44:31.399Z"}, {"lat": "44.135358", "lng": "9.682907", "elevation": "83.30000305175781", "time": "2013-09-20T09:44:37.373Z"}, {"lat": "44.134955", "lng": "9.683342", "elevation": "82.30000305175781", "time": "2013-09-20T09:45:28.402Z"}, {"lat": "44.135033", "lng": "9.683385", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:33.739Z"}, {"lat": "44.135044", "lng": "9.683382", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:34.380Z"}, {"lat": "44.135052", "lng": "9.68337", "elevation": "82.19999694824219", "time": "2013-09-20T09:45:39.418Z"}, {"lat": "44.135049", "lng": "9.683368", "elevation": "82.19999694824219", "time": "2013-09-20T09:45:40.395Z"}, {"lat": "44.135041", "lng": "9.683368", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:42.379Z"}, {"lat": "44.135027", "lng": "9.683364", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:43.366Z"}, {"lat": "44.134971", "lng": "9.683342", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:53.383Z"}, {"lat": "44.134971", "lng": "9.683342", "elevation": "82.0999984741211", "time": "2013-09-20T09:45:54.394Z"}, {"lat": "44.134975", "lng": "9.683344", "elevation": "82.0999984741211", "time": "2013-09-20T09:46:03.372Z"}, {"lat": "44.134663", "lng": "9.685106", "elevation": "53.70000076293945", "time": "2013-09-20T11:43:28.836Z"}, {"lat": "44.134807", "lng": "9.685014", "elevation": "31.0", "time": "2013-09-20T11:43:29.744Z"}, {"lat": "44.134857", "lng": "9.68503", "elevation": "39.400001525878906", "time": "2013-09-20T11:43:30.744Z"}, {"lat": "44.134907", "lng": "9.685014", "elevation": "22.299999237060547", "time": "2013-09-20T11:43:31.765Z"}, {"lat": "44.134884", "lng": "9.685022", "elevation": "36.20000076293945", "time": "2013-09-20T11:43:32.744Z"}, {"lat": "44.134806", "lng": "9.684967", "elevation": "65.0", "time": "2013-09-20T11:43:33.745Z"}, {"lat": "44.134806", "lng": "9.684967", "elevation": "65.0", "time": "2013-09-20T11:43:33.845Z"}, {"lat": "44.134683", "lng": "9.685084", "elevation": "65.30000305175781", "time": "2013-09-20T11:43:34.761Z"}, {"lat": "44.134762", "lng": "9.685107", "elevation": "42.70000076293945", "time": "2013-09-20T11:43:35.764Z"}, {"lat": "44.134857", "lng": "9.685136", "elevation": "41.099998474121094", "time": "2013-09-20T11:43:36.749Z"}, {"lat": "44.134875", "lng": "9.685044", "elevation": "59.5", "time": "2013-09-20T11:43:40.751Z"}, {"lat": "44.134842", "lng": "9.68501", "elevation": "51.5", "time": "2013-09-20T11:43:41.759Z"}, {"lat": "44.134779", "lng": "9.684951", "elevation": "56.0", "time": "2013-09-20T11:43:44.756Z"}, {"lat": "44.13472", "lng": "9.684971", "elevation": "65.19999694824219", "time": "2013-09-20T11:43:45.758Z"}, {"lat": "44.13465", "lng": "9.684897", "elevation": "68.5", "time": "2013-09-20T11:44:01.151Z"}, {"lat": "44.134646", "lng": "9.684896", "elevation": "69.30000305175781", "time": "2013-09-20T11:44:02.144Z"}, {"lat": "44.134566", "lng": "9.684845", "elevation": "69.19999694824219", "time": "2013-09-20T11:44:31.143Z"}, {"lat": "44.134553", "lng": "9.684842", "elevation": "69.0999984741211", "time": "2013-09-20T11:44:32.151Z"}, {"lat": "44.134488", "lng": "9.684778", "elevation": "72.9000015258789", "time": "2013-09-20T11:44:39.145Z"}, {"lat": "44.134478", "lng": "9.684769", "elevation": "72.80000305175781", "time": "2013-09-20T11:44:40.148Z"}, {"lat": "44.134453", "lng": "9.684716", "elevation": "71.9000015258789", "time": "2013-09-20T11:44:49.178Z"}, {"lat": "44.134452", "lng": "9.684716", "elevation": "72.0999984741211", "time": "2013-09-20T11:44:50.145Z"}, {"lat": "44.134453", "lng": "9.684705", "elevation": "72.9000015258789", "time": "2013-09-20T11:45:05.201Z"}, {"lat": "44.134451", "lng": "9.684696", "elevation": "74.30000305175781", "time": "2013-09-20T11:45:06.153Z"}, {"lat": "44.134457", "lng": "9.684581", "elevation": "77.19999694824219", "time": "2013-09-20T11:45:19.243Z"}, {"lat": "44.134457", "lng": "9.684581", "elevation": "77.30000305175781", "time": "2013-09-20T11:45:20.223Z"}, {"lat": "44.134455", "lng": "9.684583", "elevation": "77.5", "time": "2013-09-20T11:45:36.233Z"}, {"lat": "44.134449", "lng": "9.684583", "elevation": "78.0999984741211", "time": "2013-09-20T11:45:37.173Z"}, {"lat": "44.134413", "lng": "9.684617", "elevation": "80.4000015258789", "time": "2013-09-20T11:45:43.284Z"}, {"lat": "44.134391", "lng": "9.684664", "elevation": "82.30000305175781", "time": "2013-09-20T11:45:48.165Z"}, {"lat": "44.134324", "lng": "9.684725", "elevation": "79.80000305175781", "time": "2013-09-20T11:46:00.155Z"}, {"lat": "44.134327", "lng": "9.684724", "elevation": "79.9000015258789", "time": "2013-09-20T11:46:01.160Z"}, {"lat": "44.134329", "lng": "9.684725", "elevation": "80.19999694824219", "time": "2013-09-20T11:46:02.155Z"}, {"lat": "44.13434", "lng": "9.68473", "elevation": "80.9000015258789", "time": "2013-09-20T11:46:03.152Z"}, {"lat": "44.134355", "lng": "9.684735", "elevation": "80.69999694824219", "time": "2013-09-20T11:46:06.161Z"}, {"lat": "44.134355", "lng": "9.684736", "elevation": "80.80000305175781", "time": "2013-09-20T11:46:07.157Z"}, {"lat": "44.134354", "lng": "9.684733", "elevation": "81.19999694824219", "time": "2013-09-20T11:46:12.212Z"}, {"lat": "44.134351", "lng": "9.68473", "elevation": "81.19999694824219", "time": "2013-09-20T11:46:13.160Z"}, {"lat": "44.134286", "lng": "9.684733", "elevation": "83.5", "time": "2013-09-20T11:46:27.161Z"}, {"lat": "44.134287", "lng": "9.684733", "elevation": "83.4000015258789", "time": "2013-09-20T11:46:28.168Z"}, {"lat": "44.134277", "lng": "9.684726", "elevation": "87.5999984741211", "time": "2013-09-20T11:48:06.183Z"}, {"lat": "44.134276", "lng": "9.684735", "elevation": "88.5", "time": "2013-09-20T11:48:07.274Z"}, {"lat": "44.134284", "lng": "9.684856", "elevation": "90.0", "time": "2013-09-20T11:48:16.232Z"}, {"lat": "44.134286", "lng": "9.684878", "elevation": "90.5", "time": "2013-09-20T11:48:17.215Z"}, {"lat": "44.134275", "lng": "9.684992", "elevation": "88.9000015258789", "time": "2013-09-20T11:48:23.341Z"}, {"lat": "44.134271", "lng": "9.685005", "elevation": "88.30000305175781", "time": "2013-09-20T11:48:24.244Z"}, {"lat": "44.134207", "lng": "9.685056", "elevation": "88.19999694824219", "time": "2013-09-20T11:48:37.186Z"}, {"lat": "44.134207", "lng": "9.685055", "elevation": "88.19999694824219", "time": "2013-09-20T11:48:38.203Z"}, {"lat": "44.134212", "lng": "9.685062", "elevation": "88.4000015258789", "time": "2013-09-20T11:48:45.212Z"}, {"lat": "44.134217", "lng": "9.685065", "elevation": "87.80000305175781", "time": "2013-09-20T11:48:46.171Z"}, {"lat": "44.134234", "lng": "9.68509", "elevation": "87.0999984741211", "time": "2013-09-20T11:48:53.125Z"}, {"lat": "44.134234", "lng": "9.68509", "elevation": "86.80000305175781", "time": "2013-09-20T11:48:53.208Z"}, {"lat": "44.13423", "lng": "9.685092", "elevation": "87.0", "time": "2013-09-20T11:48:56.175Z"}, {"lat": "44.134225", "lng": "9.685089", "elevation": "86.5", "time": "2013-09-20T11:48:57.241Z"}, {"lat": "44.134191", "lng": "9.685074", "elevation": "90.30000305175781", "time": "2013-09-20T11:49:07.183Z"}, {"lat": "44.13419", "lng": "9.685075", "elevation": "90.19999694824219", "time": "2013-09-20T11:49:08.182Z"}, {"lat": "44.134185", "lng": "9.685077", "elevation": "91.0999984741211", "time": "2013-09-20T11:49:14.181Z"}, {"lat": "44.134182", "lng": "9.685085", "elevation": "90.69999694824219", "time": "2013-09-20T11:49:15.193Z"}, {"lat": "44.134132", "lng": "9.685179", "elevation": "91.0999984741211", "time": "2013-09-20T11:49:23.282Z"}, {"lat": "44.13413", "lng": "9.685195", "elevation": "91.0", "time": "2013-09-20T11:49:24.179Z"}, {"lat": "44.134134", "lng": "9.685243", "elevation": "92.5", "time": "2013-09-20T11:49:30.174Z"}, {"lat": "44.134135", "lng": "9.685239", "elevation": "92.4000015258789", "time": "2013-09-20T11:49:31.178Z"}, {"lat": "44.134131", "lng": "9.685253", "elevation": "94.0", "time": "2013-09-20T11:50:03.199Z"}, {"lat": "44.134127", "lng": "9.685259", "elevation": "94.5", "time": "2013-09-20T11:50:04.179Z"}, {"lat": "44.134111", "lng": "9.685357", "elevation": "98.0999984741211", "time": "2013-09-20T11:50:16.243Z"}, {"lat": "44.13411", "lng": "9.685357", "elevation": "98.30000305175781", "time": "2013-09-20T11:50:17.221Z"}, {"lat": "44.134101", "lng": "9.685357", "elevation": "99.5", "time": "2013-09-20T11:50:28.418Z"}, {"lat": "44.134095", "lng": "9.685358", "elevation": "100.0999984741211", "time": "2013-09-20T11:50:29.194Z"}, {"lat": "44.13401", "lng": "9.685381", "elevation": "100.69999694824219", "time": "2013-09-20T11:50:38.677Z"}, {"lat": "44.134001", "lng": "9.685388", "elevation": "99.80000305175781", "time": "2013-09-20T11:50:39.276Z"}, {"lat": "44.133965", "lng": "9.685502", "elevation": "90.30000305175781", "time": "2013-09-20T11:50:44.181Z"}, {"lat": "44.133962", "lng": "9.685525", "elevation": "90.0", "time": "2013-09-20T11:50:45.215Z"}, {"lat": "44.133912", "lng": "9.68562", "elevation": "91.0", "time": "2013-09-20T11:50:53.178Z"}, {"lat": "44.133905", "lng": "9.685635", "elevation": "90.80000305175781", "time": "2013-09-20T11:50:54.180Z"}, {"lat": "44.133842", "lng": "9.685708", "elevation": "91.80000305175781", "time": "2013-09-20T11:51:01.181Z"}, {"lat": "44.133834", "lng": "9.685717", "elevation": "90.69999694824219", "time": "2013-09-20T11:51:02.196Z"}, {"lat": "44.133794", "lng": "9.685815", "elevation": "90.5999984741211", "time": "2013-09-20T11:51:10.184Z"}, {"lat": "44.133788", "lng": "9.685826", "elevation": "91.5999984741211", "time": "2013-09-20T11:51:11.186Z"}, {"lat": "44.133727", "lng": "9.685894", "elevation": "96.0999984741211", "time": "2013-09-20T11:51:17.190Z"}, {"lat": "44.133715", "lng": "9.685901", "elevation": "95.5", "time": "2013-09-20T11:51:18.182Z"}, {"lat": "44.133657", "lng": "9.685932", "elevation": "98.5", "time": "2013-09-20T11:51:28.938Z"}, {"lat": "44.133658", "lng": "9.68593", "elevation": "99.69999694824219", "time": "2013-09-20T11:51:29.213Z"}, {"lat": "44.133655", "lng": "9.685926", "elevation": "101.0", "time": "2013-09-20T11:51:30.182Z"}, {"lat": "44.133635", "lng": "9.685912", "elevation": "103.0", "time": "2013-09-20T11:51:37.203Z"}, {"lat": "44.133633", "lng": "9.685913", "elevation": "103.69999694824219", "time": "2013-09-20T11:51:38.197Z"}, {"lat": "44.133632", "lng": "9.685919", "elevation": "110.4000015258789", "time": "2013-09-20T11:51:47.194Z"}, {"lat": "44.133627", "lng": "9.685926", "elevation": "110.5", "time": "2013-09-20T11:51:48.197Z"}, {"lat": "44.133579", "lng": "9.686024", "elevation": "112.0", "time": "2013-09-20T11:51:58.253Z"}, {"lat": "44.133574", "lng": "9.68603", "elevation": "112.9000015258789", "time": "2013-09-20T11:51:59.286Z"}, {"lat": "44.13351", "lng": "9.686115", "elevation": "113.19999694824219", "time": "2013-09-20T11:52:15.260Z"}, {"lat": "44.133508", "lng": "9.686117", "elevation": "111.4000015258789", "time": "2013-09-20T11:52:16.195Z"}, {"lat": "44.133449", "lng": "9.6862", "elevation": "120.0999984741211", "time": "2013-09-20T11:52:30.195Z"}, {"lat": "44.133443", "lng": "9.686208", "elevation": "119.69999694824219", "time": "2013-09-20T11:52:31.202Z"}, {"lat": "44.133378", "lng": "9.68628", "elevation": "124.69999694824219", "time": "2013-09-20T11:52:41.234Z"}, {"lat": "44.133366", "lng": "9.686279", "elevation": "125.19999694824219", "time": "2013-09-20T11:52:42.203Z"}, {"lat": "44.133279", "lng": "9.68631", "elevation": "125.80000305175781", "time": "2013-09-20T11:52:51.199Z"}, {"lat": "44.133273", "lng": "9.686313", "elevation": "126.0999984741211", "time": "2013-09-20T11:52:52.207Z"}, {"lat": "44.133205", "lng": "9.686391", "elevation": "129.89999389648438", "time": "2013-09-20T11:53:05.190Z"}, {"lat": "44.133198", "lng": "9.686383", "elevation": "129.6999969482422", "time": "2013-09-20T11:53:06.199Z"}, {"lat": "44.133194", "lng": "9.686363", "elevation": "129.39999389648438", "time": "2013-09-20T11:53:11.197Z"}, {"lat": "44.133194", "lng": "9.686364", "elevation": "128.89999389648438", "time": "2013-09-20T11:53:12.211Z"}, {"lat": "44.133195", "lng": "9.686378", "elevation": "129.10000610351563", "time": "2013-09-20T11:53:15.348Z"}, {"lat": "44.133196", "lng": "9.686384", "elevation": "128.6999969482422", "time": "2013-09-20T11:53:16.197Z"}, {"lat": "44.133205", "lng": "9.68641", "elevation": "129.5", "time": "2013-09-20T11:53:22.202Z"}, {"lat": "44.133205", "lng": "9.68641", "elevation": "129.5", "time": "2013-09-20T11:53:23.205Z"}, {"lat": "44.133202", "lng": "9.686414", "elevation": "128.60000610351563", "time": "2013-09-20T11:53:49.203Z"}, {"lat": "44.133203", "lng": "9.686419", "elevation": "128.39999389648438", "time": "2013-09-20T11:53:50.325Z"}, {"lat": "44.133202", "lng": "9.686527", "elevation": "126.5999984741211", "time": "2013-09-20T11:54:00.263Z"}, {"lat": "44.133202", "lng": "9.686549", "elevation": "125.69999694824219", "time": "2013-09-20T11:54:01.240Z"}, {"lat": "44.133192", "lng": "9.686659", "elevation": "127.30000305175781", "time": "2013-09-20T11:54:06.214Z"}, {"lat": "44.133187", "lng": "9.686677", "elevation": "127.0", "time": "2013-09-20T11:54:07.201Z"}, {"lat": "44.133136", "lng": "9.686765", "elevation": "129.1999969482422", "time": "2013-09-20T11:54:14.205Z"}, {"lat": "44.133128", "lng": "9.686776", "elevation": "129.3000030517578", "time": "2013-09-20T11:54:15.298Z"}, {"lat": "44.133068", "lng": "9.686852", "elevation": "131.10000610351563", "time": "2013-09-20T11:54:23.211Z"}, {"lat": "44.133064", "lng": "9.686867", "elevation": "131.0", "time": "2013-09-20T11:54:24.209Z"}, {"lat": "44.133024", "lng": "9.686974", "elevation": "128.60000610351563", "time": "2013-09-20T11:54:33.237Z"}, {"lat": "44.133018", "lng": "9.686986", "elevation": "128.5", "time": "2013-09-20T11:54:34.206Z"}, {"lat": "44.132967", "lng": "9.687078", "elevation": "131.1999969482422", "time": "2013-09-20T11:54:47.329Z"}, {"lat": "44.132962", "lng": "9.687087", "elevation": "129.8000030517578", "time": "2013-09-20T11:54:48.300Z"}, {"lat": "44.132916", "lng": "9.68719", "elevation": "128.8000030517578", "time": "2013-09-20T11:54:55.296Z"}, {"lat": "44.132911", "lng": "9.6872", "elevation": "128.89999389648438", "time": "2013-09-20T11:54:56.281Z"}, {"lat": "44.132873", "lng": "9.6873", "elevation": "130.1999969482422", "time": "2013-09-20T11:55:06.211Z"}, {"lat": "44.132865", "lng": "9.687314", "elevation": "131.39999389648438", "time": "2013-09-20T11:55:07.215Z"}, {"lat": "44.13282", "lng": "9.687412", "elevation": "134.1999969482422", "time": "2013-09-20T11:55:14.225Z"}, {"lat": "44.132812", "lng": "9.687425", "elevation": "134.5", "time": "2013-09-20T11:55:15.211Z"}, {"lat": "44.132782", "lng": "9.687533", "elevation": "130.5", "time": "2013-09-20T11:55:22.210Z"}, {"lat": "44.132784", "lng": "9.687551", "elevation": "130.89999389648438", "time": "2013-09-20T11:55:23.215Z"}, {"lat": "44.132749", "lng": "9.687664", "elevation": "133.6999969482422", "time": "2013-09-20T11:55:31.210Z"}, {"lat": "44.132745", "lng": "9.68768", "elevation": "134.1999969482422", "time": "2013-09-20T11:55:32.223Z"}, {"lat": "44.132741", "lng": "9.687762", "elevation": "133.89999389648438", "time": "2013-09-20T11:55:43.210Z"}, {"lat": "44.132742", "lng": "9.687762", "elevation": "134.3000030517578", "time": "2013-09-20T11:55:44.224Z"}, {"lat": "44.13274", "lng": "9.687767", "elevation": "135.10000610351563", "time": "2013-09-20T11:55:47.211Z"}, {"lat": "44.132738", "lng": "9.687772", "elevation": "136.39999389648438", "time": "2013-09-20T11:55:48.204Z"}, {"lat": "44.132731", "lng": "9.687895", "elevation": "138.1999969482422", "time": "2013-09-20T11:56:00.256Z"}, {"lat": "44.132735", "lng": "9.687903", "elevation": "137.6999969482422", "time": "2013-09-20T11:56:01.214Z"}, {"lat": "44.132757", "lng": "9.688023", "elevation": "137.6999969482422", "time": "2013-09-20T11:56:13.312Z"}, {"lat": "44.132754", "lng": "9.688027", "elevation": "138.3000030517578", "time": "2013-09-20T11:56:14.220Z"}, {"lat": "44.132759", "lng": "9.688064", "elevation": "141.3000030517578", "time": "2013-09-20T11:56:24.216Z"}, {"lat": "44.132759", "lng": "9.688065", "elevation": "141.3000030517578", "time": "2013-09-20T11:56:25.213Z"}, {"lat": "44.13275", "lng": "9.688102", "elevation": "143.39999389648438", "time": "2013-09-20T11:56:49.219Z"}, {"lat": "44.13275", "lng": "9.688108", "elevation": "143.3000030517578", "time": "2013-09-20T11:56:50.290Z"}, {"lat": "44.132754", "lng": "9.688224", "elevation": "144.1999969482422", "time": "2013-09-20T11:56:57.274Z"}, {"lat": "44.132755", "lng": "9.688243", "elevation": "144.39999389648438", "time": "2013-09-20T11:56:58.229Z"}, {"lat": "44.132775", "lng": "9.688359", "elevation": "141.8000030517578", "time": "2013-09-20T11:57:05.219Z"}, {"lat": "44.132778", "lng": "9.688378", "elevation": "141.5", "time": "2013-09-20T11:57:06.218Z"}, {"lat": "44.132775", "lng": "9.688494", "elevation": "142.0", "time": "2013-09-20T11:57:12.226Z"}, {"lat": "44.132773", "lng": "9.688514", "elevation": "140.8000030517578", "time": "2013-09-20T11:57:13.327Z"}, {"lat": "44.132758", "lng": "9.688635", "elevation": "142.39999389648438", "time": "2013-09-20T11:57:19.222Z"}, {"lat": "44.132756", "lng": "9.688659", "elevation": "142.39999389648438", "time": "2013-09-20T11:57:20.225Z"}, {"lat": "44.132744", "lng": "9.688781", "elevation": "142.3000030517578", "time": "2013-09-20T11:57:25.257Z"}, {"lat": "44.132741", "lng": "9.688806", "elevation": "142.5", "time": "2013-09-20T11:57:26.309Z"}, {"lat": "44.132725", "lng": "9.688919", "elevation": "141.6999969482422", "time": "2013-09-20T11:57:31.226Z"}, {"lat": "44.132725", "lng": "9.688943", "elevation": "141.10000610351563", "time": "2013-09-20T11:57:32.270Z"}, {"lat": "44.132733", "lng": "9.689059", "elevation": "138.89999389648438", "time": "2013-09-20T11:57:37.226Z"}, {"lat": "44.132738", "lng": "9.689083", "elevation": "139.39999389648438", "time": "2013-09-20T11:57:38.218Z"}, {"lat": "44.132716", "lng": "9.689201", "elevation": "137.89999389648438", "time": "2013-09-20T11:57:45.262Z"}, {"lat": "44.132707", "lng": "9.689214", "elevation": "138.6999969482422", "time": "2013-09-20T11:57:46.421Z"}, {"lat": "44.13265", "lng": "9.689307", "elevation": "137.3000030517578", "time": "2013-09-20T11:58:02.222Z"}, {"lat": "44.132656", "lng": "9.689324", "elevation": "137.8000030517578", "time": "2013-09-20T11:58:03.306Z"}, {"lat": "44.13269", "lng": "9.689431", "elevation": "135.6999969482422", "time": "2013-09-20T11:58:11.338Z"}, {"lat": "44.132692", "lng": "9.689446", "elevation": "134.89999389648438", "time": "2013-09-20T11:58:12.383Z"}, {"lat": "44.132681", "lng": "9.689563", "elevation": "135.1999969482422", "time": "2013-09-20T11:58:20.266Z"}, {"lat": "44.132678", "lng": "9.689578", "elevation": "135.1999969482422", "time": "2013-09-20T11:58:21.231Z"}, {"lat": "44.13266", "lng": "9.689694", "elevation": "137.10000610351563", "time": "2013-09-20T11:58:30.227Z"}, {"lat": "44.132654", "lng": "9.689707", "elevation": "136.60000610351563", "time": "2013-09-20T11:58:31.226Z"}, {"lat": "44.132598", "lng": "9.689803", "elevation": "138.39999389648438", "time": "2013-09-20T11:58:39.221Z"}, {"lat": "44.132596", "lng": "9.689812", "elevation": "138.10000610351563", "time": "2013-09-20T11:58:40.275Z"}, {"lat": "44.132594", "lng": "9.689823", "elevation": "137.89999389648438", "time": "2013-09-20T11:58:45.241Z"}, {"lat": "44.132593", "lng": "9.689823", "elevation": "138.39999389648438", "time": "2013-09-20T11:58:46.230Z"}, {"lat": "44.132585", "lng": "9.689827", "elevation": "138.3000030517578", "time": "2013-09-20T11:58:55.225Z"}, {"lat": "44.132582", "lng": "9.689834", "elevation": "137.89999389648438", "time": "2013-09-20T11:58:56.231Z"}, {"lat": "44.132518", "lng": "9.689917", "elevation": "139.1999969482422", "time": "2013-09-20T11:59:03.223Z"}, {"lat": "44.132503", "lng": "9.689923", "elevation": "139.1999969482422", "time": "2013-09-20T11:59:04.391Z"}, {"lat": "44.132441", "lng": "9.689981", "elevation": "135.5", "time": "2013-09-20T11:59:10.226Z"}, {"lat": "44.13243", "lng": "9.689998", "elevation": "136.39999389648438", "time": "2013-09-20T11:59:11.227Z"}, {"lat": "44.132368", "lng": "9.690086", "elevation": "136.89999389648438", "time": "2013-09-20T11:59:16.265Z"}, {"lat": "44.132359", "lng": "9.690106", "elevation": "137.6999969482422", "time": "2013-09-20T11:59:17.276Z"}, {"lat": "44.132334", "lng": "9.690212", "elevation": "133.1999969482422", "time": "2013-09-20T11:59:22.229Z"}, {"lat": "44.132329", "lng": "9.690232", "elevation": "133.89999389648438", "time": "2013-09-20T11:59:23.234Z"}, {"lat": "44.132301", "lng": "9.690341", "elevation": "130.0", "time": "2013-09-20T11:59:30.317Z"}, {"lat": "44.132303", "lng": "9.690363", "elevation": "130.3000030517578", "time": "2013-09-20T11:59:31.239Z"}, {"lat": "44.132272", "lng": "9.690465", "elevation": "127.9000015258789", "time": "2013-09-20T11:59:38.243Z"}, {"lat": "44.132265", "lng": "9.690477", "elevation": "129.60000610351563", "time": "2013-09-20T11:59:39.334Z"}, {"lat": "44.132224", "lng": "9.690581", "elevation": "129.39999389648438", "time": "2013-09-20T11:59:48.453Z"}, {"lat": "44.132226", "lng": "9.690597", "elevation": "128.8000030517578", "time": "2013-09-20T11:59:49.327Z"}, {"lat": "44.132231", "lng": "9.690711", "elevation": "127.19999694824219", "time": "2013-09-20T11:59:56.311Z"}, {"lat": "44.132231", "lng": "9.690723", "elevation": "125.9000015258789", "time": "2013-09-20T11:59:57.255Z"}, {"lat": "44.13221", "lng": "9.690783", "elevation": "129.0", "time": "2013-09-20T12:00:11.240Z"}, {"lat": "44.132213", "lng": "9.690787", "elevation": "129.0", "time": "2013-09-20T12:00:12.304Z"}, {"lat": "44.132218", "lng": "9.690793", "elevation": "128.60000610351563", "time": "2013-09-20T12:00:13.262Z"}, {"lat": "44.132238", "lng": "9.690817", "elevation": "128.10000610351563", "time": "2013-09-20T12:00:20.231Z"}, {"lat": "44.132239", "lng": "9.690817", "elevation": "128.1999969482422", "time": "2013-09-20T12:00:21.233Z"}, {"lat": "44.132235", "lng": "9.690816", "elevation": "128.1999969482422", "time": "2013-09-20T12:00:31.234Z"}, {"lat": "44.132228", "lng": "9.690816", "elevation": "128.5", "time": "2013-09-20T12:00:32.233Z"}, {"lat": "44.132147", "lng": "9.690857", "elevation": "129.8000030517578", "time": "2013-09-20T12:00:38.262Z"}, {"lat": "44.132133", "lng": "9.690872", "elevation": "130.5", "time": "2013-09-20T12:00:39.241Z"}, {"lat": "44.132094", "lng": "9.690973", "elevation": "131.10000610351563", "time": "2013-09-20T12:00:44.244Z"}, {"lat": "44.132088", "lng": "9.690988", "elevation": "131.3000030517578", "time": "2013-09-20T12:00:45.234Z"}, {"lat": "44.132052", "lng": "9.691091", "elevation": "130.8000030517578", "time": "2013-09-20T12:00:55.233Z"}, {"lat": "44.132049", "lng": "9.691105", "elevation": "131.60000610351563", "time": "2013-09-20T12:00:56.232Z"}, {"lat": "44.132004", "lng": "9.691212", "elevation": "131.39999389648438", "time": "2013-09-20T12:01:03.239Z"}, {"lat": "44.131997", "lng": "9.691229", "elevation": "131.3000030517578", "time": "2013-09-20T12:01:04.244Z"}, {"lat": "44.13198", "lng": "9.69126", "elevation": "131.0", "time": "2013-09-20T12:01:06.256Z"}, {"lat": "44.131924", "lng": "9.691412", "elevation": "132.6999969482422", "time": "2013-09-20T12:01:16.223Z"}, {"lat": "44.131908", "lng": "9.691519", "elevation": "131.89999389648438", "time": "2013-09-20T12:01:22.282Z"}, {"lat": "44.131905", "lng": "9.691535", "elevation": "131.60000610351563", "time": "2013-09-20T12:01:23.256Z"}, {"lat": "44.131852", "lng": "9.691635", "elevation": "133.10000610351563", "time": "2013-09-20T12:01:32.270Z"}, {"lat": "44.131838", "lng": "9.691635", "elevation": "133.8000030517578", "time": "2013-09-20T12:01:33.237Z"}, {"lat": "44.131776", "lng": "9.691718", "elevation": "134.10000610351563", "time": "2013-09-20T12:01:42.268Z"}, {"lat": "44.131766", "lng": "9.691726", "elevation": "134.0", "time": "2013-09-20T12:01:43.254Z"}, {"lat": "44.131685", "lng": "9.691775", "elevation": "133.39999389648438", "time": "2013-09-20T12:01:50.249Z"}, {"lat": "44.131671", "lng": "9.691779", "elevation": "132.60000610351563", "time": "2013-09-20T12:01:51.242Z"}, {"lat": "44.131625", "lng": "9.691876", "elevation": "129.89999389648438", "time": "2013-09-20T12:01:59.401Z"}, {"lat": "44.13162", "lng": "9.691888", "elevation": "130.5", "time": "2013-09-20T12:02:00.452Z"}, {"lat": "44.13156", "lng": "9.691973", "elevation": "133.1999969482422", "time": "2013-09-20T12:02:09.242Z"}, {"lat": "44.131555", "lng": "9.691984", "elevation": "133.39999389648438", "time": "2013-09-20T12:02:10.252Z"}, {"lat": "44.13151", "lng": "9.692083", "elevation": "133.6999969482422", "time": "2013-09-20T12:02:19.252Z"}, {"lat": "44.131508", "lng": "9.692097", "elevation": "133.8000030517578", "time": "2013-09-20T12:02:20.245Z"}, {"lat": "44.13145", "lng": "9.692182", "elevation": "134.3000030517578", "time": "2013-09-20T12:02:29.238Z"}, {"lat": "44.131441", "lng": "9.692189", "elevation": "134.39999389648438", "time": "2013-09-20T12:02:30.245Z"}, {"lat": "44.131361", "lng": "9.692245", "elevation": "140.89999389648438", "time": "2013-09-20T12:02:40.358Z"}, {"lat": "44.131355", "lng": "9.69225", "elevation": "140.6999969482422", "time": "2013-09-20T12:02:41.279Z"}, {"lat": "44.131277", "lng": "9.692304", "elevation": "144.1999969482422", "time": "2013-09-20T12:02:53.277Z"}, {"lat": "44.131271", "lng": "9.692311", "elevation": "143.89999389648438", "time": "2013-09-20T12:02:54.368Z"}, {"lat": "44.131207", "lng": "9.692396", "elevation": "149.89999389648438", "time": "2013-09-20T12:03:07.248Z"}, {"lat": "44.131205", "lng": "9.692406", "elevation": "149.60000610351563", "time": "2013-09-20T12:03:08.265Z"}, {"lat": "44.1312", "lng": "9.692455", "elevation": "149.8000030517578", "time": "2013-09-20T12:03:15.265Z"}, {"lat": "44.131202", "lng": "9.692456", "elevation": "149.60000610351563", "time": "2013-09-20T12:03:16.256Z"}, {"lat": "44.131204", "lng": "9.692461", "elevation": "149.5", "time": "2013-09-20T12:03:19.361Z"}, {"lat": "44.131203", "lng": "9.692466", "elevation": "149.6999969482422", "time": "2013-09-20T12:03:20.312Z"}, {"lat": "44.131145", "lng": "9.692559", "elevation": "153.0", "time": "2013-09-20T12:03:32.242Z"}, {"lat": "44.131137", "lng": "9.692568", "elevation": "152.8000030517578", "time": "2013-09-20T12:03:33.275Z"}, {"lat": "44.131071", "lng": "9.692643", "elevation": "153.89999389648438", "time": "2013-09-20T12:03:45.243Z"}, {"lat": "44.131065", "lng": "9.692646", "elevation": "154.8000030517578", "time": "2013-09-20T12:03:46.251Z"}, {"lat": "44.131004", "lng": "9.692735", "elevation": "162.6999969482422", "time": "2013-09-20T12:04:00.249Z"}, {"lat": "44.130998", "lng": "9.692735", "elevation": "161.89999389648438", "time": "2013-09-20T12:04:01.252Z"}, {"lat": "44.130974", "lng": "9.69277", "elevation": "162.8000030517578", "time": "2013-09-20T12:04:10.244Z"}, {"lat": "44.130975", "lng": "9.692769", "elevation": "163.6999969482422", "time": "2013-09-20T12:04:11.252Z"}, {"lat": "44.13096", "lng": "9.692758", "elevation": "165.60000610351563", "time": "2013-09-20T12:05:04.248Z"}, {"lat": "44.130953", "lng": "9.692763", "elevation": "165.1999969482422", "time": "2013-09-20T12:05:05.256Z"}, {"lat": "44.130945", "lng": "9.692876", "elevation": "160.10000610351563", "time": "2013-09-20T12:05:14.265Z"}, {"lat": "44.130943", "lng": "9.692887", "elevation": "160.5", "time": "2013-09-20T12:05:15.256Z"}, {"lat": "44.130979", "lng": "9.692999", "elevation": "169.10000610351563", "time": "2013-09-20T12:05:39.270Z"}, {"lat": "44.130976", "lng": "9.693003", "elevation": "169.89999389648438", "time": "2013-09-20T12:05:40.264Z"}, {"lat": "44.130921", "lng": "9.693018", "elevation": "171.39999389648438", "time": "2013-09-20T12:05:50.347Z"}, {"lat": "44.13092", "lng": "9.693017", "elevation": "172.3000030517578", "time": "2013-09-20T12:05:51.355Z"}, {"lat": "44.130914", "lng": "9.693011", "elevation": "172.89999389648438", "time": "2013-09-20T12:06:20.277Z"}, {"lat": "44.130908", "lng": "9.693012", "elevation": "173.89999389648438", "time": "2013-09-20T12:06:21.281Z"}, {"lat": "44.130823", "lng": "9.693026", "elevation": "172.5", "time": "2013-09-20T12:06:28.273Z"}, {"lat": "44.13081", "lng": "9.693039", "elevation": "170.3000030517578", "time": "2013-09-20T12:06:29.270Z"}, {"lat": "44.130792", "lng": "9.693152", "elevation": "166.39999389648438", "time": "2013-09-20T12:06:33.302Z"}, {"lat": "44.130789", "lng": "9.693182", "elevation": "165.89999389648438", "time": "2013-09-20T12:06:34.265Z"}, {"lat": "44.130722", "lng": "9.693263", "elevation": "166.6999969482422", "time": "2013-09-20T12:06:42.283Z"}, {"lat": "44.130721", "lng": "9.69327", "elevation": "166.8000030517578", "time": "2013-09-20T12:06:43.292Z"}, {"lat": "44.130718", "lng": "9.69328", "elevation": "167.5", "time": "2013-09-20T12:06:48.361Z"}, {"lat": "44.130717", "lng": "9.693279", "elevation": "167.39999389648438", "time": "2013-09-20T12:06:49.323Z"}, {"lat": "44.130711", "lng": "9.693278", "elevation": "168.39999389648438", "time": "2013-09-20T12:06:52.273Z"}, {"lat": "44.130706", "lng": "9.693278", "elevation": "168.8000030517578", "time": "2013-09-20T12:06:53.377Z"}, {"lat": "44.130619", "lng": "9.69328", "elevation": "174.5", "time": "2013-09-20T12:07:06.282Z"}, {"lat": "44.130612", "lng": "9.693275", "elevation": "174.6999969482422", "time": "2013-09-20T12:07:07.273Z"}, {"lat": "44.130602", "lng": "9.693267", "elevation": "174.89999389648438", "time": "2013-09-20T12:07:12.271Z"}, {"lat": "44.130603", "lng": "9.693267", "elevation": "174.89999389648438", "time": "2013-09-20T12:07:13.263Z"}, {"lat": "44.130599", "lng": "9.693273", "elevation": "178.1999969482422", "time": "2013-09-20T12:07:58.269Z"}, {"lat": "44.130594", "lng": "9.693271", "elevation": "179.5", "time": "2013-09-20T12:07:59.267Z"}, {"lat": "44.130574", "lng": "9.693262", "elevation": "180.1999969482422", "time": "2013-09-20T12:08:06.271Z"}, {"lat": "44.130573", "lng": "9.693263", "elevation": "180.3000030517578", "time": "2013-09-20T12:08:07.271Z"}, {"lat": "44.130558", "lng": "9.693261", "elevation": "180.5", "time": "2013-09-20T12:09:44.286Z"}, {"lat": "44.130556", "lng": "9.69326", "elevation": "180.8000030517578", "time": "2013-09-20T12:09:45.282Z"}, {"lat": "44.13054", "lng": "9.693267", "elevation": "180.5", "time": "2013-09-20T12:09:51.288Z"}, {"lat": "44.130539", "lng": "9.693268", "elevation": "180.60000610351563", "time": "2013-09-20T12:09:52.279Z"}, {"lat": "44.130536", "lng": "9.693273", "elevation": "180.89999389648438", "time": "2013-09-20T12:10:40.324Z"}, {"lat": "44.130534", "lng": "9.693277", "elevation": "181.0", "time": "2013-09-20T12:10:41.273Z"}, {"lat": "44.130497", "lng": "9.693262", "elevation": "184.0", "time": "2013-09-20T12:10:49.271Z"}, {"lat": "44.130496", "lng": "9.693261", "elevation": "184.0", "time": "2013-09-20T12:10:50.323Z"}, {"lat": "44.130496", "lng": "9.693252", "elevation": "186.10000610351563", "time": "2013-09-20T12:11:13.302Z"}, {"lat": "44.130495", "lng": "9.693244", "elevation": "185.6999969482422", "time": "2013-09-20T12:11:14.285Z"}, {"lat": "44.13048", "lng": "9.693198", "elevation": "189.6999969482422", "time": "2013-09-20T12:11:24.287Z"}, {"lat": "44.13048", "lng": "9.693199", "elevation": "189.1999969482422", "time": "2013-09-20T12:11:25.286Z"}, {"lat": "44.130479", "lng": "9.693214", "elevation": "192.39999389648438", "time": "2013-09-20T12:11:39.289Z"}, {"lat": "44.130476", "lng": "9.693218", "elevation": "190.6999969482422", "time": "2013-09-20T12:11:40.286Z"}, {"lat": "44.130508", "lng": "9.693334", "elevation": "197.5", "time": "2013-09-20T12:11:52.324Z"}, {"lat": "44.130514", "lng": "9.693345", "elevation": "197.1999969482422", "time": "2013-09-20T12:11:53.332Z"}, {"lat": "44.130525", "lng": "9.693463", "elevation": "196.10000610351563", "time": "2013-09-20T12:12:04.317Z"}, {"lat": "44.130519", "lng": "9.693474", "elevation": "196.60000610351563", "time": "2013-09-20T12:12:05.373Z"}, {"lat": "44.13049", "lng": "9.693587", "elevation": "198.6999969482422", "time": "2013-09-20T12:12:19.306Z"}, {"lat": "44.13049", "lng": "9.693596", "elevation": "199.3000030517578", "time": "2013-09-20T12:12:20.297Z"}, {"lat": "44.130512", "lng": "9.693715", "elevation": "200.0", "time": "2013-09-20T12:12:34.299Z"}, {"lat": "44.130511", "lng": "9.693722", "elevation": "200.0", "time": "2013-09-20T12:12:35.307Z"}, {"lat": "44.130487", "lng": "9.693839", "elevation": "203.6999969482422", "time": "2013-09-20T12:12:51.308Z"}, {"lat": "44.130481", "lng": "9.693851", "elevation": "204.60000610351563", "time": "2013-09-20T12:12:52.298Z"}, {"lat": "44.130447", "lng": "9.693964", "elevation": "207.5", "time": "2013-09-20T12:13:03.396Z"}, {"lat": "44.130444", "lng": "9.693975", "elevation": "208.3000030517578", "time": "2013-09-20T12:13:04.382Z"}, {"lat": "44.130406", "lng": "9.69408", "elevation": "210.10000610351563", "time": "2013-09-20T12:13:12.317Z"}, {"lat": "44.130399", "lng": "9.694092", "elevation": "210.1999969482422", "time": "2013-09-20T12:13:13.334Z"}, {"lat": "44.130349", "lng": "9.694189", "elevation": "213.1999969482422", "time": "2013-09-20T12:13:22.314Z"}, {"lat": "44.130347", "lng": "9.694202", "elevation": "213.6999969482422", "time": "2013-09-20T12:13:23.311Z"}, {"lat": "44.130305", "lng": "9.6943", "elevation": "217.39999389648438", "time": "2013-09-20T12:13:33.398Z"}, {"lat": "44.130299", "lng": "9.694309", "elevation": "217.1999969482422", "time": "2013-09-20T12:13:34.295Z"}, {"lat": "44.13023", "lng": "9.694378", "elevation": "217.8000030517578", "time": "2013-09-20T12:13:42.425Z"}, {"lat": "44.130222", "lng": "9.694384", "elevation": "216.3000030517578", "time": "2013-09-20T12:13:43.388Z"}, {"lat": "44.130217", "lng": "9.694383", "elevation": "217.60000610351563", "time": "2013-09-20T12:13:47.297Z"}, {"lat": "44.130218", "lng": "9.694382", "elevation": "217.89999389648438", "time": "2013-09-20T12:13:48.305Z"}, {"lat": "44.130224", "lng": "9.694398", "elevation": "218.6999969482422", "time": "2013-09-20T12:14:10.330Z"}, {"lat": "44.13022", "lng": "9.694404", "elevation": "219.8000030517578", "time": "2013-09-20T12:14:11.311Z"}, {"lat": "44.130142", "lng": "9.694452", "elevation": "222.10000610351563", "time": "2013-09-20T12:14:17.301Z"}, {"lat": "44.130127", "lng": "9.694465", "elevation": "223.0", "time": "2013-09-20T12:14:18.305Z"}, {"lat": "44.130056", "lng": "9.694534", "elevation": "228.10000610351563", "time": "2013-09-20T12:14:23.410Z"}, {"lat": "44.130044", "lng": "9.694544", "elevation": "229.39999389648438", "time": "2013-09-20T12:14:24.362Z"}, {"lat": "44.130043", "lng": "9.694603", "elevation": "229.6999969482422", "time": "2013-09-20T12:14:34.310Z"}, {"lat": "44.130043", "lng": "9.694603", "elevation": "229.8000030517578", "time": "2013-09-20T12:14:35.301Z"}, {"lat": "44.13004", "lng": "9.694613", "elevation": "231.89999389648438", "time": "2013-09-20T12:14:48.355Z"}, {"lat": "44.130038", "lng": "9.694621", "elevation": "233.10000610351563", "time": "2013-09-20T12:14:49.341Z"}, {"lat": "44.13003", "lng": "9.69474", "elevation": "231.3000030517578", "time": "2013-09-20T12:14:56.306Z"}, {"lat": "44.130032", "lng": "9.694756", "elevation": "231.5", "time": "2013-09-20T12:14:57.298Z"}, {"lat": "44.130048", "lng": "9.694868", "elevation": "234.60000610351563", "time": "2013-09-20T12:15:09.300Z"}, {"lat": "44.13005", "lng": "9.694878", "elevation": "234.5", "time": "2013-09-20T12:15:10.323Z"}, {"lat": "44.130062", "lng": "9.695001", "elevation": "240.39999389648438", "time": "2013-09-20T12:15:24.409Z"}, {"lat": "44.130061", "lng": "9.695013", "elevation": "239.60000610351563", "time": "2013-09-20T12:15:25.381Z"}, {"lat": "44.130046", "lng": "9.695119", "elevation": "242.5", "time": "2013-09-20T12:15:32.423Z"}, {"lat": "44.130044", "lng": "9.695139", "elevation": "242.1999969482422", "time": "2013-09-20T12:15:33.383Z"}, {"lat": "44.130013", "lng": "9.695249", "elevation": "244.10000610351563", "time": "2013-09-20T12:15:44.327Z"}, {"lat": "44.130008", "lng": "9.69526", "elevation": "243.39999389648438", "time": "2013-09-20T12:15:45.378Z"}, {"lat": "44.129972", "lng": "9.695373", "elevation": "240.89999389648438", "time": "2013-09-20T12:15:53.316Z"}, {"lat": "44.129971", "lng": "9.695387", "elevation": "241.10000610351563", "time": "2013-09-20T12:15:54.306Z"}, {"lat": "44.129946", "lng": "9.695495", "elevation": "239.5", "time": "2013-09-20T12:16:00.317Z"}, {"lat": "44.129944", "lng": "9.695514", "elevation": "239.89999389648438", "time": "2013-09-20T12:16:01.319Z"}, {"lat": "44.129932", "lng": "9.695626", "elevation": "241.89999389648438", "time": "2013-09-20T12:16:08.311Z"}, {"lat": "44.129931", "lng": "9.695641", "elevation": "241.1999969482422", "time": "2013-09-20T12:16:09.307Z"}, {"lat": "44.129948", "lng": "9.695759", "elevation": "239.89999389648438", "time": "2013-09-20T12:16:18.411Z"}, {"lat": "44.129943", "lng": "9.69577", "elevation": "239.60000610351563", "time": "2013-09-20T12:16:19.441Z"}, {"lat": "44.129886", "lng": "9.695858", "elevation": "240.60000610351563", "time": "2013-09-20T12:16:27.455Z"}, {"lat": "44.129879", "lng": "9.695862", "elevation": "240.6999969482422", "time": "2013-09-20T12:16:28.430Z"}, {"lat": "44.129859", "lng": "9.69586", "elevation": "241.3000030517578", "time": "2013-09-20T12:16:34.437Z"}, {"lat": "44.129859", "lng": "9.695862", "elevation": "241.10000610351563", "time": "2013-09-20T12:16:35.468Z"}, {"lat": "44.129857", "lng": "9.695866", "elevation": "240.8000030517578", "time": "2013-09-20T12:16:37.404Z"}, {"lat": "44.129856", "lng": "9.695873", "elevation": "241.10000610351563", "time": "2013-09-20T12:16:38.370Z"}, {"lat": "44.12983", "lng": "9.695987", "elevation": "241.10000610351563", "time": "2013-09-20T12:16:47.307Z"}, {"lat": "44.129824", "lng": "9.696002", "elevation": "240.60000610351563", "time": "2013-09-20T12:16:48.301Z"}, {"lat": "44.129766", "lng": "9.696096", "elevation": "240.89999389648438", "time": "2013-09-20T12:16:56.316Z"}, {"lat": "44.129759", "lng": "9.696104", "elevation": "241.3000030517578", "time": "2013-09-20T12:16:57.311Z"}, {"lat": "44.129704", "lng": "9.696183", "elevation": "244.5", "time": "2013-09-20T12:17:06.415Z"}, {"lat": "44.129696", "lng": "9.696194", "elevation": "245.3000030517578", "time": "2013-09-20T12:17:07.464Z"}, {"lat": "44.129652", "lng": "9.6963", "elevation": "244.3000030517578", "time": "2013-09-20T12:17:14.400Z"}, {"lat": "44.129646", "lng": "9.696318", "elevation": "244.10000610351563", "time": "2013-09-20T12:17:15.472Z"}, {"lat": "44.129623", "lng": "9.696427", "elevation": "242.89999389648438", "time": "2013-09-20T12:17:22.314Z"}, {"lat": "44.12962", "lng": "9.696438", "elevation": "242.6999969482422", "time": "2013-09-20T12:17:23.344Z"}, {"lat": "44.129613", "lng": "9.696561", "elevation": "243.0", "time": "2013-09-20T12:17:33.319Z"}, {"lat": "44.129611", "lng": "9.696573", "elevation": "241.6999969482422", "time": "2013-09-20T12:17:34.316Z"}, {"lat": "44.129587", "lng": "9.696684", "elevation": "239.3000030517578", "time": "2013-09-20T12:17:42.359Z"}, {"lat": "44.129582", "lng": "9.696701", "elevation": "241.8000030517578", "time": "2013-09-20T12:17:43.346Z"}, {"lat": "44.129531", "lng": "9.696803", "elevation": "243.0", "time": "2013-09-20T12:17:49.396Z"}, {"lat": "44.129522", "lng": "9.696815", "elevation": "242.8000030517578", "time": "2013-09-20T12:17:50.363Z"}, {"lat": "44.12949", "lng": "9.696912", "elevation": "238.10000610351563", "time": "2013-09-20T12:17:57.345Z"}, {"lat": "44.129493", "lng": "9.696939", "elevation": "238.8000030517578", "time": "2013-09-20T12:17:58.320Z"}, {"lat": "44.129491", "lng": "9.697058", "elevation": "238.60000610351563", "time": "2013-09-20T12:18:03.318Z"}, {"lat": "44.129485", "lng": "9.697074", "elevation": "238.5", "time": "2013-09-20T12:18:04.317Z"}, {"lat": "44.129466", "lng": "9.697191", "elevation": "237.89999389648438", "time": "2013-09-20T12:18:15.317Z"}, {"lat": "44.129465", "lng": "9.697197", "elevation": "237.8000030517578", "time": "2013-09-20T12:18:16.318Z"}, {"lat": "44.129422", "lng": "9.697305", "elevation": "241.5", "time": "2013-09-20T12:18:24.319Z"}, {"lat": "44.129417", "lng": "9.697319", "elevation": "242.0", "time": "2013-09-20T12:18:25.318Z"}, {"lat": "44.129413", "lng": "9.697439", "elevation": "241.39999389648438", "time": "2013-09-20T12:18:34.320Z"}, {"lat": "44.129413", "lng": "9.697456", "elevation": "240.5", "time": "2013-09-20T12:18:35.318Z"}, {"lat": "44.129429", "lng": "9.697569", "elevation": "241.5", "time": "2013-09-20T12:18:43.485Z"}, {"lat": "44.12943", "lng": "9.697582", "elevation": "241.6999969482422", "time": "2013-09-20T12:18:44.328Z"}, {"lat": "44.129449", "lng": "9.697693", "elevation": "246.1999969482422", "time": "2013-09-20T12:18:51.343Z"}, {"lat": "44.129453", "lng": "9.697706", "elevation": "246.5", "time": "2013-09-20T12:18:52.324Z"}, {"lat": "44.129456", "lng": "9.697718", "elevation": "245.39999389648438", "time": "2013-09-20T12:18:56.432Z"}, {"lat": "44.129455", "lng": "9.697716", "elevation": "246.0", "time": "2013-09-20T12:18:57.403Z"}, {"lat": "44.129455", "lng": "9.697715", "elevation": "244.89999389648438", "time": "2013-09-20T12:19:34.330Z"}, {"lat": "44.12946", "lng": "9.697717", "elevation": "245.1999969482422", "time": "2013-09-20T12:19:35.323Z"}, {"lat": "44.129524", "lng": "9.697802", "elevation": "247.0", "time": "2013-09-20T12:19:49.325Z"}, {"lat": "44.129527", "lng": "9.697805", "elevation": "246.8000030517578", "time": "2013-09-20T12:19:50.325Z"}, {"lat": "44.129549", "lng": "9.697924", "elevation": "242.3000030517578", "time": "2013-09-20T12:19:58.338Z"}, {"lat": "44.129552", "lng": "9.697946", "elevation": "242.89999389648438", "time": "2013-09-20T12:19:59.326Z"}, {"lat": "44.129515", "lng": "9.698055", "elevation": "246.89999389648438", "time": "2013-09-20T12:20:06.398Z"}, {"lat": "44.129511", "lng": "9.698068", "elevation": "246.1999969482422", "time": "2013-09-20T12:20:07.425Z"}, {"lat": "44.129494", "lng": "9.698177", "elevation": "246.8000030517578", "time": "2013-09-20T12:20:14.370Z"}, {"lat": "44.12949", "lng": "9.698193", "elevation": "247.10000610351563", "time": "2013-09-20T12:20:15.431Z"}, {"lat": "44.129447", "lng": "9.698291", "elevation": "247.60000610351563", "time": "2013-09-20T12:20:22.367Z"}, {"lat": "44.129441", "lng": "9.698306", "elevation": "247.10000610351563", "time": "2013-09-20T12:20:23.420Z"}, {"lat": "44.12944", "lng": "9.698421", "elevation": "246.39999389648438", "time": "2013-09-20T12:20:32.440Z"}, {"lat": "44.129439", "lng": "9.698433", "elevation": "246.1999969482422", "time": "2013-09-20T12:20:33.453Z"}, {"lat": "44.129409", "lng": "9.698538", "elevation": "245.8000030517578", "time": "2013-09-20T12:20:40.449Z"}, {"lat": "44.129406", "lng": "9.698551", "elevation": "245.5", "time": "2013-09-20T12:20:41.411Z"}, {"lat": "44.129428", "lng": "9.698656", "elevation": "243.8000030517578", "time": "2013-09-20T12:21:03.330Z"}, {"lat": "44.129424", "lng": "9.698658", "elevation": "244.60000610351563", "time": "2013-09-20T12:21:04.335Z"}, {"lat": "44.129419", "lng": "9.698661", "elevation": "244.6999969482422", "time": "2013-09-20T12:21:05.334Z"}, {"lat": "44.129416", "lng": "9.698663", "elevation": "244.60000610351563", "time": "2013-09-20T12:21:06.334Z"}, {"lat": "44.129411", "lng": "9.698665", "elevation": "244.60000610351563", "time": "2013-09-20T12:21:08.368Z"}, {"lat": "44.12941", "lng": "9.698666", "elevation": "244.60000610351563", "time": "2013-09-20T12:21:09.331Z"}, {"lat": "44.129423", "lng": "9.698705", "elevation": "242.5", "time": "2013-09-20T12:21:36.334Z"}, {"lat": "44.129429", "lng": "9.698716", "elevation": "244.60000610351563", "time": "2013-09-20T12:21:37.334Z"}, {"lat": "44.12947", "lng": "9.698803", "elevation": "247.8000030517578", "time": "2013-09-20T12:21:41.350Z"}, {"lat": "44.129479", "lng": "9.698838", "elevation": "250.10000610351563", "time": "2013-09-20T12:21:42.377Z"}, {"lat": "44.129478", "lng": "9.698962", "elevation": "255.1999969482422", "time": "2013-09-20T12:21:47.462Z"}, {"lat": "44.129475", "lng": "9.698981", "elevation": "255.0", "time": "2013-09-20T12:21:48.471Z"}, {"lat": "44.129489", "lng": "9.6991", "elevation": "257.5", "time": "2013-09-20T12:21:55.461Z"}, {"lat": "44.129487", "lng": "9.699113", "elevation": "258.70001220703125", "time": "2013-09-20T12:21:56.350Z"}, {"lat": "44.129481", "lng": "9.699237", "elevation": "261.79998779296875", "time": "2013-09-20T12:22:06.390Z"}, {"lat": "44.129483", "lng": "9.699249", "elevation": "262.1000061035156", "time": "2013-09-20T12:22:07.378Z"}, {"lat": "44.129483", "lng": "9.69937", "elevation": "266.3999938964844", "time": "2013-09-20T12:22:16.346Z"}, {"lat": "44.129483", "lng": "9.699384", "elevation": "266.0", "time": "2013-09-20T12:22:17.341Z"}, {"lat": "44.129476", "lng": "9.699499", "elevation": "265.20001220703125", "time": "2013-09-20T12:22:26.354Z"}, {"lat": "44.129476", "lng": "9.699511", "elevation": "265.20001220703125", "time": "2013-09-20T12:22:27.362Z"}, {"lat": "44.129482", "lng": "9.699628", "elevation": "266.5", "time": "2013-09-20T12:22:35.343Z"}, {"lat": "44.129483", "lng": "9.699644", "elevation": "266.3999938964844", "time": "2013-09-20T12:22:36.344Z"}, {"lat": "44.129467", "lng": "9.69976", "elevation": "267.5", "time": "2013-09-20T12:22:49.347Z"}, {"lat": "44.129468", "lng": "9.699768", "elevation": "267.3999938964844", "time": "2013-09-20T12:22:50.393Z"}, {"lat": "44.129434", "lng": "9.699871", "elevation": "272.70001220703125", "time": "2013-09-20T12:23:01.424Z"}, {"lat": "44.129423", "lng": "9.699888", "elevation": "272.5", "time": "2013-09-20T12:23:02.388Z"}, {"lat": "44.129365", "lng": "9.699975", "elevation": "271.79998779296875", "time": "2013-09-20T12:23:08.350Z"}, {"lat": "44.12936", "lng": "9.699988", "elevation": "271.29998779296875", "time": "2013-09-20T12:23:09.347Z"}, {"lat": "44.12933", "lng": "9.700052", "elevation": "271.79998779296875", "time": "2013-09-20T12:23:20.350Z"}, {"lat": "44.12933", "lng": "9.700053", "elevation": "271.5", "time": "2013-09-20T12:23:21.346Z"}, {"lat": "44.129329", "lng": "9.700055", "elevation": "271.5", "time": "2013-09-20T12:23:22.414Z"}, {"lat": "44.129327", "lng": "9.700057", "elevation": "270.6000061035156", "time": "2013-09-20T12:23:23.346Z"}, {"lat": "44.129278", "lng": "9.700151", "elevation": "268.0", "time": "2013-09-20T12:23:32.453Z"}, {"lat": "44.129272", "lng": "9.70017", "elevation": "268.1000061035156", "time": "2013-09-20T12:23:33.429Z"}, {"lat": "44.129243", "lng": "9.700282", "elevation": "267.20001220703125", "time": "2013-09-20T12:23:39.480Z"}, {"lat": "44.129236", "lng": "9.700293", "elevation": "267.5", "time": "2013-09-20T12:23:40.440Z"}, {"lat": "44.129218", "lng": "9.700301", "elevation": "267.1000061035156", "time": "2013-09-20T12:23:46.423Z"}, {"lat": "44.129219", "lng": "9.700299", "elevation": "267.1000061035156", "time": "2013-09-20T12:23:47.492Z"}, {"lat": "44.129224", "lng": "9.700306", "elevation": "267.0", "time": "2013-09-20T12:23:51.366Z"}, {"lat": "44.129224", "lng": "9.700313", "elevation": "268.1000061035156", "time": "2013-09-20T12:23:52.360Z"}, {"lat": "44.129148", "lng": "9.70038", "elevation": "271.8999938964844", "time": "2013-09-20T12:24:00.359Z"}, {"lat": "44.129135", "lng": "9.700388", "elevation": "271.8999938964844", "time": "2013-09-20T12:24:01.357Z"}, {"lat": "44.129087", "lng": "9.700477", "elevation": "271.1000061035156", "time": "2013-09-20T12:24:08.351Z"}, {"lat": "44.12908", "lng": "9.700491", "elevation": "270.29998779296875", "time": "2013-09-20T12:24:09.370Z"}, {"lat": "44.129045", "lng": "9.700595", "elevation": "265.5", "time": "2013-09-20T12:24:17.351Z"}, {"lat": "44.129041", "lng": "9.70061", "elevation": "265.70001220703125", "time": "2013-09-20T12:24:18.484Z"}, {"lat": "44.129019", "lng": "9.700718", "elevation": "265.3999938964844", "time": "2013-09-20T12:24:25.357Z"}, {"lat": "44.129018", "lng": "9.700733", "elevation": "265.0", "time": "2013-09-20T12:24:26.436Z"}, {"lat": "44.128987", "lng": "9.700839", "elevation": "261.3999938964844", "time": "2013-09-20T12:24:34.489Z"}, {"lat": "44.128987", "lng": "9.700854", "elevation": "259.6000061035156", "time": "2013-09-20T12:24:35.475Z"}, {"lat": "44.128987", "lng": "9.700972", "elevation": "257.20001220703125", "time": "2013-09-20T12:24:43.506Z"}, {"lat": "44.128989", "lng": "9.700981", "elevation": "256.29998779296875", "time": "2013-09-20T12:24:44.354Z"}, {"lat": "44.128958", "lng": "9.701041", "elevation": "256.20001220703125", "time": "2013-09-20T12:25:03.298Z"}, {"lat": "44.128956", "lng": "9.701041", "elevation": "256.5", "time": "2013-09-20T12:25:03.369Z"}, {"lat": "44.128952", "lng": "9.70104", "elevation": "256.1000061035156", "time": "2013-09-20T12:25:04.359Z"}, {"lat": "44.128947", "lng": "9.701038", "elevation": "256.0", "time": "2013-09-20T12:25:05.360Z"}, {"lat": "44.128921", "lng": "9.701043", "elevation": "256.5", "time": "2013-09-20T12:25:11.363Z"}, {"lat": "44.128922", "lng": "9.701045", "elevation": "256.3999938964844", "time": "2013-09-20T12:25:12.357Z"}, {"lat": "44.128919", "lng": "9.701067", "elevation": "257.70001220703125", "time": "2013-09-20T12:25:46.355Z"}, {"lat": "44.128915", "lng": "9.701071", "elevation": "257.3999938964844", "time": "2013-09-20T12:25:47.359Z"}, {"lat": "44.128904", "lng": "9.701189", "elevation": "244.39999389648438", "time": "2013-09-20T12:26:03.357Z"}, {"lat": "44.128906", "lng": "9.701205", "elevation": "245.39999389648438", "time": "2013-09-20T12:26:04.361Z"}, {"lat": "44.128893", "lng": "9.701294", "elevation": "245.1999969482422", "time": "2013-09-20T12:26:13.437Z"}, {"lat": "44.128893", "lng": "9.701293", "elevation": "244.1999969482422", "time": "2013-09-20T12:26:14.473Z"}, {"lat": "44.128885", "lng": "9.701297", "elevation": "245.1999969482422", "time": "2013-09-20T12:26:26.363Z"}, {"lat": "44.128886", "lng": "9.701302", "elevation": "245.10000610351563", "time": "2013-09-20T12:26:27.362Z"}, {"lat": "44.128906", "lng": "9.701423", "elevation": "246.10000610351563", "time": "2013-09-20T12:26:42.371Z"}, {"lat": "44.128906", "lng": "9.701446", "elevation": "246.6999969482422", "time": "2013-09-20T12:26:43.362Z"}, {"lat": "44.128899", "lng": "9.701556", "elevation": "247.10000610351563", "time": "2013-09-20T12:26:48.363Z"}, {"lat": "44.128897", "lng": "9.701573", "elevation": "247.0", "time": "2013-09-20T12:26:49.367Z"}, {"lat": "44.128871", "lng": "9.701631", "elevation": "246.5", "time": "2013-09-20T12:26:59.367Z"}, {"lat": "44.128872", "lng": "9.701631", "elevation": "246.5", "time": "2013-09-20T12:27:00.366Z"}, {"lat": "44.128875", "lng": "9.701643", "elevation": "244.5", "time": "2013-09-20T12:28:08.370Z"}, {"lat": "44.128877", "lng": "9.70165", "elevation": "244.39999389648438", "time": "2013-09-20T12:28:09.380Z"}, {"lat": "44.12887", "lng": "9.701716", "elevation": "246.39999389648438", "time": "2013-09-20T12:28:23.361Z"}, {"lat": "44.12887", "lng": "9.701716", "elevation": "246.39999389648438", "time": "2013-09-20T12:28:23.393Z"}, {"lat": "44.128873", "lng": "9.701713", "elevation": "246.10000610351563", "time": "2013-09-20T12:28:52.380Z"}, {"lat": "44.128876", "lng": "9.70172", "elevation": "245.5", "time": "2013-09-20T12:28:53.441Z"}, {"lat": "44.128935", "lng": "9.701809", "elevation": "245.5", "time": "2013-09-20T12:28:59.379Z"}, {"lat": "44.128945", "lng": "9.701817", "elevation": "245.1999969482422", "time": "2013-09-20T12:29:00.380Z"}, {"lat": "44.129022", "lng": "9.701861", "elevation": "239.5", "time": "2013-09-20T12:29:09.381Z"}, {"lat": "44.129029", "lng": "9.701876", "elevation": "239.6999969482422", "time": "2013-09-20T12:29:10.387Z"}, {"lat": "44.129091", "lng": "9.701959", "elevation": "237.6999969482422", "time": "2013-09-20T12:29:19.455Z"}, {"lat": "44.129097", "lng": "9.701968", "elevation": "238.1999969482422", "time": "2013-09-20T12:29:20.479Z"}, {"lat": "44.129162", "lng": "9.702031", "elevation": "237.6999969482422", "time": "2013-09-20T12:29:29.432Z"}, {"lat": "44.129172", "lng": "9.702037", "elevation": "238.3000030517578", "time": "2013-09-20T12:29:30.416Z"}, {"lat": "44.129235", "lng": "9.702101", "elevation": "239.0", "time": "2013-09-20T12:29:36.433Z"}, {"lat": "44.129245", "lng": "9.702115", "elevation": "239.10000610351563", "time": "2013-09-20T12:29:37.451Z"}, {"lat": "44.129271", "lng": "9.702144", "elevation": "237.0", "time": "2013-09-20T12:29:43.470Z"}, {"lat": "44.129271", "lng": "9.70214", "elevation": "236.6999969482422", "time": "2013-09-20T12:29:44.482Z"}, {"lat": "44.129273", "lng": "9.70215", "elevation": "238.3000030517578", "time": "2013-09-20T12:31:03.379Z"}, {"lat": "44.129277", "lng": "9.702157", "elevation": "238.60000610351563", "time": "2013-09-20T12:31:04.389Z"}, {"lat": "44.129349", "lng": "9.702223", "elevation": "241.5", "time": "2013-09-20T12:31:13.393Z"}, {"lat": "44.129357", "lng": "9.702229", "elevation": "242.0", "time": "2013-09-20T12:31:14.389Z"}, {"lat": "44.129421", "lng": "9.7023", "elevation": "239.39999389648438", "time": "2013-09-20T12:31:21.384Z"}, {"lat": "44.129429", "lng": "9.702318", "elevation": "239.3000030517578", "time": "2013-09-20T12:31:22.388Z"}, {"lat": "44.129476", "lng": "9.702409", "elevation": "239.60000610351563", "time": "2013-09-20T12:31:26.384Z"}, {"lat": "44.129483", "lng": "9.70242", "elevation": "239.60000610351563", "time": "2013-09-20T12:31:27.386Z"}, {"lat": "44.129499", "lng": "9.702465", "elevation": "240.1999969482422", "time": "2013-09-20T12:31:37.386Z"}, {"lat": "44.129499", "lng": "9.702466", "elevation": "240.1999969482422", "time": "2013-09-20T12:31:38.430Z"}, {"lat": "44.129499", "lng": "9.702472", "elevation": "240.10000610351563", "time": "2013-09-20T12:31:44.390Z"}, {"lat": "44.1295", "lng": "9.702479", "elevation": "239.89999389648438", "time": "2013-09-20T12:31:45.392Z"}, {"lat": "44.129526", "lng": "9.702587", "elevation": "241.39999389648438", "time": "2013-09-20T12:31:58.392Z"}, {"lat": "44.129521", "lng": "9.7026", "elevation": "242.39999389648438", "time": "2013-09-20T12:31:59.517Z"}, {"lat": "44.129451", "lng": "9.70267", "elevation": "240.3000030517578", "time": "2013-09-20T12:32:11.391Z"}, {"lat": "44.129454", "lng": "9.702671", "elevation": "240.3000030517578", "time": "2013-09-20T12:32:12.394Z"}, {"lat": "44.129455", "lng": "9.702676", "elevation": "240.1999969482422", "time": "2013-09-20T12:32:16.399Z"}, {"lat": "44.129451", "lng": "9.702682", "elevation": "240.6999969482422", "time": "2013-09-20T12:32:17.395Z"}, {"lat": "44.129404", "lng": "9.702783", "elevation": "236.1999969482422", "time": "2013-09-20T12:32:28.460Z"}, {"lat": "44.1294", "lng": "9.702801", "elevation": "236.0", "time": "2013-09-20T12:32:29.594Z"}, {"lat": "44.129382", "lng": "9.702908", "elevation": "232.8000030517578", "time": "2013-09-20T12:32:40.557Z"}, {"lat": "44.129379", "lng": "9.702926", "elevation": "232.5", "time": "2013-09-20T12:32:41.438Z"}, {"lat": "44.129306", "lng": "9.703", "elevation": "236.0", "time": "2013-09-20T12:32:50.393Z"}, {"lat": "44.129296", "lng": "9.703005", "elevation": "235.89999389648438", "time": "2013-09-20T12:32:51.415Z"}, {"lat": "44.129223", "lng": "9.703072", "elevation": "233.3000030517578", "time": "2013-09-20T12:33:03.393Z"}, {"lat": "44.12922", "lng": "9.703076", "elevation": "233.3000030517578", "time": "2013-09-20T12:33:04.400Z"}, {"lat": "44.129218", "lng": "9.703082", "elevation": "232.8000030517578", "time": "2013-09-20T12:33:06.397Z"}, {"lat": "44.129217", "lng": "9.703084", "elevation": "232.6999969482422", "time": "2013-09-20T12:33:07.402Z"}, {"lat": "44.129212", "lng": "9.703099", "elevation": "232.39999389648438", "time": "2013-09-20T12:33:16.395Z"}, {"lat": "44.129208", "lng": "9.703101", "elevation": "232.1999969482422", "time": "2013-09-20T12:33:17.395Z"}, {"lat": "44.129155", "lng": "9.703195", "elevation": "229.8000030517578", "time": "2013-09-20T12:33:33.559Z"}, {"lat": "44.12915", "lng": "9.703203", "elevation": "229.3000030517578", "time": "2013-09-20T12:33:34.403Z"}, {"lat": "44.129095", "lng": "9.703298", "elevation": "224.39999389648438", "time": "2013-09-20T12:33:51.438Z"}, {"lat": "44.129092", "lng": "9.703302", "elevation": "224.5", "time": "2013-09-20T12:33:52.443Z"}, {"lat": "44.129028", "lng": "9.703389", "elevation": "223.60000610351563", "time": "2013-09-20T12:34:06.408Z"}, {"lat": "44.129018", "lng": "9.703398", "elevation": "223.6999969482422", "time": "2013-09-20T12:34:07.557Z"}, {"lat": "44.128966", "lng": "9.703497", "elevation": "219.0", "time": "2013-09-20T12:34:16.567Z"}, {"lat": "44.128962", "lng": "9.703506", "elevation": "218.39999389648438", "time": "2013-09-20T12:34:17.597Z"}, {"lat": "44.128888", "lng": "9.703562", "elevation": "214.39999389648438", "time": "2013-09-20T12:34:30.522Z"}, {"lat": "44.128883", "lng": "9.703575", "elevation": "214.10000610351563", "time": "2013-09-20T12:34:31.407Z"}, {"lat": "44.12883", "lng": "9.703668", "elevation": "213.10000610351563", "time": "2013-09-20T12:34:45.391Z"}, {"lat": "44.128823", "lng": "9.703683", "elevation": "213.0", "time": "2013-09-20T12:34:46.401Z"}, {"lat": "44.12877", "lng": "9.703782", "elevation": "212.60000610351563", "time": "2013-09-20T12:34:53.404Z"}, {"lat": "44.128767", "lng": "9.703785", "elevation": "212.5", "time": "2013-09-20T12:34:54.406Z"}, {"lat": "44.128728", "lng": "9.703793", "elevation": "212.3000030517578", "time": "2013-09-20T12:35:06.404Z"}, {"lat": "44.128728", "lng": "9.703795", "elevation": "212.3000030517578", "time": "2013-09-20T12:35:07.401Z"}, {"lat": "44.128729", "lng": "9.703799", "elevation": "212.1999969482422", "time": "2013-09-20T12:35:09.405Z"}, {"lat": "44.128728", "lng": "9.703805", "elevation": "211.10000610351563", "time": "2013-09-20T12:35:10.419Z"}, {"lat": "44.128703", "lng": "9.703918", "elevation": "208.3000030517578", "time": "2013-09-20T12:35:16.403Z"}, {"lat": "44.128695", "lng": "9.703941", "elevation": "209.60000610351563", "time": "2013-09-20T12:35:17.401Z"}, {"lat": "44.128657", "lng": "9.704026", "elevation": "209.0", "time": "2013-09-20T12:35:21.408Z"}, {"lat": "44.12865", "lng": "9.704052", "elevation": "208.1999969482422", "time": "2013-09-20T12:35:22.403Z"}, {"lat": "44.128642", "lng": "9.704164", "elevation": "204.89999389648438", "time": "2013-09-20T12:35:26.403Z"}, {"lat": "44.128642", "lng": "9.704191", "elevation": "203.8000030517578", "time": "2013-09-20T12:35:27.403Z"}, {"lat": "44.128601", "lng": "9.704301", "elevation": "204.60000610351563", "time": "2013-09-20T12:35:34.404Z"}, {"lat": "44.128592", "lng": "9.704309", "elevation": "204.60000610351563", "time": "2013-09-20T12:35:35.403Z"}, {"lat": "44.128533", "lng": "9.70439", "elevation": "202.10000610351563", "time": "2013-09-20T12:35:44.407Z"}, {"lat": "44.128522", "lng": "9.704393", "elevation": "202.3000030517578", "time": "2013-09-20T12:35:45.405Z"}, {"lat": "44.128457", "lng": "9.704467", "elevation": "202.1999969482422", "time": "2013-09-20T12:35:53.407Z"}, {"lat": "44.128454", "lng": "9.704479", "elevation": "202.10000610351563", "time": "2013-09-20T12:35:54.411Z"}, {"lat": "44.128382", "lng": "9.704547", "elevation": "199.8000030517578", "time": "2013-09-20T12:36:08.418Z"}, {"lat": "44.128374", "lng": "9.704549", "elevation": "199.0", "time": "2013-09-20T12:36:09.422Z"}, {"lat": "44.128295", "lng": "9.704591", "elevation": "203.3000030517578", "time": "2013-09-20T12:36:21.456Z"}, {"lat": "44.128287", "lng": "9.704589", "elevation": "203.10000610351563", "time": "2013-09-20T12:36:22.404Z"}, {"lat": "44.128277", "lng": "9.704572", "elevation": "204.5", "time": "2013-09-20T12:36:27.458Z"}, {"lat": "44.12828", "lng": "9.704571", "elevation": "204.10000610351563", "time": "2013-09-20T12:36:28.415Z"}, {"lat": "44.128278", "lng": "9.704576", "elevation": "203.8000030517578", "time": "2013-09-20T12:36:48.450Z"}, {"lat": "44.128273", "lng": "9.704577", "elevation": "203.6999969482422", "time": "2013-09-20T12:36:49.413Z"}, {"lat": "44.128198", "lng": "9.704595", "elevation": "201.39999389648438", "time": "2013-09-20T12:36:53.502Z"}, {"lat": "44.128176", "lng": "9.704605", "elevation": "201.0", "time": "2013-09-20T12:36:54.428Z"}, {"lat": "44.128098", "lng": "9.704642", "elevation": "201.10000610351563", "time": "2013-09-20T12:36:58.439Z"}, {"lat": "44.128086", "lng": "9.704649", "elevation": "200.60000610351563", "time": "2013-09-20T12:36:59.433Z"}, {"lat": "44.128024", "lng": "9.704725", "elevation": "201.0", "time": "2013-09-20T12:37:07.421Z"}, {"lat": "44.128015", "lng": "9.704735", "elevation": "200.3000030517578", "time": "2013-09-20T12:37:08.410Z"}, {"lat": "44.127939", "lng": "9.704802", "elevation": "201.5", "time": "2013-09-20T12:37:16.415Z"}, {"lat": "44.127932", "lng": "9.704812", "elevation": "200.39999389648438", "time": "2013-09-20T12:37:17.413Z"}, {"lat": "44.127851", "lng": "9.704862", "elevation": "199.60000610351563", "time": "2013-09-20T12:37:25.411Z"}, {"lat": "44.127842", "lng": "9.704868", "elevation": "198.0", "time": "2013-09-20T12:37:26.412Z"}, {"lat": "44.127764", "lng": "9.704929", "elevation": "196.89999389648438", "time": "2013-09-20T12:37:35.437Z"}, {"lat": "44.127755", "lng": "9.704934", "elevation": "196.60000610351563", "time": "2013-09-20T12:37:36.412Z"}, {"lat": "44.127692", "lng": "9.70502", "elevation": "195.60000610351563", "time": "2013-09-20T12:37:50.413Z"}, {"lat": "44.127682", "lng": "9.705018", "elevation": "196.0", "time": "2013-09-20T12:37:51.418Z"}, {"lat": "44.12761", "lng": "9.705085", "elevation": "197.8000030517578", "time": "2013-09-20T12:38:00.457Z"}, {"lat": "44.127601", "lng": "9.705095", "elevation": "198.10000610351563", "time": "2013-09-20T12:38:01.416Z"}, {"lat": "44.127524", "lng": "9.70515", "elevation": "201.1999969482422", "time": "2013-09-20T12:38:08.421Z"}, {"lat": "44.127507", "lng": "9.705155", "elevation": "201.60000610351563", "time": "2013-09-20T12:38:09.423Z"}, {"lat": "44.127428", "lng": "9.705203", "elevation": "199.8000030517578", "time": "2013-09-20T12:38:16.418Z"}, {"lat": "44.127425", "lng": "9.705221", "elevation": "200.5", "time": "2013-09-20T12:38:17.427Z"}, {"lat": "44.127347", "lng": "9.705267", "elevation": "200.10000610351563", "time": "2013-09-20T12:38:26.413Z"}, {"lat": "44.127337", "lng": "9.705264", "elevation": "199.8000030517578", "time": "2013-09-20T12:38:27.419Z"}, {"lat": "44.127269", "lng": "9.705196", "elevation": "199.0", "time": "2013-09-20T12:38:35.418Z"}, {"lat": "44.12726", "lng": "9.705196", "elevation": "198.8000030517578", "time": "2013-09-20T12:38:36.417Z"}, {"lat": "44.127177", "lng": "9.705226", "elevation": "198.6999969482422", "time": "2013-09-20T12:38:44.433Z"}, {"lat": "44.127162", "lng": "9.70522", "elevation": "199.1999969482422", "time": "2013-09-20T12:38:45.429Z"}, {"lat": "44.127075", "lng": "9.705187", "elevation": "201.60000610351563", "time": "2013-09-20T12:38:54.421Z"}, {"lat": "44.127073", "lng": "9.705186", "elevation": "200.6999969482422", "time": "2013-09-20T12:38:55.425Z"}, {"lat": "44.127074", "lng": "9.705191", "elevation": "202.39999389648438", "time": "2013-09-20T12:38:57.451Z"}, {"lat": "44.127075", "lng": "9.705194", "elevation": "202.6999969482422", "time": "2013-09-20T12:38:58.448Z"}, {"lat": "44.127073", "lng": "9.705195", "elevation": "202.6999969482422", "time": "2013-09-20T12:38:59.424Z"}, {"lat": "44.127069", "lng": "9.705196", "elevation": "202.39999389648438", "time": "2013-09-20T12:39:00.447Z"}, {"lat": "44.126983", "lng": "9.705228", "elevation": "200.39999389648438", "time": "2013-09-20T12:39:07.436Z"}, {"lat": "44.126973", "lng": "9.705236", "elevation": "199.89999389648438", "time": "2013-09-20T12:39:08.452Z"}, {"lat": "44.126891", "lng": "9.705271", "elevation": "200.8000030517578", "time": "2013-09-20T12:39:16.420Z"}, {"lat": "44.12688", "lng": "9.705274", "elevation": "201.39999389648438", "time": "2013-09-20T12:39:17.426Z"}, {"lat": "44.126794", "lng": "9.705289", "elevation": "201.8000030517578", "time": "2013-09-20T12:39:25.422Z"}, {"lat": "44.126784", "lng": "9.705294", "elevation": "203.5", "time": "2013-09-20T12:39:26.435Z"}, {"lat": "44.126701", "lng": "9.705321", "elevation": "204.1999969482422", "time": "2013-09-20T12:39:34.434Z"}, {"lat": "44.126692", "lng": "9.705322", "elevation": "203.3000030517578", "time": "2013-09-20T12:39:35.415Z"}, {"lat": "44.126611", "lng": "9.705346", "elevation": "204.39999389648438", "time": "2013-09-20T12:39:47.423Z"}, {"lat": "44.1266", "lng": "9.705351", "elevation": "203.8000030517578", "time": "2013-09-20T12:39:48.421Z"}, {"lat": "44.12651", "lng": "9.705339", "elevation": "204.5", "time": "2013-09-20T12:39:56.426Z"}, {"lat": "44.126498", "lng": "9.705339", "elevation": "204.89999389648438", "time": "2013-09-20T12:39:57.423Z"}, {"lat": "44.126414", "lng": "9.705331", "elevation": "203.60000610351563", "time": "2013-09-20T12:40:06.428Z"}, {"lat": "44.126406", "lng": "9.705328", "elevation": "204.5", "time": "2013-09-20T12:40:07.434Z"}, {"lat": "44.126321", "lng": "9.705322", "elevation": "205.1999969482422", "time": "2013-09-20T12:40:16.431Z"}, {"lat": "44.126312", "lng": "9.705322", "elevation": "205.39999389648438", "time": "2013-09-20T12:40:17.426Z"}, {"lat": "44.126223", "lng": "9.705316", "elevation": "205.5", "time": "2013-09-20T12:40:28.427Z"}, {"lat": "44.126218", "lng": "9.705313", "elevation": "205.3000030517578", "time": "2013-09-20T12:40:29.427Z"}, {"lat": "44.126131", "lng": "9.705326", "elevation": "205.39999389648438", "time": "2013-09-20T12:40:40.431Z"}, {"lat": "44.126122", "lng": "9.705322", "elevation": "205.1999969482422", "time": "2013-09-20T12:40:41.428Z"}, {"lat": "44.126033", "lng": "9.705315", "elevation": "207.89999389648438", "time": "2013-09-20T12:40:53.425Z"}, {"lat": "44.126028", "lng": "9.705317", "elevation": "209.0", "time": "2013-09-20T12:40:54.425Z"}, {"lat": "44.125964", "lng": "9.705384", "elevation": "205.60000610351563", "time": "2013-09-20T12:41:05.431Z"}, {"lat": "44.125953", "lng": "9.70539", "elevation": "205.39999389648438", "time": "2013-09-20T12:41:06.430Z"}, {"lat": "44.125871", "lng": "9.705406", "elevation": "206.8000030517578", "time": "2013-09-20T12:41:13.431Z"}, {"lat": "44.125863", "lng": "9.705412", "elevation": "207.60000610351563", "time": "2013-09-20T12:41:14.431Z"}, {"lat": "44.12584", "lng": "9.705447", "elevation": "209.60000610351563", "time": "2013-09-20T12:41:22.458Z"}, {"lat": "44.125839", "lng": "9.705442", "elevation": "209.6999969482422", "time": "2013-09-20T12:41:23.479Z"}, {"lat": "44.125834", "lng": "9.705434", "elevation": "209.6999969482422", "time": "2013-09-20T12:41:24.467Z"}, {"lat": "44.125754", "lng": "9.705474", "elevation": "210.0", "time": "2013-09-20T12:41:41.436Z"}, {"lat": "44.125746", "lng": "9.705478", "elevation": "209.60000610351563", "time": "2013-09-20T12:41:42.427Z"}, {"lat": "44.125667", "lng": "9.705535", "elevation": "210.89999389648438", "time": "2013-09-20T12:41:56.435Z"}, {"lat": "44.125661", "lng": "9.70553", "elevation": "211.10000610351563", "time": "2013-09-20T12:41:57.430Z"}, {"lat": "44.125642", "lng": "9.705512", "elevation": "211.1999969482422", "time": "2013-09-20T12:42:03.763Z"}, {"lat": "44.125642", "lng": "9.705512", "elevation": "211.10000610351563", "time": "2013-09-20T12:42:04.433Z"}, {"lat": "44.125641", "lng": "9.705517", "elevation": "210.10000610351563", "time": "2013-09-20T12:42:20.442Z"}, {"lat": "44.125637", "lng": "9.705523", "elevation": "209.3000030517578", "time": "2013-09-20T12:42:21.443Z"}, {"lat": "44.125573", "lng": "9.705609", "elevation": "208.6999969482422", "time": "2013-09-20T12:42:34.481Z"}, {"lat": "44.125564", "lng": "9.705608", "elevation": "208.5", "time": "2013-09-20T12:42:35.461Z"}, {"lat": "44.125502", "lng": "9.70569", "elevation": "204.10000610351563", "time": "2013-09-20T12:42:59.440Z"}, {"lat": "44.125494", "lng": "9.70569", "elevation": "205.3000030517578", "time": "2013-09-20T12:43:00.442Z"}, {"lat": "44.12542", "lng": "9.705738", "elevation": "203.89999389648438", "time": "2013-09-20T12:43:08.576Z"}, {"lat": "44.125415", "lng": "9.705751", "elevation": "204.60000610351563", "time": "2013-09-20T12:43:09.438Z"}, {"lat": "44.125409", "lng": "9.705799", "elevation": "203.1999969482422", "time": "2013-09-20T12:43:17.451Z"}, {"lat": "44.125409", "lng": "9.7058", "elevation": "203.1999969482422", "time": "2013-09-20T12:43:18.450Z"}, {"lat": "44.125401", "lng": "9.705805", "elevation": "202.6999969482422", "time": "2013-09-20T12:43:27.453Z"}, {"lat": "44.125398", "lng": "9.705807", "elevation": "203.0", "time": "2013-09-20T12:43:28.442Z"}, {"lat": "44.125308", "lng": "9.705818", "elevation": "200.39999389648438", "time": "2013-09-20T12:43:41.442Z"}, {"lat": "44.125303", "lng": "9.705818", "elevation": "200.39999389648438", "time": "2013-09-20T12:43:42.451Z"}, {"lat": "44.125296", "lng": "9.705823", "elevation": "201.1999969482422", "time": "2013-09-20T12:43:46.451Z"}, {"lat": "44.125297", "lng": "9.705825", "elevation": "200.89999389648438", "time": "2013-09-20T12:43:47.444Z"}, {"lat": "44.125301", "lng": "9.705845", "elevation": "200.8000030517578", "time": "2013-09-20T12:44:50.504Z"}, {"lat": "44.125303", "lng": "9.705852", "elevation": "200.6999969482422", "time": "2013-09-20T12:44:51.550Z"}, {"lat": "44.125306", "lng": "9.705857", "elevation": "200.1999969482422", "time": "2013-09-20T12:44:53.569Z"}, {"lat": "44.125306", "lng": "9.705857", "elevation": "200.1999969482422", "time": "2013-09-20T12:44:54.512Z"}, {"lat": "44.125297", "lng": "9.705855", "elevation": "200.10000610351563", "time": "2013-09-20T12:45:04.464Z"}, {"lat": "44.125293", "lng": "9.705857", "elevation": "200.60000610351563", "time": "2013-09-20T12:45:05.456Z"}, {"lat": "44.125211", "lng": "9.705871", "elevation": "200.5", "time": "2013-09-20T12:45:16.543Z"}, {"lat": "44.125203", "lng": "9.705875", "elevation": "200.1999969482422", "time": "2013-09-20T12:45:17.520Z"}, {"lat": "44.125137", "lng": "9.705898", "elevation": "198.8000030517578", "time": "2013-09-20T12:45:35.458Z"}, {"lat": "44.125138", "lng": "9.705901", "elevation": "198.89999389648438", "time": "2013-09-20T12:45:36.451Z"}, {"lat": "44.125132", "lng": "9.7059", "elevation": "198.8000030517578", "time": "2013-09-20T12:45:41.522Z"}, {"lat": "44.125126", "lng": "9.705904", "elevation": "198.39999389648438", "time": "2013-09-20T12:45:42.556Z"}, {"lat": "44.125051", "lng": "9.705964", "elevation": "196.3000030517578", "time": "2013-09-20T12:45:52.460Z"}, {"lat": "44.125046", "lng": "9.705967", "elevation": "195.89999389648438", "time": "2013-09-20T12:45:53.472Z"}, {"lat": "44.124957", "lng": "9.705985", "elevation": "193.5", "time": "2013-09-20T12:46:08.499Z"}, {"lat": "44.12495", "lng": "9.70599", "elevation": "193.0", "time": "2013-09-20T12:46:09.460Z"}, {"lat": "44.124887", "lng": "9.706078", "elevation": "191.10000610351563", "time": "2013-09-20T12:46:30.474Z"}, {"lat": "44.124885", "lng": "9.706087", "elevation": "190.8000030517578", "time": "2013-09-20T12:46:31.459Z"}, {"lat": "44.124875", "lng": "9.706202", "elevation": "187.1999969482422", "time": "2013-09-20T12:46:40.467Z"}, {"lat": "44.124873", "lng": "9.706214", "elevation": "186.3000030517578", "time": "2013-09-20T12:46:41.467Z"}, {"lat": "44.124862", "lng": "9.706334", "elevation": "182.1999969482422", "time": "2013-09-20T12:46:55.468Z"}, {"lat": "44.124861", "lng": "9.70634", "elevation": "181.8000030517578", "time": "2013-09-20T12:46:56.457Z"}, {"lat": "44.124781", "lng": "9.706388", "elevation": "180.60000610351563", "time": "2013-09-20T12:47:09.469Z"}, {"lat": "44.124774", "lng": "9.70639", "elevation": "180.5", "time": "2013-09-20T12:47:10.475Z"}, {"lat": "44.124711", "lng": "9.706452", "elevation": "181.0", "time": "2013-09-20T12:47:34.564Z"}, {"lat": "44.12471", "lng": "9.706451", "elevation": "181.39999389648438", "time": "2013-09-20T12:47:35.605Z"}, {"lat": "44.124705", "lng": "9.706454", "elevation": "181.3000030517578", "time": "2013-09-20T12:47:38.571Z"}, {"lat": "44.124702", "lng": "9.706456", "elevation": "181.1999969482422", "time": "2013-09-20T12:47:39.486Z"}, {"lat": "44.124628", "lng": "9.706508", "elevation": "178.3000030517578", "time": "2013-09-20T12:47:51.589Z"}, {"lat": "44.12462", "lng": "9.706511", "elevation": "178.3000030517578", "time": "2013-09-20T12:47:52.581Z"}, {"lat": "44.124535", "lng": "9.706541", "elevation": "178.0", "time": "2013-09-20T12:48:02.461Z"}, {"lat": "44.124525", "lng": "9.706549", "elevation": "178.10000610351563", "time": "2013-09-20T12:48:03.461Z"}, {"lat": "44.124455", "lng": "9.706625", "elevation": "176.6999969482422", "time": "2013-09-20T12:48:18.527Z"}, {"lat": "44.124454", "lng": "9.706628", "elevation": "176.1999969482422", "time": "2013-09-20T12:48:19.495Z"}, {"lat": "44.124452", "lng": "9.706645", "elevation": "174.10000610351563", "time": "2013-09-20T12:48:25.559Z"}, {"lat": "44.124451", "lng": "9.706647", "elevation": "174.0", "time": "2013-09-20T12:48:26.517Z"}, {"lat": "44.124445", "lng": "9.706657", "elevation": "172.89999389648438", "time": "2013-09-20T12:48:31.569Z"}, {"lat": "44.124444", "lng": "9.706659", "elevation": "173.0", "time": "2013-09-20T12:48:32.560Z"}, {"lat": "44.124443", "lng": "9.706661", "elevation": "172.6999969482422", "time": "2013-09-20T12:48:33.561Z"}, {"lat": "44.12444", "lng": "9.706667", "elevation": "171.8000030517578", "time": "2013-09-20T12:48:36.475Z"}, {"lat": "44.124437", "lng": "9.70667", "elevation": "171.60000610351563", "time": "2013-09-20T12:48:37.470Z"}, {"lat": "44.124409", "lng": "9.706687", "elevation": "170.5", "time": "2013-09-20T12:48:46.479Z"}, {"lat": "44.124408", "lng": "9.706689", "elevation": "169.8000030517578", "time": "2013-09-20T12:48:47.472Z"}, {"lat": "44.124407", "lng": "9.706693", "elevation": "169.60000610351563", "time": "2013-09-20T12:48:48.479Z"}, {"lat": "44.124402", "lng": "9.706717", "elevation": "169.6999969482422", "time": "2013-09-20T12:48:54.477Z"}, {"lat": "44.124401", "lng": "9.706718", "elevation": "169.6999969482422", "time": "2013-09-20T12:48:55.476Z"}, {"lat": "44.124399", "lng": "9.706725", "elevation": "169.60000610351563", "time": "2013-09-20T12:49:12.474Z"}, {"lat": "44.124399", "lng": "9.706731", "elevation": "169.60000610351563", "time": "2013-09-20T12:49:13.473Z"}, {"lat": "44.124433", "lng": "9.706832", "elevation": "162.3000030517578", "time": "2013-09-20T12:49:22.492Z"}, {"lat": "44.124436", "lng": "9.706845", "elevation": "161.8000030517578", "time": "2013-09-20T12:49:23.488Z"}, {"lat": "44.124378", "lng": "9.70694", "elevation": "160.6999969482422", "time": "2013-09-20T12:49:30.523Z"}, {"lat": "44.124371", "lng": "9.706949", "elevation": "160.6999969482422", "time": "2013-09-20T12:49:31.483Z"}, {"lat": "44.124356", "lng": "9.706976", "elevation": "160.8000030517578", "time": "2013-09-20T12:49:39.501Z"}, {"lat": "44.124356", "lng": "9.706977", "elevation": "160.89999389648438", "time": "2013-09-20T12:49:40.486Z"}, {"lat": "44.124355", "lng": "9.706983", "elevation": "159.60000610351563", "time": "2013-09-20T12:49:55.493Z"}, {"lat": "44.124357", "lng": "9.70699", "elevation": "159.1999969482422", "time": "2013-09-20T12:49:56.485Z"}, {"lat": "44.124397", "lng": "9.707086", "elevation": "159.1999969482422", "time": "2013-09-20T12:50:04.485Z"}, {"lat": "44.124403", "lng": "9.707097", "elevation": "159.60000610351563", "time": "2013-09-20T12:50:05.512Z"}, {"lat": "44.12445", "lng": "9.707187", "elevation": "157.5", "time": "2013-09-20T12:50:17.483Z"}, {"lat": "44.124449", "lng": "9.707187", "elevation": "157.8000030517578", "time": "2013-09-20T12:50:18.482Z"}, {"lat": "44.124453", "lng": "9.707198", "elevation": "158.10000610351563", "time": "2013-09-20T12:50:40.536Z"}, {"lat": "44.124457", "lng": "9.707203", "elevation": "156.89999389648438", "time": "2013-09-20T12:50:41.491Z"}, {"lat": "44.124518", "lng": "9.707285", "elevation": "158.10000610351563", "time": "2013-09-20T12:50:52.656Z"}, {"lat": "44.12452", "lng": "9.707295", "elevation": "158.60000610351563", "time": "2013-09-20T12:50:53.649Z"}, {"lat": "44.124515", "lng": "9.707414", "elevation": "158.0", "time": "2013-09-20T12:51:04.490Z"}, {"lat": "44.124513", "lng": "9.707434", "elevation": "157.39999389648438", "time": "2013-09-20T12:51:05.683Z"}, {"lat": "44.12446", "lng": "9.707516", "elevation": "159.60000610351563", "time": "2013-09-20T12:51:12.562Z"}, {"lat": "44.124451", "lng": "9.707528", "elevation": "161.10000610351563", "time": "2013-09-20T12:51:13.518Z"}, {"lat": "44.1244", "lng": "9.707616", "elevation": "162.1999969482422", "time": "2013-09-20T12:51:20.684Z"}, {"lat": "44.124394", "lng": "9.70763", "elevation": "162.0", "time": "2013-09-20T12:51:21.661Z"}, {"lat": "44.124326", "lng": "9.707697", "elevation": "159.5", "time": "2013-09-20T12:51:29.516Z"}, {"lat": "44.124322", "lng": "9.707712", "elevation": "158.39999389648438", "time": "2013-09-20T12:51:30.487Z"}, {"lat": "44.124296", "lng": "9.707822", "elevation": "157.5", "time": "2013-09-20T12:51:39.488Z"}, {"lat": "44.124289", "lng": "9.707829", "elevation": "158.10000610351563", "time": "2013-09-20T12:51:40.485Z"}, {"lat": "44.124213", "lng": "9.707878", "elevation": "160.10000610351563", "time": "2013-09-20T12:51:51.489Z"}, {"lat": "44.124205", "lng": "9.707883", "elevation": "159.89999389648438", "time": "2013-09-20T12:51:52.487Z"}, {"lat": "44.124127", "lng": "9.707928", "elevation": "159.8000030517578", "time": "2013-09-20T12:52:01.486Z"}, {"lat": "44.124119", "lng": "9.707933", "elevation": "160.39999389648438", "time": "2013-09-20T12:52:02.496Z"}, {"lat": "44.12404", "lng": "9.707982", "elevation": "160.3000030517578", "time": "2013-09-20T12:52:12.487Z"}, {"lat": "44.12403", "lng": "9.707983", "elevation": "160.60000610351563", "time": "2013-09-20T12:52:13.491Z"}, {"lat": "44.123946", "lng": "9.708006", "elevation": "160.5", "time": "2013-09-20T12:52:22.489Z"}, {"lat": "44.123936", "lng": "9.70801", "elevation": "160.6999969482422", "time": "2013-09-20T12:52:23.489Z"}, {"lat": "44.123856", "lng": "9.70806", "elevation": "159.0", "time": "2013-09-20T12:52:31.678Z"}, {"lat": "44.12385", "lng": "9.708069", "elevation": "158.89999389648438", "time": "2013-09-20T12:52:32.600Z"}, {"lat": "44.123791", "lng": "9.70815", "elevation": "156.0", "time": "2013-09-20T12:52:40.651Z"}, {"lat": "44.123784", "lng": "9.70816", "elevation": "156.8000030517578", "time": "2013-09-20T12:52:41.668Z"}, {"lat": "44.123718", "lng": "9.708225", "elevation": "155.5", "time": "2013-09-20T12:52:49.611Z"}, {"lat": "44.123711", "lng": "9.708235", "elevation": "154.8000030517578", "time": "2013-09-20T12:52:50.673Z"}, {"lat": "44.123644", "lng": "9.708297", "elevation": "154.60000610351563", "time": "2013-09-20T12:52:58.579Z"}, {"lat": "44.123633", "lng": "9.708301", "elevation": "154.5", "time": "2013-09-20T12:52:59.698Z"}, {"lat": "44.123547", "lng": "9.708334", "elevation": "151.1999969482422", "time": "2013-09-20T12:53:08.497Z"}, {"lat": "44.123539", "lng": "9.70834", "elevation": "150.60000610351563", "time": "2013-09-20T12:53:09.504Z"}, {"lat": "44.123486", "lng": "9.708428", "elevation": "147.60000610351563", "time": "2013-09-20T12:53:17.492Z"}, {"lat": "44.123481", "lng": "9.708442", "elevation": "147.39999389648438", "time": "2013-09-20T12:53:18.495Z"}, {"lat": "44.123423", "lng": "9.708533", "elevation": "148.8000030517578", "time": "2013-09-20T12:53:28.646Z"}, {"lat": "44.12342", "lng": "9.708543", "elevation": "148.89999389648438", "time": "2013-09-20T12:53:29.629Z"}, {"lat": "44.123342", "lng": "9.708599", "elevation": "147.1999969482422", "time": "2013-09-20T12:53:39.554Z"}, {"lat": "44.123333", "lng": "9.708604", "elevation": "145.5", "time": "2013-09-20T12:53:40.655Z"}, {"lat": "44.123279", "lng": "9.708686", "elevation": "144.39999389648438", "time": "2013-09-20T12:53:48.492Z"}, {"lat": "44.123273", "lng": "9.708699", "elevation": "143.6999969482422", "time": "2013-09-20T12:53:49.500Z"}, {"lat": "44.123238", "lng": "9.708806", "elevation": "143.1999969482422", "time": "2013-09-20T12:53:57.495Z"}, {"lat": "44.123227", "lng": "9.708813", "elevation": "144.10000610351563", "time": "2013-09-20T12:53:58.668Z"}, {"lat": "44.123139", "lng": "9.708827", "elevation": "143.8000030517578", "time": "2013-09-20T12:54:07.671Z"}, {"lat": "44.123132", "lng": "9.708825", "elevation": "143.89999389648438", "time": "2013-09-20T12:54:08.594Z"}, {"lat": "44.12305", "lng": "9.708873", "elevation": "145.5", "time": "2013-09-20T12:54:19.653Z"}, {"lat": "44.123044", "lng": "9.708883", "elevation": "146.89999389648438", "time": "2013-09-20T12:54:20.530Z"}, {"lat": "44.123033", "lng": "9.709003", "elevation": "142.1999969482422", "time": "2013-09-20T12:54:27.547Z"}, {"lat": "44.123037", "lng": "9.709016", "elevation": "142.0", "time": "2013-09-20T12:54:28.661Z"}, {"lat": "44.123044", "lng": "9.709024", "elevation": "141.6999969482422", "time": "2013-09-20T12:54:33.566Z"}, {"lat": "44.123044", "lng": "9.709024", "elevation": "141.39999389648438", "time": "2013-09-20T12:54:34.573Z"}, {"lat": "44.123045", "lng": "9.709027", "elevation": "140.8000030517578", "time": "2013-09-20T12:54:37.499Z"}, {"lat": "44.123046", "lng": "9.709033", "elevation": "140.0", "time": "2013-09-20T12:54:38.502Z"}, {"lat": "44.123049", "lng": "9.709143", "elevation": "141.89999389648438", "time": "2013-09-20T12:54:45.501Z"}, {"lat": "44.123042", "lng": "9.70916", "elevation": "143.6999969482422", "time": "2013-09-20T12:54:46.499Z"}, {"lat": "44.122993", "lng": "9.709248", "elevation": "144.10000610351563", "time": "2013-09-20T12:54:52.577Z"}, {"lat": "44.122987", "lng": "9.709261", "elevation": "144.39999389648438", "time": "2013-09-20T12:54:53.616Z"}, {"lat": "44.12295", "lng": "9.709362", "elevation": "143.60000610351563", "time": "2013-09-20T12:55:04.674Z"}, {"lat": "44.122939", "lng": "9.70937", "elevation": "144.1999969482422", "time": "2013-09-20T12:55:05.669Z"}, {"lat": "44.122904", "lng": "9.709478", "elevation": "141.1999969482422", "time": "2013-09-20T12:55:11.596Z"}, {"lat": "44.122901", "lng": "9.709496", "elevation": "140.8000030517578", "time": "2013-09-20T12:55:12.702Z"}, {"lat": "44.122878", "lng": "9.709545", "elevation": "136.0", "time": "2013-09-20T12:55:20.525Z"}, {"lat": "44.122881", "lng": "9.709537", "elevation": "135.8000030517578", "time": "2013-09-20T12:55:21.639Z"}, {"lat": "44.122882", "lng": "9.709527", "elevation": "136.3000030517578", "time": "2013-09-20T12:55:22.615Z"}, {"lat": "44.122811", "lng": "9.709459", "elevation": "139.8000030517578", "time": "2013-09-20T12:55:30.609Z"}, {"lat": "44.122801", "lng": "9.709451", "elevation": "139.1999969482422", "time": "2013-09-20T12:55:31.549Z"}, {"lat": "44.122742", "lng": "9.709389", "elevation": "138.60000610351563", "time": "2013-09-20T12:55:47.577Z"}, {"lat": "44.122741", "lng": "9.70939", "elevation": "138.6999969482422", "time": "2013-09-20T12:55:48.739Z"}, {"lat": "44.122741", "lng": "9.70938", "elevation": "138.89999389648438", "time": "2013-09-20T12:55:53.713Z"}, {"lat": "44.122739", "lng": "9.709376", "elevation": "137.6999969482422", "time": "2013-09-20T12:55:54.606Z"}, {"lat": "44.122705", "lng": "9.709331", "elevation": "139.0", "time": "2013-09-20T12:56:03.557Z"}, {"lat": "44.122706", "lng": "9.709335", "elevation": "138.89999389648438", "time": "2013-09-20T12:56:04.738Z"}] \ No newline at end of file diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/uk.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/uk.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/uk.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/vi.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/vi.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/vi.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_CN.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_CN.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_CN.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_TW.lproj/InfoPlist.strings b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_TW.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Resources/zh_TW.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Info.plist b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Info.plist new file mode 100644 index 0000000..47cc809 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.example.SDKDemos + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + Show your location on the map + UILaunchStoryboardName + Launch + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Prefix.pch b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Prefix.pch new file mode 100644 index 0000000..f5560a7 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemo-Prefix.pch @@ -0,0 +1,14 @@ +// +// Prefix header for all source files of the 'SDKDemo' target in the 'Google Maps SDK for iOS' project +// + +#import + +#ifndef __IPHONE_6_0 +#warning "This project uses features only available in iOS SDK 6.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAPIKey.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAPIKey.h new file mode 100644 index 0000000..142b44f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAPIKey.h @@ -0,0 +1,10 @@ +/** + * To use GoogleMapsSDKDemos, please register an APIKey for your application + * and set it here. Your APIKey should be kept private. + * + * See documentation on getting an API Key for your API Project here: + * https://developers.google.com/maps/documentation/ios/start#get-key + */ + +#error Register for API Key and insert here. Then delete this line. +static NSString *const kAPIKey = @""; diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.h new file mode 100644 index 0000000..326053c --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.h @@ -0,0 +1,17 @@ +#import + +@interface SDKDemoAppDelegate : UIResponder < + UIApplicationDelegate, + UISplitViewControllerDelegate> + +@property(strong, nonatomic) UIWindow *window; +@property(strong, nonatomic) UINavigationController *navigationController; +@property(strong, nonatomic) UISplitViewController *splitViewController; + +/** + * If the device is an iPad, this property controls the sample displayed in the + * right side of its split view controller. + */ +@property(strong, nonatomic) UIViewController *sample; + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.m new file mode 100644 index 0000000..7192043 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoAppDelegate.m @@ -0,0 +1,102 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/SDKDemoAppDelegate.h" + +#import "SDKDemos/SDKDemoAPIKey.h" +#import "SDKDemos/SDKDemoMasterViewController.h" +#import + +@implementation SDKDemoAppDelegate { + id services_; +} + +@synthesize window = _window; + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSLog(@"Build verison: %d", __apple_build_version__); + + if ([kAPIKey length] == 0) { + // Blow up if APIKey has not yet been set. + NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier]; + NSString *format = @"Configure APIKey inside SDKDemoAPIKey.h for your " + @"bundle `%@`, see README.GoogleMapsSDKDemos for more information"; + @throw [NSException exceptionWithName:@"SDKDemoAppDelegate" + reason:[NSString stringWithFormat:format, bundleId] + userInfo:nil]; + } + [GMSServices provideAPIKey:kAPIKey]; + services_ = [GMSServices sharedServices]; + + // Log the required open source licenses! Yes, just NSLog-ing them is not + // enough but is good for a demo. + NSLog(@"Open source licenses:\n%@", [GMSServices openSourceLicenseInfo]); + + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + SDKDemoMasterViewController *master = [[SDKDemoMasterViewController alloc] init]; + master.appDelegate = self; + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + // This is an iPhone; configure the top-level navigation controller as the + // rootViewController, which contains the 'master' list of samples. + self.navigationController = + [[UINavigationController alloc] initWithRootViewController:master]; + + // Force non-translucent navigation bar for consistency of demo between + // iOS 6 and iOS 7. + self.navigationController.navigationBar.translucent = NO; + + self.window.rootViewController = self.navigationController; + } else { + // This is an iPad; configure a split-view controller that contains the + // the 'master' list of samples on the left side, and the current displayed + // sample on the right (begins empty). + UINavigationController *masterNavigationController = + [[UINavigationController alloc] initWithRootViewController:master]; + + UIViewController *empty = [[UIViewController alloc] init]; + UINavigationController *detailNavigationController = + [[UINavigationController alloc] initWithRootViewController:empty]; + + // Force non-translucent navigation bar for consistency of demo between + // iOS 6 and iOS 7. + detailNavigationController.navigationBar.translucent = NO; + + self.splitViewController = [[UISplitViewController alloc] init]; + self.splitViewController.delegate = master; + self.splitViewController.viewControllers = + @[masterNavigationController, detailNavigationController]; + self.splitViewController.presentsWithGesture = NO; + + self.window.rootViewController = self.splitViewController; + } + + [self.window makeKeyAndVisible]; + return YES; +} + +- (void)setSample:(UIViewController *)sample { + NSAssert([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad, + @"Expected device to be iPad inside setSample:"); + + // Finds the UINavigationController in the right side of the sample, and + // replace its displayed controller with the new sample. + UINavigationController *nav = + [self.splitViewController.viewControllers objectAtIndex:1]; + [nav setViewControllers:[NSArray arrayWithObject:sample] animated:NO]; +} + +- (UIViewController *)sample { + NSAssert([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad, + @"Expected device to be iPad inside sample"); + + // The current sample is the top-most VC in the right-hand pane of the + // splitViewController. + UINavigationController *nav = + [self.splitViewController.viewControllers objectAtIndex:1]; + return [[nav viewControllers] objectAtIndex:0]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.h new file mode 100644 index 0000000..27dab51 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.h @@ -0,0 +1,12 @@ +#import + +@class SDKDemoAppDelegate; + +@interface SDKDemoMasterViewController : UITableViewController < + UISplitViewControllerDelegate, + UITableViewDataSource, + UITableViewDelegate> + +@property(nonatomic, assign) SDKDemoAppDelegate *appDelegate; + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.m new file mode 100644 index 0000000..11c696d --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/SDKDemoMasterViewController.m @@ -0,0 +1,167 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/SDKDemoMasterViewController.h" + +#import "SDKDemos/PlacesSamples/Samples+Places.h" +#import "SDKDemos/SDKDemoAppDelegate.h" +#import +#import "SDKDemos/Samples/Samples.h" + +@implementation SDKDemoMasterViewController { + NSArray *demos_; + NSArray *demoSections_; + BOOL isPhone_; + UIPopoverController *popover_; + UIBarButtonItem *samplesButton_; + __weak UIViewController *controller_; + CLLocationManager *locationManager_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + isPhone_ = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone; + + if (!isPhone_) { + self.clearsSelectionOnViewWillAppear = NO; + } else { + UIBarButtonItem *backButton = + [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Back", @"Back") + style:UIBarButtonItemStyleBordered + target:nil + action:nil]; + [self.navigationItem setBackBarButtonItem:backButton]; + } + + self.title = NSLocalizedString(@"Maps SDK Demos", @"Maps SDK Demos"); + self.title = [NSString stringWithFormat:@"%@: %@", self.title, [GMSServices SDKVersion]]; + + self.tableView.autoresizingMask = + UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth; + self.tableView.delegate = self; + self.tableView.dataSource = self; + + demoSections_ = [Samples loadSections]; + demos_ = [Samples loadDemos]; + [self addPlacesDemos]; + + if (!isPhone_) { + [self loadDemo:0 atIndex:0]; + } +} +- (void)addPlacesDemos { + NSMutableArray *sections = [NSMutableArray arrayWithArray:demoSections_]; + [sections insertObject:@"Places" atIndex:0]; + demoSections_ = [sections copy]; + + NSMutableArray *demos = [NSMutableArray arrayWithArray:demos_]; + [demos insertObject:[Samples placesDemos] + atIndex:0]; + demos_ = [demos copy]; +} + +#pragma mark - UITableViewController + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return demoSections_.count; +} + +- (CGFloat)tableView:(UITableView *)tableView + heightForHeaderInSection:(NSInteger)section { + return 35.0; +} + +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return [demoSections_ objectAtIndex:section]; +} + +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [[demos_ objectAtIndex: section] count]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellIdentifier = @"Cell"; + UITableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle + reuseIdentifier:cellIdentifier]; + + if (isPhone_) { + [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; + } + } + + NSDictionary *demo = [[demos_ objectAtIndex:indexPath.section] + objectAtIndex:indexPath.row]; + cell.textLabel.text = [demo objectForKey:@"title"]; + cell.detailTextLabel.text = [demo objectForKey:@"description"]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + // The user has chosen a sample; load it and clear the selection! + [self loadDemo:indexPath.section atIndex:indexPath.row]; + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + +#pragma mark - Split view + +- (void)splitViewController:(UISplitViewController *)splitController + willHideViewController:(UIViewController *)viewController + withBarButtonItem:(UIBarButtonItem *)barButtonItem + forPopoverController:(UIPopoverController *)popoverController { + popover_ = popoverController; + samplesButton_ = barButtonItem; + samplesButton_.title = NSLocalizedString(@"Samples", @"Samples"); + samplesButton_.style = UIBarButtonItemStyleDone; + [self updateSamplesButton]; +} + +- (void)splitViewController:(UISplitViewController *)splitController + willShowViewController:(UIViewController *)viewController + invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem { + popover_ = nil; + samplesButton_ = nil; + [self updateSamplesButton]; +} + +#pragma mark - Private methods + +- (void)loadDemo:(NSUInteger)section + atIndex:(NSUInteger)index { + NSDictionary *demo = [[demos_ objectAtIndex:section] objectAtIndex:index]; + UIViewController *controller = + [[[demo objectForKey:@"controller"] alloc] init]; + controller_ = controller; + + if (controller != nil) { + controller.title = [demo objectForKey:@"title"]; + + if (isPhone_) { + [self.navigationController pushViewController:controller animated:YES]; + } else { + [self.appDelegate setSample:controller]; + [popover_ dismissPopoverAnimated:YES]; + } + + [self updateSamplesButton]; + } +} + +// This method is invoked when the left 'back' button in the split view +// controller on iPad should be updated (either made visible or hidden). +// It assumes that the left bar button item may be safely modified to contain +// the samples button. +- (void)updateSamplesButton { + controller_.navigationItem.leftBarButtonItem = samplesButton_; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.h new file mode 100644 index 0000000..fd45b85 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.h @@ -0,0 +1,9 @@ + +#import +#import + +#import + +@interface AnimatedCurrentLocationViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.m new file mode 100644 index 0000000..dc50540 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/AnimatedCurrentLocationViewController.m @@ -0,0 +1,91 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/AnimatedCurrentLocationViewController.h" + +@implementation AnimatedCurrentLocationViewController { + CLLocationManager *_manager; + GMSMapView *_mapView; + GMSMarker *_locationMarker; + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:38.8879 + longitude:-77.0200 + zoom:17]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.settings.myLocationButton = NO; + _mapView.settings.indoorPicker = NO; + + self.view = _mapView; + + // Setup location services + if (![CLLocationManager locationServicesEnabled]) { + NSLog(@"Please enable location services"); + return; + } + + if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) { + NSLog(@"Please authorize location services"); + return; + } + + _manager = [[CLLocationManager alloc] init]; + _manager.delegate = self; + _manager.desiredAccuracy = kCLLocationAccuracyBest; + _manager.distanceFilter = 5.0f; + [_manager startUpdatingLocation]; + +} + +#pragma mark - CLLocationManagerDelegate + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { + if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) { + NSLog(@"Please authorize location services"); + return; + } + + NSLog(@"CLLocationManager error: %@", error.localizedFailureReason); + return; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { + CLLocation *location = [locations lastObject]; + + if (_locationMarker == nil) { + _locationMarker = [[GMSMarker alloc] init]; + _locationMarker.position = CLLocationCoordinate2DMake(-33.86, 151.20); + + // Animated walker images derived from an www.angryanimator.com tutorial. + // See: http://www.angryanimator.com/word/2010/11/26/tutorial-2-walk-cycle/ + + NSArray *frames = @[[UIImage imageNamed:@"step1"], + [UIImage imageNamed:@"step2"], + [UIImage imageNamed:@"step3"], + [UIImage imageNamed:@"step4"], + [UIImage imageNamed:@"step5"], + [UIImage imageNamed:@"step6"], + [UIImage imageNamed:@"step7"], + [UIImage imageNamed:@"step8"]]; + + _locationMarker.icon = [UIImage animatedImageWithImages:frames duration:0.8]; + _locationMarker.groundAnchor = CGPointMake(0.5f, 0.97f); // Taking into account walker's shadow + _locationMarker.map = _mapView; + } else { + [CATransaction begin]; + [CATransaction setAnimationDuration:2.0]; + _locationMarker.position = location.coordinate; + [CATransaction commit]; + } + + GMSCameraUpdate *move = [GMSCameraUpdate setTarget:location.coordinate zoom:17]; + [_mapView animateWithCameraUpdate:move]; +} + + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.h new file mode 100644 index 0000000..d6611e3 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.h @@ -0,0 +1,5 @@ +#import + +@interface BasicMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.m new file mode 100644 index 0000000..7ab1dbd --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/BasicMapViewController.m @@ -0,0 +1,19 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/BasicMapViewController.h" + +#import + +@implementation BasicMapViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:6]; + self.view = [GMSMapView mapWithFrame:CGRectZero camera:camera]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.h new file mode 100644 index 0000000..0853237 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.h @@ -0,0 +1,5 @@ +#import + +@interface CameraViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.m new file mode 100644 index 0000000..7291897 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CameraViewController.m @@ -0,0 +1,61 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/CameraViewController.h" + +#import + +@implementation CameraViewController { + GMSMapView *_mapView; + NSTimer *timer; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.809487 + longitude:144.965699 + zoom:20 + bearing:0 + viewingAngle:0]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.settings.zoomGestures = NO; + _mapView.settings.scrollGestures = NO; + _mapView.settings.rotateGestures = NO; + _mapView.settings.tiltGestures = NO; + + self.view = _mapView; +} + +- (void)moveCamera { + GMSCameraPosition *camera = _mapView.camera; + float zoom = fmaxf(camera.zoom - 0.1f, 17.5f); + + GMSCameraPosition *newCamera = + [[GMSCameraPosition alloc] initWithTarget:camera.target + zoom:zoom + bearing:camera.bearing + 10 + viewingAngle:camera.viewingAngle + 10]; + [_mapView animateToCameraPosition:newCamera]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + timer = [NSTimer scheduledTimerWithTimeInterval:1.f/30.f + target:self + selector:@selector(moveCamera) + userInfo:nil + repeats:YES]; +} + +- (void)viewDidDisappear:(BOOL)animated { + [super viewDidDisappear:animated]; + [timer invalidate]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + [timer invalidate]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.h new file mode 100644 index 0000000..3379794 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.h @@ -0,0 +1,4 @@ +#import + +@interface CustomIndoorViewController : UIViewController +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.m new file mode 100644 index 0000000..d2f0e3c --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomIndoorViewController.m @@ -0,0 +1,139 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/CustomIndoorViewController.h" + +#import + +@interface CustomIndoorViewController () < + GMSIndoorDisplayDelegate, + UIPickerViewDelegate, + UIPickerViewDataSource> + +@end + +@implementation CustomIndoorViewController { + GMSMapView *_mapView; + UIPickerView *_levelPickerView; + NSArray *_levels; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:37.78318 + longitude:-122.403874 + zoom:18]; + + // set backgroundColor, otherwise UIPickerView fades into the background + self.view.backgroundColor = [UIColor grayColor]; + + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.settings.myLocationButton = NO; + _mapView.settings.indoorPicker = NO; // We are implementing a custom level picker. + + _mapView.indoorEnabled = YES; // Defaults to YES. Set to NO to hide indoor maps. + _mapView.indoorDisplay.delegate = self; + _mapView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:_mapView]; + + // This UIPickerView will be populated with the levels of the active building. + _levelPickerView = [[UIPickerView alloc] init]; + _levelPickerView.delegate = self; + _levelPickerView.dataSource = self; + _levelPickerView.showsSelectionIndicator = YES; + _levelPickerView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:_levelPickerView]; + + // The height of the UIPickerView, used below in the vertical constraint + NSDictionary *metrics = @{@"height": @180.0}; + NSDictionary *views = NSDictionaryOfVariableBindings(_mapView, _levelPickerView); + + // Constraining the map to the full width of the display. + // The |_levelPickerView| is constrained below with the NSLayoutFormatAlignAll* + // See http://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Articles/formatLanguage.html + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"|[_mapView]|" + options:0 + metrics:metrics + views:views]]; + + // Constraining the _mapView and the _levelPickerView as siblings taking + // the full height of the display, with _levelPickerView at 200 points high + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|[_mapView][_levelPickerView(height)]|" + options:NSLayoutFormatAlignAllLeft|NSLayoutFormatAlignAllRight + metrics:metrics + views:views]]; +} + +#pragma mark - GMSIndoorDisplayDelegate + +- (void)didChangeActiveBuilding:(GMSIndoorBuilding *)building { + // Everytime we change active building force the picker to re-display the labels. + + NSMutableArray *levels = [NSMutableArray array]; + if (building.underground) { + // If this building is completely underground, add a fake 'top' floor. This must be the 'boxed' + // nil, [NSNull null], as NSArray/NSMutableArray cannot contain nils. + [levels addObject:[NSNull null]]; + } + [levels addObjectsFromArray:building.levels]; + _levels = [levels copy]; + + [_levelPickerView reloadAllComponents]; + [_levelPickerView selectRow:-1 inComponent:0 animated:NO]; + + // UIPickerView insists on having some data; disable interaction if there's no levels. + _levelPickerView.userInteractionEnabled = ([_levels count] > 0); +} + +- (void)didChangeActiveLevel:(GMSIndoorLevel *)level { + // On level change, sync our level picker's selection to the IndoorDisplay. + if (level == nil) { + level = (id)[NSNull null]; // box nil to NSNull for use in NSArray + } + NSUInteger index = [_levels indexOfObject:level]; + if (index != NSNotFound) { + NSInteger currentlySelectedLevel = [_levelPickerView selectedRowInComponent:0]; + if ((NSInteger)index != currentlySelectedLevel) { + [_levelPickerView selectRow:index inComponent:0 animated:NO]; + } + } +} + +#pragma mark - UIPickerViewDelegate + +- (void)pickerView:(UIPickerView *)pickerView + didSelectRow:(NSInteger)row + inComponent:(NSInteger)component { + // On user selection of a level in the picker, set the right level in IndoorDisplay + id level = _levels[row]; + if (level == [NSNull null]) { + level = nil; // unbox NSNull + } + [_mapView.indoorDisplay setActiveLevel:level]; +} + +- (NSString *)pickerView:(UIPickerView *)pickerView + titleForRow:(NSInteger)row + forComponent:(NSInteger)component { + id object = _levels[row]; + if (object == [NSNull null]) { + return @"\u2014"; // use an em dash for 'above ground' + } + GMSIndoorLevel *level = object; + return level.name; +} + +#pragma mark - UIPickerViewDataSource + +- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { + return 1; +} + +- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { + return [_levels count]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.h new file mode 100644 index 0000000..f5b839f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.h @@ -0,0 +1,5 @@ +#import + +@interface CustomMarkersViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.m new file mode 100644 index 0000000..249e4cb --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/CustomMarkersViewController.m @@ -0,0 +1,111 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/CustomMarkersViewController.h" + +#import + +static int kMarkerCount = 0; + +// Returns a random value from 0-1.0f. +static CGFloat randf() { + return (((float)arc4random()/0x100000000)*1.0f); +} + +@implementation CustomMarkersViewController { + GMSMapView *_mapView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + [self addDefaultMarkers]; + + // Add a button which adds random markers to the map. + UIBarButtonItem *addButton = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd + target:self + action:@selector(didTapAdd)]; + addButton.accessibilityLabel = @"Add Markers"; + UIBarButtonItem *clearButton = + [[UIBarButtonItem alloc] initWithTitle:@"Clear Markers" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapClear)]; + self.navigationItem.rightBarButtonItems = @[ addButton, clearButton ]; + + self.view = _mapView; +} + +- (void)addDefaultMarkers { + // Add a custom 'glow' marker around Sydney. + GMSMarker *sydneyMarker = [[GMSMarker alloc] init]; + sydneyMarker.title = @"Sydney!"; + sydneyMarker.icon = [UIImage imageNamed:@"glow-marker"]; + sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + sydneyMarker.map = _mapView; + + // Add a custom 'arrow' marker pointing to Melbourne. + GMSMarker *melbourneMarker = [[GMSMarker alloc] init]; + melbourneMarker.title = @"Melbourne!"; + melbourneMarker.icon = [UIImage imageNamed:@"arrow"]; + melbourneMarker.position = CLLocationCoordinate2DMake(-37.81969, 144.966085); + melbourneMarker.map = _mapView; +} + +- (void)didTapAdd { + for (int i = 0; i < 10; ++i) { + // Add a marker every 0.25 seconds for the next ten markers, randomly + // within the bounds of the camera as it is at that point. + double delayInSeconds = (i * 0.25); + dispatch_time_t popTime = + dispatch_time(DISPATCH_TIME_NOW, + (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { + GMSVisibleRegion region = [_mapView.projection visibleRegion]; + GMSCoordinateBounds *bounds = + [[GMSCoordinateBounds alloc] initWithRegion:region]; + [self addMarkerInBounds:bounds]; + }); + } +} + +- (void)addMarkerInBounds:(GMSCoordinateBounds *)bounds { + CLLocationDegrees latitude = bounds.southWest.latitude + + randf() * (bounds.northEast.latitude - bounds.southWest.latitude); + + // If the visible region crosses the antimeridian (the right-most point is + // "smaller" than the left-most point), adjust the longitude accordingly. + BOOL offset = (bounds.northEast.longitude < bounds.southWest.longitude); + CLLocationDegrees longitude = bounds.southWest.longitude + randf() * + (bounds.northEast.longitude - bounds.southWest.longitude + (offset ? + 360 : 0)); + if (longitude > 180.f) { + longitude -= 360.f; + } + + UIColor *color = + [UIColor colorWithHue:randf() saturation:1.f brightness:1.f alpha:1.0f]; + + CLLocationCoordinate2D position = + CLLocationCoordinate2DMake(latitude, longitude); + GMSMarker *marker = [GMSMarker markerWithPosition:position]; + marker.title = [NSString stringWithFormat:@"Marker #%d", ++kMarkerCount]; + marker.appearAnimation = kGMSMarkerAnimationPop; + marker.icon = [GMSMarker markerImageWithColor:color]; + + marker.rotation = (randf()-0.5f)*20; // rotate between -20 and +20 degrees + + marker.map = _mapView; +} + +- (void)didTapClear { + [_mapView clear]; + [self addDefaultMarkers]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.h new file mode 100644 index 0000000..0a1d237 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.h @@ -0,0 +1,5 @@ +#import + +@interface DoubleMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.m new file mode 100644 index 0000000..0948acc --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/DoubleMapViewController.m @@ -0,0 +1,65 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/DoubleMapViewController.h" + +#import + +@interface DoubleMapViewController () +@end + +@implementation DoubleMapViewController { + GMSMapView *_mapView; + GMSMapView *_boundMapView; +} + ++ (GMSCameraPosition *)defaultCamera { + return [GMSCameraPosition cameraWithLatitude:37.7847 + longitude:-122.41 + zoom:5]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Two map views, second one has its camera target controlled by the first. + CGRect frame = self.view.bounds; + frame.size.height = frame.size.height / 2; + _mapView = [GMSMapView mapWithFrame:frame camera:[DoubleMapViewController defaultCamera]]; + _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleBottomMargin; + + _mapView.delegate = self; + [self.view addSubview:_mapView]; + + frame = self.view.bounds; + frame.size.height = frame.size.height / 2; + frame.origin.y = frame.size.height; + _boundMapView = + [GMSMapView mapWithFrame:frame camera:[DoubleMapViewController defaultCamera]]; + _boundMapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleTopMargin; + _boundMapView.settings.scrollGestures = NO; + + [self.view addSubview:_boundMapView]; +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation + duration:(NSTimeInterval)duration { + CGRect frame = self.view.bounds; + frame.size.height = frame.size.height / 2; + _mapView.frame = frame; +} + +- (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { + GMSCameraPosition *previousCamera = _boundMapView.camera; + _boundMapView.camera = [GMSCameraPosition cameraWithTarget:position.target + zoom:previousCamera.zoom + bearing:previousCamera.bearing + viewingAngle:previousCamera.viewingAngle]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.h new file mode 100644 index 0000000..3cc38b0 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.h @@ -0,0 +1,5 @@ +#import + +@interface FitBoundsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.m new file mode 100644 index 0000000..f730201 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FitBoundsViewController.m @@ -0,0 +1,82 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/FitBoundsViewController.h" + +#import + +@interface FitBoundsViewController () +@end + +@implementation FitBoundsViewController { + GMSMapView *_mapView; + NSMutableArray *_markers; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.delegate = self; + self.view = _mapView; + + // Add a default marker around Sydney. + GMSMarker *sydneyMarker = [[GMSMarker alloc] init]; + sydneyMarker.title = @"Sydney!"; + sydneyMarker.icon = [UIImage imageNamed:@"glow-marker"]; + sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + sydneyMarker.map = _mapView; + + GMSMarker *anotherSydneyMarker = [[GMSMarker alloc] init]; + anotherSydneyMarker.title = @"Sydney 2!"; + anotherSydneyMarker.icon = [UIImage imageNamed:@"glow-marker"]; + anotherSydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 149.2086); + anotherSydneyMarker.map = _mapView; + + // Create a list of markers, adding the Sydney marker. + _markers = [NSMutableArray arrayWithObject:sydneyMarker]; + [_markers addObject:anotherSydneyMarker]; + + // Create a button that, when pressed, updates the camera to fit the bounds + // of the specified markers. + UIBarButtonItem *fitBoundsButton = + [[UIBarButtonItem alloc] initWithTitle:@"Fit Bounds" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapFitBounds)]; + self.navigationItem.rightBarButtonItem = fitBoundsButton; +} + +- (void)didTapFitBounds { + GMSCoordinateBounds *bounds; + for (GMSMarker *marker in _markers) { + if (bounds == nil) { + bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:marker.position + coordinate:marker.position]; + } + bounds = [bounds includingCoordinate:marker.position]; + } + GMSCameraUpdate *update = [GMSCameraUpdate fitBounds:bounds + withPadding:50.0f]; + [_mapView moveCamera:update]; +} + +#pragma mark - GMSMapViewDelegate + +- (void)mapView:(GMSMapView *)mapView + didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { + GMSMarker *marker = [[GMSMarker alloc] init]; + marker.title = [NSString stringWithFormat:@"Marker at: %.2f,%.2f", + coordinate.latitude, coordinate.longitude]; + marker.position = coordinate; + marker.appearAnimation = kGMSMarkerAnimationPop; + marker.map = _mapView; + + // Add the new marker to the list of markers. + [_markers addObject:marker]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.h new file mode 100644 index 0000000..29968c5 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.h @@ -0,0 +1,5 @@ +#import + +@interface FixedPanoramaViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.m new file mode 100644 index 0000000..0ef880e --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/FixedPanoramaViewController.m @@ -0,0 +1,33 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/FixedPanoramaViewController.h" + +#import + +static CLLocationCoordinate2D kPanoramaNear = {-33.732022, 150.312114}; + +@interface FixedPanoramaViewController () +@end + +@implementation FixedPanoramaViewController { + GMSPanoramaView *_view; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + _view = [GMSPanoramaView panoramaWithFrame:CGRectZero + nearCoordinate:kPanoramaNear]; + _view.camera = [GMSPanoramaCamera cameraWithHeading:180 + pitch:-10 + zoom:0]; + _view.delegate = self; + _view.orientationGestures = NO; + _view.navigationGestures = NO; + _view.navigationLinksHidden = YES; + self.view = _view; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.h new file mode 100644 index 0000000..26bf09d --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface GeocoderViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.m new file mode 100644 index 0000000..bc830f8 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GeocoderViewController.m @@ -0,0 +1,53 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/GeocoderViewController.h" + +#import + +@implementation GeocoderViewController { + GMSMapView *mapView_; + GMSGeocoder *geocoder_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:12]; + + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.delegate = self; + + geocoder_ = [[GMSGeocoder alloc] init]; + + self.view = mapView_; +} + +- (void)mapView:(GMSMapView *)mapView + didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { + // On a long press, reverse geocode this location. + GMSReverseGeocodeCallback handler = ^(GMSReverseGeocodeResponse *response, NSError *error) { + GMSAddress *address = response.firstResult; + if (address) { + NSLog(@"Geocoder result: %@", address); + + GMSMarker *marker = [GMSMarker markerWithPosition:address.coordinate]; + + marker.title = [[address lines] firstObject]; + if ([[address lines] count] > 1) { + marker.snippet = [[address lines] objectAtIndex:1]; + } + + marker.appearAnimation = kGMSMarkerAnimationPop; + marker.map = mapView_; + } else { + NSLog(@"Could not reverse geocode point (%f,%f): %@", + coordinate.latitude, coordinate.longitude, error); + } + }; + [geocoder_ reverseGeocodeCoordinate:coordinate completionHandler:handler]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.h new file mode 100644 index 0000000..fda8840 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.h @@ -0,0 +1,5 @@ +#import + +@interface GestureControlViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.m new file mode 100644 index 0000000..556af4a --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GestureControlViewController.m @@ -0,0 +1,59 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/GestureControlViewController.h" + +#import + +@implementation GestureControlViewController { + GMSMapView *mapView_; + UISwitch *zoomSwitch_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-25.5605 + longitude:133.605097 + zoom:3]; + + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + self.view = [[UIView alloc] initWithFrame:CGRectZero]; + [self.view addSubview:mapView_]; + + UIView *holder = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 59)]; + holder.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; + holder.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.8f]; + [self.view addSubview:holder]; + + // Zoom label. + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(16, 16, 200, 29)]; + label.text = @"Zooming?"; + label.font = [UIFont boldSystemFontOfSize:18.0f]; + label.textAlignment = NSTextAlignmentLeft; + label.backgroundColor = [UIColor clearColor]; + label.layer.shadowColor = [[UIColor whiteColor] CGColor]; + label.layer.shadowOffset = CGSizeMake(0.0f, 1.0f); + label.layer.shadowOpacity = 1.0f; + label.layer.shadowRadius = 0.0f; + [holder addSubview:label]; + + // Control zooming. + zoomSwitch_ = [[UISwitch alloc] initWithFrame:CGRectMake(-90, 16, 0, 0)]; + zoomSwitch_.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + [zoomSwitch_ addTarget:self + action:@selector(didChangeZoomSwitch) + forControlEvents:UIControlEventValueChanged]; + zoomSwitch_.on = YES; + [holder addSubview:zoomSwitch_]; +} + +- (void)didChangeZoomSwitch { + mapView_.settings.zoomGestures = zoomSwitch_.isOn; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.h new file mode 100644 index 0000000..2de863c --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.h @@ -0,0 +1,5 @@ +#import + +@interface GradientPolylinesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.m new file mode 100644 index 0000000..13b51e4 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GradientPolylinesViewController.m @@ -0,0 +1,76 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/GradientPolylinesViewController.h" + +#import + + +@implementation GradientPolylinesViewController { + GMSMapView *mapView_; + GMSPolyline *polyline_; + NSMutableArray *trackData_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:44.1314 + longitude:9.6921 + zoom:14.059f + bearing:328.f + viewingAngle:40.f]; + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.view = mapView_; + + [self parseTrackFile]; + [polyline_ setSpans:[self gradientSpans]]; +} + +- (NSArray *)gradientSpans { + NSMutableArray *colorSpans = [NSMutableArray array]; + NSUInteger count = [trackData_ count]; + UIColor *prevColor; + for (NSUInteger i = 0; i < count; i++) { + double elevation = [[[trackData_ objectAtIndex:i] objectForKey:@"elevation"] doubleValue]; + + UIColor *toColor = [UIColor colorWithHue:(float)elevation/700 + saturation:1.f + brightness:.9f + alpha:1.f]; + + if (prevColor == nil) { + prevColor = toColor; + } + + GMSStrokeStyle *style = [GMSStrokeStyle gradientFromColor:prevColor toColor:toColor]; + [colorSpans addObject:[GMSStyleSpan spanWithStyle:style]]; + + prevColor = toColor; + } + return colorSpans; +} + +- (void)parseTrackFile { + NSString *filePath = [[NSBundle mainBundle] pathForResource:@"track" ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + trackData_ = [[NSMutableArray alloc] init]; + GMSMutablePath *path = [GMSMutablePath path]; + + for (NSUInteger i = 0; i < [json count]; i++) { + NSDictionary *info = [json objectAtIndex:i]; + NSNumber *elevation = [info objectForKey:@"elevation"]; + CLLocationDegrees lat = [[info objectForKey:@"lat"] doubleValue]; + CLLocationDegrees lng = [[info objectForKey:@"lng"] doubleValue]; + CLLocation *loc = [[CLLocation alloc] initWithLatitude:lat longitude:lng]; + [trackData_ addObject:@{@"loc": loc, @"elevation": elevation}]; + [path addLatitude:lat longitude:lng]; + } + + polyline_ = [GMSPolyline polylineWithPath:path]; + polyline_.strokeWidth = 6; + polyline_.map = mapView_; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.h new file mode 100644 index 0000000..e9bfa76 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.h @@ -0,0 +1,5 @@ +#import + +@interface GroundOverlayViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.m new file mode 100644 index 0000000..bfb200a --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/GroundOverlayViewController.m @@ -0,0 +1,41 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/GroundOverlayViewController.h" + +#import + +@implementation GroundOverlayViewController { + GMSGroundOverlay *overlay_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(40.712216, -74.22655); + CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(40.773941, -74.12544); + + GMSCoordinateBounds *overlayBounds = [[GMSCoordinateBounds alloc] initWithCoordinate:southWest + coordinate:northEast]; + + // Choose the midpoint of the coordinate to focus the camera on. + CLLocationCoordinate2D newark = GMSGeometryInterpolate(southWest, northEast, 0.5); + GMSCameraPosition *camera = [GMSCameraPosition cameraWithTarget:newark + zoom:12 + bearing:0 + viewingAngle:45]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + + // Add the ground overlay, centered in Newark, NJ + GMSGroundOverlay *groundOverlay = [[GMSGroundOverlay alloc] init]; + // Image from http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg + groundOverlay.icon = [UIImage imageNamed:@"newark_nj_1922.jpg"]; + groundOverlay.position = newark; + groundOverlay.bounds = overlayBounds; + groundOverlay.map = mapView; + + self.view = mapView; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.h new file mode 100644 index 0000000..1dc3756 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.h @@ -0,0 +1,9 @@ +#import + +#import + +@interface IndoorMuseumNavigationViewController : UIViewController< + GMSMapViewDelegate, + GMSIndoorDisplayDelegate> + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.m new file mode 100644 index 0000000..ca393b6 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorMuseumNavigationViewController.m @@ -0,0 +1,115 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/IndoorMuseumNavigationViewController.h" + +@implementation IndoorMuseumNavigationViewController { + GMSMapView *mapView_; + NSArray *exhibits_; // Array of JSON exhibit data. + NSDictionary *exhibit_; // The currently selected exhibit. Will be nil initially. + GMSMarker *marker_; + NSDictionary *levels_; // The levels dictionary is updated when a new building is selected, and + // contains mapping from localized level name to GMSIndoorLevel. +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:38.8879 + longitude:-77.0200 + zoom:17]; + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.settings.myLocationButton = NO; + mapView_.settings.indoorPicker = NO; + mapView_.delegate = self; + mapView_.indoorDisplay.delegate = self; + + self.view = mapView_; + + // Load the exhibits configuration from JSON + NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"museum-exhibits" ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:jsonPath]; + exhibits_ = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:nil]; + + + UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] init]; + [segmentedControl setTintColor:[UIColor colorWithRed:0.373f green:0.667f blue:0.882f alpha:1.0f]]; + + segmentedControl.translatesAutoresizingMaskIntoConstraints = NO; + [segmentedControl addTarget:self + action:@selector(exhibitSelected:) + forControlEvents:UIControlEventValueChanged]; + [self.view addSubview:segmentedControl]; + + for (NSDictionary *exhibit in exhibits_) { + [segmentedControl insertSegmentWithImage:[UIImage imageNamed:exhibit[@"key"]] + atIndex:[exhibits_ indexOfObject:exhibit] + animated:NO]; + } + + NSDictionary *views = NSDictionaryOfVariableBindings(segmentedControl); + + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"[segmentedControl]-|" + options:kNilOptions + metrics:nil + views:views]]; + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[segmentedControl]-|" + options:kNilOptions + metrics:nil + views:views]]; + +} + +- (void)moveMarker { + CLLocationCoordinate2D loc = CLLocationCoordinate2DMake([exhibit_[@"lat"] doubleValue], + [exhibit_[@"lng"] doubleValue]); + if (marker_ == nil) { + marker_ = [GMSMarker markerWithPosition:loc]; + marker_.map = mapView_; + } else { + marker_.position = loc; + } + marker_.title = exhibit_[@"name"]; + [mapView_ animateToLocation:loc]; + [mapView_ animateToZoom:19]; +} + +- (void)exhibitSelected:(UISegmentedControl *)segmentedControl { + exhibit_ = exhibits_[[segmentedControl selectedSegmentIndex]]; + [self moveMarker]; +} + +#pragma mark - GMSMapViewDelegate + +- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)camera { + if (exhibit_ != nil) { + CLLocationCoordinate2D loc = CLLocationCoordinate2DMake([exhibit_[@"lat"] doubleValue], + [exhibit_[@"lng"] doubleValue]); + if ([mapView_.projection containsCoordinate:loc] && levels_ != nil) { + [mapView.indoorDisplay setActiveLevel:levels_[exhibit_[@"level"]]]; + } + } +} + +#pragma mark - GMSIndoorDisplayDelegate + +- (void)didChangeActiveBuilding:(GMSIndoorBuilding *)building { + if (building != nil) { + NSMutableDictionary *levels = [NSMutableDictionary dictionary]; + + for (GMSIndoorLevel *level in building.levels) { + [levels setObject:level forKey:level.shortName]; + } + + levels_ = [NSDictionary dictionaryWithDictionary:levels]; + } else { + levels_ = nil; + } +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.h new file mode 100644 index 0000000..95c10e1 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.h @@ -0,0 +1,5 @@ +#import + +@interface IndoorViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.m new file mode 100644 index 0000000..6d849e3 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/IndoorViewController.m @@ -0,0 +1,26 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/IndoorViewController.h" + +#import + +@implementation IndoorViewController { + GMSMapView *mapView_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:37.78318 + longitude:-122.403874 + zoom:18]; + + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.settings.myLocationButton = YES; + + self.view = mapView_; +} + + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.h new file mode 100644 index 0000000..6bc88e4 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MapLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.m new file mode 100644 index 0000000..40b31b0 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapLayerViewController.m @@ -0,0 +1,79 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MapLayerViewController.h" + +#import + +@implementation MapLayerViewController { + GMSMapView *mapView_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.view = mapView_; + + dispatch_async(dispatch_get_main_queue(), ^{ + mapView_.myLocationEnabled = YES; + }); + + UIBarButtonItem *myLocationButton = + [[UIBarButtonItem alloc] initWithTitle:@"Fly to My Location" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapMyLocation)]; + self.navigationItem.rightBarButtonItem = myLocationButton; + +} + +- (void)didTapMyLocation { + CLLocation *location = mapView_.myLocation; + if (!location || !CLLocationCoordinate2DIsValid(location.coordinate)) { + return; + } + + mapView_.layer.cameraLatitude = location.coordinate.latitude; + mapView_.layer.cameraLongitude = location.coordinate.longitude; + mapView_.layer.cameraBearing = 0.0; + + // Access the GMSMapLayer directly to modify the following properties with a + // specified timing function and duration. + + CAMediaTimingFunction *curve = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + CABasicAnimation *animation; + + animation = [CABasicAnimation animationWithKeyPath:kGMSLayerCameraLatitudeKey]; + animation.duration = 2.0f; + animation.timingFunction = curve; + animation.toValue = @(location.coordinate.latitude); + [mapView_.layer addAnimation:animation forKey:kGMSLayerCameraLatitudeKey]; + + animation = [CABasicAnimation animationWithKeyPath:kGMSLayerCameraLongitudeKey]; + animation.duration = 2.0f; + animation.timingFunction = curve; + animation.toValue = @(location.coordinate.longitude); + [mapView_.layer addAnimation:animation forKey:kGMSLayerCameraLongitudeKey]; + + animation = [CABasicAnimation animationWithKeyPath:kGMSLayerCameraBearingKey]; + animation.duration = 2.0f; + animation.timingFunction = curve; + animation.toValue = @0.0; + [mapView_.layer addAnimation:animation forKey:kGMSLayerCameraBearingKey]; + + // Fly out to the minimum zoom and then zoom back to the current zoom! + CGFloat zoom = mapView_.camera.zoom; + NSArray *keyValues = @[@(zoom), @(kGMSMinZoomLevel), @(zoom)]; + CAKeyframeAnimation *keyFrameAnimation = + [CAKeyframeAnimation animationWithKeyPath:kGMSLayerCameraZoomLevelKey]; + keyFrameAnimation.duration = 2.0f; + keyFrameAnimation.values = keyValues; + [mapView_.layer addAnimation:keyFrameAnimation forKey:kGMSLayerCameraZoomLevelKey]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.h new file mode 100644 index 0000000..33f5b67 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MapTypesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.m new file mode 100644 index 0000000..6d256c6 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapTypesViewController.m @@ -0,0 +1,60 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MapTypesViewController.h" + +#import + +static NSString const * kNormalType = @"Normal"; +static NSString const * kSatelliteType = @"Satellite"; +static NSString const * kHybridType = @"Hybrid"; +static NSString const * kTerrainType = @"Terrain"; + +@implementation MapTypesViewController { + UISegmentedControl *switcher_; + GMSMapView *mapView_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:12]; + + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.view = mapView_; + + // The possible different types to show. + NSArray *types = @[kNormalType, kSatelliteType, kHybridType, kTerrainType]; + + // Create a UISegmentedControl that is the navigationItem's titleView. + switcher_ = [[UISegmentedControl alloc] initWithItems:types]; + switcher_.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleBottomMargin; + switcher_.selectedSegmentIndex = 0; + self.navigationItem.titleView = switcher_; + + // Listen to touch events on the UISegmentedControl. + [switcher_ addTarget:self action:@selector(didChangeSwitcher) + forControlEvents:UIControlEventValueChanged]; +} + +- (void)didChangeSwitcher { + // Switch to the type clicked on. + NSString *title = + [switcher_ titleForSegmentAtIndex:switcher_.selectedSegmentIndex]; + if ([kNormalType isEqualToString:title]) { + mapView_.mapType = kGMSTypeNormal; + } else if ([kSatelliteType isEqualToString:title]) { + mapView_.mapType = kGMSTypeSatellite; + } else if ([kHybridType isEqualToString:title]) { + mapView_.mapType = kGMSTypeHybrid; + } else if ([kTerrainType isEqualToString:title]) { + mapView_.mapType = kGMSTypeTerrain; + } +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.h new file mode 100644 index 0000000..d610aa9 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MapZoomViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.m new file mode 100644 index 0000000..2371f87 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MapZoomViewController.m @@ -0,0 +1,73 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MapZoomViewController.h" + +#import + +@implementation MapZoomViewController { + GMSMapView *mapView_; + UITextView *zoomRangeView_; + NSUInteger nextMode_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:6]; + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.settings.scrollGestures = NO; + self.view = mapView_; + + // Add a display for the current zoom range restriction. + zoomRangeView_ = [[UITextView alloc] init]; + zoomRangeView_.frame = + CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0); + zoomRangeView_.text = @""; + zoomRangeView_.textAlignment = NSTextAlignmentCenter; + zoomRangeView_.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8f]; + zoomRangeView_.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [self.view addSubview:zoomRangeView_]; + [zoomRangeView_ sizeToFit]; + [self didTapNext]; + + // Add a button toggling through modes. + self.navigationItem.rightBarButtonItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay + target:self + action:@selector(didTapNext)]; +} + +- (void)didTapNext { + NSString *label = @""; + float minZoom = kGMSMinZoomLevel; + float maxZoom = kGMSMaxZoomLevel; + + switch (nextMode_) { + case 0: + label = @"Default"; + break; + case 1: + minZoom = 18; + label = @"Zoomed in"; + break; + case 2: + maxZoom = 8; + label = @"Zoomed out"; + break; + case 3: + minZoom = 10; + maxZoom = 11.5; + label = @"Small range"; + break; + } + nextMode_ = (nextMode_ + 1) % 4; + + [mapView_ setMinZoom:minZoom maxZoom:maxZoom]; + zoomRangeView_.text = + [NSString stringWithFormat:@"%@ (%.2f - %.2f)", label, mapView_.minZoom, mapView_.maxZoom]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.h new file mode 100644 index 0000000..414b293 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface MarkerEventsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.m new file mode 100644 index 0000000..ad498c7 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerEventsViewController.m @@ -0,0 +1,70 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MarkerEventsViewController.h" + +#import + +#import + +@implementation MarkerEventsViewController { + GMSMapView *mapView_; + GMSMarker *melbourneMarker_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + + GMSMarker *sydneyMarker = [[GMSMarker alloc] init]; + sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + sydneyMarker.map = mapView_; + + melbourneMarker_ = [[GMSMarker alloc] init]; + melbourneMarker_.position = CLLocationCoordinate2DMake(-37.81969, 144.966085); + melbourneMarker_.map = mapView_; + + mapView_.delegate = self; + self.view = mapView_; +} + +#pragma mark - GMSMapViewDelegate + +- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker { + if (marker == melbourneMarker_) { + return [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Icon"]]; + } + + return nil; +} + +- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { + // Animate to the marker + [CATransaction begin]; + [CATransaction setAnimationDuration:3.f]; // 3 second animation + + GMSCameraPosition *camera = + [[GMSCameraPosition alloc] initWithTarget:marker.position + zoom:8 + bearing:50 + viewingAngle:60]; + [mapView animateToCameraPosition:camera]; + [CATransaction commit]; + + // Melbourne marker has a InfoWindow so return NO to allow markerInfoWindow to + // fire. Also check that the marker isn't already selected so that the + // InfoWindow doesn't close. + if (marker == melbourneMarker_ && + mapView.selectedMarker != melbourneMarker_) { + return NO; + } + + // The Tap has been handled so return YES + return YES; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.h new file mode 100644 index 0000000..74c7953 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MarkerInfoWindowViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.m new file mode 100644 index 0000000..5f6c931 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerInfoWindowViewController.m @@ -0,0 +1,73 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MarkerInfoWindowViewController.h" + +#import + +@interface MarkerInfoWindowViewController () +@end + +@implementation MarkerInfoWindowViewController { + GMSMarker *_sydneyMarker; + GMSMarker *_melbourneMarker; + GMSMarker *_brisbaneMarker; + UIView *_contentView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + + + _sydneyMarker = [[GMSMarker alloc] init]; + _sydneyMarker.title = @"Sydney"; + _sydneyMarker.snippet = @"Population: 4,605,992"; + _sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + _sydneyMarker.map = mapView; + NSLog(@"sydneyMarker: %@", _sydneyMarker); + + + _melbourneMarker.map = nil; + _melbourneMarker = [[GMSMarker alloc] init]; + _melbourneMarker.title = @"Melbourne"; + _melbourneMarker.snippet = @"Population: 4,169,103"; + _melbourneMarker.position = CLLocationCoordinate2DMake(-37.81969, 144.966085); + _melbourneMarker.map = mapView; + NSLog(@"melbourneMarker: %@", _melbourneMarker); + + _brisbaneMarker.map = nil; + _brisbaneMarker = [[GMSMarker alloc] init]; + _brisbaneMarker.title = @"Brisbane"; + _brisbaneMarker.snippet = @"Population: 2,189,878"; + _brisbaneMarker.position = CLLocationCoordinate2DMake(-27.4710107, 153.0234489); + _brisbaneMarker.map = mapView; + NSLog(@"brisbaneMarker: %@", _brisbaneMarker); + + _contentView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"aeroplane"]]; + + mapView.delegate = self; + self.view = mapView; +} + +#pragma mark GMSMapViewDelegate + +- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker { + if (marker == _sydneyMarker) { + return _contentView; + } + return nil; +} + +- (UIView *)mapView:(GMSMapView *)mapView markerInfoContents:(GMSMarker *)marker { + if (marker == _brisbaneMarker) { + return _contentView; + } + return nil; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.h new file mode 100644 index 0000000..d40e364 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface MarkerLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.m new file mode 100644 index 0000000..0bc8992 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkerLayerViewController.m @@ -0,0 +1,137 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MarkerLayerViewController.h" + +#import + +@interface CoordsList : NSObject +@property(nonatomic, readonly, copy) GMSPath *path; +@property(nonatomic, readonly) NSUInteger target; + +- (id)initWithPath:(GMSPath *)path; + +- (CLLocationCoordinate2D)next; + +@end + +@implementation CoordsList + +- (id)initWithPath:(GMSPath *)path { + if ((self = [super init])) { + _path = [path copy]; + _target = 0; + } + return self; +} + +- (CLLocationCoordinate2D)next { + ++_target; + if (_target == [_path count]) { + _target = 0; + } + return [_path coordinateAtIndex:_target]; +} + +@end + +@implementation MarkerLayerViewController { + GMSMapView *mapView_; + GMSMarker *fadedMarker_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + mapView_ = [[GMSMapView alloc] init]; + mapView_.camera = [GMSCameraPosition cameraWithLatitude:50.6042 longitude:3.9599 zoom:5]; + mapView_.delegate = self; + self.view = mapView_; + + GMSMutablePath *coords; + GMSMarker *marker; + + // Create a plane that flies to several airports around western Europe. + coords = [GMSMutablePath path]; + [coords addLatitude:52.310683 longitude:4.765121]; + [coords addLatitude:51.471386 longitude:-0.457148]; + [coords addLatitude:49.01378 longitude:2.5542943]; + [coords addLatitude:50.036194 longitude:8.554519]; + marker = [GMSMarker markerWithPosition:[coords coordinateAtIndex:0]]; + marker.icon = [UIImage imageNamed:@"aeroplane"]; + marker.groundAnchor = CGPointMake(0.5f, 0.5f); + marker.flat = YES; + marker.map = mapView_; + marker.userData = [[CoordsList alloc] initWithPath:coords]; + [self animateToNextCoord:marker]; + + // Create a boat that moves around the Baltic Sea. + coords = [GMSMutablePath path]; + [coords addLatitude:57.598335 longitude:11.290512]; + [coords addLatitude:55.665193 longitude:10.741196]; + [coords addLatitude:55.065787 longitude:11.083488]; + [coords addLatitude:54.699234 longitude:10.863762]; + [coords addLatitude:54.482805 longitude:12.061272]; + [coords addLatitude:55.819802 longitude:16.148186]; // final point + [coords addLatitude:54.927142 longitude:16.455803]; // final point + [coords addLatitude:54.482805 longitude:12.061272]; // and back again + [coords addLatitude:54.699234 longitude:10.863762]; + [coords addLatitude:55.065787 longitude:11.083488]; + [coords addLatitude:55.665193 longitude:10.741196]; + marker = [GMSMarker markerWithPosition:[coords coordinateAtIndex:0]]; + marker.icon = [UIImage imageNamed:@"boat"]; + marker.map = mapView_; + marker.userData = [[CoordsList alloc] initWithPath:coords]; + [self animateToNextCoord:marker]; +} + +- (void)animateToNextCoord:(GMSMarker *)marker { + CoordsList *coords = marker.userData; + CLLocationCoordinate2D coord = [coords next]; + CLLocationCoordinate2D previous = marker.position; + + CLLocationDirection heading = GMSGeometryHeading(previous, coord); + CLLocationDistance distance = GMSGeometryDistance(previous, coord); + + // Use CATransaction to set a custom duration for this animation. By default, changes to the + // position are already animated, but with a very short default duration. When the animation is + // complete, trigger another animation step. + + [CATransaction begin]; + [CATransaction setAnimationDuration:(distance / (50 * 1000))]; // custom duration, 50km/sec + + __weak MarkerLayerViewController *weakSelf = self; + [CATransaction setCompletionBlock:^{ + [weakSelf animateToNextCoord:marker]; + }]; + + marker.position = coord; + + [CATransaction commit]; + + // If this marker is flat, implicitly trigger a change in rotation, which will finish quickly. + if (marker.flat) { + marker.rotation = heading; + } +} + +- (void)fadeMarker:(GMSMarker *)marker { + fadedMarker_.opacity = 1.0f; // reset previous faded marker + + // Fade this new marker. + fadedMarker_ = marker; + fadedMarker_.opacity = 0.5f; +} + +#pragma mark - GMSMapViewDelegate + +- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { + [self fadeMarker:marker]; + return YES; +} + +- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { + [self fadeMarker:nil]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.h new file mode 100644 index 0000000..f9eaeee --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MarkersViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.m new file mode 100644 index 0000000..b00ec08 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MarkersViewController.m @@ -0,0 +1,63 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MarkersViewController.h" + +#import + +@implementation MarkersViewController { + GMSMarker *_sydneyMarker; + GMSMarker *_melbourneMarker; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + + _sydneyMarker = [[GMSMarker alloc] init]; + _sydneyMarker.title = @"Sydney"; + _sydneyMarker.snippet = @"Population: 4,605,992"; + _sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + _sydneyMarker.flat = NO; + _sydneyMarker.rotation = 30.0; + NSLog(@"sydneyMarker: %@", _sydneyMarker); + + GMSMarker *australiaMarker = [[GMSMarker alloc] init]; + australiaMarker.title = @"Australia"; + australiaMarker.position = CLLocationCoordinate2DMake(-27.994401,140.07019); + australiaMarker.appearAnimation = kGMSMarkerAnimationPop; + australiaMarker.flat = YES; + australiaMarker.draggable = YES; + australiaMarker.groundAnchor = CGPointMake(0.5, 0.5); + australiaMarker.icon = [UIImage imageNamed:@"australia"]; + australiaMarker.map = mapView; + + // Set the marker in Sydney to be selected + mapView.selectedMarker = _sydneyMarker; + + self.view = mapView; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(didTapAdd)]; +} + +- (void)didTapAdd { + if (_sydneyMarker.map == nil) { + _sydneyMarker.map = (GMSMapView *)self.view; +// _sydneyMarker.rotation += 45.0; + } else { + _sydneyMarker.map = nil; + } + + _melbourneMarker.map = nil; + _melbourneMarker = [[GMSMarker alloc] init]; + _melbourneMarker.title = @"Melbourne"; + _melbourneMarker.snippet = @"Population: 4,169,103"; + _melbourneMarker.position = CLLocationCoordinate2DMake(-37.81969, 144.966085); + _melbourneMarker.map = (GMSMapView *)self.view; +} + + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.h new file mode 100644 index 0000000..a592024 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.h @@ -0,0 +1,5 @@ +#import + +@interface MyLocationViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.m new file mode 100644 index 0000000..baff8da --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/MyLocationViewController.m @@ -0,0 +1,60 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/MyLocationViewController.h" + +#import + +@implementation MyLocationViewController { + GMSMapView *mapView_; + BOOL firstLocationUpdate_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:12]; + + mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView_.settings.compassButton = YES; + mapView_.settings.myLocationButton = YES; + + // Listen to the myLocation property of GMSMapView. + [mapView_ addObserver:self + forKeyPath:@"myLocation" + options:NSKeyValueObservingOptionNew + context:NULL]; + + self.view = mapView_; + + // Ask for My Location data after the map has already been added to the UI. + dispatch_async(dispatch_get_main_queue(), ^{ + mapView_.myLocationEnabled = YES; + }); +} + +- (void)dealloc { + [mapView_ removeObserver:self + forKeyPath:@"myLocation" + context:NULL]; +} + +#pragma mark - KVO updates + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (!firstLocationUpdate_) { + // If the first location update has not yet been recieved, then jump to that + // location. + firstLocationUpdate_ = YES; + CLLocation *location = [change objectForKey:NSKeyValueChangeNewKey]; + mapView_.camera = [GMSCameraPosition cameraWithTarget:location.coordinate + zoom:14]; + } +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.h new file mode 100644 index 0000000..3d65447 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.h @@ -0,0 +1,5 @@ +#import + +@interface PanoramaViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.m new file mode 100644 index 0000000..c4ce998 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PanoramaViewController.m @@ -0,0 +1,53 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/PanoramaViewController.h" + +#import + +static CLLocationCoordinate2D kPanoramaNear = {40.761388, -73.978133}; +static CLLocationCoordinate2D kMarkerAt = {40.761455, -73.977814}; + +@interface PanoramaViewController () +@end + +@implementation PanoramaViewController { + GMSPanoramaView *view_; + BOOL configured_; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + view_ = [GMSPanoramaView panoramaWithFrame:CGRectZero + nearCoordinate:kPanoramaNear]; + view_.backgroundColor = [UIColor grayColor]; + view_.delegate = self; + self.view = view_; +} + +#pragma mark - GMSPanoramaDelegate + +- (void)panoramaView:(GMSPanoramaView *)panoramaView + didMoveCamera:(GMSPanoramaCamera *)camera { + NSLog(@"Camera: (%f,%f,%f)", + camera.orientation.heading, camera.orientation.pitch, camera.zoom); +} + +- (void)panoramaView:(GMSPanoramaView *)view + didMoveToPanorama:(GMSPanorama *)panorama { + if (!configured_) { + GMSMarker *marker = [GMSMarker markerWithPosition:kMarkerAt]; + marker.icon = [GMSMarker markerImageWithColor:[UIColor purpleColor]]; + marker.panoramaView = view_; + + CLLocationDegrees heading = GMSGeometryHeading(kPanoramaNear, kMarkerAt); + view_.camera = + [GMSPanoramaCamera cameraWithHeading:heading pitch:0 zoom:1]; + + configured_ = YES; + } +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.h new file mode 100644 index 0000000..2a7cf41 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface PolygonsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.m new file mode 100644 index 0000000..e3495ee --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolygonsViewController.m @@ -0,0 +1,246 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/PolygonsViewController.h" + +#import + +@implementation PolygonsViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:39.13006 + longitude:-77.508545 + zoom:4]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView.delegate = self; // needed for didTapOverlay delegate method + + // Create the first polygon. + GMSPolygon *polygon = [[GMSPolygon alloc] init]; + polygon.path = [self pathOfNewYorkState]; + polygon.title = @"New York"; + polygon.fillColor = [UIColor colorWithRed:0.25 green:0 blue:0 alpha:0.2f]; + polygon.strokeColor = [UIColor blackColor]; + polygon.strokeWidth = 2; + polygon.tappable = YES; + polygon.map = mapView; + + // Copy the existing polygon and its settings and use it as a base for the + // second polygon. + polygon = [polygon copy]; + polygon.title = @"North Carolina"; + polygon.path = [self pathOfNorthCarolina]; + polygon.fillColor = [UIColor colorWithRed:0 green:0.25 blue:0 alpha:0.5]; + polygon.map = mapView; + + self.view = mapView; +} + +- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { + // When a polygon is tapped, randomly change its fill color to a new hue. + if ([overlay isKindOfClass:[GMSPolygon class]]) { + GMSPolygon *polygon = (GMSPolygon *)overlay; + CGFloat hue = (((float)arc4random()/0x100000000)*1.0f); + polygon.fillColor = + [UIColor colorWithHue:hue saturation:1 brightness:1 alpha:0.5]; + } +} + +- (GMSPath *)pathOfNewYorkState { + GMSMutablePath *path = [GMSMutablePath path]; + [path addLatitude:42.5142 longitude:-79.7624]; + [path addLatitude:42.7783 longitude:-79.0672]; + [path addLatitude:42.8508 longitude:-78.9313]; + [path addLatitude:42.9061 longitude:-78.9024]; + [path addLatitude:42.9554 longitude:-78.9313]; + [path addLatitude:42.9584 longitude:-78.9656]; + [path addLatitude:42.9886 longitude:-79.0219]; + [path addLatitude:43.0568 longitude:-79.0027]; + [path addLatitude:43.0769 longitude:-79.0727]; + [path addLatitude:43.1220 longitude:-79.0713]; + [path addLatitude:43.1441 longitude:-79.0302]; + [path addLatitude:43.1801 longitude:-79.0576]; + [path addLatitude:43.2482 longitude:-79.0604]; + [path addLatitude:43.2812 longitude:-79.0837]; + [path addLatitude:43.4509 longitude:-79.2004]; + [path addLatitude:43.6311 longitude:-78.6909]; + [path addLatitude:43.6321 longitude:-76.7958]; + [path addLatitude:43.9987 longitude:-76.4978]; + [path addLatitude:44.0965 longitude:-76.4388]; + [path addLatitude:44.1349 longitude:-76.3536]; + [path addLatitude:44.1989 longitude:-76.3124]; + [path addLatitude:44.2049 longitude:-76.2437]; + [path addLatitude:44.2413 longitude:-76.1655]; + [path addLatitude:44.2973 longitude:-76.1353]; + [path addLatitude:44.3327 longitude:-76.0474]; + [path addLatitude:44.3553 longitude:-75.9856]; + [path addLatitude:44.3749 longitude:-75.9196]; + [path addLatitude:44.3994 longitude:-75.8730]; + [path addLatitude:44.4308 longitude:-75.8221]; + [path addLatitude:44.4740 longitude:-75.8098]; + [path addLatitude:44.5425 longitude:-75.7288]; + [path addLatitude:44.6647 longitude:-75.5585]; + [path addLatitude:44.7672 longitude:-75.4088]; + [path addLatitude:44.8101 longitude:-75.3442]; + [path addLatitude:44.8383 longitude:-75.3058]; + [path addLatitude:44.8676 longitude:-75.2399]; + [path addLatitude:44.9211 longitude:-75.1204]; + [path addLatitude:44.9609 longitude:-74.9995]; + [path addLatitude:44.9803 longitude:-74.9899]; + [path addLatitude:44.9852 longitude:-74.9103]; + [path addLatitude:45.0017 longitude:-74.8856]; + [path addLatitude:45.0153 longitude:-74.8306]; + [path addLatitude:45.0046 longitude:-74.7633]; + [path addLatitude:45.0027 longitude:-74.7070]; + [path addLatitude:45.0007 longitude:-74.5642]; + [path addLatitude:44.9920 longitude:-74.1467]; + [path addLatitude:45.0037 longitude:-73.7306]; + [path addLatitude:45.0085 longitude:-73.4203]; + [path addLatitude:45.0109 longitude:-73.3430]; + [path addLatitude:44.9874 longitude:-73.3547]; + [path addLatitude:44.9648 longitude:-73.3379]; + [path addLatitude:44.9160 longitude:-73.3396]; + [path addLatitude:44.8354 longitude:-73.3739]; + [path addLatitude:44.8013 longitude:-73.3324]; + [path addLatitude:44.7419 longitude:-73.3667]; + [path addLatitude:44.6139 longitude:-73.3873]; + [path addLatitude:44.5787 longitude:-73.3736]; + [path addLatitude:44.4916 longitude:-73.3049]; + [path addLatitude:44.4289 longitude:-73.2953]; + [path addLatitude:44.3513 longitude:-73.3365]; + [path addLatitude:44.2757 longitude:-73.3118]; + [path addLatitude:44.1980 longitude:-73.3818]; + [path addLatitude:44.1142 longitude:-73.4079]; + [path addLatitude:44.0511 longitude:-73.4367]; + [path addLatitude:44.0165 longitude:-73.4065]; + [path addLatitude:43.9375 longitude:-73.4079]; + [path addLatitude:43.8771 longitude:-73.3749]; + [path addLatitude:43.8167 longitude:-73.3914]; + [path addLatitude:43.7790 longitude:-73.3557]; + [path addLatitude:43.6460 longitude:-73.4244]; + [path addLatitude:43.5893 longitude:-73.4340]; + [path addLatitude:43.5655 longitude:-73.3969]; + [path addLatitude:43.6112 longitude:-73.3818]; + [path addLatitude:43.6271 longitude:-73.3049]; + [path addLatitude:43.5764 longitude:-73.3063]; + [path addLatitude:43.5675 longitude:-73.2582]; + [path addLatitude:43.5227 longitude:-73.2445]; + [path addLatitude:43.2582 longitude:-73.2582]; + [path addLatitude:42.9715 longitude:-73.2733]; + [path addLatitude:42.8004 longitude:-73.2898]; + [path addLatitude:42.7460 longitude:-73.2664]; + [path addLatitude:42.4630 longitude:-73.3708]; + [path addLatitude:42.0840 longitude:-73.5095]; + [path addLatitude:42.0218 longitude:-73.4903]; + [path addLatitude:41.8808 longitude:-73.4999]; + [path addLatitude:41.2953 longitude:-73.5535]; + [path addLatitude:41.2128 longitude:-73.4834]; + [path addLatitude:41.1011 longitude:-73.7275]; + [path addLatitude:41.0237 longitude:-73.6644]; + [path addLatitude:40.9851 longitude:-73.6578]; + [path addLatitude:40.9509 longitude:-73.6132]; + [path addLatitude:41.1869 longitude:-72.4823]; + [path addLatitude:41.2551 longitude:-72.0950]; + [path addLatitude:41.3005 longitude:-71.9714]; + [path addLatitude:41.3108 longitude:-71.9193]; + [path addLatitude:41.1838 longitude:-71.7915]; + [path addLatitude:41.1249 longitude:-71.7929]; + [path addLatitude:41.0462 longitude:-71.7517]; + [path addLatitude:40.6306 longitude:-72.9465]; + [path addLatitude:40.5368 longitude:-73.4628]; + [path addLatitude:40.4887 longitude:-73.8885]; + [path addLatitude:40.5232 longitude:-73.9490]; + [path addLatitude:40.4772 longitude:-74.2271]; + [path addLatitude:40.4861 longitude:-74.2532]; + [path addLatitude:40.6468 longitude:-74.1866]; + [path addLatitude:40.6556 longitude:-74.0547]; + [path addLatitude:40.7618 longitude:-74.0156]; + [path addLatitude:40.8699 longitude:-73.9421]; + [path addLatitude:40.9980 longitude:-73.8934]; + [path addLatitude:41.0343 longitude:-73.9854]; + [path addLatitude:41.3268 longitude:-74.6274]; + [path addLatitude:41.3583 longitude:-74.7084]; + [path addLatitude:41.3811 longitude:-74.7101]; + [path addLatitude:41.4386 longitude:-74.8265]; + [path addLatitude:41.5075 longitude:-74.9913]; + [path addLatitude:41.6000 longitude:-75.0668]; + [path addLatitude:41.6719 longitude:-75.0366]; + [path addLatitude:41.7672 longitude:-75.0545]; + [path addLatitude:41.8808 longitude:-75.1945]; + [path addLatitude:42.0013 longitude:-75.3552]; + [path addLatitude:42.0003 longitude:-75.4266]; + [path addLatitude:42.0013 longitude:-77.0306]; + [path addLatitude:41.9993 longitude:-79.7250]; + [path addLatitude:42.0003 longitude:-79.7621]; + [path addLatitude:42.1827 longitude:-79.7621]; + [path addLatitude:42.5146 longitude:-79.7621]; + return path; +} + +- (GMSPath *)pathOfNorthCarolina { + GMSMutablePath *path = [GMSMutablePath path]; + [path addLatitude:33.7963 longitude:-78.4850]; + [path addLatitude:34.8037 longitude:-79.6742]; + [path addLatitude:34.8206 longitude:-80.8003]; + [path addLatitude:34.9377 longitude:-80.7880]; + [path addLatitude:35.1019 longitude:-80.9377]; + [path addLatitude:35.0356 longitude:-81.0379]; + [path addLatitude:35.1457 longitude:-81.0324]; + [path addLatitude:35.1660 longitude:-81.3867]; + [path addLatitude:35.1985 longitude:-82.2739]; + [path addLatitude:35.2041 longitude:-82.3933]; + [path addLatitude:35.0637 longitude:-82.7765]; + [path addLatitude:35.0817 longitude:-82.7861]; + [path addLatitude:34.9996 longitude:-83.1075]; + [path addLatitude:34.9918 longitude:-83.6183]; + [path addLatitude:34.9918 longitude:-84.3201]; + [path addLatitude:35.2131 longitude:-84.2885]; + [path addLatitude:35.2680 longitude:-84.2226]; + [path addLatitude:35.2310 longitude:-84.1113]; + [path addLatitude:35.2815 longitude:-84.0454]; + [path addLatitude:35.4058 longitude:-84.0248]; + [path addLatitude:35.4719 longitude:-83.9424]; + [path addLatitude:35.5166 longitude:-83.8559]; + [path addLatitude:35.5512 longitude:-83.6938]; + [path addLatitude:35.5680 longitude:-83.5181]; + [path addLatitude:35.6327 longitude:-83.3849]; + [path addLatitude:35.7142 longitude:-83.2475]; + [path addLatitude:35.7799 longitude:-82.9962]; + [path addLatitude:35.8445 longitude:-82.9276]; + [path addLatitude:35.9224 longitude:-82.8191]; + [path addLatitude:35.9958 longitude:-82.7710]; + [path addLatitude:36.0613 longitude:-82.6419]; + [path addLatitude:35.9702 longitude:-82.6103]; + [path addLatitude:35.9547 longitude:-82.5677]; + [path addLatitude:36.0236 longitude:-82.4730]; + [path addLatitude:36.0669 longitude:-82.4194]; + [path addLatitude:36.1168 longitude:-82.3535]; + [path addLatitude:36.1345 longitude:-82.2862]; + [path addLatitude:36.1467 longitude:-82.1461]; + [path addLatitude:36.1035 longitude:-82.1228]; + [path addLatitude:36.1268 longitude:-82.0267]; + [path addLatitude:36.2797 longitude:-81.9360]; + [path addLatitude:36.3527 longitude:-81.7987]; + [path addLatitude:36.3361 longitude:-81.7081]; + [path addLatitude:36.5880 longitude:-81.6724]; + [path addLatitude:36.5659 longitude:-80.7234]; + [path addLatitude:36.5438 longitude:-80.2977]; + [path addLatitude:36.5449 longitude:-79.6729]; + [path addLatitude:36.5449 longitude:-77.2559]; + [path addLatitude:36.5505 longitude:-75.7562]; + [path addLatitude:36.3129 longitude:-75.7068]; + [path addLatitude:35.7131 longitude:-75.4129]; + [path addLatitude:35.2041 longitude:-75.4720]; + [path addLatitude:34.9794 longitude:-76.0748]; + [path addLatitude:34.5258 longitude:-76.4951]; + [path addLatitude:34.5880 longitude:-76.8109]; + [path addLatitude:34.5314 longitude:-77.1378]; + [path addLatitude:34.3910 longitude:-77.4481]; + [path addLatitude:34.0481 longitude:-77.7983]; + [path addLatitude:33.7666 longitude:-77.9260]; + [path addLatitude:33.7963 longitude:-78.4863]; + return path; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.h new file mode 100644 index 0000000..56a9d37 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface PolylinesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.m new file mode 100644 index 0000000..518d796 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/PolylinesViewController.m @@ -0,0 +1,111 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/PolylinesViewController.h" + +#import + +@interface GMSPolyline (length) + +@property(nonatomic, readonly) double length; + +@end + +@implementation GMSPolyline (length) + +- (double)length { + GMSLengthKind kind = [self geodesic] ? kGMSLengthGeodesic : kGMSLengthRhumb; + return [[self path] lengthOfKind:kind]; +} + +@end + +static CLLocationCoordinate2D kSydneyAustralia = {-33.866901, 151.195988}; +static CLLocationCoordinate2D kHawaiiUSA = {21.291982, -157.821856}; +static CLLocationCoordinate2D kFiji = {-18, 179}; +static CLLocationCoordinate2D kMountainViewUSA = {37.423802, -122.091859}; +static CLLocationCoordinate2D kLimaPeru = {-12, -77}; +static bool kAnimate = true; + +@implementation PolylinesViewController { + NSArray *_styles; + NSArray *_lengths; + NSArray *_polys; + double _pos, _step; + GMSMapView *_mapView; +} + +- (void)tick { + for (GMSPolyline *poly in _polys) { + poly.spans = + GMSStyleSpansOffset(poly.path, _styles, _lengths, kGMSLengthGeodesic, _pos); + } + _pos -= _step; + if (kAnimate) { + __weak id weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), + dispatch_get_main_queue(), + ^{ [weakSelf tick]; }); + } +} + +- (void)initLines { + if (!_polys) { + NSMutableArray *polys = [NSMutableArray array]; + GMSMutablePath *path = [GMSMutablePath path]; + [path addCoordinate:kSydneyAustralia]; + [path addCoordinate:kFiji]; + [path addCoordinate:kHawaiiUSA]; + [path addCoordinate:kMountainViewUSA]; + [path addCoordinate:kLimaPeru]; + [path addCoordinate:kSydneyAustralia]; + path = [path pathOffsetByLatitude:-30 longitude:0]; + _lengths = @[@([path lengthOfKind:kGMSLengthGeodesic] / 21)]; + for (int i = 0; i < 30; ++i) { + GMSPolyline *poly = [[GMSPolyline alloc] init]; + poly.path = [path pathOffsetByLatitude:(i * 1.5) longitude:0]; + poly.strokeWidth = 8; + poly.geodesic = YES; + poly.map = _mapView; + [polys addObject:poly]; + } + _polys = polys; + } +} + +- (void)mapView:(GMSMapView *)mapView + didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { + [self initLines]; + [self tick]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-30 + longitude:-175 + zoom:3]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView.accessibilityElementsHidden = YES; + self.view = mapView; + mapView.delegate = self; + _mapView = mapView; + + CGFloat alpha = 1; + UIColor *green = [UIColor colorWithRed:0 green:1 blue: 0 alpha:alpha]; + UIColor *greenTransp = [UIColor colorWithRed:0 green:1 blue: 0 alpha:0]; + UIColor *red = [UIColor colorWithRed:1 green:0 blue: 0 alpha:alpha]; + UIColor *redTransp = [UIColor colorWithRed:1 green:0 blue: 0 alpha:0]; + GMSStrokeStyle *grad1 = [GMSStrokeStyle gradientFromColor:green toColor:greenTransp]; + GMSStrokeStyle *grad2 = [GMSStrokeStyle gradientFromColor:redTransp toColor:red]; + _styles = @[ + grad1, + grad2, + [GMSStrokeStyle solidColor:[UIColor colorWithWhite:0 alpha:0]], + ]; + _step = 50000; + [self initLines]; + [self tick]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.h new file mode 100644 index 0000000..fdb0b1f --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.h @@ -0,0 +1,9 @@ +#import + +@interface Samples : NSObject ++ (NSArray *)loadSections; ++ (NSArray *)loadDemos; ++ (NSDictionary *)newDemo:(Class) class + withTitle:(NSString *)title + andDescription:(NSString *)description; +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.m new file mode 100644 index 0000000..9b8ab4a --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/Samples.m @@ -0,0 +1,161 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/Samples.h" + +// Map Demos +#import "SDKDemos/Samples/BasicMapViewController.h" +#import "SDKDemos/Samples/CustomIndoorViewController.h" +#import "SDKDemos/Samples/DoubleMapViewController.h" +#import "SDKDemos/Samples/GestureControlViewController.h" +#import "SDKDemos/Samples/IndoorMuseumNavigationViewController.h" +#import "SDKDemos/Samples/IndoorViewController.h" +#import "SDKDemos/Samples/MapTypesViewController.h" +#import "SDKDemos/Samples/MapZoomViewController.h" +#import "SDKDemos/Samples/MyLocationViewController.h" +#import "SDKDemos/Samples/TrafficMapViewController.h" +#import "SDKDemos/Samples/VisibleRegionViewController.h" + +// Panorama Demos +#import "SDKDemos/Samples/FixedPanoramaViewController.h" +#import "SDKDemos/Samples/PanoramaViewController.h" + +// Overlay Demos +#import "SDKDemos/Samples/AnimatedCurrentLocationViewController.h" +#import "SDKDemos/Samples/CustomMarkersViewController.h" +#import "SDKDemos/Samples/GradientPolylinesViewController.h" +#import "SDKDemos/Samples/GroundOverlayViewController.h" +#import "SDKDemos/Samples/MarkerEventsViewController.h" +#import "SDKDemos/Samples/MarkerInfoWindowViewController.h" +#import "SDKDemos/Samples/MarkerLayerViewController.h" +#import "SDKDemos/Samples/MarkersViewController.h" +#import "SDKDemos/Samples/PolygonsViewController.h" +#import "SDKDemos/Samples/PolylinesViewController.h" +#import "SDKDemos/Samples/TileLayerViewController.h" + +// Camera Demos +#import "SDKDemos/Samples/CameraViewController.h" +#import "SDKDemos/Samples/FitBoundsViewController.h" +#import "SDKDemos/Samples/MapLayerViewController.h" + +// Services +#import "SDKDemos/Samples/GeocoderViewController.h" +#import "SDKDemos/Samples/StructuredGeocoderViewController.h" + +@implementation Samples + ++ (NSArray *)loadSections { + return @[ @"Map", @"Panorama", @"Overlays", @"Camera", @"Services" ]; +} + ++ (NSArray *)loadDemos { + NSArray *mapDemos = + @[[self newDemo:[BasicMapViewController class] + withTitle:@"Basic Map" + andDescription:nil], + [self newDemo:[MapTypesViewController class] + withTitle:@"Map Types" + andDescription:nil], + [self newDemo:[TrafficMapViewController class] + withTitle:@"Traffic Layer" + andDescription:nil], + [self newDemo:[MyLocationViewController class] + withTitle:@"My Location" + andDescription:nil], + [self newDemo:[IndoorViewController class] + withTitle:@"Indoor" + andDescription:nil], + [self newDemo:[CustomIndoorViewController class] + withTitle:@"Indoor with Custom Level Select" + andDescription:nil], + [self newDemo:[IndoorMuseumNavigationViewController class] + withTitle:@"Indoor Museum Navigator" + andDescription:nil], + [self newDemo:[GestureControlViewController class] + withTitle:@"Gesture Control" + andDescription:nil], + [self newDemo:[DoubleMapViewController class] + withTitle:@"Two Maps" + andDescription:nil], + [self newDemo:[VisibleRegionViewController class] + withTitle:@"Visible Regions" + andDescription:nil], + [self newDemo:[MapZoomViewController class] + withTitle:@"Min/Max Zoom" + andDescription:nil], + ]; + + NSArray *panoramaDemos = + @[[self newDemo:[PanoramaViewController class] + withTitle:@"Street View" + andDescription:nil], + [self newDemo:[FixedPanoramaViewController class] + withTitle:@"Fixed Street View" + andDescription:nil]]; + + NSArray *overlayDemos = + @[[self newDemo:[MarkersViewController class] + withTitle:@"Markers" + andDescription:nil], + [self newDemo:[CustomMarkersViewController class] + withTitle:@"Custom Markers" + andDescription:nil], + [self newDemo:[MarkerEventsViewController class] + withTitle:@"Marker Events" + andDescription:nil], + [self newDemo:[MarkerLayerViewController class] + withTitle:@"Marker Layer" + andDescription:nil], + [self newDemo:[MarkerInfoWindowViewController class] + withTitle:@"Custom Info Windows" + andDescription:nil], + [self newDemo:[PolygonsViewController class] + withTitle:@"Polygons" + andDescription:nil], + [self newDemo:[PolylinesViewController class] + withTitle:@"Polylines" + andDescription:nil], + [self newDemo:[GroundOverlayViewController class] + withTitle:@"Ground Overlays" + andDescription:nil], + [self newDemo:[TileLayerViewController class] + withTitle:@"Tile Layers" + andDescription:nil], + [self newDemo:[AnimatedCurrentLocationViewController class] + withTitle:@"Animated Current Location" + andDescription:nil], + [self newDemo:[GradientPolylinesViewController class] + withTitle:@"Gradient Polylines" + andDescription:nil]]; + + NSArray *cameraDemos = + @[[self newDemo:[FitBoundsViewController class] + withTitle:@"Fit Bounds" + andDescription:nil], + [self newDemo:[CameraViewController class] + withTitle:@"Camera Animation" + andDescription:nil], + [self newDemo:[MapLayerViewController class] + withTitle:@"Map Layer" + andDescription:nil]]; + + NSArray *servicesDemos = + @[[self newDemo:[GeocoderViewController class] + withTitle:@"Geocoder" + andDescription:nil], + [self newDemo:[StructuredGeocoderViewController class] + withTitle:@"Structured Geocoder" + andDescription:nil], + ]; + + return @[mapDemos, panoramaDemos, overlayDemos, cameraDemos, servicesDemos]; +} + ++ (NSDictionary *)newDemo:(Class) class + withTitle:(NSString *)title + andDescription:(NSString *)description { + return [[NSDictionary alloc] initWithObjectsAndKeys:class, @"controller", + title, @"title", description, @"description", nil]; +} +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.h new file mode 100644 index 0000000..bc852a8 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.h @@ -0,0 +1,5 @@ +#import + +@interface StructuredGeocoderViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.m new file mode 100644 index 0000000..ca66c61 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/StructuredGeocoderViewController.m @@ -0,0 +1,77 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/StructuredGeocoderViewController.h" + +#import + +@interface StructuredGeocoderViewController () + +@end + +@implementation StructuredGeocoderViewController { + GMSMapView *_mapView; + GMSGeocoder *_geocoder; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:12]; + + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.delegate = self; + + _geocoder = [[GMSGeocoder alloc] init]; + + self.view = _mapView; +} + +#pragma mark - GMSMapViewDelegate + +- (void)mapView:(GMSMapView *)mapView + didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { + // On a long press, reverse geocode this location. + GMSReverseGeocodeCallback handler = ^(GMSReverseGeocodeResponse *response, NSError *error) { + GMSAddress *address = response.firstResult; + if (address) { + NSLog(@"Geocoder result: %@", address); + + GMSMarker *marker = [GMSMarker markerWithPosition:address.coordinate]; + + marker.title = address.thoroughfare; + + NSMutableString *snippet = [[NSMutableString alloc] init]; + if (address.subLocality != NULL) { + [snippet appendString:[NSString stringWithFormat:@"subLocality: %@\n", + address.subLocality]]; + } + if (address.locality != NULL) { + [snippet appendString:[NSString stringWithFormat:@"locality: %@\n", + address.locality]]; + } + if (address.administrativeArea != NULL) { + [snippet appendString:[NSString stringWithFormat:@"administrativeArea: %@\n", + address.administrativeArea]]; + } + if (address.country != NULL) { + [snippet appendString:[NSString stringWithFormat:@"country: %@\n", + address.country]]; + } + + marker.snippet = snippet; + + marker.appearAnimation = kGMSMarkerAnimationPop; + mapView.selectedMarker = marker; + marker.map = _mapView; + } else { + NSLog(@"Could not reverse geocode point (%f,%f): %@", + coordinate.latitude, coordinate.longitude, error); + } + }; + [_geocoder reverseGeocodeCoordinate:coordinate completionHandler:handler]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.h new file mode 100644 index 0000000..e330a66 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.h @@ -0,0 +1,5 @@ +#import + +@interface TileLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.m new file mode 100644 index 0000000..d23788e --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TileLayerViewController.m @@ -0,0 +1,63 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/TileLayerViewController.h" + +#import + +@implementation TileLayerViewController { + UISegmentedControl *_switcher; + GMSMapView *_mapView; + GMSTileLayer *_tileLayer; + NSInteger _floor; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:37.78318 + longitude:-122.403874 + zoom:18]; + + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.buildingsEnabled = NO; + _mapView.indoorEnabled = NO; + self.view = _mapView; + + // The possible floors that might be shown. + NSArray *types = @[ @"1", @"2", @"3" ]; + + // Create a UISegmentedControl that is the navigationItem's titleView. + _switcher = [[UISegmentedControl alloc] initWithItems:types]; + _switcher.selectedSegmentIndex = 0; + _switcher.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _switcher.frame = + CGRectMake(0, 0, 300, _switcher.frame.size.height); + self.navigationItem.titleView = _switcher; + + // Listen to touch events on the UISegmentedControl, force initial update. + [_switcher addTarget:self action:@selector(didChangeSwitcher) + forControlEvents:UIControlEventValueChanged]; + [self didChangeSwitcher]; +} + +- (void)didChangeSwitcher { + NSString *title = + [_switcher titleForSegmentAtIndex:_switcher.selectedSegmentIndex]; + NSInteger floor = [title integerValue]; + if (_floor != floor) { + // Clear existing tileLayer, if any. + _tileLayer.map = nil; + + // Create a new GMSTileLayer with the new floor choice. + GMSTileURLConstructor urls = ^(NSUInteger x, NSUInteger y, NSUInteger zoom) { + NSString *url = [NSString stringWithFormat:@"http://www.gstatic.com/io2010maps/tiles/9/L%zd_%tu_%tu_%tu.png", floor, zoom, x, y]; + return [NSURL URLWithString:url]; + }; + _tileLayer = [GMSURLTileLayer tileLayerWithURLConstructor:urls]; + _tileLayer.map = _mapView; + _floor = floor; + } +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.h new file mode 100644 index 0000000..f7c69f2 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.h @@ -0,0 +1,5 @@ +#import + +@interface TrafficMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.m new file mode 100644 index 0000000..9ee26dd --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/TrafficMapViewController.m @@ -0,0 +1,22 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/TrafficMapViewController.h" + +#import + +@implementation TrafficMapViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:12]; + + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView.trafficEnabled = YES; + self.view = mapView; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.h b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.h new file mode 100644 index 0000000..d7747c4 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.h @@ -0,0 +1,5 @@ +#import + +@interface VisibleRegionViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.m new file mode 100644 index 0000000..e14dddd --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/Samples/VisibleRegionViewController.m @@ -0,0 +1,60 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "SDKDemos/Samples/VisibleRegionViewController.h" + +#import + +static CGFloat kOverlayHeight = 140.0f; + +@implementation VisibleRegionViewController { + GMSMapView *_mapView; + UIView *_overlay; + UIBarButtonItem *_flyInButton; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-37.81969 + longitude:144.966085 + zoom:4]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + + // Enable my location button to show more UI components updating. + _mapView.settings.myLocationButton = YES; + _mapView.myLocationEnabled = YES; + _mapView.padding = UIEdgeInsetsMake(0, 0, kOverlayHeight, 0); + self.view = _mapView; + + // Create a button that, when pressed, causes an overlaying view to fly-in/out. + _flyInButton = [[UIBarButtonItem alloc] initWithTitle:@"Toggle Overlay" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapFlyIn)]; + self.navigationItem.rightBarButtonItem = _flyInButton; + + CGRect overlayFrame = CGRectMake(0, -kOverlayHeight, 0, kOverlayHeight); + _overlay = [[UIView alloc] initWithFrame:overlayFrame]; + _overlay.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; + + _overlay.backgroundColor = [UIColor colorWithHue:0.0 saturation:1.0 brightness:1.0 alpha:0.5]; + [self.view addSubview:_overlay]; +} + +- (void)didTapFlyIn { + UIEdgeInsets padding = _mapView.padding; + + [UIView animateWithDuration:2.0 animations:^{ + CGSize size = self.view.bounds.size; + if (padding.bottom == 0.0f) { + _overlay.frame = CGRectMake(0, size.height - kOverlayHeight, size.width, kOverlayHeight); + _mapView.padding = UIEdgeInsetsMake(0, 0, kOverlayHeight, 0); + } else { + _overlay.frame = CGRectMake(0, _mapView.bounds.size.height, size.width, 0); + _mapView.padding = UIEdgeInsetsZero; + } + }]; +} + +@end diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/iTunesArtwork-1024.png b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/iTunesArtwork-1024.png new file mode 100644 index 0000000..eff1c1f Binary files /dev/null and b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/iTunesArtwork-1024.png differ diff --git a/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/main.m b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/main.m new file mode 100644 index 0000000..d0d2818 --- /dev/null +++ b/Pods/GoogleMaps/GoogleMapsSDKDemos/SDKDemos/main.m @@ -0,0 +1,13 @@ +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import + +#import "SDKDemos/SDKDemoAppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([SDKDemoAppDelegate class])); + } +} diff --git a/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h b/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h new file mode 120000 index 0000000..ac762c8 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPRequestOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h b/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h new file mode 120000 index 0000000..9dcc623 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h new file mode 120000 index 0000000..56feb9f --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 120000 index 0000000..67519d9 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h new file mode 120000 index 0000000..68fc774 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFNetworking.h b/Pods/Headers/Private/AFNetworking/AFNetworking.h new file mode 120000 index 0000000..a5a38da --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h new file mode 120000 index 0000000..fd1322d --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h b/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h new file mode 120000 index 0000000..d9b35fb --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLConnectionOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h new file mode 120000 index 0000000..ca8209b --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h new file mode 120000 index 0000000..e36a765 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h new file mode 120000 index 0000000..835101d --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 120000 index 0000000..c534ebf --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h new file mode 120000 index 0000000..f992813 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h new file mode 120000 index 0000000..8f2e221 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h new file mode 120000 index 0000000..74f6649 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIImage+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h new file mode 120000 index 0000000..a95d673 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h new file mode 120000 index 0000000..95017cc --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h new file mode 120000 index 0000000..730b167 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h new file mode 120000 index 0000000..8efd826 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h b/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h new file mode 120000 index 0000000..c8df6ef --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFCancellationToken.h b/Pods/Headers/Private/Bolts/BFCancellationToken.h new file mode 120000 index 0000000..0b69486 --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFCancellationToken.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFCancellationToken.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFCancellationTokenRegistration.h b/Pods/Headers/Private/Bolts/BFCancellationTokenRegistration.h new file mode 120000 index 0000000..c587ca7 --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFCancellationTokenRegistration.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFCancellationTokenRegistration.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFCancellationTokenSource.h b/Pods/Headers/Private/Bolts/BFCancellationTokenSource.h new file mode 120000 index 0000000..d3d5985 --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFCancellationTokenSource.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFCancellationTokenSource.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFDefines.h b/Pods/Headers/Private/Bolts/BFDefines.h new file mode 120000 index 0000000..5df18ff --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFDefines.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFDefines.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFExecutor.h b/Pods/Headers/Private/Bolts/BFExecutor.h new file mode 120000 index 0000000..c071e8c --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFExecutor.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFExecutor.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFTask.h b/Pods/Headers/Private/Bolts/BFTask.h new file mode 120000 index 0000000..5468334 --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFTask.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFTask.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BFTaskCompletionSource.h b/Pods/Headers/Private/Bolts/BFTaskCompletionSource.h new file mode 120000 index 0000000..c74760f --- /dev/null +++ b/Pods/Headers/Private/Bolts/BFTaskCompletionSource.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BFTaskCompletionSource.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/Bolts.h b/Pods/Headers/Private/Bolts/Bolts.h new file mode 120000 index 0000000..146ac6e --- /dev/null +++ b/Pods/Headers/Private/Bolts/Bolts.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/Bolts.h \ No newline at end of file diff --git a/Pods/Headers/Private/Bolts/BoltsVersion.h b/Pods/Headers/Private/Bolts/BoltsVersion.h new file mode 120000 index 0000000..0fa0e2d --- /dev/null +++ b/Pods/Headers/Private/Bolts/BoltsVersion.h @@ -0,0 +1 @@ +../../../Bolts/Bolts/Common/BoltsVersion.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/BFTask+Private.h b/Pods/Headers/Private/Parse/BFTask+Private.h new file mode 120000 index 0000000..10159b2 --- /dev/null +++ b/Pods/Headers/Private/Parse/BFTask+Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/BFTask+Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFACL.h b/Pods/Headers/Private/Parse/PFACL.h new file mode 120000 index 0000000..7c3c14f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFACL.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFACL.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFACLPrivate.h b/Pods/Headers/Private/Parse/PFACLPrivate.h new file mode 120000 index 0000000..2879692 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFACLPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ACL/PFACLPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFACLState.h b/Pods/Headers/Private/Parse/PFACLState.h new file mode 120000 index 0000000..d3b8d9b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFACLState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ACL/State/PFACLState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFACLState_Private.h b/Pods/Headers/Private/Parse/PFACLState_Private.h new file mode 120000 index 0000000..d9dd502 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFACLState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ACL/State/PFACLState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAlertView.h b/Pods/Headers/Private/Parse/PFAlertView.h new file mode 120000 index 0000000..85018b0 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAlertView.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFAlertView.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnalytics.h b/Pods/Headers/Private/Parse/PFAnalytics.h new file mode 120000 index 0000000..7751f45 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnalytics.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFAnalytics.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnalyticsController.h b/Pods/Headers/Private/Parse/PFAnalyticsController.h new file mode 120000 index 0000000..e1fc43f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnalyticsController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnalyticsUtilities.h b/Pods/Headers/Private/Parse/PFAnalyticsUtilities.h new file mode 120000 index 0000000..b383f8f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnalyticsUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnalytics_Private.h b/Pods/Headers/Private/Parse/PFAnalytics_Private.h new file mode 120000 index 0000000..defa006 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnalytics_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Analytics/PFAnalytics_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnonymousAuthenticationProvider.h b/Pods/Headers/Private/Parse/PFAnonymousAuthenticationProvider.h new file mode 120000 index 0000000..14cb5b3 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnonymousAuthenticationProvider.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnonymousUtils.h b/Pods/Headers/Private/Parse/PFAnonymousUtils.h new file mode 120000 index 0000000..7b9b437 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnonymousUtils.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFAnonymousUtils.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAnonymousUtils_Private.h b/Pods/Headers/Private/Parse/PFAnonymousUtils_Private.h new file mode 120000 index 0000000..8b4fcef --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAnonymousUtils_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousUtils_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFApplication.h b/Pods/Headers/Private/Parse/PFApplication.h new file mode 120000 index 0000000..b0d8ee6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFApplication.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFApplication.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAssert.h b/Pods/Headers/Private/Parse/PFAssert.h new file mode 120000 index 0000000..370352c --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAssert.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFAssert.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFAsyncTaskQueue.h b/Pods/Headers/Private/Parse/PFAsyncTaskQueue.h new file mode 120000 index 0000000..87cce9d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFAsyncTaskQueue.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFAsyncTaskQueue.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFBase64Encoder.h b/Pods/Headers/Private/Parse/PFBase64Encoder.h new file mode 120000 index 0000000..adae939 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFBase64Encoder.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFBase64Encoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFBaseState.h b/Pods/Headers/Private/Parse/PFBaseState.h new file mode 120000 index 0000000..6c1b489 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFBaseState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFBaseState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCachedQueryController.h b/Pods/Headers/Private/Parse/PFCachedQueryController.h new file mode 120000 index 0000000..0712e6e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCachedQueryController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/Controller/PFCachedQueryController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCategoryLoader.h b/Pods/Headers/Private/Parse/PFCategoryLoader.h new file mode 120000 index 0000000..7dedc13 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCategoryLoader.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCategoryLoader.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCloud.h b/Pods/Headers/Private/Parse/PFCloud.h new file mode 120000 index 0000000..4070bb1 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCloud.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFCloud.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCloudCodeController.h b/Pods/Headers/Private/Parse/PFCloudCodeController.h new file mode 120000 index 0000000..38ca262 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCloudCodeController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/CloudCode/PFCloudCodeController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandCache.h b/Pods/Headers/Private/Parse/PFCommandCache.h new file mode 120000 index 0000000..b9d3183 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandCache.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCommandCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandCache_Private.h b/Pods/Headers/Private/Parse/PFCommandCache_Private.h new file mode 120000 index 0000000..aafb22b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandCache_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCommandCache_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandResult.h b/Pods/Headers/Private/Parse/PFCommandResult.h new file mode 120000 index 0000000..2f1114a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandResult.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCommandResult.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandRunning.h b/Pods/Headers/Private/Parse/PFCommandRunning.h new file mode 120000 index 0000000..f2d7c78 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandRunning.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandRunningConstants.h b/Pods/Headers/Private/Parse/PFCommandRunningConstants.h new file mode 120000 index 0000000..3a73786 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandRunningConstants.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCommandURLRequestConstructor.h b/Pods/Headers/Private/Parse/PFCommandURLRequestConstructor.h new file mode 120000 index 0000000..bb68366 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCommandURLRequestConstructor.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFConfig.h b/Pods/Headers/Private/Parse/PFConfig.h new file mode 120000 index 0000000..919b834 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFConfig.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFConfig.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFConfigController.h b/Pods/Headers/Private/Parse/PFConfigController.h new file mode 120000 index 0000000..354d1e5 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFConfigController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Config/Controller/PFConfigController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFConfig_Private.h b/Pods/Headers/Private/Parse/PFConfig_Private.h new file mode 120000 index 0000000..64be7b4 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFConfig_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Config/PFConfig_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFConstants.h b/Pods/Headers/Private/Parse/PFConstants.h new file mode 120000 index 0000000..3b96186 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFConstants.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCoreDataProvider.h b/Pods/Headers/Private/Parse/PFCoreDataProvider.h new file mode 120000 index 0000000..738cd66 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCoreDataProvider.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCoreDataProvider.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCoreManager.h b/Pods/Headers/Private/Parse/PFCoreManager.h new file mode 120000 index 0000000..c856497 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCoreManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFCoreManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCurrentConfigController.h b/Pods/Headers/Private/Parse/PFCurrentConfigController.h new file mode 120000 index 0000000..aa2f13d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCurrentConfigController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCurrentInstallationController.h b/Pods/Headers/Private/Parse/PFCurrentInstallationController.h new file mode 120000 index 0000000..c935649 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCurrentInstallationController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCurrentObjectControlling.h b/Pods/Headers/Private/Parse/PFCurrentObjectControlling.h new file mode 120000 index 0000000..0d5e144 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCurrentObjectControlling.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/CurrentController/PFCurrentObjectControlling.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFCurrentUserController.h b/Pods/Headers/Private/Parse/PFCurrentUserController.h new file mode 120000 index 0000000..4c70eaa --- /dev/null +++ b/Pods/Headers/Private/Parse/PFCurrentUserController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFDataProvider.h b/Pods/Headers/Private/Parse/PFDataProvider.h new file mode 120000 index 0000000..52db407 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFDataProvider.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFDataProvider.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFDateFormatter.h b/Pods/Headers/Private/Parse/PFDateFormatter.h new file mode 120000 index 0000000..14af49b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFDateFormatter.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFDateFormatter.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFDecoder.h b/Pods/Headers/Private/Parse/PFDecoder.h new file mode 120000 index 0000000..e8ca6e2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFDecoder.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFDecoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFDefaultACLController.h b/Pods/Headers/Private/Parse/PFDefaultACLController.h new file mode 120000 index 0000000..28ee2d0 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFDefaultACLController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFDevice.h b/Pods/Headers/Private/Parse/PFDevice.h new file mode 120000 index 0000000..45f3174 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFDevice.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFDevice.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFEncoder.h b/Pods/Headers/Private/Parse/PFEncoder.h new file mode 120000 index 0000000..ccfe72c --- /dev/null +++ b/Pods/Headers/Private/Parse/PFEncoder.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFEncoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFErrorUtilities.h b/Pods/Headers/Private/Parse/PFErrorUtilities.h new file mode 120000 index 0000000..6cc92b4 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFErrorUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFErrorUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFEventuallyPin.h b/Pods/Headers/Private/Parse/PFEventuallyPin.h new file mode 120000 index 0000000..d7914fd --- /dev/null +++ b/Pods/Headers/Private/Parse/PFEventuallyPin.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFEventuallyPin.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFEventuallyQueue.h b/Pods/Headers/Private/Parse/PFEventuallyQueue.h new file mode 120000 index 0000000..93c4119 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFEventuallyQueue.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFEventuallyQueue.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFEventuallyQueue_Private.h b/Pods/Headers/Private/Parse/PFEventuallyQueue_Private.h new file mode 120000 index 0000000..b7d09c4 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFEventuallyQueue_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFEventuallyQueue_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFieldOperation.h b/Pods/Headers/Private/Parse/PFFieldOperation.h new file mode 120000 index 0000000..f0a3bab --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFieldOperation.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/FieldOperation/PFFieldOperation.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFieldOperationDecoder.h b/Pods/Headers/Private/Parse/PFFieldOperationDecoder.h new file mode 120000 index 0000000..2cd60fe --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFieldOperationDecoder.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFile.h b/Pods/Headers/Private/Parse/PFFile.h new file mode 120000 index 0000000..05ca154 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFile.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFFile.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileController.h b/Pods/Headers/Private/Parse/PFFileController.h new file mode 120000 index 0000000..8c0f296 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/Controller/PFFileController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileDataStream.h b/Pods/Headers/Private/Parse/PFFileDataStream.h new file mode 120000 index 0000000..54cbb17 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileDataStream.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileManager.h b/Pods/Headers/Private/Parse/PFFileManager.h new file mode 120000 index 0000000..09a9757 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFFileManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileStagingController.h b/Pods/Headers/Private/Parse/PFFileStagingController.h new file mode 120000 index 0000000..af38d1e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileStagingController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/Controller/PFFileStagingController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileState.h b/Pods/Headers/Private/Parse/PFFileState.h new file mode 120000 index 0000000..f70310a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/State/PFFileState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFileState_Private.h b/Pods/Headers/Private/Parse/PFFileState_Private.h new file mode 120000 index 0000000..fd636fa --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFileState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/State/PFFileState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFFile_Private.h b/Pods/Headers/Private/Parse/PFFile_Private.h new file mode 120000 index 0000000..53b4a61 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFFile_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/PFFile_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFGeoPoint.h b/Pods/Headers/Private/Parse/PFGeoPoint.h new file mode 120000 index 0000000..e9079fe --- /dev/null +++ b/Pods/Headers/Private/Parse/PFGeoPoint.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFGeoPoint.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFGeoPointPrivate.h b/Pods/Headers/Private/Parse/PFGeoPointPrivate.h new file mode 120000 index 0000000..f151ab9 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFGeoPointPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFGeoPointPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFHTTPRequest.h b/Pods/Headers/Private/Parse/PFHTTPRequest.h new file mode 120000 index 0000000..578b9f2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFHTTPRequest.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/HTTPRequest/PFHTTPRequest.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFHTTPURLRequestConstructor.h b/Pods/Headers/Private/Parse/PFHTTPURLRequestConstructor.h new file mode 120000 index 0000000..f2e4304 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFHTTPURLRequestConstructor.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFHash.h b/Pods/Headers/Private/Parse/PFHash.h new file mode 120000 index 0000000..ecea29b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFHash.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFHash.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallation.h b/Pods/Headers/Private/Parse/PFInstallation.h new file mode 120000 index 0000000..1f2d37a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallation.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFInstallation.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallationConstants.h b/Pods/Headers/Private/Parse/PFInstallationConstants.h new file mode 120000 index 0000000..a4ca8a6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallationConstants.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallationController.h b/Pods/Headers/Private/Parse/PFInstallationController.h new file mode 120000 index 0000000..530f6c2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallationController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/Controller/PFInstallationController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallationIdentifierStore.h b/Pods/Headers/Private/Parse/PFInstallationIdentifierStore.h new file mode 120000 index 0000000..827fb6c --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallationIdentifierStore.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallationIdentifierStore_Private.h b/Pods/Headers/Private/Parse/PFInstallationIdentifierStore_Private.h new file mode 120000 index 0000000..acff66f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallationIdentifierStore_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInstallationPrivate.h b/Pods/Headers/Private/Parse/PFInstallationPrivate.h new file mode 120000 index 0000000..fc6ac83 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInstallationPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Installation/PFInstallationPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFInternalUtils.h b/Pods/Headers/Private/Parse/PFInternalUtils.h new file mode 120000 index 0000000..a0e19bf --- /dev/null +++ b/Pods/Headers/Private/Parse/PFInternalUtils.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFInternalUtils.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFJSONSerialization.h b/Pods/Headers/Private/Parse/PFJSONSerialization.h new file mode 120000 index 0000000..786edd6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFJSONSerialization.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFJSONSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFKeyValueCache.h b/Pods/Headers/Private/Parse/PFKeyValueCache.h new file mode 120000 index 0000000..f68af04 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFKeyValueCache.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFKeyValueCache_Private.h b/Pods/Headers/Private/Parse/PFKeyValueCache_Private.h new file mode 120000 index 0000000..d971da7 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFKeyValueCache_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/KeyValueCache/PFKeyValueCache_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFKeychainStore.h b/Pods/Headers/Private/Parse/PFKeychainStore.h new file mode 120000 index 0000000..1609762 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFKeychainStore.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFKeychainStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFLocationManager.h b/Pods/Headers/Private/Parse/PFLocationManager.h new file mode 120000 index 0000000..c534560 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFLocationManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFLocationManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFLogger.h b/Pods/Headers/Private/Parse/PFLogger.h new file mode 120000 index 0000000..f100f88 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFLogger.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFLogger.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFLogging.h b/Pods/Headers/Private/Parse/PFLogging.h new file mode 120000 index 0000000..0957f9c --- /dev/null +++ b/Pods/Headers/Private/Parse/PFLogging.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFLogging.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMacros.h b/Pods/Headers/Private/Parse/PFMacros.h new file mode 120000 index 0000000..be0a4a8 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMacros.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFMacros.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMultiProcessFileLock.h b/Pods/Headers/Private/Parse/PFMultiProcessFileLock.h new file mode 120000 index 0000000..d40b109 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMultiProcessFileLock.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMultiProcessFileLockController.h b/Pods/Headers/Private/Parse/PFMultiProcessFileLockController.h new file mode 120000 index 0000000..8e59276 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMultiProcessFileLockController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMulticastDelegate.h b/Pods/Headers/Private/Parse/PFMulticastDelegate.h new file mode 120000 index 0000000..c731e9b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMulticastDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFMulticastDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableACLState.h b/Pods/Headers/Private/Parse/PFMutableACLState.h new file mode 120000 index 0000000..05bf135 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableACLState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ACL/State/PFMutableACLState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableFileState.h b/Pods/Headers/Private/Parse/PFMutableFileState.h new file mode 120000 index 0000000..d08ab4a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableFileState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/File/State/PFMutableFileState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableObjectState.h b/Pods/Headers/Private/Parse/PFMutableObjectState.h new file mode 120000 index 0000000..e715fb4 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableObjectState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/State/PFMutableObjectState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutablePushState.h b/Pods/Headers/Private/Parse/PFMutablePushState.h new file mode 120000 index 0000000..3fe0c5d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutablePushState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/State/PFMutablePushState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableQueryState.h b/Pods/Headers/Private/Parse/PFMutableQueryState.h new file mode 120000 index 0000000..8e8c7a5 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableQueryState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/State/PFMutableQueryState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableRelationState.h b/Pods/Headers/Private/Parse/PFMutableRelationState.h new file mode 120000 index 0000000..13a1999 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableRelationState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Relation/State/PFMutableRelationState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFMutableUserState.h b/Pods/Headers/Private/Parse/PFMutableUserState.h new file mode 120000 index 0000000..d5dbab7 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFMutableUserState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/State/PFMutableUserState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFNetworkActivityIndicatorManager.h b/Pods/Headers/Private/Parse/PFNetworkActivityIndicatorManager.h new file mode 120000 index 0000000..1ae2fdb --- /dev/null +++ b/Pods/Headers/Private/Parse/PFNetworkActivityIndicatorManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFNetworkCommand.h b/Pods/Headers/Private/Parse/PFNetworkCommand.h new file mode 120000 index 0000000..2c8a391 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFNetworkCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFNetworkCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFNullability.h b/Pods/Headers/Private/Parse/PFNullability.h new file mode 120000 index 0000000..d3b6c2e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFNullability.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFNullability.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObject+Subclass.h b/Pods/Headers/Private/Parse/PFObject+Subclass.h new file mode 120000 index 0000000..ccbad16 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObject+Subclass.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFObject+Subclass.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObject.h b/Pods/Headers/Private/Parse/PFObject.h new file mode 120000 index 0000000..b00d5bb --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObject.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFObject.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectBatchController.h b/Pods/Headers/Private/Parse/PFObjectBatchController.h new file mode 120000 index 0000000..ed0585f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectBatchController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectConstants.h b/Pods/Headers/Private/Parse/PFObjectConstants.h new file mode 120000 index 0000000..2834ef6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectConstants.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Constants/PFObjectConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectController.h b/Pods/Headers/Private/Parse/PFObjectController.h new file mode 120000 index 0000000..19d572b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Controller/PFObjectController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectController_Private.h b/Pods/Headers/Private/Parse/PFObjectController_Private.h new file mode 120000 index 0000000..3df14b2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectController_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Controller/PFObjectController_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectControlling.h b/Pods/Headers/Private/Parse/PFObjectControlling.h new file mode 120000 index 0000000..facfd09 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectControlling.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Controller/PFObjectControlling.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectEstimatedData.h b/Pods/Headers/Private/Parse/PFObjectEstimatedData.h new file mode 120000 index 0000000..2627e92 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectEstimatedData.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectFileCoder.h b/Pods/Headers/Private/Parse/PFObjectFileCoder.h new file mode 120000 index 0000000..4d9fc61 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectFileCoder.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectFileCodingLogic.h b/Pods/Headers/Private/Parse/PFObjectFileCodingLogic.h new file mode 120000 index 0000000..c5bb3c6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectFileCodingLogic.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectFilePersistenceController.h b/Pods/Headers/Private/Parse/PFObjectFilePersistenceController.h new file mode 120000 index 0000000..b2f5bb6 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectFilePersistenceController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectLocalIdStore.h b/Pods/Headers/Private/Parse/PFObjectLocalIdStore.h new file mode 120000 index 0000000..e2cbdf8 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectLocalIdStore.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectPrivate.h b/Pods/Headers/Private/Parse/PFObjectPrivate.h new file mode 120000 index 0000000..849167d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/PFObjectPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectState.h b/Pods/Headers/Private/Parse/PFObjectState.h new file mode 120000 index 0000000..8835423 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/State/PFObjectState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectState_Private.h b/Pods/Headers/Private/Parse/PFObjectState_Private.h new file mode 120000 index 0000000..0068676 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/State/PFObjectState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectSubclassInfo.h b/Pods/Headers/Private/Parse/PFObjectSubclassInfo.h new file mode 120000 index 0000000..f408e73 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectSubclassInfo.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectSubclassingController.h b/Pods/Headers/Private/Parse/PFObjectSubclassingController.h new file mode 120000 index 0000000..6f54ea8 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectSubclassingController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFObjectUtilities.h b/Pods/Headers/Private/Parse/PFObjectUtilities.h new file mode 120000 index 0000000..ca1e591 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFObjectUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFOfflineObjectController.h b/Pods/Headers/Private/Parse/PFOfflineObjectController.h new file mode 120000 index 0000000..2450581 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFOfflineObjectController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFOfflineQueryController.h b/Pods/Headers/Private/Parse/PFOfflineQueryController.h new file mode 120000 index 0000000..61522e9 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFOfflineQueryController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFOfflineQueryLogic.h b/Pods/Headers/Private/Parse/PFOfflineQueryLogic.h new file mode 120000 index 0000000..51de493 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFOfflineQueryLogic.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFOfflineStore.h b/Pods/Headers/Private/Parse/PFOfflineStore.h new file mode 120000 index 0000000..e2a28d5 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFOfflineStore.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFOperationSet.h b/Pods/Headers/Private/Parse/PFOperationSet.h new file mode 120000 index 0000000..b62dded --- /dev/null +++ b/Pods/Headers/Private/Parse/PFOperationSet.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/OperationSet/PFOperationSet.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPaymentTransactionObserver.h b/Pods/Headers/Private/Parse/PFPaymentTransactionObserver.h new file mode 120000 index 0000000..0cf1120 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPaymentTransactionObserver.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPaymentTransactionObserver_Private.h b/Pods/Headers/Private/Parse/PFPaymentTransactionObserver_Private.h new file mode 120000 index 0000000..80578e3 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPaymentTransactionObserver_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPin.h b/Pods/Headers/Private/Parse/PFPin.h new file mode 120000 index 0000000..a7301b9 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPin.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/Pin/PFPin.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPinningEventuallyQueue.h b/Pods/Headers/Private/Parse/PFPinningEventuallyQueue.h new file mode 120000 index 0000000..5481dca --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPinningEventuallyQueue.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFPinningEventuallyQueue.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPinningObjectStore.h b/Pods/Headers/Private/Parse/PFPinningObjectStore.h new file mode 120000 index 0000000..5ac2044 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPinningObjectStore.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFProduct+Private.h b/Pods/Headers/Private/Parse/PFProduct+Private.h new file mode 120000 index 0000000..d698072 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFProduct+Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Product/PFProduct+Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFProduct.h b/Pods/Headers/Private/Parse/PFProduct.h new file mode 120000 index 0000000..f4f4940 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFProduct.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFProduct.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFProductsRequestHandler.h b/Pods/Headers/Private/Parse/PFProductsRequestHandler.h new file mode 120000 index 0000000..b06ea1d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFProductsRequestHandler.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPropertyInfo.h b/Pods/Headers/Private/Parse/PFPropertyInfo.h new file mode 120000 index 0000000..3571a7e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPropertyInfo.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPropertyInfo_Private.h b/Pods/Headers/Private/Parse/PFPropertyInfo_Private.h new file mode 120000 index 0000000..3568406 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPropertyInfo_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPropertyInfo_Runtime.h b/Pods/Headers/Private/Parse/PFPropertyInfo_Runtime.h new file mode 120000 index 0000000..6c7c2b3 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPropertyInfo_Runtime.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPurchase.h b/Pods/Headers/Private/Parse/PFPurchase.h new file mode 120000 index 0000000..8d7cb4a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPurchase.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFPurchase.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPurchaseController.h b/Pods/Headers/Private/Parse/PFPurchaseController.h new file mode 120000 index 0000000..91f42f4 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPurchaseController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPush.h b/Pods/Headers/Private/Parse/PFPush.h new file mode 120000 index 0000000..930cb47 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPush.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFPush.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushChannelsController.h b/Pods/Headers/Private/Parse/PFPushChannelsController.h new file mode 120000 index 0000000..84a2d30 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushChannelsController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushController.h b/Pods/Headers/Private/Parse/PFPushController.h new file mode 120000 index 0000000..3f11f0e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/Controller/PFPushController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushManager.h b/Pods/Headers/Private/Parse/PFPushManager.h new file mode 120000 index 0000000..04cbb0d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/Manager/PFPushManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushPrivate.h b/Pods/Headers/Private/Parse/PFPushPrivate.h new file mode 120000 index 0000000..d25e5ff --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/PFPushPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushState.h b/Pods/Headers/Private/Parse/PFPushState.h new file mode 120000 index 0000000..686f491 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/State/PFPushState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushState_Private.h b/Pods/Headers/Private/Parse/PFPushState_Private.h new file mode 120000 index 0000000..87e61e5 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/State/PFPushState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFPushUtilities.h b/Pods/Headers/Private/Parse/PFPushUtilities.h new file mode 120000 index 0000000..e6460a3 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFPushUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Push/Utilites/PFPushUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQuery.h b/Pods/Headers/Private/Parse/PFQuery.h new file mode 120000 index 0000000..1ea0442 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQuery.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFQuery.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQueryController.h b/Pods/Headers/Private/Parse/PFQueryController.h new file mode 120000 index 0000000..5635d68 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQueryController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/Controller/PFQueryController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQueryPrivate.h b/Pods/Headers/Private/Parse/PFQueryPrivate.h new file mode 120000 index 0000000..2f1bf2f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQueryPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/PFQueryPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQueryState.h b/Pods/Headers/Private/Parse/PFQueryState.h new file mode 120000 index 0000000..381352e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQueryState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/State/PFQueryState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQueryState_Private.h b/Pods/Headers/Private/Parse/PFQueryState_Private.h new file mode 120000 index 0000000..97a6657 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQueryState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/State/PFQueryState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFQueryUtilities.h b/Pods/Headers/Private/Parse/PFQueryUtilities.h new file mode 120000 index 0000000..f5c92e2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFQueryUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTAnalyticsCommand.h b/Pods/Headers/Private/Parse/PFRESTAnalyticsCommand.h new file mode 120000 index 0000000..6fe355e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTAnalyticsCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTCloudCommand.h b/Pods/Headers/Private/Parse/PFRESTCloudCommand.h new file mode 120000 index 0000000..46f5e1f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTCloudCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTCloudCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTCommand.h b/Pods/Headers/Private/Parse/PFRESTCommand.h new file mode 120000 index 0000000..4fe070b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTCommand_Private.h b/Pods/Headers/Private/Parse/PFRESTCommand_Private.h new file mode 120000 index 0000000..df23738 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTCommand_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTCommand_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTConfigCommand.h b/Pods/Headers/Private/Parse/PFRESTConfigCommand.h new file mode 120000 index 0000000..f671a72 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTConfigCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTConfigCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTFileCommand.h b/Pods/Headers/Private/Parse/PFRESTFileCommand.h new file mode 120000 index 0000000..e31ba58 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTFileCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTFileCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTObjectBatchCommand.h b/Pods/Headers/Private/Parse/PFRESTObjectBatchCommand.h new file mode 120000 index 0000000..828b203 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTObjectBatchCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTObjectCommand.h b/Pods/Headers/Private/Parse/PFRESTObjectCommand.h new file mode 120000 index 0000000..7de026d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTObjectCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTObjectCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTPushCommand.h b/Pods/Headers/Private/Parse/PFRESTPushCommand.h new file mode 120000 index 0000000..d2a676e --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTPushCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTPushCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTQueryCommand.h b/Pods/Headers/Private/Parse/PFRESTQueryCommand.h new file mode 120000 index 0000000..82f2793 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTQueryCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTQueryCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTSessionCommand.h b/Pods/Headers/Private/Parse/PFRESTSessionCommand.h new file mode 120000 index 0000000..e3651cb --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTSessionCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTSessionCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRESTUserCommand.h b/Pods/Headers/Private/Parse/PFRESTUserCommand.h new file mode 120000 index 0000000..9a13083 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRESTUserCommand.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/PFRESTUserCommand.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFReachability.h b/Pods/Headers/Private/Parse/PFReachability.h new file mode 120000 index 0000000..ac4e8fa --- /dev/null +++ b/Pods/Headers/Private/Parse/PFReachability.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFReachability.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRelation.h b/Pods/Headers/Private/Parse/PFRelation.h new file mode 120000 index 0000000..a3e8444 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRelation.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFRelation.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRelationPrivate.h b/Pods/Headers/Private/Parse/PFRelationPrivate.h new file mode 120000 index 0000000..0115a6f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRelationPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Relation/PFRelationPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRelationState.h b/Pods/Headers/Private/Parse/PFRelationState.h new file mode 120000 index 0000000..f2659be --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRelationState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Relation/State/PFRelationState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRelationState_Private.h b/Pods/Headers/Private/Parse/PFRelationState_Private.h new file mode 120000 index 0000000..ac2cfe1 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRelationState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Relation/State/PFRelationState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFRole.h b/Pods/Headers/Private/Parse/PFRole.h new file mode 120000 index 0000000..26cd86c --- /dev/null +++ b/Pods/Headers/Private/Parse/PFRole.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFRole.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSQLiteDatabase.h b/Pods/Headers/Private/Parse/PFSQLiteDatabase.h new file mode 120000 index 0000000..787b1e8 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSQLiteDatabase.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSQLiteDatabaseController.h b/Pods/Headers/Private/Parse/PFSQLiteDatabaseController.h new file mode 120000 index 0000000..37b616f --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSQLiteDatabaseController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSQLiteDatabaseResult.h b/Pods/Headers/Private/Parse/PFSQLiteDatabaseResult.h new file mode 120000 index 0000000..a2c98a3 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSQLiteDatabaseResult.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSQLiteDatabase_Private.h b/Pods/Headers/Private/Parse/PFSQLiteDatabase_Private.h new file mode 120000 index 0000000..3165b80 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSQLiteDatabase_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSQLiteStatement.h b/Pods/Headers/Private/Parse/PFSQLiteStatement.h new file mode 120000 index 0000000..3ef2050 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSQLiteStatement.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSession.h b/Pods/Headers/Private/Parse/PFSession.h new file mode 120000 index 0000000..4c43360 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSession.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFSession.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSessionController.h b/Pods/Headers/Private/Parse/PFSessionController.h new file mode 120000 index 0000000..19acd46 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSessionController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Session/Controller/PFSessionController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSessionUtilities.h b/Pods/Headers/Private/Parse/PFSessionUtilities.h new file mode 120000 index 0000000..13ac6c5 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSessionUtilities.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSession_Private.h b/Pods/Headers/Private/Parse/PFSession_Private.h new file mode 120000 index 0000000..077e55a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSession_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Session/PFSession_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFSubclassing.h b/Pods/Headers/Private/Parse/PFSubclassing.h new file mode 120000 index 0000000..901cadc --- /dev/null +++ b/Pods/Headers/Private/Parse/PFSubclassing.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFSubclassing.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFTaskQueue.h b/Pods/Headers/Private/Parse/PFTaskQueue.h new file mode 120000 index 0000000..d7ccda7 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFTaskQueue.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFTaskQueue.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFThreadsafety.h b/Pods/Headers/Private/Parse/PFThreadsafety.h new file mode 120000 index 0000000..d670b07 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFThreadsafety.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ThreadSafety/PFThreadsafety.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLConstructor.h b/Pods/Headers/Private/Parse/PFURLConstructor.h new file mode 120000 index 0000000..6760b55 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLConstructor.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/HTTPRequest/PFURLConstructor.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSession.h b/Pods/Headers/Private/Parse/PFURLSession.h new file mode 120000 index 0000000..767bbbf --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSession.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionCommandRunner.h b/Pods/Headers/Private/Parse/PFURLSessionCommandRunner.h new file mode 120000 index 0000000..a5bcf23 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionCommandRunner.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionCommandRunner_Private.h b/Pods/Headers/Private/Parse/PFURLSessionCommandRunner_Private.h new file mode 120000 index 0000000..4079814 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionCommandRunner_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate.h b/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate.h new file mode 120000 index 0000000..c0c5ccb --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate_Private.h b/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate_Private.h new file mode 120000 index 0000000..8d6abcf --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionDataTaskDelegate_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionFileDownloadTaskDelegate.h b/Pods/Headers/Private/Parse/PFURLSessionFileDownloadTaskDelegate.h new file mode 120000 index 0000000..61d5d80 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionFileDownloadTaskDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionJSONDataTaskDelegate.h b/Pods/Headers/Private/Parse/PFURLSessionJSONDataTaskDelegate.h new file mode 120000 index 0000000..1933d5b --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionJSONDataTaskDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSessionUploadTaskDelegate.h b/Pods/Headers/Private/Parse/PFURLSessionUploadTaskDelegate.h new file mode 120000 index 0000000..5327ead --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSessionUploadTaskDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFURLSession_Private.h b/Pods/Headers/Private/Parse/PFURLSession_Private.h new file mode 120000 index 0000000..d3c1649 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFURLSession_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUser.h b/Pods/Headers/Private/Parse/PFUser.h new file mode 120000 index 0000000..01e4199 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUser.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFUser.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserAuthenticationController.h b/Pods/Headers/Private/Parse/PFUserAuthenticationController.h new file mode 120000 index 0000000..3b222ff --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserAuthenticationController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserAuthenticationDelegate.h b/Pods/Headers/Private/Parse/PFUserAuthenticationDelegate.h new file mode 120000 index 0000000..3b6b473 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserAuthenticationDelegate.h @@ -0,0 +1 @@ +../../../Parse/Parse/PFUserAuthenticationDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserConstants.h b/Pods/Headers/Private/Parse/PFUserConstants.h new file mode 120000 index 0000000..2ad9193 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserConstants.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/Constants/PFUserConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserController.h b/Pods/Headers/Private/Parse/PFUserController.h new file mode 120000 index 0000000..ed6c494 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserController.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/Controller/PFUserController.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserFileCodingLogic.h b/Pods/Headers/Private/Parse/PFUserFileCodingLogic.h new file mode 120000 index 0000000..ca0302d --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserFileCodingLogic.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserPrivate.h b/Pods/Headers/Private/Parse/PFUserPrivate.h new file mode 120000 index 0000000..114e09a --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserPrivate.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/PFUserPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserState.h b/Pods/Headers/Private/Parse/PFUserState.h new file mode 120000 index 0000000..f18a548 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserState.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/State/PFUserState.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFUserState_Private.h b/Pods/Headers/Private/Parse/PFUserState_Private.h new file mode 120000 index 0000000..b5b0d81 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFUserState_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/User/State/PFUserState_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/PFWeakValue.h b/Pods/Headers/Private/Parse/PFWeakValue.h new file mode 120000 index 0000000..7cc66d2 --- /dev/null +++ b/Pods/Headers/Private/Parse/PFWeakValue.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/PFWeakValue.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/Parse.h b/Pods/Headers/Private/Parse/Parse.h new file mode 120000 index 0000000..699c11c --- /dev/null +++ b/Pods/Headers/Private/Parse/Parse.h @@ -0,0 +1 @@ +../../../Parse/Parse/Parse.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/ParseInternal.h b/Pods/Headers/Private/Parse/ParseInternal.h new file mode 120000 index 0000000..96a779a --- /dev/null +++ b/Pods/Headers/Private/Parse/ParseInternal.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ParseInternal.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/ParseManager.h b/Pods/Headers/Private/Parse/ParseManager.h new file mode 120000 index 0000000..1644f32 --- /dev/null +++ b/Pods/Headers/Private/Parse/ParseManager.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ParseManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/ParseModule.h b/Pods/Headers/Private/Parse/ParseModule.h new file mode 120000 index 0000000..9f00abd --- /dev/null +++ b/Pods/Headers/Private/Parse/ParseModule.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/ParseModule.h \ No newline at end of file diff --git a/Pods/Headers/Private/Parse/Parse_Private.h b/Pods/Headers/Private/Parse/Parse_Private.h new file mode 120000 index 0000000..c149580 --- /dev/null +++ b/Pods/Headers/Private/Parse/Parse_Private.h @@ -0,0 +1 @@ +../../../Parse/Parse/Internal/Parse_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFActionButton.h b/Pods/Headers/Private/ParseUI/PFActionButton.h new file mode 120000 index 0000000..4218e48 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFActionButton.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFActivityIndicatorCollectionReusableView.h b/Pods/Headers/Private/ParseUI/PFActivityIndicatorCollectionReusableView.h new file mode 120000 index 0000000..df46b16 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFActivityIndicatorCollectionReusableView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFActivityIndicatorTableViewCell.h b/Pods/Headers/Private/ParseUI/PFActivityIndicatorTableViewCell.h new file mode 120000 index 0000000..50c1ce3 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFActivityIndicatorTableViewCell.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFCollectionViewCell.h b/Pods/Headers/Private/ParseUI/PFCollectionViewCell.h new file mode 120000 index 0000000..af4cf00 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFCollectionViewCell.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFColor.h b/Pods/Headers/Private/ParseUI/PFColor.h new file mode 120000 index 0000000..2042f25 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFColor.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFDismissButton.h b/Pods/Headers/Private/ParseUI/PFDismissButton.h new file mode 120000 index 0000000..a736731 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFDismissButton.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFImage.h b/Pods/Headers/Private/ParseUI/PFImage.h new file mode 120000 index 0000000..8da6b34 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFImage.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFImageCache.h b/Pods/Headers/Private/ParseUI/PFImageCache.h new file mode 120000 index 0000000..6aefcce --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFImageCache.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/PFImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFImageView.h b/Pods/Headers/Private/ParseUI/PFImageView.h new file mode 120000 index 0000000..6998b25 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFImageView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Views/PFImageView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFLoadingView.h b/Pods/Headers/Private/ParseUI/PFLoadingView.h new file mode 120000 index 0000000..23ffd12 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFLoadingView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFLocalization.h b/Pods/Headers/Private/ParseUI/PFLocalization.h new file mode 120000 index 0000000..81c19f7 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFLocalization.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/PFLocalization.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFLogInView.h b/Pods/Headers/Private/ParseUI/PFLogInView.h new file mode 120000 index 0000000..03efbc7 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFLogInView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFLogInViewController.h b/Pods/Headers/Private/ParseUI/PFLogInViewController.h new file mode 120000 index 0000000..f640f6e --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFLogInViewController.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFLogInView_Private.h b/Pods/Headers/Private/ParseUI/PFLogInView_Private.h new file mode 120000 index 0000000..5a1445a --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFLogInView_Private.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/LogInViewController/PFLogInView_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFPrimaryButton.h b/Pods/Headers/Private/ParseUI/PFPrimaryButton.h new file mode 120000 index 0000000..b3b12bf --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFPrimaryButton.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFProductTableViewController.h b/Pods/Headers/Private/ParseUI/PFProductTableViewController.h new file mode 120000 index 0000000..b59c01b --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFProductTableViewController.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFPurchaseTableViewCell.h b/Pods/Headers/Private/ParseUI/PFPurchaseTableViewCell.h new file mode 120000 index 0000000..4832020 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFPurchaseTableViewCell.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFQueryCollectionViewController.h b/Pods/Headers/Private/ParseUI/PFQueryCollectionViewController.h new file mode 120000 index 0000000..2912eff --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFQueryCollectionViewController.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFQueryTableViewController.h b/Pods/Headers/Private/ParseUI/PFQueryTableViewController.h new file mode 120000 index 0000000..e61be1d --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFQueryTableViewController.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFRect.h b/Pods/Headers/Private/ParseUI/PFRect.h new file mode 120000 index 0000000..fcab421 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFRect.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFResources.h b/Pods/Headers/Private/ParseUI/PFResources.h new file mode 120000 index 0000000..cb18b2f --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFResources.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Generated/PFResources.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFSignUpView.h b/Pods/Headers/Private/ParseUI/PFSignUpView.h new file mode 120000 index 0000000..ce44295 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFSignUpView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFSignUpViewController.h b/Pods/Headers/Private/ParseUI/PFSignUpViewController.h new file mode 120000 index 0000000..5c962e0 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFSignUpViewController.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFTableViewCell.h b/Pods/Headers/Private/ParseUI/PFTableViewCell.h new file mode 120000 index 0000000..1f9ce36 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFTableViewCell.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Cells/PFTableViewCell.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFTextButton.h b/Pods/Headers/Private/ParseUI/PFTextButton.h new file mode 120000 index 0000000..bd398e9 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFTextButton.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFTextField.h b/Pods/Headers/Private/ParseUI/PFTextField.h new file mode 120000 index 0000000..51edbe9 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFTextField.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Views/PFTextField.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/PFUIAlertView.h b/Pods/Headers/Private/ParseUI/PFUIAlertView.h new file mode 120000 index 0000000..0f32862 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/PFUIAlertView.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/ParseUI.h b/Pods/Headers/Private/ParseUI/ParseUI.h new file mode 120000 index 0000000..6b5f250 --- /dev/null +++ b/Pods/Headers/Private/ParseUI/ParseUI.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Other/ParseUI.h \ No newline at end of file diff --git a/Pods/Headers/Private/ParseUI/ParseUIConstants.h b/Pods/Headers/Private/ParseUI/ParseUIConstants.h new file mode 120000 index 0000000..58c59ff --- /dev/null +++ b/Pods/Headers/Private/ParseUI/ParseUIConstants.h @@ -0,0 +1 @@ +../../../ParseUI/ParseUI/Other/ParseUIConstants.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAddress.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAddress.h new file mode 120000 index 0000000..e90bc36 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAddress.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteFilter.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteFilter.h new file mode 120000 index 0000000..481b137 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteFilter.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteFilter.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteMatchFragment.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteMatchFragment.h new file mode 120000 index 0000000..35dd8ce --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompleteMatchFragment.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompletePrediction.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompletePrediction.h new file mode 120000 index 0000000..1d0b893 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAutocompletePrediction.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAutocompletePrediction.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCALayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCALayer.h new file mode 120000 index 0000000..28ee5f1 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCALayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraPosition.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraPosition.h new file mode 120000 index 0000000..378aded --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraPosition.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraUpdate.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraUpdate.h new file mode 120000 index 0000000..1750643 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraUpdate.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCircle.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCircle.h new file mode 120000 index 0000000..97cf3b4 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCircle.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds.h new file mode 120000 index 0000000..b8f035b --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeocoder.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeocoder.h new file mode 120000 index 0000000..3ce1121 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeocoder.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeometryUtils.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeometryUtils.h new file mode 120000 index 0000000..5e0ca0a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeometryUtils.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGroundOverlay.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGroundOverlay.h new file mode 120000 index 0000000..dc73548 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGroundOverlay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorBuilding.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorBuilding.h new file mode 120000 index 0000000..a0d4dae --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorBuilding.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorDisplay.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorDisplay.h new file mode 120000 index 0000000..59d87f8 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorDisplay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorLevel.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorLevel.h new file mode 120000 index 0000000..e662766 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorLevel.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapLayer.h new file mode 120000 index 0000000..b794812 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView+Animation.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView+Animation.h new file mode 120000 index 0000000..e514ed7 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView+Animation.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView.h new file mode 120000 index 0000000..ede2388 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarker.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarker.h new file mode 120000 index 0000000..f756122 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarker.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarkerLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarkerLayer.h new file mode 120000 index 0000000..9f2229c --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarkerLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMutablePath.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMutablePath.h new file mode 120000 index 0000000..0019964 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMutablePath.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOrientation.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOrientation.h new file mode 120000 index 0000000..56c98c6 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOrientation.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOverlay.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOverlay.h new file mode 120000 index 0000000..446d69f --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOverlay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanorama.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanorama.h new file mode 120000 index 0000000..5ed18c8 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanorama.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCamera.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCamera.h new file mode 120000 index 0000000..84670fe --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCamera.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCameraUpdate.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCameraUpdate.h new file mode 120000 index 0000000..57a601c --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCameraUpdate.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLayer.h new file mode 120000 index 0000000..822f8b1 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLink.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLink.h new file mode 120000 index 0000000..bd626ae --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLink.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaService.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaService.h new file mode 120000 index 0000000..1b54576 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaService.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaView.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaView.h new file mode 120000 index 0000000..2dd1b1d --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaView.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPath.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPath.h new file mode 120000 index 0000000..0c77038 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPath.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlace.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlace.h new file mode 120000 index 0000000..9afb1ed --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlace.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlace.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihood.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihood.h new file mode 120000 index 0000000..8de608a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihood.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihood.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihoodList.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihoodList.h new file mode 120000 index 0000000..a471b77 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceLikelihoodList.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePicker.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePicker.h new file mode 120000 index 0000000..c521b88 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePicker.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePicker.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePickerConfig.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePickerConfig.h new file mode 120000 index 0000000..f3df3de --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacePickerConfig.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacePickerConfig.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceTypes.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceTypes.h new file mode 120000 index 0000000..437a1a7 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlaceTypes.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlaceTypes.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesClient.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesClient.h new file mode 120000 index 0000000..360582a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesClient.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesClient.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesMacros.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesMacros.h new file mode 120000 index 0000000..05d05fe --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPlacesMacros.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPlacesMacros.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolygon.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolygon.h new file mode 120000 index 0000000..2c636db --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolygon.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolyline.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolyline.h new file mode 120000 index 0000000..f8a3104 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolyline.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSProjection.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSProjection.h new file mode 120000 index 0000000..3f51982 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSProjection.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSServices.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSServices.h new file mode 120000 index 0000000..6fe6119 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSServices.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSSyncTileLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSSyncTileLayer.h new file mode 120000 index 0000000..c00d001 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSSyncTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSTileLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSTileLayer.h new file mode 120000 index 0000000..4713811 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUISettings.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUISettings.h new file mode 120000 index 0000000..8c881d6 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUISettings.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSURLTileLayer.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSURLTileLayer.h new file mode 120000 index 0000000..52a895a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSURLTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUserAddedPlace.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUserAddedPlace.h new file mode 120000 index 0000000..f9b53c7 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUserAddedPlace.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUserAddedPlace.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GoogleMaps.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GoogleMaps.h new file mode 120000 index 0000000..904fee4 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GoogleMaps.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h \ No newline at end of file diff --git a/Pods/LiquidFloatingActionButton/LICENSE b/Pods/LiquidFloatingActionButton/LICENSE new file mode 100644 index 0000000..0c9d668 --- /dev/null +++ b/Pods/LiquidFloatingActionButton/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Takuma Yoshida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/ArrayEx.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/ArrayEx.swift new file mode 100644 index 0000000..2e4c76f --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/ArrayEx.swift @@ -0,0 +1,17 @@ +// +// ArrayEx.swift +// Ampdot +// +// Created by Takuma Yoshida on 2015/06/02. +// Copyright (c) 2015年 yoavlt All rights reserved. +// + +import Foundation + +extension Array { + func each(f: (Element) -> ()) { + for item in self { + f(item) + } + } +} diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/CGPointEx.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/CGPointEx.swift new file mode 100644 index 0000000..6eea558 --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/CGPointEx.swift @@ -0,0 +1,87 @@ +// +// CGPointEx.swift +// LiquidLoading +// +// Created by Takuma Yoshida on 2015/08/17. +// Copyright (c) 2015年 yoavlt. All rights reserved. +// + +import Foundation +import UIKit + +extension CGPoint { + + // 足し算 + func plus(point: CGPoint) -> CGPoint { + return CGPoint(x: self.x + point.x, y: self.y + point.y) + } + + // 引き算 + func minus(point: CGPoint) -> CGPoint { + return CGPoint(x: self.x - point.x, y: self.y - point.y) + } + + func minusX(dx: CGFloat) -> CGPoint { + return CGPoint(x: self.x - dx, y: self.y) + } + + func minusY(dy: CGFloat) -> CGPoint { + return CGPoint(x: self.x, y: self.y - dy) + } + + // 掛け算 + func mul(rhs: CGFloat) -> CGPoint { + return CGPoint(x: self.x * rhs, y: self.y * rhs) + } + + // 割り算 + func div(rhs: CGFloat) -> CGPoint { + return CGPoint(x: self.x / rhs, y: self.y / rhs) + } + + // 長さ + func length() -> CGFloat { + return sqrt(self.x * self.x + self.y * self.y) + } + + // 正規化 + func normalized() -> CGPoint { + return self.div(self.length()) + } + + // 内積 + func dot(point: CGPoint) -> CGFloat { + return self.x * point.x + self.y * point.y + } + + // 外積 + func cross(point: CGPoint) -> CGFloat { + return self.x * point.y - self.y * point.x + } + + func split(point: CGPoint, ratio: CGFloat) -> CGPoint { + return self.mul(ratio).plus(point.mul(1.0 - ratio)) + } + + func mid(point: CGPoint) -> CGPoint { + return split(point, ratio: 0.5) + } + + static func intersection(from: CGPoint, to: CGPoint, from2: CGPoint, to2: CGPoint) -> CGPoint? { + let ac = CGPoint(x: to.x - from.x, y: to.y - from.y) + let bd = CGPoint(x: to2.x - from2.x, y: to2.y - from2.y) + let ab = CGPoint(x: from2.x - from.x, y: from2.y - from.y) + let bc = CGPoint(x: to.x - from2.x, y: to.y - from2.y) + + let area = bd.cross(ab) + let area2 = bd.cross(bc) + + if abs(area + area2) >= 0.1 { + let ratio = area / (area + area2) + return CGPoint(x: from.x + ratio * ac.x, y: from.y + ratio * ac.y) + } + + return nil + } + +} \ No newline at end of file diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/CGRectEx.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/CGRectEx.swift new file mode 100644 index 0000000..11cd8f3 --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/CGRectEx.swift @@ -0,0 +1,23 @@ +// +// CGRectEx.swift +// LiquidLoading +// +// Created by Takuma Yoshida on 2015/08/20. +// Copyright (c) 2015年 yoavlt. All rights reserved. +// + +import Foundation +import UIKit + +extension CGRect { + var rightBottom: CGPoint { + get { + return CGPoint(x: origin.x + width, y: origin.y + height) + } + } + var center: CGPoint { + get { + return origin.plus(rightBottom).mul(0.5) + } + } +} diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidFloatingActionButton.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidFloatingActionButton.swift new file mode 100644 index 0000000..718ba1c --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidFloatingActionButton.swift @@ -0,0 +1,547 @@ +// +// LiquidFloatingActionButton.swift +// Pods +// +// Created by Takuma Yoshida on 2015/08/25. +// +// + +import Foundation +import QuartzCore + +// LiquidFloatingButton DataSource methods +@objc public protocol LiquidFloatingActionButtonDataSource { + func numberOfCells(liquidFloatingActionButton: LiquidFloatingActionButton) -> Int + func cellForIndex(index: Int) -> LiquidFloatingCell +} + +@objc public protocol LiquidFloatingActionButtonDelegate { + // selected method + optional func liquidFloatingActionButton(liquidFloatingActionButton: LiquidFloatingActionButton, didSelectItemAtIndex index: Int) +} + +public enum LiquidFloatingActionButtonAnimateStyle : Int { + case Up + case Right + case Left + case Down +} + +@IBDesignable +public class LiquidFloatingActionButton : UIView { + + private let internalRadiusRatio: CGFloat = 20.0 / 56.0 + public var cellRadiusRatio: CGFloat = 0.38 + public var animateStyle: LiquidFloatingActionButtonAnimateStyle = .Left { + didSet { + baseView.animateStyle = animateStyle + } + } + public var enableShadow = true { + didSet { + setNeedsDisplay() + } + } + + public var delegate: LiquidFloatingActionButtonDelegate? + public var dataSource: LiquidFloatingActionButtonDataSource? + + public var responsible = true + public var isOpening: Bool { + get { + return !baseView.openingCells.isEmpty + } + } + public var isClosed: Bool { + get { + return plusRotation == 0 + } + } + + @IBInspectable public var color: UIColor = UIColor(red: 82 / 255.0, green: 112 / 255.0, blue: 235 / 255.0, alpha: 1.0) { + didSet { + baseView.color = color + } + } + + private let plusLayer = CAShapeLayer() + private let circleLayer = CAShapeLayer() + + private var touching = false + private var plusRotation: CGFloat = 0 + + private var baseView = CircleLiquidBaseView() + private let liquidView = UIView() + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + private func insertCell(cell: LiquidFloatingCell) { + cell.color = self.color + cell.radius = self.frame.width * cellRadiusRatio + cell.center = self.center.minus(self.frame.origin) + cell.actionButton = self + insertSubview(cell, aboveSubview: baseView) + } + + private func cellArray() -> [LiquidFloatingCell] { + var result: [LiquidFloatingCell] = [] + if let source = dataSource { + for i in 0.. UIBezierPath { + let radius = self.frame.width * internalRadiusRatio * 0.5 + let center = self.center.minus(self.frame.origin) + let points = [ + CGMath.circlePoint(center, radius: radius, rad: rotation), + CGMath.circlePoint(center, radius: radius, rad: CGFloat(M_PI_2) + rotation), + CGMath.circlePoint(center, radius: radius, rad: CGFloat(M_PI_2) * 2 + rotation), + CGMath.circlePoint(center, radius: radius, rad: CGFloat(M_PI_2) * 3 + rotation) + ] + let path = UIBezierPath() + path.moveToPoint(points[0]) + path.addLineToPoint(points[2]) + path.moveToPoint(points[1]) + path.addLineToPoint(points[3]) + return path + } + + private func plusKeyframe(closed: Bool) -> CAKeyframeAnimation { + let paths = closed ? [ + pathPlus(CGFloat(M_PI * 0)), + pathPlus(CGFloat(M_PI * 0.125)), + pathPlus(CGFloat(M_PI * 0.25)), + ] : [ + pathPlus(CGFloat(M_PI * 0.25)), + pathPlus(CGFloat(M_PI * 0.125)), + pathPlus(CGFloat(M_PI * 0)), + ] + let anim = CAKeyframeAnimation(keyPath: "path") + anim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + anim.values = paths.map { $0.CGPath } + anim.duration = 0.5 + anim.removedOnCompletion = true + anim.fillMode = kCAFillModeForwards + anim.delegate = self + return anim + } + + // MARK: Events + public override func touchesBegan(touches: Set, withEvent event: UIEvent?) { + self.touching = true + setNeedsDisplay() + } + + public override func touchesEnded(touches: Set, withEvent event: UIEvent?) { + self.touching = false + setNeedsDisplay() + didTapped() + } + + public override func touchesCancelled(touches: Set?, withEvent event: UIEvent?) { + self.touching = false + setNeedsDisplay() + } + + public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { + for cell in cellArray() { + let pointForTargetView = cell.convertPoint(point, fromView: self) + + if (CGRectContainsPoint(cell.bounds, pointForTargetView)) { + if cell.userInteractionEnabled { + return cell.hitTest(pointForTargetView, withEvent: event) + } + } + } + + return super.hitTest(point, withEvent: event) + } + + // MARK: private methods + private func setup() { + self.backgroundColor = UIColor.clearColor() + self.clipsToBounds = false + + baseView.setup(self) + addSubview(baseView) + + liquidView.frame = baseView.frame + liquidView.userInteractionEnabled = false + addSubview(liquidView) + + liquidView.layer.addSublayer(circleLayer) + circleLayer.addSublayer(plusLayer) + } + + private func didTapped() { + if isClosed { + open() + } else { + close() + } + } + + public func didTappedCell(target: LiquidFloatingCell) { + if let _ = dataSource { + let cells = cellArray() + for i in 0.. ()) { + let translate = CABasicAnimation(keyPath: "transform.translation.y") + f(translate) + translate.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + translate.removedOnCompletion = false + translate.fillMode = kCAFillModeForwards + translate.duration = duration + layer.addAnimation(translate, forKey: "transYAnim") + } +} + +class CircleLiquidBaseView : ActionBarBaseView { + + let openDuration: CGFloat = 0.6 + let closeDuration: CGFloat = 0.2 + let viscosity: CGFloat = 0.65 + var animateStyle: LiquidFloatingActionButtonAnimateStyle = .Up + var color: UIColor = UIColor(red: 82 / 255.0, green: 112 / 255.0, blue: 235 / 255.0, alpha: 1.0) { + didSet { + engine?.color = color + bigEngine?.color = color + } + } + + var baseLiquid: LiquittableCircle? + var engine: SimpleCircleLiquidEngine? + var bigEngine: SimpleCircleLiquidEngine? + var enableShadow = true + + private var openingCells: [LiquidFloatingCell] = [] + private var keyDuration: CGFloat = 0 + private var displayLink: CADisplayLink? + + override func setup(actionButton: LiquidFloatingActionButton) { + self.frame = actionButton.frame + self.center = actionButton.center.minus(actionButton.frame.origin) + self.animateStyle = actionButton.animateStyle + let radius = min(self.frame.width, self.frame.height) * 0.5 + self.engine = SimpleCircleLiquidEngine(radiusThresh: radius * 0.73, angleThresh: 0.45) + engine?.viscosity = viscosity + self.bigEngine = SimpleCircleLiquidEngine(radiusThresh: radius, angleThresh: 0.55) + bigEngine?.viscosity = viscosity + self.engine?.color = actionButton.color + self.bigEngine?.color = actionButton.color + + baseLiquid = LiquittableCircle(center: self.center.minus(self.frame.origin), radius: radius, color: actionButton.color) + baseLiquid?.clipsToBounds = false + baseLiquid?.layer.masksToBounds = false + + clipsToBounds = false + layer.masksToBounds = false + addSubview(baseLiquid!) + } + + func open(cells: [LiquidFloatingCell]) { + stop() + displayLink = CADisplayLink(target: self, selector: Selector("didDisplayRefresh:")) + displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes) + opening = true + for cell in cells { + cell.layer.removeAllAnimations() + cell.layer.eraseShadow() + openingCells.append(cell) + } + } + + func close(cells: [LiquidFloatingCell]) { + stop() + opening = false + displayLink = CADisplayLink(target: self, selector: Selector("didDisplayRefresh:")) + displayLink?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes) + for cell in cells { + cell.layer.removeAllAnimations() + cell.layer.eraseShadow() + openingCells.append(cell) + cell.userInteractionEnabled = false + } + } + + func didFinishUpdate() { + if opening { + for cell in openingCells { + cell.userInteractionEnabled = true + } + } else { + for cell in openingCells { + cell.removeFromSuperview() + } + } + } + + func update(delay: CGFloat, duration: CGFloat, f: (LiquidFloatingCell, Int, CGFloat) -> ()) { + if openingCells.isEmpty { + return + } + + let maxDuration = duration + CGFloat(openingCells.count) * CGFloat(delay) + let t = keyDuration + let allRatio = easeInEaseOut(t / maxDuration) + + if allRatio >= 1.0 { + didFinishUpdate() + stop() + return + } + + engine?.clear() + bigEngine?.clear() + for i in 0.. CGFloat(i) / CGFloat(self.openingCells.count) ? ratio : 0 + let distance = (cell.frame.height * 0.5 + CGFloat(i + 1) * cell.frame.height * 1.5) * posRatio + cell.center = self.center.plus(self.differencePoint(distance)) + cell.update(ratio, open: true) + } + } + + func updateClose() { + update(0, duration: closeDuration) { cell, i, ratio in + let distance = (cell.frame.height * 0.5 + CGFloat(i + 1) * cell.frame.height * 1.5) * (1 - ratio) + cell.center = self.center.plus(self.differencePoint(distance)) + cell.update(ratio, open: false) + } + } + + func differencePoint(distance: CGFloat) -> CGPoint { + switch animateStyle { + case .Up: + return CGPoint(x: 0, y: -distance) + case .Right: + return CGPoint(x: distance, y: 0) + case .Left: + return CGPoint(x: -distance, y: 0) + case .Down: + return CGPoint(x: 0, y: distance) + } + } + + func stop() { + for cell in openingCells { + if enableShadow { + cell.layer.appendShadow() + } + } + openingCells = [] + keyDuration = 0 + displayLink?.invalidate() + } + + func easeInEaseOut(t: CGFloat) -> CGFloat { + if t >= 1.0 { + return 1.0 + } + if t < 0 { + return 0 + } + return -1 * t * (t - 2) + } + + func didDisplayRefresh(displayLink: CADisplayLink) { + if opening { + keyDuration += CGFloat(displayLink.duration) + updateOpen() + } else { + keyDuration += CGFloat(displayLink.duration) + updateClose() + } + } + +} + +public class LiquidFloatingCell : LiquittableCircle { + + let internalRatio: CGFloat = 0.75 + + public var responsible = true + public var imageView = UIImageView() + weak var actionButton: LiquidFloatingActionButton? + + // for implement responsible color + private var originalColor: UIColor + + public override var frame: CGRect { + didSet { + resizeSubviews() + } + } + + init(center: CGPoint, radius: CGFloat, color: UIColor, icon: UIImage) { + self.originalColor = color + super.init(center: center, radius: radius, color: color) + setup(icon) + } + + init(center: CGPoint, radius: CGFloat, color: UIColor, view: UIView) { + self.originalColor = color + super.init(center: center, radius: radius, color: color) + setupView(view) + } + + public init(icon: UIImage) { + self.originalColor = UIColor.clearColor() + super.init() + setup(icon) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setup(image: UIImage, tintColor: UIColor = UIColor.whiteColor()) { + imageView.image = image.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) + imageView.tintColor = tintColor + setupView(imageView) + } + + public func setupView(view: UIView) { + userInteractionEnabled = false + addSubview(view) + resizeSubviews() + } + + private func resizeSubviews() { + let size = CGSize(width: frame.width * 0.5, height: frame.height * 0.5) + imageView.frame = CGRect(x: frame.width - frame.width * internalRatio, y: frame.height - frame.height * internalRatio, width: size.width, height: size.height) + } + + func update(key: CGFloat, open: Bool) { + for subview in self.subviews { + let ratio = max(2 * (key * key - 0.5), 0) + subview.alpha = open ? ratio : -ratio + } + } + + public override func touchesBegan(touches: Set, withEvent event: UIEvent?) { + if responsible { + originalColor = color + color = originalColor.white(0.5) + setNeedsDisplay() + } + } + + public override func touchesCancelled(touches: Set?, withEvent event: UIEvent?) { + if responsible { + color = originalColor + setNeedsDisplay() + } + } + + override public func touchesEnded(touches: Set, withEvent event: UIEvent?) { + color = originalColor + actionButton?.didTappedCell(self) + } + +} \ No newline at end of file diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidUtil.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidUtil.swift new file mode 100644 index 0000000..1eeeacd --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/LiquidUtil.swift @@ -0,0 +1,56 @@ +// +// LiquidUtil.swift +// LiquidLoading +// +// Created by Takuma Yoshida on 2015/08/17. +// Copyright (c) 2015年 yoavlt. All rights reserved. +// + +import Foundation +import UIKit + +func withBezier(f: (UIBezierPath) -> ()) -> UIBezierPath { + let bezierPath = UIBezierPath() + f(bezierPath) + bezierPath.closePath() + return bezierPath +} + +extension CALayer { + func appendShadow() { + shadowColor = UIColor.blackColor().CGColor + shadowRadius = 2.0 + shadowOpacity = 0.1 + shadowOffset = CGSize(width: 4, height: 4) + masksToBounds = false + } + + func eraseShadow() { + shadowRadius = 0.0 + shadowColor = UIColor.clearColor().CGColor + } +} + +class CGMath { + static func radToDeg(rad: CGFloat) -> CGFloat { + return rad * 180 / CGFloat(M_PI) + } + + static func degToRad(deg: CGFloat) -> CGFloat { + return deg * CGFloat(M_PI) / 180 + } + + static func circlePoint(center: CGPoint, radius: CGFloat, rad: CGFloat) -> CGPoint { + let x = center.x + radius * cos(rad) + let y = center.y + radius * sin(rad) + return CGPoint(x: x, y: y) + } + + static func linSpace(from: CGFloat, to: CGFloat, n: Int) -> [CGFloat] { + var values: [CGFloat] = [] + for i in 0.. CAShapeLayer { + circleLayer.lineWidth = 3.0 + circleLayer.fillColor = self.color.CGColor + circleLayer.path = path.CGPath + return circleLayer + } + + func circlePoint(rad: CGFloat) -> CGPoint { + return CGMath.circlePoint(center, radius: radius, rad: rad) + } + + public override func drawRect(rect: CGRect) { + drawCircle() + } + +} \ No newline at end of file diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/SimpleCircleLiquidEngine.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/SimpleCircleLiquidEngine.swift new file mode 100644 index 0000000..a1f6c5f --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/SimpleCircleLiquidEngine.swift @@ -0,0 +1,151 @@ +// +// SimpleCircleLiquidEngine.swift +// LiquidLoading +// +// Created by Takuma Yoshida on 2015/08/19. +// Copyright (c) 2015年 yoavlt. All rights reserved. +// + +import Foundation +import UIKit + +/** + * This class is so fast, but allowed only same color. + */ +class SimpleCircleLiquidEngine { + + let radiusThresh: CGFloat + private var layer: CALayer = CAShapeLayer() + + var viscosity: CGFloat = 0.65 + var color = UIColor.redColor() + var angleOpen: CGFloat = 1.0 + + let ConnectThresh: CGFloat = 0.3 + var angleThresh: CGFloat = 0.5 + + init(radiusThresh: CGFloat, angleThresh: CGFloat) { + self.radiusThresh = radiusThresh + self.angleThresh = angleThresh + } + + func push(circle: LiquittableCircle, other: LiquittableCircle) -> [LiquittableCircle] { + if let paths = generateConnectedPath(circle, other: other) { + let layers = paths.map(self.constructLayer) + layers.each(layer.addSublayer) + return [circle, other] + } + return [] + } + + func draw(parent: UIView) { + parent.layer.addSublayer(layer) + } + + func clear() { + layer.removeFromSuperlayer() + layer.sublayers?.each{ $0.removeFromSuperlayer() } + layer = CAShapeLayer() + } + + func constructLayer(path: UIBezierPath) -> CALayer { + let pathBounds = CGPathGetBoundingBox(path.CGPath); + + let shape = CAShapeLayer() + shape.fillColor = self.color.CGColor + shape.path = path.CGPath + shape.frame = CGRect(x: 0, y: 0, width: pathBounds.width, height: pathBounds.height) + + return shape + } + + private func circleConnectedPoint(circle: LiquittableCircle, other: LiquittableCircle, angle: CGFloat) -> (CGPoint, CGPoint) { + let vec = other.center.minus(circle.center) + let radian = atan2(vec.y, vec.x) + let p1 = circle.circlePoint(radian + angle) + let p2 = circle.circlePoint(radian - angle) + return (p1, p2) + } + + private func circleConnectedPoint(circle: LiquittableCircle, other: LiquittableCircle) -> (CGPoint, CGPoint) { + var ratio = circleRatio(circle, other: other) + ratio = (ratio + ConnectThresh) / (1.0 + ConnectThresh) + let angle = CGFloat(M_PI_2) * angleOpen * ratio + return circleConnectedPoint(circle, other: other, angle: angle) + } + + func generateConnectedPath(circle: LiquittableCircle, other: LiquittableCircle) -> [UIBezierPath]? { + if isConnected(circle, other: other) { + let ratio = circleRatio(circle, other: other) + switch ratio { + case angleThresh...1.0: + if let path = normalPath(circle, other: other) { + return [path] + } + return nil + case 0.0.. UIBezierPath? { + let (p1, p2) = circleConnectedPoint(circle, other: other) + let (p3, p4) = circleConnectedPoint(other, other: circle) + if let crossed = CGPoint.intersection(p1, to: p3, from2: p2, to2: p4) { + return withBezier { path in + let r = self.circleRatio(circle, other: other) + path.moveToPoint(p1) + let r1 = p2.mid(p3) + let r2 = p1.mid(p4) + let rate = (1 - r) / (1 - self.angleThresh) * self.viscosity + let mul = r1.mid(crossed).split(r2, ratio: rate) + let mul2 = r2.mid(crossed).split(r1, ratio: rate) + path.addQuadCurveToPoint(p4, controlPoint: mul) + path.addLineToPoint(p3) + path.addQuadCurveToPoint(p2, controlPoint: mul2) + } + } + return nil + } + + private func splitPath(circle: LiquittableCircle, other: LiquittableCircle, ratio: CGFloat) -> [UIBezierPath] { + let (p1, p2) = circleConnectedPoint(circle, other: other, angle: CGMath.degToRad(60)) + let (p3, p4) = circleConnectedPoint(other, other: circle, angle: CGMath.degToRad(60)) + + if let crossed = CGPoint.intersection(p1, to: p3, from2: p2, to2: p4) { + let (d1, _) = self.circleConnectedPoint(circle, other: other, angle: 0) + let (d2, _) = self.circleConnectedPoint(other, other: circle, angle: 0) + let r = (ratio - ConnectThresh) / (angleThresh - ConnectThresh) + + let a1 = d2.split(crossed, ratio: (r * r)) + let part = withBezier { path in + path.moveToPoint(p1) + path.addQuadCurveToPoint(p2, controlPoint: a1) + } + let a2 = d1.split(crossed, ratio: (r * r)) + let part2 = withBezier { path in + path.moveToPoint(p3) + path.addQuadCurveToPoint(p4, controlPoint: a2) + } + return [part, part2] + } + return [] + } + + private func circleRatio(circle: LiquittableCircle, other: LiquittableCircle) -> CGFloat { + let distance = other.center.minus(circle.center).length() + let ratio = 1.0 - (distance - radiusThresh) / (circle.radius + other.radius + radiusThresh) + return min(max(ratio, 0.0), 1.0) + } + + func isConnected(circle: LiquittableCircle, other: LiquittableCircle) -> Bool { + let distance = circle.center.minus(other.center).length() + return distance - circle.radius - other.radius < radiusThresh + } + +} diff --git a/Pods/LiquidFloatingActionButton/Pod/Classes/UIColorEx.swift b/Pods/LiquidFloatingActionButton/Pod/Classes/UIColorEx.swift new file mode 100644 index 0000000..33c77f7 --- /dev/null +++ b/Pods/LiquidFloatingActionButton/Pod/Classes/UIColorEx.swift @@ -0,0 +1,53 @@ +// +// UIColorEx.swift +// LiquidLoading +// +// Created by Takuma Yoshida on 2015/08/21. +// Copyright (c) 2015年 yoavlt. All rights reserved. +// + +import Foundation +import UIKit + +extension UIColor { + + var red: CGFloat { + get { + let components = CGColorGetComponents(self.CGColor) + return components[0] + } + } + + var green: CGFloat { + get { + let components = CGColorGetComponents(self.CGColor) + return components[1] + } + } + + var blue: CGFloat { + get { + let components = CGColorGetComponents(self.CGColor) + return components[2] + } + } + + var alpha: CGFloat { + get { + return CGColorGetAlpha(self.CGColor) + } + } + + func alpha(alpha: CGFloat) -> UIColor { + return UIColor(red: self.red, green: self.green, blue: self.blue, alpha: alpha) + } + + func white(scale: CGFloat) -> UIColor { + return UIColor( + red: self.red + (1.0 - self.red) * scale, + green: self.green + (1.0 - self.green) * scale, + blue: self.blue + (1.0 - self.blue) * scale, + alpha: 1.0 + ) + } +} \ No newline at end of file diff --git a/Pods/LiquidFloatingActionButton/README.md b/Pods/LiquidFloatingActionButton/README.md new file mode 100644 index 0000000..cb1acff --- /dev/null +++ b/Pods/LiquidFloatingActionButton/README.md @@ -0,0 +1,58 @@ +# LiquidFloatingActionButton + +[![CI Status](http://img.shields.io/travis/yoavlt/LiquidFloatingActionButton.svg?style=flat)](https://travis-ci.org/yoavlt/LiquidFloatingActionButton) +[![Version](https://img.shields.io/cocoapods/v/LiquidFloatingActionButton.svg?style=flat)](http://cocoapods.org/pods/LiquidFloatingActionButton) +[![License](https://img.shields.io/cocoapods/l/LiquidFloatingActionButton.svg?style=flat)](http://cocoapods.org/pods/LiquidFloatingActionButton) +[![Platform](https://img.shields.io/cocoapods/p/LiquidFloatingActionButton.svg?style=flat)](http://cocoapods.org/pods/LiquidFloatingActionButton) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)] +(https://github.com/Carthage/Carthage) + +LiquidFloatingActionButton is floating action button component of [material design](https://www.google.com/design/spec/material-design/introduction.html) in liquid state, inspired by [Material In a Liquid State](http://www.materialup.com/posts/material-in-a-liquid-state). +This is also [spinner loader](https://github.com/yoavlt/LiquidLoader) components in liquid state. + +![Demo](https://github.com/yoavlt/LiquidFloatingActionButton/blob/master/Demo/top.gif?raw=true) + +## Features +- [x] liquid animation +- [x] easily custoizable +- [x] Objective-C compatible + +You can play a demo with [appetize.io](https://appetize.io/app/f4t42hgqbnbma4m12jcg3aeebg?device=iphone5s&scale=75&orientation=portrait) + +## Usage + +You just need implement `LiquidFloatingActionButtonDataSource` and `LiquidFloatingActionButtonDelegate` similar to well-known UIKit design. + +```swift +let floatingActionButton = LiquidFloatingActionButton(frame: floatingFrame) +floatingActionButton.dataSource = self +floatingActionButton.delegate = self +``` + +### LiquidFloatingActionButtonDataSource +* func numberOfCells(liquidFloatingActionButton: LiquidFloatingActionButton) -> Int +* func cellForIndex(index: Int) -> LiquidFloatingCell + +### LiquidFloatingActionButtonDelegate +* optional func liquidFloatingActionButton(liquidFloatingActionButton: LiquidFloatingActionButton, didSelectItemAtIndex index: Int) + +## Easily customizable +![Demo](https://github.com/yoavlt/LiquidFloatingActionButton/blob/master/Demo/customizable.gif?raw=true) + +## Installation + +LiquidFloatingActionButton is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod "LiquidFloatingActionButton" +``` +or, if you use [Carthage](https://github.com/Carthage/Carthage), add the following line to your `Carthage` file. + +``` +github "yoavlt/LiquidFloatingActionButton" +``` + +## License + +LiquidFloatingActionButton is available under the MIT license. See the LICENSE file for more info. diff --git a/Pods/Local Podspecs/LiquidFloatingActionButton.podspec.json b/Pods/Local Podspecs/LiquidFloatingActionButton.podspec.json new file mode 100644 index 0000000..dc8ed83 --- /dev/null +++ b/Pods/Local Podspecs/LiquidFloatingActionButton.podspec.json @@ -0,0 +1,25 @@ +{ + "name": "LiquidFloatingActionButton", + "version": "0.1.1", + "summary": "Material Design Floating Action Button in liquid state", + "description": "Material Design Floating Action Button in liquid state inspired by http://www.materialup.com/posts/material-in-a-liquid-state", + "homepage": "https://github.com/yoavlt/LiquidFloatingActionButton", + "license": "MIT", + "authors": { + "Takuma Yoshida": "yoa.jmpr.w@gmail.com" + }, + "source": { + "git": "https://github.com/yoavlt/LiquidFloatingActionButton.git", + "tag": "0.1.1" + }, + "platforms": { + "ios": "8.0" + }, + "requires_arc": true, + "source_files": "Pod/Classes/**/*", + "resource_bundles": { + "LiquidFloatingActionButton": [ + "Pod/Assets/*.png" + ] + } +} diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..09e0d1e --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,58 @@ +PODS: + - AFNetworking (2.6.1): + - AFNetworking/NSURLConnection (= 2.6.1) + - AFNetworking/NSURLSession (= 2.6.1) + - AFNetworking/Reachability (= 2.6.1) + - AFNetworking/Security (= 2.6.1) + - AFNetworking/Serialization (= 2.6.1) + - AFNetworking/UIKit (= 2.6.1) + - AFNetworking/NSURLConnection (2.6.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/NSURLSession (2.6.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (2.6.1) + - AFNetworking/Security (2.6.1) + - AFNetworking/Serialization (2.6.1) + - AFNetworking/UIKit (2.6.1): + - AFNetworking/NSURLConnection + - AFNetworking/NSURLSession + - Bolts/Tasks (1.3.0) + - GoogleMaps (1.10.4) + - LiquidFloatingActionButton (0.1.1) + - Parse (1.9.0): + - Bolts/Tasks (>= 1.3.0) + - ParseUI (1.1.6): + - Bolts/Tasks (~> 1.2) + - Parse (~> 1.8) + +DEPENDENCIES: + - AFNetworking (~> 2.6) + - GoogleMaps + - LiquidFloatingActionButton (from `https://github.com/yoavlt/LiquidFloatingActionButton.git`, + branch `swift-2.0`) + - Parse + - ParseUI (~> 1.1) + +EXTERNAL SOURCES: + LiquidFloatingActionButton: + :branch: swift-2.0 + :git: https://github.com/yoavlt/LiquidFloatingActionButton.git + +CHECKOUT OPTIONS: + LiquidFloatingActionButton: + :commit: 6c2a30a548d1f44e78b54571e616d637c92b5206 + :git: https://github.com/yoavlt/LiquidFloatingActionButton.git + +SPEC CHECKSUMS: + AFNetworking: 8e4e60500beb8bec644cf575beee72990a76d399 + Bolts: 805a4a87413e49d4a0c2b7d469084cbc46b09342 + GoogleMaps: 134bcf57e8d489efbc0c29d532ed421554fabdb2 + LiquidFloatingActionButton: dcdae1e59eb07eccbf60c027001a4d6731c345c9 + Parse: 712efbc476d4f47b0f96b70db7e53101575753aa + ParseUI: 8a22e448c03f825203d8c379c80523517f68baa3 + +COCOAPODS: 0.38.2 diff --git a/Pods/Parse/LICENSE b/Pods/Parse/LICENSE new file mode 100644 index 0000000..d98b0e0 --- /dev/null +++ b/Pods/Parse/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For Parse iOS/OSX SDK software + +Copyright (c) 2015-present, Parse, LLC. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Parse nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.h b/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.h new file mode 100644 index 0000000..cff7a3e --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFACL; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFDefaultACLController : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +// TODO: (nlutsenko, richardross) Make it not terrible aka don't have singletons ++ (instancetype)defaultController; ++ (void)clearDefaultController; + +///-------------------------------------- +/// @name Default ACL +///-------------------------------------- + +/*! + Get the default ACL managed by this controller. + + @return A task that returns the ACL encapsulated by this controller. + */ +- (BFTask *)getDefaultACLAsync; + +/*! + Set the new default default ACL to be encapsulated in this controller. + + @param acl The new ACL. Will be copied. + @param accessForCurrentUser Whether or not we should add special access for the current user on this ACL. + + @return A task that returns the new (copied) ACL now encapsulated in this controller. + */ +- (BFTask *)setDefaultACLAsync:(PFACL *)acl withCurrentUserAccess:(BOOL)accessForCurrentUser; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.m b/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.m new file mode 100644 index 0000000..82bd638 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.m @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFDefaultACLController.h" + +#import + +#import "PFACLPrivate.h" +#import "PFAsyncTaskQueue.h" +#import "PFCoreManager.h" +#import "PFCurrentUserController.h" +#import "Parse_Private.h" + +@implementation PFDefaultACLController { + PFAsyncTaskQueue *_taskQueue; + + PFACL *_defaultACL; + BOOL _useCurrentUser; + + PFUser *_lastCurrentUser; + PFACL *_defaultACLWithCurrentUser; +} + +static PFDefaultACLController *defaultController_; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)defaultController { + if (!defaultController_) { + defaultController_ = [[self alloc] init]; + } + return defaultController_; +} + ++ (void)clearDefaultController { + defaultController_ = nil; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _taskQueue = [[PFAsyncTaskQueue alloc] init]; + + return self; +} + +///-------------------------------------- +#pragma mark - ACL +///-------------------------------------- + +- (BFTask PF_GENERIC(PFACL *)*)getDefaultACLAsync { + return [_taskQueue enqueue:^id(BFTask *task) { + if (!_defaultACL || !_useCurrentUser) { + return _defaultACL; + } + + PFCurrentUserController *currentUserController = [Parse _currentManager].coreManager.currentUserController; + return [[currentUserController getCurrentObjectAsync] continueWithBlock:^id(BFTask *task) { + PFUser *currentUser = task.result; + if (!currentUser) { + return _defaultACL; + } + + if (currentUser != _lastCurrentUser) { + _defaultACLWithCurrentUser = [_defaultACL createUnsharedCopy]; + [_defaultACLWithCurrentUser setShared:YES]; + [_defaultACLWithCurrentUser setReadAccess:YES forUser:currentUser]; + [_defaultACLWithCurrentUser setWriteAccess:YES forUser:currentUser]; + _lastCurrentUser = currentUser; + } + return _defaultACLWithCurrentUser; + }]; + }]; +} + +- (BFTask PF_GENERIC(PFACL *)*)setDefaultACLAsync:(PFACL *)acl withCurrentUserAccess:(BOOL)accessForCurrentUser { + return [_taskQueue enqueue:^id(BFTask *task) { + _defaultACLWithCurrentUser = nil; + _lastCurrentUser = nil; + + _defaultACL = [acl createUnsharedCopy]; + [_defaultACL setShared:YES]; + + _useCurrentUser = accessForCurrentUser; + + return _defaultACL; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/ACL/PFACLPrivate.h b/Pods/Parse/Parse/Internal/ACL/PFACLPrivate.h new file mode 100644 index 0000000..6a641d6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/PFACLPrivate.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFACL.h" + +@class PFUser; + +@interface PFACL (Private) + +// Internal commands + +/* + Gets the encoded format for an ACL. + */ +- (NSDictionary *)encodeIntoDictionary; + +/* + Creates a new ACL object from an existing dictionary. + */ +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + +/*! + Creates an ACL from its encoded format. + */ ++ (instancetype)ACLWithDictionary:(NSDictionary *)dictionary; + +- (void)setShared:(BOOL)shared; +- (BOOL)isShared; +- (instancetype)createUnsharedCopy; +- (BOOL)hasUnresolvedUser; + ++ (instancetype)defaultACL; + +@end diff --git a/Pods/Parse/Parse/Internal/ACL/State/PFACLState.h b/Pods/Parse/Parse/Internal/ACL/State/PFACLState.h new file mode 100644 index 0000000..90e06af --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/State/PFACLState.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFBaseState.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PFMutableACLState; + +typedef void (^PFACLStateMutationBlock)(PFMutableACLState *); + +@interface PFACLState : PFBaseState + +@property (nonatomic, copy, readonly) NSDictionary *permissions; +@property (nonatomic, assign, readonly, getter=isShared) BOOL shared; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithState:(PFACLState *)otherState NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithState:(PFACLState *)otherState mutatingBlock:(PFACLStateMutationBlock)mutatingBlock; + ++ (instancetype)stateWithState:(PFACLState *)otherState; ++ (instancetype)stateWithState:(PFACLState *)otherState mutatingBlock:(PFACLStateMutationBlock)mutatingBlock; + +///-------------------------------------- +/// @name Mutating +///-------------------------------------- + +- (instancetype)copyByMutatingWithBlock:(PFACLStateMutationBlock)mutatingBlock NS_RETURNS_RETAINED; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/ACL/State/PFACLState.m b/Pods/Parse/Parse/Internal/ACL/State/PFACLState.m new file mode 100644 index 0000000..a80649e --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/State/PFACLState.m @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFACLState_Private.h" + +#import "PFMutableACLState.h" + +@implementation PFACLState + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + return @{ + @"permissions": [PFPropertyAttributes attributes], + @"shared": [PFPropertyAttributes attributes], + }; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _permissions = [NSDictionary dictionary]; + _shared = NO; + + return self; +} + +- (instancetype)initWithState:(PFACLState *)otherState { + return [super initWithState:otherState]; +} + +- (instancetype)initWithState:(PFACLState *)otherState mutatingBlock:(PFACLStateMutationBlock)mutatingBlock { + self = [self initWithState:otherState]; + if (!self) return nil; + + // Make permissions mutable for the duration of the block. + _permissions = [_permissions mutableCopy]; + + mutatingBlock((PFMutableACLState *)self); + + _permissions = [_permissions copy]; + + return self; +} + ++ (instancetype)stateWithState:(PFACLState *)otherState { + return [super stateWithState:otherState]; +} + ++ (instancetype)stateWithState:(PFACLState *)otherState mutatingBlock:(PFACLStateMutationBlock)mutatingBlock { + return [[self alloc] initWithState:otherState mutatingBlock:mutatingBlock]; +} + +///-------------------------------------- +#pragma mark - Copying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[PFACLState allocWithZone:zone] initWithState:self]; +} + +- (instancetype)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableACLState allocWithZone:zone] initWithState:self]; +} + +///-------------------------------------- +#pragma mark - Mutating +///-------------------------------------- + +- (instancetype)copyByMutatingWithBlock:(PFACLStateMutationBlock)mutationsBlock { + return [[PFACLState alloc] initWithState:self mutatingBlock:mutationsBlock]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/ACL/State/PFACLState_Private.h b/Pods/Parse/Parse/Internal/ACL/State/PFACLState_Private.h new file mode 100644 index 0000000..a3ed153 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/State/PFACLState_Private.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFACLState.h" + +@interface PFACLState () { +@protected + NSDictionary *_permissions; + BOOL _shared; +} + +@property (nonatomic, copy, readwrite) NSDictionary *permissions; +@property (nonatomic, assign, readwrite, getter=isShared) BOOL shared; + +@end diff --git a/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.h b/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.h new file mode 100644 index 0000000..d2bda0c --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFACLState.h" + +@interface PFMutableACLState : PFACLState + +@property (nonatomic, copy, readwrite) NSMutableDictionary *permissions; +@property (nonatomic, assign, readwrite, getter=isShared) BOOL shared; + +@end diff --git a/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.m b/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.m new file mode 100644 index 0000000..5361b8d --- /dev/null +++ b/Pods/Parse/Parse/Internal/ACL/State/PFMutableACLState.m @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableACLState.h" + +#import "PFACLState_Private.h" + +@implementation PFMutableACLState + +@dynamic permissions; +@dynamic shared; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _permissions = [NSMutableDictionary dictionary]; + + return self; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.h b/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.h new file mode 100644 index 0000000..0681ec4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +@interface PFAnalyticsController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Track Event +///-------------------------------------- + +/*! + @abstract Tracks this application being launched. If this happened as the result of the + user opening a push notification, this method sends along information to + correlate this open with that push. + + @param payload The Remote Notification payload. + @param sessionToken Current user session token. + + @returns `BFTask` with result set to `@YES`. + */ +- (BFTask *)trackAppOpenedEventAsyncWithRemoteNotificationPayload:(NSDictionary *)payload + sessionToken:(NSString *)sessionToken; + +/*! + @abstract Tracks the occurrence of a custom event with additional dimensions. + + @param name Event name. + @param dimensions `NSDictionary` of information by which to segment this event. + @param sessionToken Current user session token. + + @returns `BFTask` with result set to `@YES`. + */ +- (BFTask *)trackEventAsyncWithName:(NSString *)name + dimensions:(NSDictionary *)dimensions + sessionToken:(NSString *)sessionToken; + +@end diff --git a/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.m b/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.m new file mode 100644 index 0000000..b77eba6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.m @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAnalyticsController.h" + +#import "BFTask+Private.h" +#import "PFAnalyticsUtilities.h" +#import "PFAssert.h" +#import "PFEventuallyQueue.h" +#import "PFRESTAnalyticsCommand.h" + +@interface PFAnalyticsController () + +@property (nonatomic, weak, readonly) PFEventuallyQueue *eventuallyQueue; + +@end + +@implementation PFAnalyticsController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Track Event +///-------------------------------------- + +- (BFTask *)trackAppOpenedEventAsyncWithRemoteNotificationPayload:(NSDictionary *)payload + sessionToken:(NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + // If the Remote Notification payload had a message sent along with it, make + // sure to send that along so the server can identify "app opened from push" + // instead. + id alert = payload[@"aps"][@"alert"]; + NSString *pushDigest = (alert ? [PFAnalyticsUtilities md5DigestFromPushPayload:alert] : nil); + + PFRESTCommand *command = [PFRESTAnalyticsCommand trackAppOpenedEventCommandWithPushHash:pushDigest + sessionToken:sessionToken]; + return [self.eventuallyQueue enqueueCommandInBackground:command]; + }] continueWithSuccessResult:@YES]; +} + +- (BFTask *)trackEventAsyncWithName:(NSString *)name + dimensions:(NSDictionary *)dimensions + sessionToken:(NSString *)sessionToken { + PFParameterAssert([[name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length], + @"A name for the custom event must be provided."); + + if (dimensions) { + [dimensions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + PFParameterAssert([key isKindOfClass:[NSString class]] && [obj isKindOfClass:[NSString class]], + @"trackEvent dimensions expect keys and values of type NSString."); + }]; + } + + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + NSDictionary *encodedDimensions = [[PFNoObjectEncoder objectEncoder] encodeObject:dimensions]; + PFRESTCommand *command = [PFRESTAnalyticsCommand trackEventCommandWithEventName:name + dimensions:encodedDimensions + sessionToken:sessionToken]; + return [self.eventuallyQueue enqueueCommandInBackground:command]; + }] continueWithSuccessResult:@YES]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFEventuallyQueue *)eventuallyQueue { + return self.dataSource.eventuallyQueue; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Analytics/PFAnalytics_Private.h b/Pods/Parse/Parse/Internal/Analytics/PFAnalytics_Private.h new file mode 100644 index 0000000..d001798 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Analytics/PFAnalytics_Private.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +/*! + Predefined events - AppOpened, CrashReport + Coming soon - Log, ... + */ +extern NSString *const PFAnalyticsEventAppOpened; +extern NSString *const PFAnalyticsEventCrashReport; diff --git a/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.h b/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.h new file mode 100644 index 0000000..69f581f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFAnalyticsUtilities : NSObject + +/*! + Serializes and hexdigests an alert payload into a "push_hash" identifier + for use in Analytics. + Limitedly flexible - the payload is the value under the "alert" key in the + "aps" hash of a remote notification, so we can reasonably assume that the + complexity of its structure is limited to that accepted by Apple (in its + "The Notification Payload" docs) + + @param payload `alert` value from a push notification. + + @returns md5 identifier. + */ ++ (NSString *)md5DigestFromPushPayload:(id)payload; + +@end diff --git a/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.m b/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.m new file mode 100644 index 0000000..9107d18 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.m @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAnalyticsUtilities.h" + +#import "PFHash.h" + +@implementation PFAnalyticsUtilities + ++ (NSString *)md5DigestFromPushPayload:(id)payload { + if (!payload || payload == [NSNull null]) { + payload = @""; + } else if ([payload isKindOfClass:[NSDictionary class]]) { + NSDictionary *dict = payload; + NSArray *keys = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)]; + NSMutableArray *components = [NSMutableArray arrayWithCapacity:[dict count] * 2]; + [keys enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) { + [components addObject:key]; + + // alert[@"loc-args"] can be an NSArray + id value = [dict objectForKey:key]; + if ([value isKindOfClass:[NSArray class]]) { + value = [value componentsJoinedByString:@""]; + } + [components addObject:value]; + }]; + payload = [components componentsJoinedByString:@""]; + } + return PFMD5HashFromString([payload description]); +} + +@end diff --git a/Pods/Parse/Parse/Internal/BFTask+Private.h b/Pods/Parse/Parse/Internal/BFTask+Private.h new file mode 100644 index 0000000..0d01c82 --- /dev/null +++ b/Pods/Parse/Parse/Internal/BFTask+Private.h @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +#import "PFInternalUtils.h" + +@interface BFExecutor (Background) + ++ (instancetype)defaultPriorityBackgroundExecutor; + +@end + +@interface BFTask (Private) + +- (instancetype)continueAsyncWithBlock:(BFContinuationBlock)block; +- (instancetype)continueAsyncWithSuccessBlock:(BFContinuationBlock)block; + +- (instancetype)continueWithResult:(id)result; +- (instancetype)continueWithSuccessResult:(id)result; + +- (instancetype)continueWithMainThreadResultBlock:(PFIdResultBlock)resultBlock + executeIfCancelled:(BOOL)executeIfCancelled; +- (instancetype)continueWithMainThreadBooleanResultBlock:(PFBooleanResultBlock)resultBlock + executeIfCancelled:(BOOL)executeIfCancelled; + +/*! + Adds a continuation to the task that will run the given block on the main + thread sometime after this task has finished. If the task was cancelled, + the block will never be called. If the task had an exception, the exception + will be throw on the main thread instead of running the block. Otherwise, + the block will be given the result and error of this task. + @returns A new task that will be finished once the block has run. + */ +- (BFTask *)thenCallBackOnMainThreadAsync:(void(^)(id result, NSError *error))block; + +/*! + Identical to thenCallBackOnMainThreadAsync:, except that the result of a successful + task will be converted to a BOOL using the boolValue method, and that will + be passed to the block instead of the original result. + */ +- (BFTask *)thenCallBackOnMainThreadWithBoolValueAsync:(void(^)(BOOL result, NSError *error))block; + +/*! + Same as `waitForResult:error withMainThreadWarning:YES` + */ +- (id)waitForResult:(NSError **)error; + +/*! + Waits until this operation is completed, then returns its value. + This method is inefficient and consumes a thread resource while its running. + + @param error If an error occurs, upon return contains an `NSError` object that describes the problem. + @param warningEnabled `BOOL` value that + + @return Returns a `self.result` if task completed. `nil` - if cancelled. + */ +- (id)waitForResult:(NSError **)error withMainThreadWarning:(BOOL)warningEnabled; + +@end + +extern void forceLoadCategory_BFTask_Private(); diff --git a/Pods/Parse/Parse/Internal/BFTask+Private.m b/Pods/Parse/Parse/Internal/BFTask+Private.m new file mode 100644 index 0000000..5ed4959 --- /dev/null +++ b/Pods/Parse/Parse/Internal/BFTask+Private.m @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "BFTask+Private.h" + +#import +#import + +#import "PFLogging.h" + +@implementation BFExecutor (Background) + ++ (instancetype)defaultPriorityBackgroundExecutor { + static BFExecutor *executor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + executor = [BFExecutor executorWithDispatchQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; + }); + return executor; +} + +@end + +@implementation BFTask (Private) + +- (instancetype)continueAsyncWithBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:block]; +} + +- (instancetype)continueAsyncWithSuccessBlock:(BFContinuationBlock)block { + return [self continueWithExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withSuccessBlock:block]; +} + +- (instancetype)continueWithResult:(id)result { + return [self continueWithBlock:^id(BFTask *task) { + return result; + }]; +} + +- (instancetype)continueWithSuccessResult:(id)result { + return [self continueWithSuccessBlock:^id(BFTask *task) { + return result; + }]; +} + +- (instancetype)continueWithMainThreadResultBlock:(PFIdResultBlock)resultBlock + executeIfCancelled:(BOOL)executeIfCancelled { + if (!resultBlock) { + return self; + } + return [self continueWithExecutor:[BFExecutor mainThreadExecutor] + withBlock:^id(BFTask *task) { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + + @try { + if (self.exception) { + //TODO: (nlutsenko) Add more context, by passing a `_cmd` from the caller method + PFLogException(self.exception); + @throw self.exception; + } + + if (!self.cancelled || executeIfCancelled) { + resultBlock(self.result, self.error); + } + } @finally { + tcs.result = nil; + } + + return tcs.task; + }]; +} + +- (instancetype)continueWithMainThreadBooleanResultBlock:(PFBooleanResultBlock)resultBlock + executeIfCancelled:(BOOL)executeIfCancelled { + return [self continueWithMainThreadResultBlock:^(id object, NSError *error) { + resultBlock([object boolValue], error); + } executeIfCancelled:executeIfCancelled]; +} + +- (BFTask *)thenCallBackOnMainThreadAsync:(void(^)(id result, NSError *error))block { + return [self continueWithMainThreadResultBlock:block executeIfCancelled:NO]; +} + +- (BFTask *)thenCallBackOnMainThreadWithBoolValueAsync:(void(^)(BOOL result, NSError *error))block { + if (!block) { + return self; + } + return [self thenCallBackOnMainThreadAsync:^(id blockResult, NSError *blockError) { + block([blockResult boolValue], blockError); + }]; +} + +- (id)waitForResult:(NSError **)error { + return [self waitForResult:error withMainThreadWarning:YES]; +} + +- (id)waitForResult:(NSError **)error withMainThreadWarning:(BOOL)warningEnabled { + if (warningEnabled) { + [self waitUntilFinished]; + } else { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [self continueWithBlock:^id(BFTask *task) { + dispatch_semaphore_signal(semaphore); + return nil; + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + } + if (self.cancelled) { + return nil; + } else if (self.exception) { + @throw self.exception; + } + if (self.error && error) { + *error = self.error; + } + return self.result; +} + +@end + +void forceLoadCategory_BFTask_Private() { + NSString *string = nil; + [string description]; +} diff --git a/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.h b/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.h new file mode 100644 index 0000000..8532572 --- /dev/null +++ b/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@protocol PFCommandRunning; + +@interface PFCloudCodeController : NSObject + +@property (nonatomic, strong, readonly) id commandRunner; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommandRunner:(id)commandRunner NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner; + +///-------------------------------------- +/// @name Cloud Functions +///-------------------------------------- + +/*! + Calls a Cloud Code function and returns a result of it's execution. + + @param functionName Function name to call. + @param parameters Parameters to pass. (can't be nil). + @param sessionToken Session token to use. + + @returns `BFTask` with a result set to a result of Cloud Function. + */ +- (BFTask *)callCloudCodeFunctionAsync:(NSString *)functionName + withParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken; + +@end diff --git a/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.m b/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.m new file mode 100644 index 0000000..212e47a --- /dev/null +++ b/Pods/Parse/Parse/Internal/CloudCode/PFCloudCodeController.m @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCloudCodeController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFInternalUtils.h" +#import "PFRESTCloudCommand.h" + +@implementation PFCloudCodeController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner { + self = [super init]; + if (!self) return nil; + + _commandRunner = commandRunner; + + return self; +} + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner { + return [[self alloc] initWithCommandRunner:commandRunner]; +} + +///-------------------------------------- +#pragma mark - Cloud Functions +///-------------------------------------- + +- (BFTask *)callCloudCodeFunctionAsync:(NSString *)functionName + withParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + @weakify(self); + return [[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + NSDictionary *encodedParameters = [[PFNoObjectEncoder objectEncoder] encodeObject:parameters]; + PFRESTCloudCommand *command = [PFRESTCloudCommand commandForFunction:functionName + withParameters:encodedParameters + sessionToken:sessionToken]; + return [self.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return ((PFCommandResult *)(task.result)).result[@"result"]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [[PFDecoder objectDecoder] decodeObject:task.result]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.h new file mode 100644 index 0000000..c07f754 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.h @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" + +@class BFCancellationToken; +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFCommandResult; +@class PFRESTCommand; +@protocol PFNetworkCommand; + +typedef NS_OPTIONS(NSUInteger, PFCommandRunningOptions) { + PFCommandRunningOptionRetryIfFailed = 1 << 0, +}; + +extern NSTimeInterval const PFCommandRunningDefaultRetryDelay; + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFCommandRunning + +@property (nonatomic, weak, readonly) id dataSource; + +@property (nonatomic, copy, readonly) NSString *applicationId; +@property (nonatomic, copy, readonly) NSString *clientKey; + +@property (nonatomic, assign) NSTimeInterval initialRetryDelay; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource + applicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey; ++ (instancetype)commandRunnerWithDataSource:(id)dataSource + applicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey; + +///-------------------------------------- +/// @name Data Commands +///-------------------------------------- + +/*! + Run command. + + @param command Command to run. + @param options Options to use to run command. + + @returns `BFTask` with result set to `PFCommandResult`. + */ +- (BFTask *)runCommandAsync:(PFRESTCommand *)command + withOptions:(PFCommandRunningOptions)options; + +/*! + Run command. + + @param command Command to run. + @param options Options to use to run command. + @param cancellationToken Operation to use as a cancellation token. + + @returns `BFTask` with result set to `PFCommandResult`. + */ +- (BFTask *)runCommandAsync:(PFRESTCommand *)command + withOptions:(PFCommandRunningOptions)options + cancellationToken:(nullable BFCancellationToken *)cancellationToken; + +///-------------------------------------- +/// @name File Commands +///-------------------------------------- + +- (BFTask *)runFileUploadCommandAsync:(PFRESTCommand *)command + withContentType:(NSString *)contentType + contentSourceFilePath:(NSString *)sourceFilePath + options:(PFCommandRunningOptions)options + cancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock; + +- (BFTask *)runFileDownloadCommandAsyncWithFileURL:(NSURL *)url + targetFilePath:(NSString *)filePath + cancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.m new file mode 100644 index 0000000..6b0c49f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunning.m @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandRunning.h" + +NSTimeInterval const PFCommandRunningDefaultRetryDelay = 1.0; diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.h new file mode 100644 index 0000000..7531fde --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +///-------------------------------------- +/// @name Running +///-------------------------------------- + +extern uint8_t const PFCommandRunningDefaultMaxAttemptsCount; + +///-------------------------------------- +/// @name Headers +///-------------------------------------- + +extern NSString *const PFCommandHeaderNameApplicationId; +extern NSString *const PFCommandHeaderNameClientKey; +extern NSString *const PFCommandHeaderNameClientVersion; +extern NSString *const PFCommandHeaderNameInstallationId; +extern NSString *const PFCommandHeaderNameAppBuildVersion; +extern NSString *const PFCommandHeaderNameAppDisplayVersion; +extern NSString *const PFCommandHeaderNameOSVersion; +extern NSString *const PFCommandHeaderNameSessionToken; + +///-------------------------------------- +/// @name HTTP Method Override +///-------------------------------------- + +extern NSString *const PFCommandParameterNameMethodOverride; diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.m new file mode 100644 index 0000000..3ea747f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.m @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandRunningConstants.h" + +uint8_t const PFCommandRunningDefaultMaxAttemptsCount = 5; + +NSString *const PFCommandHeaderNameApplicationId = @"X-Parse-Application-Id"; +NSString *const PFCommandHeaderNameClientKey = @"X-Parse-Client-Key"; +NSString *const PFCommandHeaderNameClientVersion = @"X-Parse-Client-Version"; +NSString *const PFCommandHeaderNameInstallationId = @"X-Parse-Installation-Id"; +NSString *const PFCommandHeaderNameAppBuildVersion = @"X-Parse-App-Build-Version"; +NSString *const PFCommandHeaderNameAppDisplayVersion = @"X-Parse-App-Display-Version"; +NSString *const PFCommandHeaderNameOSVersion = @"X-Parse-OS-Version"; +NSString *const PFCommandHeaderNameSessionToken = @"X-Parse-Session-Token"; + +NSString *const PFCommandParameterNameMethodOverride = @"_method"; diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.h new file mode 100644 index 0000000..6583f5b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFDataProvider.h" + +@class PFRESTCommand; + +@interface PFCommandURLRequestConstructor : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)constructorWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Data +///-------------------------------------- + +- (NSURLRequest *)dataURLRequestForCommand:(PFRESTCommand *)command; + +///-------------------------------------- +/// @name File Upload +///-------------------------------------- + +- (NSURLRequest *)fileUploadURLRequestForCommand:(PFRESTCommand *)command + withContentType:(NSString *)contentType + contentSourceFilePath:(NSString *)contentFilePath; + +///-------------------------------------- +/// @name Headers +///-------------------------------------- + ++ (NSDictionary *)defaultURLRequestHeadersForApplicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey + bundle:(NSBundle *)bundle; + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.m new file mode 100644 index 0000000..b489704 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.m @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandURLRequestConstructor.h" + +#import "PFAssert.h" +#import "PFCommandRunningConstants.h" +#import "PFDevice.h" +#import "PFHTTPRequest.h" +#import "PFHTTPURLRequestConstructor.h" +#import "PFInstallationIdentifierStore.h" +#import "PFInternalUtils.h" +#import "PFRESTCommand.h" +#import "PFURLConstructor.h" +#import "Parse_Private.h" + +@implementation PFCommandURLRequestConstructor + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)constructorWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Data +///-------------------------------------- + +- (NSURLRequest *)dataURLRequestForCommand:(PFRESTCommand *)command { + NSURL *url = [PFURLConstructor URLFromAbsoluteString:[PFInternalUtils parseServerURLString] + path:[NSString stringWithFormat:@"/1/%@", command.httpPath] + query:nil]; + NSDictionary *headers = [self _URLRequestHeadersForCommand:command]; + + NSString *requestMethod = command.httpMethod; + NSDictionary *requestParameters = nil; + if (command.parameters) { + NSDictionary *parameters = nil; + + // The request URI may be too long to include parameters in the URI. + // To avoid this problem we send the parameters in a POST request json-encoded body + // and add a custom parameter that overrides the method in a request. + if ([requestMethod isEqualToString:PFHTTPRequestMethodGET] || + [requestMethod isEqualToString:PFHTTPRequestMethodHEAD] || + [requestMethod isEqualToString:PFHTTPRequestMethodDELETE]) { + NSMutableDictionary *mutableParameters = [command.parameters mutableCopy]; + mutableParameters[PFCommandParameterNameMethodOverride] = command.httpMethod; + + requestMethod = PFHTTPRequestMethodPOST; + parameters = [mutableParameters copy]; + } else { + parameters = command.parameters; + } + requestParameters = [[PFPointerObjectEncoder objectEncoder] encodeObject:parameters]; + } + + return [PFHTTPURLRequestConstructor urlRequestWithURL:url + httpMethod:requestMethod + httpHeaders:headers + parameters:requestParameters]; +} + +///-------------------------------------- +#pragma mark - File +///-------------------------------------- + +- (NSURLRequest *)fileUploadURLRequestForCommand:(PFRESTCommand *)command + withContentType:(NSString *)contentType + contentSourceFilePath:(NSString *)contentFilePath { + NSMutableURLRequest *request = [[self dataURLRequestForCommand:command] mutableCopy]; + + if (contentType) { + [request setValue:contentType forHTTPHeaderField:PFHTTPRequestHeaderNameContentType]; + } + + //TODO (nlutsenko): Check for error here. + NSNumber *fileSize = [PFInternalUtils fileSizeOfFileAtPath:contentFilePath error:nil]; + [request setValue:[fileSize stringValue] forHTTPHeaderField:PFHTTPRequestHeaderNameContentLength]; + + return request; +} + +///-------------------------------------- +#pragma mark - Headers +///-------------------------------------- + ++ (NSDictionary *)defaultURLRequestHeadersForApplicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey + bundle:(NSBundle *)bundle { +#if TARGET_OS_IPHONE + NSString *versionPrefix = @"i"; +#else + NSString *versionPrefix = @"osx"; +#endif + + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + + mutableHeaders[PFCommandHeaderNameApplicationId] = applicationId; + mutableHeaders[PFCommandHeaderNameClientKey] = clientKey; + + mutableHeaders[PFCommandHeaderNameClientVersion] = [versionPrefix stringByAppendingString:PARSE_VERSION]; + mutableHeaders[PFCommandHeaderNameOSVersion] = [PFDevice currentDevice].operatingSystemFullVersion; + + // Bundle Version and Display Version can be null, when running tests + NSString *bundleVersion = [bundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + if (bundleVersion) { + mutableHeaders[PFCommandHeaderNameAppBuildVersion] = bundleVersion; + } + NSString *displayVersion = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if (displayVersion) { + mutableHeaders[PFCommandHeaderNameAppDisplayVersion] = displayVersion; + } + + return [mutableHeaders copy]; +} + +- (NSDictionary *)_URLRequestHeadersForCommand:(PFRESTCommand *)command { + NSMutableDictionary *headers = [NSMutableDictionary dictionary]; + [headers addEntriesFromDictionary:command.additionalRequestHeaders]; + PFInstallationIdentifierStore *installationIdentifierStore = self.dataSource.installationIdentifierStore; + headers[PFCommandHeaderNameInstallationId] = installationIdentifierStore.installationIdentifier; + if (command.sessionToken) { + headers[PFCommandHeaderNameSessionToken] = command.sessionToken; + } + return [headers copy]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.h new file mode 100644 index 0000000..7f75dfd --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFCommandRunning.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionCommandRunner : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.m new file mode 100644 index 0000000..d504980 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.m @@ -0,0 +1,286 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionCommandRunner.h" +#import "PFURLSessionCommandRunner_Private.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunningConstants.h" +#import "PFCommandURLRequestConstructor.h" +#import "PFConstants.h" +#import "PFDevice.h" +#import "PFEncoder.h" +#import "PFHTTPRequest.h" +#import "PFHTTPURLRequestConstructor.h" +#import "PFInstallationIdentifierStore.h" +#import "PFInternalUtils.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFRESTCommand.h" +#import "PFURLConstructor.h" +#import "PFURLSession.h" + +@interface PFURLSessionCommandRunner () + +@property (nonatomic, strong) NSNotificationCenter *notificationCenter; + +@end + +@implementation PFURLSessionCommandRunner + +@synthesize applicationId = _applicationId; +@synthesize clientKey = _clientKey; +@synthesize initialRetryDelay = _initialRetryDelay; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource + applicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey { + NSURLSessionConfiguration *configuration = [[self class] _urlSessionConfigurationForApplicationId:applicationId + clientKey:clientKey]; + PFURLSession *session = [PFURLSession sessionWithConfiguration:configuration delegate:self]; + PFCommandURLRequestConstructor *constructor = [PFCommandURLRequestConstructor constructorWithDataSource:dataSource]; + self = [self initWithDataSource:dataSource + session:session + requestConstructor:constructor + notificationCenter:[NSNotificationCenter defaultCenter]]; + if (!self) return nil; + + _applicationId = [applicationId copy]; + _clientKey = [clientKey copy]; + + return self; +} + +- (instancetype)initWithDataSource:(id)dataSource + session:(PFURLSession *)session + requestConstructor:(PFCommandURLRequestConstructor *)requestConstructor + notificationCenter:(NSNotificationCenter *)notificationCenter { + self = [super init]; + if (!self) return nil; + + _initialRetryDelay = PFCommandRunningDefaultRetryDelay; + + _requestConstructor = requestConstructor; + _session = session; + _notificationCenter = notificationCenter; + + return self; +} + ++ (instancetype)commandRunnerWithDataSource:(id)dataSource + applicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey { + return [[self alloc] initWithDataSource:dataSource applicationId:applicationId clientKey:clientKey]; +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + // This is required to call, since session will continue to be present in memory and running otherwise. + [_session invalidateAndCancel]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (id)dataSource { + return _requestConstructor.dataSource; +} + +///-------------------------------------- +#pragma mark - Data Commands +///-------------------------------------- + +- (BFTask PF_GENERIC(PFCommandResult *)*)runCommandAsync:(PFRESTCommand *)command withOptions:(PFCommandRunningOptions)options { + return [self runCommandAsync:command withOptions:options cancellationToken:nil]; +} + +- (BFTask PF_GENERIC(PFCommandResult *)*)runCommandAsync:(PFRESTCommand *)command + withOptions:(PFCommandRunningOptions)options + cancellationToken:(BFCancellationToken *)cancellationToken { + return [self _performCommandRunningBlock:^id { + [command resolveLocalIds]; + NSURLRequest *request = [self.requestConstructor dataURLRequestForCommand:command]; + return [_session performDataURLRequestAsync:request forCommand:command cancellationToken:cancellationToken]; + } withOptions:options cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - File Commands +///-------------------------------------- + +- (BFTask PF_GENERIC(PFCommandResult *)*)runFileUploadCommandAsync:(PFRESTCommand *)command + withContentType:(NSString *)contentType + contentSourceFilePath:(NSString *)sourceFilePath + options:(PFCommandRunningOptions)options + cancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock { + @weakify(self); + return [self _performCommandRunningBlock:^id { + @strongify(self); + + [command resolveLocalIds]; + NSURLRequest *request = [self.requestConstructor fileUploadURLRequestForCommand:command + withContentType:contentType + contentSourceFilePath:sourceFilePath]; + return [_session performFileUploadURLRequestAsync:request + forCommand:command + withContentSourceFilePath:sourceFilePath + cancellationToken:cancellationToken + progressBlock:progressBlock]; + + } withOptions:options cancellationToken:cancellationToken]; +} + +- (BFTask PF_GENERIC(PFCommandResult *)*)runFileDownloadCommandAsyncWithFileURL:(NSURL *)url + targetFilePath:(NSString *)filePath + cancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock { + return [self _performCommandRunningBlock:^id { + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + return [_session performFileDownloadURLRequestAsync:request + toFileAtPath:filePath + withCancellationToken:cancellationToken + progressBlock:progressBlock]; + } withOptions:PFCommandRunningOptionRetryIfFailed + cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Retrying +///-------------------------------------- + +- (BFTask *)_performCommandRunningBlock:(nonnull id (^)())block + withOptions:(PFCommandRunningOptions)options + cancellationToken:(BFCancellationToken *)cancellationToken { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + if (!(options & PFCommandRunningOptionRetryIfFailed)) { + return block(); + } + + NSTimeInterval delay = self.initialRetryDelay; // Delay (secs) of next retry attempt + + // Set the initial delay to something between 1 and 2 seconds. We want it to be + // random so that clients that fail simultaneously don't retry on simultaneous + // intervals. + delay += self.initialRetryDelay * ((double)(arc4random() & 0x0FFFF) / (double)0x0FFFF); + return [self _performCommandRunningBlock:block + withCancellationToken:cancellationToken + delay:delay + forAttempts:PFCommandRunningDefaultMaxAttemptsCount]; +} + +- (BFTask *)_performCommandRunningBlock:(nonnull id (^)())block + withCancellationToken:(BFCancellationToken *)cancellationToken + delay:(NSTimeInterval)delay + forAttempts:(NSUInteger)attempts { + @weakify(self); + return [block() continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (task.cancelled) { + return task; + } + + if ([[task.error userInfo][@"temporary"] boolValue] && attempts > 1) { + PFLogError(PFLoggingTagCommon, + @"Network connection failed. Making attempt %lu after sleeping for %f seconds.", + (unsigned long)(PFCommandRunningDefaultMaxAttemptsCount - attempts + 1), (double)delay); + + return [[BFTask taskWithDelay:(int)(delay * 1000)] continueWithBlock:^id(BFTask *task) { + return [self _performCommandRunningBlock:block + withCancellationToken:cancellationToken + delay:delay * 2.0 + forAttempts:attempts - 1]; + } cancellationToken:cancellationToken]; + } + return task; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - NSURLSessionConfiguration +///-------------------------------------- + ++ (NSURLSessionConfiguration *)_urlSessionConfigurationForApplicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + + // No cookies, they are bad for you. + configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever; + configuration.HTTPShouldSetCookies = NO; + + // Completely disable caching of responses for security reasons. + configuration.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:[NSURLCache sharedURLCache].memoryCapacity + diskCapacity:0 + diskPath:nil]; + + NSBundle *bundle = [NSBundle mainBundle]; + NSDictionary *headers = [PFCommandURLRequestConstructor defaultURLRequestHeadersForApplicationId:applicationId + clientKey:clientKey + bundle:bundle]; + configuration.HTTPAdditionalHeaders = headers; + + return configuration; +} + +///-------------------------------------- +#pragma mark - PFURLSessionDelegate +///-------------------------------------- + +- (void)urlSession:(PFURLSession *)session willPerformURLRequest:(NSURLRequest *)request { + [[BFExecutor defaultPriorityBackgroundExecutor] execute:^{ + NSDictionary *userInfo = ([PFLogger sharedLogger].logLevel == PFLogLevelDebug ? + @{ PFNetworkNotificationURLRequestUserInfoKey : request } : nil); + [self.notificationCenter postNotificationName:PFNetworkWillSendURLRequestNotification + object:self + userInfo:userInfo]; + }]; +} + +- (void)urlSession:(PFURLSession *)session +didPerformURLRequest:(NSURLRequest *)request + withURLResponse:(nullable NSURLResponse *)response + responseString:(nullable NSString *)responseString { + [[BFExecutor defaultPriorityBackgroundExecutor] execute:^{ + NSMutableDictionary *userInfo = nil; + if ([PFLogger sharedLogger].logLevel == PFLogLevelDebug) { + userInfo = [NSMutableDictionary dictionaryWithObject:request + forKey:PFNetworkNotificationURLRequestUserInfoKey]; + if (response) { + userInfo[PFNetworkNotificationURLResponseUserInfoKey] = response; + } + if (responseString) { + userInfo[PFNetworkNotificationURLResponseBodyUserInfoKey] = responseString; + } + } + [self.notificationCenter postNotificationName:PFNetworkDidReceiveURLResponseNotification + object:self + userInfo:userInfo]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner_Private.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner_Private.h new file mode 100644 index 0000000..e10c6f1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner_Private.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionCommandRunner.h" + +@class PFCommandURLRequestConstructor; +@class PFURLSession; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionCommandRunner () + +@property (nonatomic, strong, readonly) PFURLSession *session; +@property (nonatomic, strong, readonly) PFCommandURLRequestConstructor *requestConstructor; + +- (instancetype)initWithDataSource:(id)dataSource + session:(PFURLSession *)session + requestConstructor:(PFCommandURLRequestConstructor *)requestConstructor + notificationCenter:(NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.h new file mode 100644 index 0000000..ff53d22 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.h @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFCancellationToken; + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFRESTCommand; + +NS_ASSUME_NONNULL_BEGIN + +@class PFURLSession; + +@protocol PFURLSessionDelegate + +- (void)urlSession:(PFURLSession *)session willPerformURLRequest:(NSURLRequest *)request; + +- (void)urlSession:(PFURLSession *)session didPerformURLRequest:(NSURLRequest *)request withURLResponse:(nullable NSURLResponse *)response responseString:(nullable NSString *)string; + +@end + +@interface PFURLSession : NSObject + +@property (nonatomic, weak, readonly) id delegate; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(id)delegate NS_DESIGNATED_INITIALIZER; + ++ (instancetype)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(id)delegate; + +///-------------------------------------- +/// @name Teardown +///-------------------------------------- + +- (void)invalidateAndCancel; + +///-------------------------------------- +/// @name Network Requests +///-------------------------------------- + +- (BFTask *)performDataURLRequestAsync:(NSURLRequest *)request + forCommand:(PFRESTCommand *)command + cancellationToken:(nullable BFCancellationToken *)cancellationToken; + +- (BFTask *)performFileUploadURLRequestAsync:(NSURLRequest *)request + forCommand:(PFRESTCommand *)command + withContentSourceFilePath:(NSString *)sourceFilePath + cancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock; + +- (BFTask *)performFileDownloadURLRequestAsync:(NSURLRequest *)request + toFileAtPath:(NSString *)filePath + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.m new file mode 100644 index 0000000..9934f47 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.m @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSession.h" +#import "PFURLSession_Private.h" + +#import + +#import "BFTask+Private.h" +#import "PFCommandResult.h" +#import "PFMacros.h" +#import "PFAssert.h" +#import "PFURLSessionJSONDataTaskDelegate.h" +#import "PFURLSessionUploadTaskDelegate.h" +#import "PFURLSessionFileDownloadTaskDelegate.h" + +typedef void (^PFURLSessionTaskCompletionHandler)(NSData *data, NSURLResponse *response, NSError *error); + +@interface PFURLSession () { + dispatch_queue_t _sessionTaskQueue; + NSURLSession *_urlSession; + NSMutableDictionary *_delegatesDictionary; + dispatch_queue_t _delegatesAccessQueue; +} + +@end + +@implementation PFURLSession + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(id)delegate { + // NOTE: cast to id suppresses warning about designated initializer. + return [(id)self initWithURLSession:[NSURLSession sessionWithConfiguration:configuration + delegate:self + delegateQueue:nil] + delegate:delegate]; +} + +- (instancetype)initWithURLSession:(NSURLSession *)session + delegate:(id)delegate { + self = [super init]; + if (!self) return nil; + + _delegate = delegate; + _urlSession = session; + + _sessionTaskQueue = dispatch_queue_create("com.parse.urlSession.tasks", DISPATCH_QUEUE_SERIAL); + + _delegatesDictionary = [NSMutableDictionary dictionary]; + _delegatesAccessQueue = dispatch_queue_create("com.parse.urlSession.delegates", DISPATCH_QUEUE_CONCURRENT); + + return self; +} + ++ (instancetype)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(id)delegate { + return [[self alloc] initWithConfiguration:configuration delegate:delegate]; +} + ++ (instancetype)sessionWithURLSession:(nonnull NSURLSession *)session + delegate:(id)delegate { + return [[self alloc] initWithURLSession:session delegate:delegate]; +} + +///-------------------------------------- +#pragma mark - Teardown +///-------------------------------------- + +- (void)invalidateAndCancel { + [_urlSession invalidateAndCancel]; +} + +///-------------------------------------- +#pragma mark - Network Requests +///-------------------------------------- + +- (BFTask *)performDataURLRequestAsync:(NSURLRequest *)request + forCommand:(PFRESTCommand *)command + cancellationToken:(BFCancellationToken *)cancellationToken { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + __block NSURLSessionDataTask *task = nil; + dispatch_sync(_sessionTaskQueue, ^{ + task = [_urlSession dataTaskWithRequest:request]; + }); + PFURLSessionDataTaskDelegate *delegate = [PFURLSessionJSONDataTaskDelegate taskDelegateForDataTask:task + withCancellationToken:cancellationToken]; + return [self _performDataTask:task withDelegate:delegate]; + }]; +} + +- (BFTask *)performFileUploadURLRequestAsync:(NSURLRequest *)request + forCommand:(PFRESTCommand *)command + withContentSourceFilePath:(NSString *)sourceFilePath + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + __block NSURLSessionDataTask *task = nil; + dispatch_sync(_sessionTaskQueue, ^{ + task = [_urlSession uploadTaskWithRequest:request fromFile:[NSURL fileURLWithPath:sourceFilePath]]; + }); + PFURLSessionUploadTaskDelegate *delegate = [PFURLSessionUploadTaskDelegate taskDelegateForDataTask:task + withCancellationToken:cancellationToken + uploadProgressBlock:progressBlock]; + return [self _performDataTask:task withDelegate:delegate]; + }]; +} + +- (BFTask *)performFileDownloadURLRequestAsync:(NSURLRequest *)request + toFileAtPath:(NSString *)filePath + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + progressBlock:(nullable PFProgressBlock)progressBlock { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + __block NSURLSessionDataTask *task = nil; + dispatch_sync(_sessionTaskQueue, ^{ + task = [_urlSession dataTaskWithRequest:request]; + }); + PFURLSessionFileDownloadTaskDelegate *delegate = [PFURLSessionFileDownloadTaskDelegate taskDelegateForDataTask:task + withCancellationToken:cancellationToken + targetFilePath:filePath + progressBlock:progressBlock]; + return [self _performDataTask:task withDelegate:delegate]; + }]; +} + +- (BFTask *)_performDataTask:(NSURLSessionDataTask *)dataTask withDelegate:(PFURLSessionDataTaskDelegate *)delegate { + [self.delegate urlSession:self willPerformURLRequest:dataTask.originalRequest]; + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + @strongify(self); + NSNumber *taskIdentifier = @(dataTask.taskIdentifier); + [self setDelegate:delegate forDataTask:dataTask]; + + BFTask *resultTask = [delegate.resultTask continueWithBlock:^id(BFTask *task) { + @strongify(self); + [self.delegate urlSession:self + didPerformURLRequest:dataTask.originalRequest + withURLResponse:delegate.response + responseString:delegate.responseString]; + + [self _removeDelegateForTaskWithIdentifier:taskIdentifier]; + return task; + }]; + [dataTask resume]; + + return resultTask; + }]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (PFURLSessionDataTaskDelegate *)_taskDelegateForDataTask:(NSURLSessionDataTask *)task { + __block PFURLSessionDataTaskDelegate *delegate = nil; + dispatch_sync(_delegatesAccessQueue, ^{ + delegate = _delegatesDictionary[@(task.taskIdentifier)]; + }); + return delegate; +} + +- (void)setDelegate:(PFURLSessionDataTaskDelegate *)delegate forDataTask:(NSURLSessionDataTask *)task { + dispatch_barrier_async(_delegatesAccessQueue, ^{ + _delegatesDictionary[@(task.taskIdentifier)] = delegate; + }); +} + +- (void)_removeDelegateForTaskWithIdentifier:(NSNumber *)identifier { + dispatch_barrier_async(_delegatesAccessQueue, ^{ + [_delegatesDictionary removeObjectForKey:identifier]; + }); +} + +///-------------------------------------- +#pragma mark - NSURLSessionTaskDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionDataTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + PFURLSessionDataTaskDelegate *delegate = [self _taskDelegateForDataTask:task]; + [delegate URLSession:session + task:task + didSendBodyData:bytesSent + totalBytesSent:totalBytesSent +totalBytesExpectedToSend:totalBytesExpectedToSend]; +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionDataTask *)task didCompleteWithError:(NSError *)error { + PFURLSessionDataTaskDelegate *delegate = [self _taskDelegateForDataTask:task]; + [delegate URLSession:session task:task didCompleteWithError:error]; +} + +///-------------------------------------- +#pragma mark - NSURLSessionDataDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { + PFURLSessionDataTaskDelegate *delegate = [self _taskDelegateForDataTask:dataTask]; + [delegate URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { + PFURLSessionDataTaskDelegate *delegate = [self _taskDelegateForDataTask:dataTask]; + [delegate URLSession:session dataTask:dataTask didReceiveData:data]; +} + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask + willCacheResponse:(NSCachedURLResponse *)proposedResponse + completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { + completionHandler(nil); // Prevent any caching for security reasons +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession_Private.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession_Private.h new file mode 100644 index 0000000..1f047ac --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession_Private.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSession.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSession () + +- (instancetype)initWithURLSession:(NSURLSession *)session + delegate:(id)delegate NS_DESIGNATED_INITIALIZER; + ++ (instancetype)sessionWithURLSession:(NSURLSession *)session + delegate:(id)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.h new file mode 100644 index 0000000..a1f8ca9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFCancellationToken; + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionDataTaskDelegate : NSObject + +@property (nonatomic, strong, readonly) NSURLSessionDataTask *dataTask; +@property (nonatomic, strong, readonly) BFTask *resultTask; + +@property (nonatomic, strong, readonly) NSHTTPURLResponse *response; +@property (nullable, nonatomic, copy, readonly) NSString *responseString; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken NS_DESIGNATED_INITIALIZER; + ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.m new file mode 100644 index 0000000..f81e9d6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.m @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionDataTaskDelegate.h" +#import "PFURLSessionDataTaskDelegate_Private.h" + +#import +#import + +#import "PFAssert.h" +#import "PFMacros.h" + +@interface PFURLSessionDataTaskDelegate () { + BFTaskCompletionSource *_taskCompletionSource; +} + +@end + +@implementation PFURLSessionDataTaskDelegate + +@synthesize dataOutputStream = _dataOutputStream; +@synthesize downloadedBytes = _downloadedBytes; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(BFCancellationToken *)cancellationToken { + self = [super init]; + if (!self) return nil; + + _taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; + + _dataTask = dataTask; + @weakify(self); + [cancellationToken registerCancellationObserverWithBlock:^{ + @strongify(self); + [self _cancel]; + }]; + + return self; +} + ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken { + return [[self alloc] initForDataTask:dataTask withCancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (BFTask *)resultTask { + return _taskCompletionSource.task; +} + +- (NSOutputStream *)dataOutputStream { + if (!_dataOutputStream) { + _dataOutputStream = [NSOutputStream outputStreamToMemory]; + } + return _dataOutputStream; +} + +///-------------------------------------- +#pragma mark - Task +///-------------------------------------- + +- (void)_taskDidFinish { + [self _closeDataOutputStream]; + if (self.error) { + [_taskCompletionSource trySetError:self.error]; + } else { + [_taskCompletionSource trySetResult:self.result]; + } +} + +- (void)_taskDidCancel { + [self _closeDataOutputStream]; + [_taskCompletionSource trySetCancelled]; +} + +- (void)_cancel { + [self.dataTask cancel]; +} + +///-------------------------------------- +#pragma mark - Stream +///-------------------------------------- + +- (void)_openDataOutputStream { + [self.dataOutputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.dataOutputStream open]; +} + +- (void)_writeDataOutputStreamData:(NSData *)data { + NSInteger length = [data length]; + while (YES) { + NSInteger bytesWritten = 0; + if ([self.dataOutputStream hasSpaceAvailable]) { + const uint8_t *dataBuffer = (uint8_t *)[data bytes]; + + NSInteger numberOfBytesWritten = 0; + while (bytesWritten < length) { + numberOfBytesWritten = [self.dataOutputStream write:&dataBuffer[bytesWritten] + maxLength:(length - bytesWritten)]; + if (numberOfBytesWritten == -1) { + break; + } + + bytesWritten += numberOfBytesWritten; + } + break; + } + + if (self.dataOutputStream.streamError) { + [self.dataTask cancel]; + self.error = self.dataOutputStream.streamError; + // Don't finish the delegate here, as we will finish when NSURLSessionTask calls back about cancellation. + return; + } + } + _downloadedBytes += length; +} + +- (void)_closeDataOutputStream { + [self.dataOutputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [self.dataOutputStream close]; +} + +///-------------------------------------- +#pragma mark - NSURLSessionTaskDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + // No-op, we don't care about progress here. +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + [self _taskDidCancel]; + } else { + self.error = self.error ?: error; + [self _taskDidFinish]; + } +} + +///-------------------------------------- +#pragma mark - NSURLSessionDataDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session + dataTask:(NSURLSessionDataTask *)dataTask +didReceiveResponse:(NSURLResponse *)response + completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { + _response = (NSHTTPURLResponse *)response; + [self _openDataOutputStream]; + + completionHandler(NSURLSessionResponseAllow); +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { + [self _writeDataOutputStreamData:data]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate_Private.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate_Private.h new file mode 100644 index 0000000..030d4f0 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate_Private.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionDataTaskDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionDataTaskDelegate () + +@property (nonatomic, strong, readonly) dispatch_queue_t dataQueue; + +/*! + @abstract Defaults to to-memory output stream if not overwritten. + */ +@property (nonatomic, strong, readonly) NSOutputStream *dataOutputStream; +@property (nonatomic, assign, readonly) uint64_t downloadedBytes; + +@property (nullable, nonatomic, strong) id result; +@property (nullable, nonatomic, strong) NSError *error; + +@property (nullable, nonatomic, copy, readwrite) NSString *responseString; + +- (void)_taskDidFinish NS_REQUIRES_SUPER; +- (void)_taskDidCancel NS_REQUIRES_SUPER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.h new file mode 100644 index 0000000..918e1b5 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionDataTaskDelegate.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionFileDownloadTaskDelegate : PFURLSessionDataTaskDelegate + +@property (nonatomic, copy, readonly) NSString *targetFilePath; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + targetFilePath:(NSString *)targetFilePath + progressBlock:(nullable PFProgressBlock)progressBlock; ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + targetFilePath:(NSString *)targetFilePath + progressBlock:(nullable PFProgressBlock)progressBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.m new file mode 100644 index 0000000..d770267 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.m @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionFileDownloadTaskDelegate.h" + +#import "PFErrorUtilities.h" +#import "PFHash.h" +#import "PFURLSessionDataTaskDelegate_Private.h" + +@interface PFURLSessionFileDownloadTaskDelegate () { + NSOutputStream *_fileDataOutputStream; + PFProgressBlock _progressBlock; +} + +@end + +@implementation PFURLSessionFileDownloadTaskDelegate + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(BFCancellationToken *)cancellationToken + targetFilePath:(NSString *)targetFilePath + progressBlock:(PFProgressBlock)progressBlock { + self = [super initForDataTask:dataTask withCancellationToken:cancellationToken]; + if (!self) return nil; + + _targetFilePath = targetFilePath; + _fileDataOutputStream = [NSOutputStream outputStreamToFileAtPath:_targetFilePath append:NO]; + _progressBlock = progressBlock; + + return self; +} + ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(BFCancellationToken *)cancellationToken + targetFilePath:(NSString *)targetFilePath + progressBlock:(PFProgressBlock)progressBlock { + return [[self alloc] initForDataTask:dataTask + withCancellationToken:cancellationToken + targetFilePath:targetFilePath + progressBlock:progressBlock]; +} + +///-------------------------------------- +#pragma mark - Progress +///-------------------------------------- + +- (void)_reportProgress { + if (!_progressBlock) { + return; + } + + int progress = (int)(self.downloadedBytes / (CGFloat)self.response.expectedContentLength * 100); + _progressBlock(progress); +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSOutputStream *)dataOutputStream { + return _fileDataOutputStream; +} + +///-------------------------------------- +#pragma mark - Task +///-------------------------------------- + +- (void)_taskDidFinish { + if (self.error) { + // TODO: (nlutsenko) Unify this with code from PFURLSessionJSONDataTaskDelegate + NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; + errorDictionary[@"code"] = @(kPFErrorConnectionFailed); + errorDictionary[@"error"] = [self.error localizedDescription]; + errorDictionary[@"originalError"] = self.error; + errorDictionary[NSUnderlyingErrorKey] = self.error; + errorDictionary[@"temporary"] = @(self.response.statusCode >= 500 || self.response.statusCode < 400); + self.error = [PFErrorUtilities errorFromResult:errorDictionary]; + } + [super _taskDidFinish]; +} + +///-------------------------------------- +#pragma mark - NSURLSessionDataDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { + [super URLSession:session dataTask:dataTask didReceiveData:data]; + [self _reportProgress]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.h new file mode 100644 index 0000000..66a06d1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFURLSessionDataTaskDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionJSONDataTaskDelegate : PFURLSessionDataTaskDelegate + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.m new file mode 100644 index 0000000..f10eac3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.m @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionJSONDataTaskDelegate.h" + +#import +#import +#import + +#import "PFCommandResult.h" +#import "PFConstants.h" +#import "PFErrorUtilities.h" +#import "PFMacros.h" +#import "PFURLSessionDataTaskDelegate_Private.h" + +@interface PFURLSessionJSONDataTaskDelegate () + +@end + +@implementation PFURLSessionJSONDataTaskDelegate + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (void)_taskDidFinish { + NSData *data = [self.dataOutputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + + id result = nil; + + NSError *jsonError = nil; + if (data) { + self.responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + result = [NSJSONSerialization JSONObjectWithData:data + options:0 + error:&jsonError]; + + if (jsonError && !self.error) { + self.error = jsonError; + [super _taskDidFinish]; + return; + } + } + + if (self.error) { + NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; + errorDictionary[@"code"] = @(kPFErrorConnectionFailed); + errorDictionary[@"originalError"] = self.error; + errorDictionary[NSUnderlyingErrorKey] = self.error; + errorDictionary[@"temporary"] = @(self.response.statusCode >= 500 || self.response.statusCode < 400); + + NSString *description = [self.error localizedDescription] ?: [self.error localizedFailureReason]; + if (description) { + errorDictionary[@"error"] = description; + } + + self.error = [PFErrorUtilities errorFromResult:errorDictionary]; + [super _taskDidFinish]; + return; + } + + if (self.response.statusCode >= 200) { + if (self.response.statusCode < 400) { + PFCommandResult *commandResult = [PFCommandResult commandResultWithResult:result + resultString:self.responseString + httpResponse:self.response]; + self.result = commandResult; + } else if ([result isKindOfClass:[NSDictionary class]]) { + NSDictionary *resultDictionary = (NSDictionary *)result; + if (resultDictionary[@"error"]) { + NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionaryWithDictionary:resultDictionary]; + errorDictionary[@"temporary"] = @(self.response.statusCode >= 500 || self.response.statusCode < 400); + self.error = [PFErrorUtilities errorFromResult:errorDictionary]; + } + } + } + + if (!self.result && !self.error) { + self.error = [PFErrorUtilities errorWithCode:kPFErrorInternalServer message:self.responseString]; + } + [super _taskDidFinish]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.h b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.h new file mode 100644 index 0000000..d08225f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFURLSessionJSONDataTaskDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLSessionUploadTaskDelegate : PFURLSessionJSONDataTaskDelegate + +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + uploadProgressBlock:(nullable PFProgressBlock)progressBlock; ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + uploadProgressBlock:(nullable PFProgressBlock)progressBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.m b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.m new file mode 100644 index 0000000..8cf9698 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.m @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLSessionUploadTaskDelegate.h" + +@implementation PFURLSessionUploadTaskDelegate { + __nullable PFProgressBlock _progressBlock; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + uploadProgressBlock:(nullable PFProgressBlock)progressBlock { + self = [self initForDataTask:dataTask withCancellationToken:cancellationToken]; + if (!self) return nil; + + _progressBlock = [progressBlock copy]; + + return self; +} + ++ (instancetype)taskDelegateForDataTask:(NSURLSessionDataTask *)dataTask + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + uploadProgressBlock:(nullable PFProgressBlock)progressBlock { + return [[self alloc] initForDataTask:dataTask + withCancellationToken:cancellationToken + uploadProgressBlock:progressBlock]; +} + +///-------------------------------------- +#pragma mark - NSURLSessionTaskDelegate +///-------------------------------------- + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent +totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { + int progress = (int)round(totalBytesSent / (CGFloat)totalBytesExpectedToSend * 100); + dispatch_async(dispatch_get_main_queue(), ^{ + if (_progressBlock) { + _progressBlock(progress); + } + }); +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.h new file mode 100644 index 0000000..585b141 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const PFRESTAnalyticsEventNameAppOpened; +extern NSString *const PFRESTAnalyticsEventNameCrashReport; + +@interface PFRESTAnalyticsCommand : PFRESTCommand + ++ (instancetype)trackAppOpenedEventCommandWithPushHash:(nullable NSString *)pushHash + sessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)trackEventCommandWithEventName:(NSString *)eventName + dimensions:(nullable NSDictionary *)dimensions + sessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)trackCrashReportCommandWithBreakpadDumpParameters:(NSDictionary *)parameters + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.m new file mode 100644 index 0000000..6badd35 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.m @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTAnalyticsCommand.h" + +#import "PFHTTPRequest.h" + +/** + * Predefined events - AppOpened, CrashReport + * Coming soon - Log, ... + */ +NSString *const PFRESTAnalyticsEventNameAppOpened = @"AppOpened"; +NSString *const PFRESTAnalyticsEventNameCrashReport = @"_CrashReport"; + +@implementation PFRESTAnalyticsCommand + ++ (instancetype)trackAppOpenedEventCommandWithPushHash:(NSString *)pushHash + sessionToken:(NSString *)sessionToken { + NSDictionary *parameters = (pushHash ? @{ @"push_hash" : pushHash } : nil); + return [self _trackEventCommandWithEventName:PFRESTAnalyticsEventNameAppOpened + parameters:parameters + sessionToken:sessionToken]; +} + ++ (instancetype)trackEventCommandWithEventName:(NSString *)eventName + dimensions:(NSDictionary *)dimensions + sessionToken:(NSString *)sessionToken { + NSDictionary *parameters = (dimensions ? @{ @"dimensions" : dimensions } : nil); + return [self _trackEventCommandWithEventName:eventName parameters:parameters sessionToken:sessionToken]; +} + ++ (instancetype)trackCrashReportCommandWithBreakpadDumpParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + return [self _trackEventCommandWithEventName:PFRESTAnalyticsEventNameCrashReport + parameters:@{ @"breakpadDump" : parameters } + sessionToken:sessionToken]; +} + ++ (instancetype)_trackEventCommandWithEventName:(NSString *)eventName + parameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + NSString *httpPath = [NSString stringWithFormat:@"events/%@", eventName]; + + NSMutableDictionary *dictionary = (parameters ? [parameters mutableCopy] : [NSMutableDictionary dictionary]); + if (!dictionary[@"at"]) { + dictionary[@"at"] = [NSDate date]; + } + + return [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodPOST + parameters:dictionary + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.h new file mode 100644 index 0000000..b15bf7a --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTCloudCommand : PFRESTCommand + ++ (instancetype)commandForFunction:(NSString *)function + withParameters:(nullable NSDictionary *)parameters + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.m new file mode 100644 index 0000000..5bd85a9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTCloudCommand.m @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCloudCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" + +@implementation PFRESTCloudCommand + ++ (instancetype)commandForFunction:(NSString *)function + withParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + NSString *path = [NSString stringWithFormat:@"functions/%@", function]; + return [self commandWithHTTPPath:path + httpMethod:PFHTTPRequestMethodPOST + parameters:parameters + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.h new file mode 100644 index 0000000..0e6ad3e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFNetworkCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTCommand : NSObject + +@property (nonatomic, copy, readonly) NSString *httpPath; +@property (nonatomic, copy, readonly) NSString *httpMethod; + +@property (nullable, nonatomic, copy, readonly) NSDictionary *parameters; +@property (nullable, nonatomic, copy) NSDictionary *additionalRequestHeaders; + +@property (nonatomic, copy, readonly) NSString *cacheKey; + +@property (nullable, nonatomic, copy) NSString *localId; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + ++ (instancetype)commandWithHTTPPath:(NSString *)path + httpMethod:(NSString *)httpMethod + parameters:(nullable NSDictionary *)parameters + sessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)commandWithHTTPPath:(NSString *)path + httpMethod:(NSString *)httpMethod + parameters:(nullable NSDictionary *)parameters + operationSetUUID:(nullable NSString *)operationSetIdentifier + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.m new file mode 100644 index 0000000..b9460fa --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand.m @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" +#import "PFRESTCommand_Private.h" + +#import "PFAssert.h" +#import "PFCoreManager.h" +#import "PFFieldOperation.h" +#import "PFHTTPRequest.h" +#import "PFHash.h" +#import "PFInternalUtils.h" +#import "PFObjectLocalIdStore.h" +#import "PFObjectPrivate.h" +#import "Parse_Private.h" + +static NSString *const PFRESTCommandHTTPPathEncodingKey = @"httpPath"; +static NSString *const PFRESTCommandHTTPMethodEncodingKey = @"httpMethod"; +static NSString *const PFRESTCommandParametersEncodingKey = @"parameters"; +static NSString *const PFRESTCommandSessionTokenEncodingKey = @"sessionToken"; +static NSString *const PFRESTCommandLocalIdEncodingKey = @"localId"; + +// Increment this when you change the format of cache values. +static const int PFRESTCommandCacheKeyVersion = 1; + +@implementation PFRESTCommand + +@synthesize sessionToken = _sessionToken; +@synthesize operationSetUUID = _operationSetUUID; +@synthesize localId = _localId; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)commandWithHTTPPath:(NSString *)path + httpMethod:(NSString *)httpMethod + parameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + return [self commandWithHTTPPath:path + httpMethod:httpMethod + parameters:parameters + operationSetUUID:nil + sessionToken:sessionToken]; +} + ++ (instancetype)commandWithHTTPPath:(NSString *)path + httpMethod:(NSString *)httpMethod + parameters:(NSDictionary *)parameters + operationSetUUID:(NSString *)operationSetIdentifier + sessionToken:(NSString *)sessionToken { + PFRESTCommand *command = [[self alloc] init]; + command.httpPath = path; + command.httpMethod = httpMethod; + command.parameters = parameters; + command.operationSetUUID = operationSetIdentifier; + command.sessionToken = sessionToken; + return command; +} + +///-------------------------------------- +#pragma mark - CacheKey +///-------------------------------------- + +- (NSString *)cacheKey { + if (_cacheKey) { + return _cacheKey; + } + + NSMutableDictionary *cacheParameters = [NSMutableDictionary dictionaryWithCapacity:2]; + if (self.parameters) { + cacheParameters[PFRESTCommandParametersEncodingKey] = self.parameters; + } + if (self.sessionToken) { + cacheParameters[PFRESTCommandSessionTokenEncodingKey] = self.sessionToken; + } + + NSString *parametersCacheKey = [PFInternalUtils cacheKeyForObject:cacheParameters]; + + _cacheKey = [NSString stringWithFormat:@"PFRESTCommand.%i.%@.%@.%ld.%@", + PFRESTCommandCacheKeyVersion, self.httpMethod, PFMD5HashFromString(self.httpPath), + // We use MD5 instead of native hash because it collides too much. + (long)PARSE_API_VERSION, PFMD5HashFromString(parametersCacheKey)]; + return _cacheKey; +} + +///-------------------------------------- +#pragma mark - PFNetworkCommand +///-------------------------------------- + +#pragma mark Encoding/Decoding + ++ (instancetype)commandFromDictionaryRepresentation:(NSDictionary *)dictionary { + if (![self isValidDictionaryRepresentation:dictionary]) { + return nil; + } + + PFRESTCommand *command = [self commandWithHTTPPath:dictionary[PFRESTCommandHTTPPathEncodingKey] + httpMethod:dictionary[PFRESTCommandHTTPMethodEncodingKey] + parameters:dictionary[PFRESTCommandParametersEncodingKey] + sessionToken:dictionary[PFRESTCommandSessionTokenEncodingKey]]; + command.localId = dictionary[PFRESTCommandLocalIdEncodingKey]; + return command; +} + +- (NSDictionary *)dictionaryRepresentation { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + if (self.httpPath) { + dictionary[PFRESTCommandHTTPPathEncodingKey] = self.httpPath; + } + if (self.httpMethod) { + dictionary[PFRESTCommandHTTPMethodEncodingKey] = self.httpMethod; + } + if (self.parameters) { + NSDictionary *parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:self.parameters]; + dictionary[PFRESTCommandParametersEncodingKey] = parameters; + } + if (self.sessionToken) { + dictionary[PFRESTCommandSessionTokenEncodingKey] = self.sessionToken; + } + if (self.localId) { + dictionary[PFRESTCommandLocalIdEncodingKey] = self.localId; + } + return [dictionary copy]; +} + ++ (BOOL)isValidDictionaryRepresentation:(NSDictionary *)dictionary { + return dictionary[PFRESTCommandHTTPPathEncodingKey] != nil; +} + +#pragma mark Local Identifiers + +/*! + If this was the second save on a new object while offline, then its objectId + wasn't yet set when the command was created, so it would have been considered a + "create". But if the first save succeeded, then there is an objectId now, and it + will be mapped to the localId for this command's result. If so, change the + "create" operation to an "update", and add the objectId to the command. + */ +- (void)maybeChangeServerOperation { + if (self.localId) { + NSString *objectId = [[Parse _currentManager].coreManager.objectLocalIdStore objectIdForLocalId:self.localId]; + if (objectId) { + self.localId = nil; + + NSArray *components = [self.httpPath pathComponents]; + if ([components count] == 2) { + self.httpPath = [NSString pathWithComponents:[components arrayByAddingObject:objectId]]; + } + + if ([self.httpPath hasPrefix:@"classes"] && + [self.httpMethod isEqualToString:PFHTTPRequestMethodPOST]) { + self.httpMethod = PFHTTPRequestMethodPUT; + } + } + + PFConsistencyAssert(![self.httpMethod isEqualToString:PFHTTPRequestMethodDELETE] || objectId, + @"Attempt to delete non-existent object."); + } +} + ++ (BOOL)forEachLocalIdIn:(id)object doBlock:(BOOL(^)(PFObject *pointer))block { + __block BOOL modified = NO; + + // If this is a Pointer with a local id, try to resolve it. + if ([object isKindOfClass:[PFObject class]] && !((PFObject *)object).objectId) { + return block(object); + } + + if ([object isKindOfClass:[NSDictionary class]]) { + [object enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + if ([[self class] forEachLocalIdIn:obj doBlock:block]) { + modified = YES; + } + }]; + } else if ([object isKindOfClass:[NSArray class]]) { + for (id value in object) { + if ([[self class] forEachLocalIdIn:value doBlock:block]) { + modified = YES; + } + } + } else if ([object isKindOfClass:[PFAddOperation class]]) { + for (id value in ((PFAddOperation *)object).objects) { + if ([[self class] forEachLocalIdIn:value doBlock:block]) { + modified = YES; + } + } + } else if ([object isKindOfClass:[PFAddUniqueOperation class]]) { + for (id value in ((PFAddUniqueOperation *)object).objects) { + if ([[self class] forEachLocalIdIn:value doBlock:block]) { + modified = YES; + } + } + } else if ([object isKindOfClass:[PFRemoveOperation class]]) { + for (id value in ((PFRemoveOperation *)object).objects) { + if ([[self class] forEachLocalIdIn:value doBlock:block]) { + modified = YES; + } + } + } + + return modified; +} + +- (void)forEachLocalId:(BOOL(^)(PFObject *pointer))block { + NSDictionary *data = [[PFDecoder objectDecoder] decodeObject:self.parameters]; + if (!data) { + return; + } + + if ([[self class] forEachLocalIdIn:data doBlock:block]) { + self.parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:data]; + } +} + +- (void)resolveLocalIds { + [self forEachLocalId:^(PFObject *pointer) { + [pointer resolveLocalId]; + return YES; + }]; + [self maybeChangeServerOperation]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTCommand_Private.h b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand_Private.h new file mode 100644 index 0000000..f3d1e4e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTCommand_Private.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +@interface PFRESTCommand () + +@property (nonatomic, copy, readwrite) NSString *sessionToken; + +@property (nonatomic, copy, readwrite) NSString *httpPath; +@property (nonatomic, copy, readwrite) NSString *httpMethod; + +@property (nonatomic, copy, readwrite) NSDictionary *parameters; + +@property (nonatomic, copy, readwrite) NSString *cacheKey; + +@property (nonatomic, copy, readwrite) NSString *operationSetUUID; + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.h new file mode 100644 index 0000000..7b52307 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTConfigCommand : PFRESTCommand + ++ (instancetype)configFetchCommandWithSessionToken:(nullable NSString *)sessionToken; ++ (instancetype)configUpdateCommandWithConfigParameters:(NSDictionary *)parameters + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.m new file mode 100644 index 0000000..6709990 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTConfigCommand.m @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTConfigCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" + +@implementation PFRESTConfigCommand + ++ (instancetype)configFetchCommandWithSessionToken:(NSString *)sessionToken { + return [self commandWithHTTPPath:@"config" + httpMethod:PFHTTPRequestMethodGET + parameters:nil + sessionToken:sessionToken]; +} + ++ (instancetype)configUpdateCommandWithConfigParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + NSDictionary *commandParameters = @{ @"params" : parameters }; + return [self commandWithHTTPPath:@"config" + httpMethod:PFHTTPRequestMethodPUT + parameters:commandParameters + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.h new file mode 100644 index 0000000..770b258 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTFileCommand : PFRESTCommand + ++ (instancetype)uploadCommandForFileWithName:(NSString *)fileName + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.m new file mode 100644 index 0000000..1de7cc9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTFileCommand.m @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTFileCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" + +@implementation PFRESTFileCommand + ++ (instancetype)uploadCommandForFileWithName:(NSString *)fileName + sessionToken:(NSString *)sessionToken { + NSMutableString *httpPath = [@"files/" mutableCopy]; + if (fileName) { + [httpPath appendString:fileName]; + } + return [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodPOST + parameters:nil + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.h new file mode 100644 index 0000000..8b28e34 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +extern NSUInteger const PFRESTObjectBatchCommandSubcommandsLimit; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTObjectBatchCommand : PFRESTCommand + ++ (instancetype)batchCommandWithCommands:(NSArray *)commands sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.m new file mode 100644 index 0000000..7aa407f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectBatchCommand.m @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTObjectBatchCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" + +NSUInteger const PFRESTObjectBatchCommandSubcommandsLimit = 50; + +@implementation PFRESTObjectBatchCommand + ++ (nonnull instancetype)batchCommandWithCommands:(NSArray *)commands + sessionToken:(NSString *)sessionToken { + PFParameterAssert([commands count] <= PFRESTObjectBatchCommandSubcommandsLimit, + @"Max of %d commands are allowed in a single batch command", + (int)PFRESTObjectBatchCommandSubcommandsLimit); + + NSMutableArray *requests = [NSMutableArray arrayWithCapacity:[commands count]]; + for (PFRESTCommand *command in commands) { + NSMutableDictionary *requestDictionary = [@{ @"method" : command.httpMethod, + @"path" : [NSString stringWithFormat:@"/1/%@", command.httpPath] + } mutableCopy]; + if (command.parameters) { + requestDictionary[@"body"] = command.parameters; + } + + [requests addObject:requestDictionary]; + } + return [self commandWithHTTPPath:@"batch" + httpMethod:PFHTTPRequestMethodPOST + parameters:@{ @"requests" : requests } + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.h new file mode 100644 index 0000000..67a8ff4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PFObjectState; + +@interface PFRESTObjectCommand : PFRESTCommand + ++ (instancetype)fetchObjectCommandForObjectState:(PFObjectState *)state + withSessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)createObjectCommandForObjectState:(PFObjectState *)state + changes:(nullable NSDictionary *)changes + operationSetUUID:(nullable NSString *)operationSetIdentifier + sessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)updateObjectCommandForObjectState:(PFObjectState *)state + changes:(nullable NSDictionary *)changes + operationSetUUID:(nullable NSString *)operationSetIdentifier + sessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)deleteObjectCommandForObjectState:(PFObjectState *)state + withSessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.m new file mode 100644 index 0000000..964273e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTObjectCommand.m @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTObjectCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" +#import "PFObjectState.h" + +@implementation PFRESTObjectCommand + ++ (instancetype)fetchObjectCommandForObjectState:(PFObjectState *)state + withSessionToken:(NSString *)sessionToken { + PFParameterAssert(state.objectId.length, @"objectId should be non nil"); + PFParameterAssert(state.parseClassName.length, @"Class name should be non nil"); + + NSString *httpPath = [NSString stringWithFormat:@"classes/%@/%@", state.parseClassName, state.objectId]; + PFRESTObjectCommand *command = [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodGET + parameters:nil + sessionToken:sessionToken]; + return command; +} + ++ (instancetype)createObjectCommandForObjectState:(PFObjectState *)state + changes:(NSDictionary *)changes + operationSetUUID:(NSString *)operationSetIdentifier + sessionToken:(NSString *)sessionToken { + PFParameterAssert(state.parseClassName.length, @"Class name should be non nil"); + + NSString *httpPath = [NSString stringWithFormat:@"classes/%@", state.parseClassName]; + PFRESTObjectCommand *command = [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodPOST + parameters:changes + operationSetUUID:operationSetIdentifier + sessionToken:sessionToken]; + return command; +} + ++ (instancetype)updateObjectCommandForObjectState:(PFObjectState *)state + changes:(NSDictionary *)changes + operationSetUUID:(NSString *)operationSetIdentifier + sessionToken:(NSString *)sessionToken { + PFParameterAssert(state.parseClassName.length, @"Class name should be non nil"); + PFParameterAssert(state.objectId.length, @"objectId should be non nil"); + + NSString *httpPath = [NSString stringWithFormat:@"classes/%@/%@", state.parseClassName, state.objectId]; + PFRESTObjectCommand *command = [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodPUT + parameters:changes + operationSetUUID:operationSetIdentifier + sessionToken:sessionToken]; + return command; +} + ++ (instancetype)deleteObjectCommandForObjectState:(PFObjectState *)state + withSessionToken:(NSString *)sessionToken { + PFParameterAssert(state.parseClassName.length, @"Class name should be non nil"); + + NSMutableString *httpPath = [NSMutableString stringWithFormat:@"classes/%@", state.parseClassName]; + if (state.objectId) { + [httpPath appendFormat:@"/%@", state.objectId]; + } + PFRESTObjectCommand *command = [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodDELETE + parameters:nil + sessionToken:sessionToken]; + return command; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.h new file mode 100644 index 0000000..d58965d --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +#import + +@class PFPushState; + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFRESTPushCommand : PFRESTCommand + ++ (instancetype)sendPushCommandWithPushState:(PFPushState *)state + sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.m new file mode 100644 index 0000000..2f7601e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTPushCommand.m @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTPushCommand.h" + +#import "PFAssert.h" +#import "PFDateFormatter.h" +#import "PFHTTPRequest.h" +#import "PFInternalUtils.h" +#import "PFPushState.h" +#import "PFQueryState.h" +#import "PFRESTQueryCommand.h" + +@implementation PFRESTPushCommand + ++ (instancetype)sendPushCommandWithPushState:(PFPushState *)state + sessionToken:(NSString *)sessionToken { + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + + if (state.queryState) { + NSDictionary *queryParameters = [PFRESTQueryCommand findCommandParametersForQueryState:state.queryState]; + parameters[@"where"] = queryParameters[@"where"]; + } else { + if (state.channels) { + parameters[@"channels"] = [state.channels allObjects]; + } + } + + // If there are no conditions set, then push to everyone by specifying empty query conditions. + if ([parameters count] == 0) { + parameters[@"where"] = @{}; + } + + if (state.expirationDate) { + parameters[@"expiration_time"] = [[PFDateFormatter sharedFormatter] preciseStringFromDate:state.expirationDate]; + } else if (state.expirationTimeInterval) { + parameters[@"expiration_interval"] = state.expirationTimeInterval; + } + + // TODO (nlutsenko): Probably we need an assert here, as there is no reason to send push without message + if (state.payload) { + parameters[@"data"] = state.payload; + } + + return [self commandWithHTTPPath:@"push" + httpMethod:PFHTTPRequestMethodPOST + parameters:parameters + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h new file mode 100644 index 0000000..a77a66f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +@class PFQueryState; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTQueryCommand : PFRESTCommand + +///-------------------------------------- +/// @name Find +///-------------------------------------- + ++ (instancetype)findCommandForQueryState:(PFQueryState *)queryState withSessionToken:(nullable NSString *)sessionToken; + ++ (instancetype)findCommandForClassWithName:(NSString *)className + order:(nullable NSString *)order + conditions:(nullable NSDictionary *)conditions + selectedKeys:(nullable NSSet *)selectedKeys + includedKeys:(nullable NSSet *)includedKeys + limit:(NSInteger)limit + skip:(NSInteger)skip + extraOptions:(nullable NSDictionary *)extraOptions + tracingEnabled:(BOOL)trace + sessionToken:(nullable NSString *)sessionToken; + +///-------------------------------------- +/// @name Count +///-------------------------------------- + ++ (instancetype)countCommandFromFindCommand:(PFRESTQueryCommand *)findCommand; + +///-------------------------------------- +/// @name Parameters +///-------------------------------------- + ++ (NSDictionary *)findCommandParametersForQueryState:(PFQueryState *)queryState; ++ (NSDictionary *)findCommandParametersWithOrder:(nullable NSString *)order + conditions:(nullable NSDictionary *)conditions + selectedKeys:(nullable NSSet *)selectedKeys + includedKeys:(nullable NSSet *)includedKeys + limit:(NSInteger)limit + skip:(NSInteger)skip + extraOptions:(nullable NSDictionary *)extraOptions + tracingEnabled:(BOOL)trace; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m new file mode 100644 index 0000000..bf90f49 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTQueryCommand.m @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTQueryCommand.h" + +#import "PFAssert.h" +#import "PFEncoder.h" +#import "PFHTTPRequest.h" +#import "PFQueryPrivate.h" +#import "PFQueryState.h" + +@implementation PFRESTQueryCommand + +///-------------------------------------- +#pragma mark - Find +///-------------------------------------- + ++ (instancetype)findCommandForQueryState:(PFQueryState *)queryState withSessionToken:(NSString *)sessionToken { + NSDictionary *parameters = [self findCommandParametersForQueryState:queryState]; + return [self _findCommandForClassWithName:queryState.parseClassName + parameters:parameters + sessionToken:sessionToken]; +} + ++ (instancetype)findCommandForClassWithName:(NSString *)className + order:(NSString *)order + conditions:(NSDictionary *)conditions + selectedKeys:(NSSet *)selectedKeys + includedKeys:(NSSet *)includedKeys + limit:(NSInteger)limit + skip:(NSInteger)skip + extraOptions:(NSDictionary *)extraOptions + tracingEnabled:(BOOL)trace + sessionToken:(NSString *)sessionToken { + NSDictionary *parameters = [self findCommandParametersWithOrder:order + conditions:conditions + selectedKeys:selectedKeys + includedKeys:includedKeys + limit:limit + skip:skip + extraOptions:extraOptions + tracingEnabled:trace]; + return [self _findCommandForClassWithName:className + parameters:parameters + sessionToken:sessionToken]; +} + ++ (instancetype)_findCommandForClassWithName:(NSString *)className + parameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken { + NSString *httpPath = [NSString stringWithFormat:@"classes/%@", className]; + PFRESTQueryCommand *command = [self commandWithHTTPPath:httpPath + httpMethod:PFHTTPRequestMethodGET + parameters:parameters + sessionToken:sessionToken]; + return command; +} + +///-------------------------------------- +#pragma mark - Count +///-------------------------------------- + ++ (instancetype)countCommandFromFindCommand:(PFRESTQueryCommand *)findCommand { + NSMutableDictionary *parameters = [findCommand.parameters mutableCopy]; + parameters[@"count"] = @"1"; + parameters[@"limit"] = @"0"; // Set the limit to 0, as we are not interested in results at all. + [parameters removeObjectForKey:@"skip"]; + + return [self commandWithHTTPPath:findCommand.httpPath + httpMethod:findCommand.httpMethod + parameters:[parameters copy] + sessionToken:findCommand.sessionToken]; +} + +///-------------------------------------- +#pragma mark - Parameters +///-------------------------------------- + ++ (NSDictionary *)findCommandParametersForQueryState:(PFQueryState *)queryState { + return [self findCommandParametersWithOrder:queryState.sortOrderString + conditions:queryState.conditions + selectedKeys:queryState.selectedKeys + includedKeys:queryState.includedKeys + limit:queryState.limit + skip:queryState.skip + extraOptions:queryState.extraOptions + tracingEnabled:queryState.trace]; +} + ++ (NSDictionary *)findCommandParametersWithOrder:(NSString *)order + conditions:(NSDictionary *)conditions + selectedKeys:(NSSet *)selectedKeys + includedKeys:(NSSet *)includedKeys + limit:(NSInteger)limit + skip:(NSInteger)skip + extraOptions:(NSDictionary *)extraOptions + tracingEnabled:(BOOL)trace { + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + + if ([order length]) { + parameters[@"order"] = order; + } + if (selectedKeys != nil) { + parameters[@"keys"] = [[selectedKeys allObjects] componentsJoinedByString:@","]; + } + if ([includedKeys count] > 0) { + parameters[@"include"] = [[includedKeys allObjects] componentsJoinedByString:@","]; + } + if (limit >= 0) { + parameters[@"limit"] = [NSString stringWithFormat:@"%d", (int)limit]; + } + if (skip > 0) { + parameters[@"skip"] = [NSString stringWithFormat:@"%d", (int)skip]; + } + if (trace) { + // TODO: (nlutsenko) Double check that tracing still works. Maybe create test for it. + parameters[@"trace"] = @"1"; + } + [extraOptions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + parameters[key] = obj; + }]; + + if ([conditions count] > 0) { + NSMutableDictionary *whereData = [[NSMutableDictionary alloc] init]; + [conditions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([key isEqualToString:@"$or"]) { + NSArray *array = (NSArray *)obj; + NSMutableArray *newArray = [NSMutableArray array]; + for (PFQuery *subquery in array) { + // TODO: (nlutsenko) Move this validation into PFQuery/PFQueryState. + PFParameterAssert(subquery.state.limit < 0, @"OR queries do not support sub queries with limits"); + PFParameterAssert(subquery.state.skip == 0, @"OR queries do not support sub queries with skip"); + PFParameterAssert(subquery.state.sortKeys.count == 0, @"OR queries do not support sub queries with order"); + PFParameterAssert(subquery.state.includedKeys.count == 0, @"OR queries do not support sub-queries with includes"); + PFParameterAssert(subquery.state.selectedKeys == nil, @"OR queries do not support sub-queries with selectKeys"); + + NSDictionary *queryDict = [self findCommandParametersWithOrder:subquery.state.sortOrderString + conditions:subquery.state.conditions + selectedKeys:subquery.state.selectedKeys + includedKeys:subquery.state.includedKeys + limit:subquery.state.limit + skip:subquery.state.skip + extraOptions:nil + tracingEnabled:NO]; + + queryDict = queryDict[@"where"]; + if ([queryDict count] > 0) { + [newArray addObject:queryDict]; + } else { + [newArray addObject:[NSDictionary dictionary]]; + } + } + whereData[key] = newArray; + } else { + id object = [self _encodeSubqueryIfNeeded:obj]; + whereData[key] = [[PFPointerObjectEncoder objectEncoder] encodeObject:object]; + } + }]; + + parameters[@"where"] = whereData; + } + + return parameters; +} + ++ (id)_encodeSubqueryIfNeeded:(id)object { + if (![object isKindOfClass:[NSDictionary class]]) { + return object; + } + + NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:[object count]]; + [object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([obj isKindOfClass:[PFQuery class]]) { + PFQuery *subquery = (PFQuery *)obj; + NSMutableDictionary *subqueryParameters = [[self findCommandParametersWithOrder:subquery.state.sortOrderString + conditions:subquery.state.conditions + selectedKeys:subquery.state.selectedKeys + includedKeys:subquery.state.includedKeys + limit:subquery.state.limit + skip:subquery.state.skip + extraOptions:subquery.state.extraOptions + tracingEnabled:NO] mutableCopy]; + subqueryParameters[@"className"] = subquery.parseClassName; + obj = subqueryParameters; + } else if ([obj isKindOfClass:[NSDictionary class]]) { + obj = [self _encodeSubqueryIfNeeded:obj]; + } + + parameters[key] = obj; + }]; + return parameters; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.h new file mode 100644 index 0000000..d777b04 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTSessionCommand : PFRESTCommand + ++ (instancetype)getCurrentSessionCommandWithSessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.m new file mode 100644 index 0000000..c38bd1c --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTSessionCommand.m @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTSessionCommand.h" + +#import "PFHTTPRequest.h" + +@implementation PFRESTSessionCommand + ++ (instancetype)getCurrentSessionCommandWithSessionToken:(nullable NSString *)sessionToken { + return [self commandWithHTTPPath:@"sessions/me" + httpMethod:PFHTTPRequestMethodGET + parameters:nil + sessionToken:sessionToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.h b/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.h new file mode 100644 index 0000000..93c81db --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTCommand.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFRESTUserCommand : PFRESTCommand + +@property (nonatomic, assign, readonly) BOOL revocableSessionEnabled; + +///-------------------------------------- +/// @name Log In +///-------------------------------------- + ++ (instancetype)logInUserCommandWithUsername:(NSString *)username + password:(NSString *)password + revocableSession:(BOOL)revocableSessionEnabled; ++ (instancetype)serviceLoginUserCommandWithAuthenticationType:(NSString *)authenticationType + authenticationData:(NSDictionary *)authenticationData + revocableSession:(BOOL)revocableSessionEnabled; ++ (instancetype)serviceLoginUserCommandWithParameters:(NSDictionary *)parameters + revocableSession:(BOOL)revocableSessionEnabled + sessionToken:(nullable NSString *)sessionToken; + +///-------------------------------------- +/// @name Sign Up +///-------------------------------------- + ++ (instancetype)signUpUserCommandWithParameters:(NSDictionary *)parameters + revocableSession:(BOOL)revocableSessionEnabled + sessionToken:(nullable NSString *)sessionToken; + +///-------------------------------------- +/// @name Current User +///-------------------------------------- + ++ (instancetype)getCurrentUserCommandWithSessionToken:(NSString *)sessionToken; ++ (instancetype)upgradeToRevocableSessionCommandWithSessionToken:(NSString *)sessionToken; ++ (instancetype)logOutUserCommandWithSessionToken:(NSString *)sessionToken; + +///-------------------------------------- +/// @name Password Rest +///-------------------------------------- + ++ (instancetype)resetPasswordCommandForUserWithEmail:(NSString *)email; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.m b/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.m new file mode 100644 index 0000000..190a5d5 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Commands/PFRESTUserCommand.m @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRESTUserCommand.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" + +static NSString *const PFRESTUserCommandRevocableSessionHeader = @"X-Parse-Revocable-Session"; +static NSString *const PFRESTUserCommandRevocableSessionHeaderEnabledValue = @"1"; + +@interface PFRESTUserCommand () + +@property (nonatomic, assign, readwrite) BOOL revocableSessionEnabled; + +@end + +@implementation PFRESTUserCommand + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)_commandWithHTTPPath:(NSString *)path + httpMethod:(NSString *)httpMethod + parameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken + revocableSession:(BOOL)revocableSessionEnabled { + PFRESTUserCommand *command = [self commandWithHTTPPath:path + httpMethod:httpMethod + parameters:parameters + sessionToken:sessionToken]; + if (revocableSessionEnabled) { + command.additionalRequestHeaders = @{ PFRESTUserCommandRevocableSessionHeader : + PFRESTUserCommandRevocableSessionHeaderEnabledValue}; + } + command.revocableSessionEnabled = revocableSessionEnabled; + return command; +} + +///-------------------------------------- +#pragma mark - Log In +///-------------------------------------- + ++ (instancetype)logInUserCommandWithUsername:(NSString *)username + password:(NSString *)password + revocableSession:(BOOL)revocableSessionEnabled { + NSDictionary *parameters = @{ @"username" : username, + @"password" : password }; + return [self _commandWithHTTPPath:@"login" + httpMethod:PFHTTPRequestMethodGET + parameters:parameters + sessionToken:nil + revocableSession:revocableSessionEnabled]; +} + ++ (instancetype)serviceLoginUserCommandWithAuthenticationType:(NSString *)authenticationType + authenticationData:(NSDictionary *)authenticationData + revocableSession:(BOOL)revocableSessionEnabled { + NSDictionary *parameters = @{ @"authData" : @{ authenticationType : authenticationData } }; + return [self serviceLoginUserCommandWithParameters:parameters + revocableSession:revocableSessionEnabled + sessionToken:nil]; +} + ++ (instancetype)serviceLoginUserCommandWithParameters:(NSDictionary *)parameters + revocableSession:(BOOL)revocableSessionEnabled + sessionToken:(NSString *)sessionToken { + return [self _commandWithHTTPPath:@"users" + httpMethod:PFHTTPRequestMethodPOST + parameters:parameters + sessionToken:sessionToken + revocableSession:revocableSessionEnabled]; +} + +///-------------------------------------- +#pragma mark - Sign Up +///-------------------------------------- + ++ (instancetype)signUpUserCommandWithParameters:(NSDictionary *)parameters + revocableSession:(BOOL)revocableSessionEnabled + sessionToken:(NSString *)sessionToken { + return [self _commandWithHTTPPath:@"users" + httpMethod:PFHTTPRequestMethodPOST + parameters:parameters + sessionToken:sessionToken + revocableSession:revocableSessionEnabled]; +} + +///-------------------------------------- +#pragma mark - Current User +///-------------------------------------- + ++ (instancetype)getCurrentUserCommandWithSessionToken:(NSString *)sessionToken { + return [self commandWithHTTPPath:@"users/me" + httpMethod:PFHTTPRequestMethodGET + parameters:nil + sessionToken:sessionToken]; +} + ++ (instancetype)upgradeToRevocableSessionCommandWithSessionToken:(NSString *)sessionToken { + return [self commandWithHTTPPath:@"upgradeToRevocableSession" + httpMethod:PFHTTPRequestMethodPOST + parameters:nil + sessionToken:sessionToken]; +} + ++ (instancetype)logOutUserCommandWithSessionToken:(NSString *)sessionToken { + return [self commandWithHTTPPath:@"logout" + httpMethod:PFHTTPRequestMethodPOST + parameters:nil + sessionToken:sessionToken]; +} + +///-------------------------------------- +#pragma mark - Additional User Commands +///-------------------------------------- + ++ (instancetype)resetPasswordCommandForUserWithEmail:(NSString *)email { + return [self commandWithHTTPPath:@"requestPasswordReset" + httpMethod:PFHTTPRequestMethodPOST + parameters:@{ @"email" : email } + sessionToken:nil]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.h b/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.h new file mode 100644 index 0000000..1333849 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFConfig; +@class PFCurrentConfigController; +@class PFFileManager; +@protocol PFCommandRunning; + +@interface PFConfigController : NSObject + +@property (nonatomic, strong, readonly) PFFileManager *fileManager; +@property (nonatomic, strong, readonly) id commandRunner; + +@property (nonatomic, strong, readonly) PFCurrentConfigController *currentConfigController; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(PFFileManager *)fileManager + commandRunner:(id)commandRunner NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Fetch +///-------------------------------------- + +/*! + Fetches current config from network async. + + @param sessionToken Current user session token. + + @returns `BFTask` with result set to `PFConfig`. + */ +- (BFTask *)fetchConfigAsyncWithSessionToken:(NSString *)sessionToken; + +@end diff --git a/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.m b/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.m new file mode 100644 index 0000000..ec26b5b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Config/Controller/PFConfigController.m @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFConfigController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFConfig_Private.h" +#import "PFCurrentConfigController.h" +#import "PFDecoder.h" +#import "PFRESTConfigCommand.h" + +@interface PFConfigController () +{ + dispatch_queue_t _dataAccessQueue; + dispatch_queue_t _networkQueue; + BFExecutor *_networkExecutor; +} + +@end + +@implementation PFConfigController + +@synthesize currentConfigController = _currentConfigController; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithFileManager:(PFFileManager *)fileManager + commandRunner:(id)commandRunner { + self = [super init]; + if (!self) return nil; + + _fileManager = fileManager; + _commandRunner = commandRunner; + + _dataAccessQueue = dispatch_queue_create("com.parse.config.access", DISPATCH_QUEUE_SERIAL); + + _networkQueue = dispatch_queue_create("com.parse.config.network", DISPATCH_QUEUE_SERIAL); + _networkExecutor = [BFExecutor executorWithDispatchQueue:_networkQueue]; + + return self; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BFTask *)fetchConfigAsyncWithSessionToken:(NSString *)sessionToken { + @weakify(self); + return [BFTask taskFromExecutor:_networkExecutor withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTConfigCommand configFetchCommandWithSessionToken:sessionToken]; + return [[[self.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed] + continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + NSDictionary *fetchedConfig = [[PFDecoder objectDecoder] decodeObject:result.result]; + return [[PFConfig alloc] initWithFetchedConfig:fetchedConfig]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // Roll-forward the config. + return [[self.currentConfigController setCurrentConfigAsync:task.result] continueWithResult:task.result]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFCurrentConfigController *)currentConfigController { + __block PFCurrentConfigController *controller = nil; + dispatch_sync(_dataAccessQueue, ^{ + if (!_currentConfigController) { + _currentConfigController = [[PFCurrentConfigController alloc] initWithFileManager:self.fileManager]; + } + controller = _currentConfigController; + }); + return controller; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.h b/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.h new file mode 100644 index 0000000..0c947c8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFConfig; +@class PFFileManager; + +@interface PFCurrentConfigController : NSObject + +@property (nonatomic, strong, readonly) PFFileManager *fileManager; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(PFFileManager *)fileManager NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithFileManager:(PFFileManager *)fileManager; + +///-------------------------------------- +/// @name Accessors +///-------------------------------------- + +- (BFTask *)getCurrentConfigAsync; +- (BFTask *)setCurrentConfigAsync:(PFConfig *)config; + +- (BFTask *)clearCurrentConfigAsync; +- (BFTask *)clearMemoryCachedCurrentConfigAsync; + +@end diff --git a/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.m b/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.m new file mode 100644 index 0000000..1378520 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Config/Controller/PFCurrentConfigController.m @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCurrentConfigController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFConfig_Private.h" +#import "PFDecoder.h" +#import "PFFileManager.h" +#import "PFJSONSerialization.h" + +static NSString *const PFConfigCurrentConfigFileName_ = @"config"; + +@interface PFCurrentConfigController () { + dispatch_queue_t _dataQueue; + BFExecutor *_dataExecutor; + PFConfig *_currentConfig; +} + +@property (nonatomic, copy, readonly) NSString *configFilePath; + +@end + +@implementation PFCurrentConfigController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithFileManager:(PFFileManager *)fileManager { + self = [super init]; + if (!self) return nil; + + _dataQueue = dispatch_queue_create("com.parse.config.current", DISPATCH_QUEUE_SERIAL); + _dataExecutor = [BFExecutor executorWithDispatchQueue:_dataQueue]; + + _fileManager = fileManager; + + return self; +} + ++ (instancetype)controllerWithFileManager:(PFFileManager *)fileManager { + return [[self alloc] initWithFileManager:fileManager]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (BFTask *)getCurrentConfigAsync { + return [BFTask taskFromExecutor:_dataExecutor withBlock:^id{ + if (!_currentConfig) { + NSDictionary *dictionary = [PFJSONSerialization JSONObjectFromFileAtPath:self.configFilePath]; + if (dictionary) { + NSDictionary *decodedDictionary = [[PFDecoder objectDecoder] decodeObject:dictionary]; + _currentConfig = [[PFConfig alloc] initWithFetchedConfig:decodedDictionary]; + } else { + _currentConfig = [[PFConfig alloc] init]; + } + } + return _currentConfig; + }]; +} + +- (BFTask *)setCurrentConfigAsync:(PFConfig *)config { + @weakify(self); + return [BFTask taskFromExecutor:_dataExecutor withBlock:^id{ + @strongify(self); + _currentConfig = config; + + NSDictionary *configParameters = @{ PFConfigParametersRESTKey : (config.parametersDictionary ?: @{}) }; + id encodedObject = [[PFPointerObjectEncoder objectEncoder] encodeObject:configParameters]; + NSData *jsonData = [PFJSONSerialization dataFromJSONObject:encodedObject]; + return [PFFileManager writeDataAsync:jsonData toFile:self.configFilePath]; + }]; +} + +- (BFTask *)clearCurrentConfigAsync { + @weakify(self); + return [BFTask taskFromExecutor:_dataExecutor withBlock:^id{ + @strongify(self); + _currentConfig = nil; + return [PFFileManager removeItemAtPathAsync:self.configFilePath]; + }]; +} + +- (BFTask *)clearMemoryCachedCurrentConfigAsync { + return [BFTask taskFromExecutor:_dataExecutor withBlock:^id{ + _currentConfig = nil; + return nil; + }]; +} + +- (NSString *)configFilePath { + return [self.fileManager parseDataItemPathForPathComponent:PFConfigCurrentConfigFileName_]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Config/PFConfig_Private.h b/Pods/Parse/Parse/Internal/Config/PFConfig_Private.h new file mode 100644 index 0000000..8ea452f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Config/PFConfig_Private.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +extern NSString *const PFConfigParametersRESTKey; + +@interface PFConfig (Private) + +@property (atomic, copy, readonly) NSDictionary *parametersDictionary; + +- (instancetype)initWithFetchedConfig:(NSDictionary *)config; + +@end diff --git a/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.h b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.h new file mode 100644 index 0000000..519353d --- /dev/null +++ b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.h @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFEncoder.h" + +@class PFDecoder; +@class PFObject; + +///-------------------------------------- +#pragma mark - PFFieldOperation +///-------------------------------------- + +/*! + A PFFieldOperation represents a modification to a value in a PFObject. + For example, setting, deleting, or incrementing a value are all different + kinds of PFFieldOperations. PFFieldOperations themselves can be considered + to be immutable. + */ +@interface PFFieldOperation : NSObject + +/*! + Converts the PFFieldOperation to a data structure (typically an NSDictionary) + that can be converted to JSON and sent to Parse as part of a save operation. + + @param objectEncoder encoder that will be used to encode the object. + @returns An object to be jsonified. + */ +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder; + +/*! + Returns a field operation that is composed of a previous operation followed by + this operation. This will not mutate either operation. However, it may return + self if the current operation is not affected by previous changes. For example: + [{increment by 2} mergeWithPrevious:{set to 5}] -> {set to 7} + [{set to 5} mergeWithPrevious:{increment by 2}] -> {set to 5} + [{add "foo"} mergeWithPrevious:{delete}] -> {set to ["foo"]} + [{delete} mergeWithPrevious:{add "foo"}] -> {delete} + + @param previous The most recent operation on the field, or nil if none. + @returns A new PFFieldOperation or self. + */ +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous; + +/*! + Returns a new estimated value based on a previous value and this operation. This + value is not intended to be sent to Parse, but it used locally on the client to + inspect the most likely current value for a field. + + The key and object are used solely for PFRelation to be able to construct objects + that refer back to its parent. + + @param oldValue The previous value for the field. + @param key The key that this value is for. + + @returns The new value for the field. + */ +- (id)applyToValue:(id)oldValue forKey:(NSString *)key; + +@end + +///-------------------------------------- +#pragma mark - Independent Operations +///-------------------------------------- + +/*! + An operation where a field is set to a given value regardless of + its previous value. + */ +@interface PFSetOperation : PFFieldOperation + +@property (nonatomic, strong, readonly) id value; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithValue:(id)value NS_DESIGNATED_INITIALIZER; ++ (instancetype)setWithValue:(id)value; + +@end + +/*! + An operation where a field is deleted from the object. + */ +@interface PFDeleteOperation : PFFieldOperation + ++ (instancetype)operation; + +@end + +///-------------------------------------- +#pragma mark - Numeric Operations +///-------------------------------------- + +/*! + An operation that increases a numeric field's value by a given amount. + */ +@interface PFIncrementOperation : PFFieldOperation + +@property (nonatomic, strong, readonly) NSNumber *amount; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithAmount:(NSNumber *)amount NS_DESIGNATED_INITIALIZER; ++ (instancetype)incrementWithAmount:(NSNumber *)amount; + +@end + +///-------------------------------------- +#pragma mark - Array Operations +///-------------------------------------- + +/*! + An operation that adds a new element to an array field. + */ +@interface PFAddOperation : PFFieldOperation + +@property (nonatomic, strong, readonly) NSArray *objects; + ++ (instancetype)addWithObjects:(NSArray *)array; + +@end + +/*! + An operation that adds a new element to an array field, + only if it wasn't already present. + */ +@interface PFAddUniqueOperation : PFFieldOperation + +@property (nonatomic, strong, readonly) NSArray *objects; + ++ (instancetype)addUniqueWithObjects:(NSArray *)array; + +@end + +/*! + An operation that removes every instance of an element from + an array field. + */ +@interface PFRemoveOperation : PFFieldOperation + +@property (nonatomic, strong, readonly) NSArray *objects; + ++ (instancetype)removeWithObjects:(NSArray *)array; + +@end + +///-------------------------------------- +#pragma mark - Relation Operations +///-------------------------------------- + +/*! + An operation where a PFRelation's value is modified. + */ +@interface PFRelationOperation : PFFieldOperation + +@property (nonatomic, copy) NSString *targetClass; +@property (nonatomic, strong) NSMutableSet *relationsToAdd; +@property (nonatomic, strong) NSMutableSet *relationsToRemove; + ++ (instancetype)addRelationToObjects:(NSArray *)targets; ++ (instancetype)removeRelationToObjects:(NSArray *)targets; + +@end diff --git a/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.m b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.m new file mode 100644 index 0000000..780347f --- /dev/null +++ b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperation.m @@ -0,0 +1,552 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFieldOperation.h" + +#import "PFAssert.h" +#import "PFDecoder.h" +#import "PFInternalUtils.h" +#import "PFObject.h" +#import "PFOfflineStore.h" +#import "PFRelation.h" +#import "PFRelationPrivate.h" + +///-------------------------------------- +#pragma mark - PFFieldOperation +///-------------------------------------- + +// PFFieldOperation and its subclasses encapsulate operations that can be done on a field. +@implementation PFFieldOperation + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + PFConsistencyAssert(NO, @"Operation is invalid."); + return nil; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + PFConsistencyAssert(NO, @"Operation is invalid."); + return nil; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + PFConsistencyAssert(NO, @"Operation is invalid."); + return nil; +} + +@end + +///-------------------------------------- +#pragma mark - Independent Operations +///-------------------------------------- + +@implementation PFSetOperation + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithValue:(id)value { + self = [super init]; + if (!self) return nil; + + PFParameterAssert(value, @"Cannot set a nil value in a PFObject."); + _value = value; + + return self; +} + ++ (id)setWithValue:(id)newValue { + return [[self alloc] initWithValue:newValue]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"set to %@", self.value]; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + return [objectEncoder encodeObject:self.value]; +} + +- (PFSetOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + return self; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + return self.value; +} + +@end + +@implementation PFDeleteOperation + ++ (instancetype)operation { + return [[self alloc] init]; +} + +- (NSString *)description { + return @"delete"; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + return @{ @"__op" : @"Delete" }; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + return self; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + return nil; +} + +@end + +///-------------------------------------- +#pragma mark - Numeric Operations +///-------------------------------------- + +@implementation PFIncrementOperation + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithAmount:(NSNumber *)amount { + self = [super init]; + if (!self) return nil; + + _amount = amount; + + return self; +} + ++ (instancetype)incrementWithAmount:(NSNumber *)newAmount { + return [[self alloc] initWithAmount:newAmount]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"increment by %@", self.amount]; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + return @{ @"__op" : @"Increment", + @"amount" : self.amount }; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + if (!previous) { + return self; + } else if ([previous isKindOfClass:[PFDeleteOperation class]]) { + return [PFSetOperation setWithValue:self.amount]; + } else if ([previous isKindOfClass:[PFSetOperation class]]) { + id oldValue = ((PFSetOperation *)previous).value; + PFParameterAssert([oldValue isKindOfClass:[NSNumber class]], @"You cannot increment a non-number."); + return [PFSetOperation setWithValue:[PFInternalUtils addNumber:self.amount withNumber:oldValue]]; + } else if ([previous isKindOfClass:[PFIncrementOperation class]]) { + NSNumber *newAmount = [PFInternalUtils addNumber:self.amount + withNumber:((PFIncrementOperation *)previous).amount]; + return [PFIncrementOperation incrementWithAmount:newAmount]; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + if (!oldValue) { + return self.amount; + } + + PFParameterAssert([oldValue isKindOfClass:[NSNumber class]], @"You cannot increment a non-number."); + return [PFInternalUtils addNumber:self.amount withNumber:oldValue]; +} + +@end + +///-------------------------------------- +#pragma mark - Array Operations +///-------------------------------------- + +@implementation PFAddOperation + +- (instancetype)initWithObjects:(NSArray *)array { + self = [super init]; + if (!self) return nil; + + _objects = array; + + return self; +} + ++ (instancetype)addWithObjects:(NSArray *)objects { + return [(PFAddOperation *)[self alloc] initWithObjects:objects]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"add %@", self.objects]; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableArray *encodedObjects = [objectEncoder encodeObject:self.objects]; + return @{ @"__op" : @"Add", + @"objects" : encodedObjects }; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + if (!previous) { + return self; + } else if ([previous isKindOfClass:[PFDeleteOperation class]]) { + return [PFSetOperation setWithValue:self.objects]; + } else if ([previous isKindOfClass:[PFSetOperation class]]) { + if ([((PFSetOperation *)previous).value isKindOfClass:[NSArray class]]) { + NSArray *oldArray = (NSArray *)(((PFSetOperation *)previous).value); + NSArray *newArray = [oldArray arrayByAddingObjectsFromArray:self.objects]; + return [PFSetOperation setWithValue:newArray]; + } else { + [NSException raise:NSInternalInconsistencyException format:@"You can't add an item to a non-array."]; + return nil; + } + } else if ([previous isKindOfClass:[PFAddOperation class]]) { + NSMutableArray *newObjects = [((PFAddOperation *)previous).objects mutableCopy]; + [newObjects addObjectsFromArray:self.objects]; + return [[self class] addWithObjects:newObjects]; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + if (!oldValue) { + return [self.objects mutableCopy]; + } else if ([oldValue isKindOfClass:[NSArray class]]) { + return [((NSArray *)oldValue)arrayByAddingObjectsFromArray:self.objects]; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +@end + +@implementation PFAddUniqueOperation + +- (instancetype)initWithObjects:(NSArray *)array { + self = [super init]; + if (!self) return nil; + + _objects = [[NSSet setWithArray:array] allObjects]; + + return self; +} + ++ (instancetype)addUniqueWithObjects:(NSArray *)objects { + return [(PFAddUniqueOperation *)[self alloc] initWithObjects:objects]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"addToSet %@", self.objects]; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableArray *encodedObjects = [objectEncoder encodeObject:self.objects]; + return @{ @"__op" : @"AddUnique", + @"objects" : encodedObjects }; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + if (!previous) { + return self; + } else if ([previous isKindOfClass:[PFDeleteOperation class]]) { + return [PFSetOperation setWithValue:self.objects]; + } else if ([previous isKindOfClass:[PFSetOperation class]]) { + if ([((PFSetOperation *)previous).value isKindOfClass:[NSArray class]]) { + NSArray *oldArray = (((PFSetOperation *)previous).value); + return [PFSetOperation setWithValue:[self applyToValue:oldArray forKey:nil]]; + } else { + [NSException raise:NSInternalInconsistencyException format:@"You can't add an item to a non-array."]; + return nil; + } + } else if ([previous isKindOfClass:[PFAddUniqueOperation class]]) { + NSArray *previousObjects = ((PFAddUniqueOperation *)previous).objects; + return [[self class] addUniqueWithObjects:[self applyToValue:previousObjects forKey:nil]]; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + if (!oldValue) { + return [self.objects mutableCopy]; + } else if ([oldValue isKindOfClass:[NSArray class]]) { + NSMutableArray *newValue = [oldValue mutableCopy]; + for (id objectToAdd in self.objects) { + if ([objectToAdd isKindOfClass:[PFObject class]] && [objectToAdd objectId]) { + // Check uniqueness by objectId instead of equality. If the PFObject + // already exists in the array, replace it with the newer one. + NSUInteger index = [newValue indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return [obj isKindOfClass:[PFObject class]] && + [[obj objectId] isEqualToString:[objectToAdd objectId]]; + }]; + if (index == NSNotFound) { + [newValue addObject:objectToAdd]; + } else { + [newValue replaceObjectAtIndex:index withObject:objectToAdd]; + } + } else if (![newValue containsObject:objectToAdd]) { + [newValue addObject:objectToAdd]; + } + } + return newValue; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +@end + +@implementation PFRemoveOperation + +- (instancetype)initWithObjects:(NSArray *)array { + self = [super init]; + + _objects = array; + + return self; +} + ++ (id)removeWithObjects:(NSArray *)objects { + return [(PFRemoveOperation *)[self alloc] initWithObjects:objects]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"remove %@", self.objects]; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableArray *encodedObjects = [objectEncoder encodeObject:self.objects]; + return @{ @"__op" : @"Remove", + @"objects" : encodedObjects }; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + if (!previous) { + return self; + } else if ([previous isKindOfClass:[PFDeleteOperation class]]) { + [NSException raise:NSInternalInconsistencyException format:@"You can't remove items from a deleted array."]; + return nil; + } else if ([previous isKindOfClass:[PFSetOperation class]]) { + if ([((PFSetOperation *)previous).value isKindOfClass:[NSArray class]]) { + NSArray *oldArray = ((PFSetOperation *)previous).value; + return [PFSetOperation setWithValue:[self applyToValue:oldArray forKey:nil]]; + } else { + [NSException raise:NSInternalInconsistencyException format:@"You can't add an item to a non-array."]; + return nil; + } + } else if ([previous isKindOfClass:[PFRemoveOperation class]]) { + NSArray *newObjects = [((PFRemoveOperation *)previous).objects arrayByAddingObjectsFromArray:self.objects]; + return [PFRemoveOperation removeWithObjects:newObjects]; + } + + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + if (!oldValue) { + return [self.objects mutableCopy]; + } else if ([oldValue isKindOfClass:[NSArray class]]) { + NSMutableArray *newValue = [((NSArray *)oldValue)mutableCopy]; + [newValue removeObjectsInArray:self.objects]; + + // Remove the removed objects from objectsToBeRemoved -- the items + // remaining should be ones that weren't removed by object equality. + NSMutableArray *objectsToBeRemoved = [self.objects mutableCopy]; + [objectsToBeRemoved removeObjectsInArray:newValue]; + for (id objectToRemove in objectsToBeRemoved) { + if ([objectToRemove isKindOfClass:[PFObject class]] && [objectToRemove objectId]) { + NSIndexSet *indexes = [newValue indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { + return ([obj isKindOfClass:[PFObject class]] && + [[obj objectId] isEqualToString:[objectToRemove objectId]]); + }]; + if ([indexes count] != 0) { + [newValue removeObjectsAtIndexes:indexes]; + } + } + } + return newValue; + } + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; +} + +@end + +///-------------------------------------- +#pragma mark - Relation Operations +///-------------------------------------- + +@implementation PFRelationOperation +@synthesize targetClass; + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _relationsToAdd = [NSMutableSet set]; + _relationsToRemove = [NSMutableSet set]; + + return self; +} + ++ (instancetype)addRelationToObjects:(NSArray *)targets { + PFRelationOperation *op = [[self alloc] init]; + if (targets.count > 0) { + op.targetClass = [[targets firstObject] parseClassName]; + } + + for (PFObject *target in targets) { + PFParameterAssert([target.parseClassName isEqualToString:op.targetClass], + @"All objects in a relation must be of the same class."); + [op.relationsToAdd addObject:target]; + } + + return op; +} + ++ (instancetype)removeRelationToObjects:(NSArray *)targets { + PFRelationOperation *operation = [[self alloc] init]; + if (targets.count > 0) { + operation.targetClass = [[targets objectAtIndex:0] parseClassName]; + } + + for (PFObject *target in targets) { + PFParameterAssert([target.parseClassName isEqualToString:operation.targetClass], + @"All objects in a relation must be of the same class."); + [operation.relationsToRemove addObject:target]; + } + + return operation; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"PFRelationOperation<%@> add:%@ remove:%@", + self.targetClass, + self.relationsToAdd, + self.relationsToRemove]; +} + +- (NSArray *)_convertToArrayInSet:(NSSet *)set withObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableArray *array = [NSMutableArray arrayWithCapacity:set.count]; + for (PFObject *object in set) { + id encodedDict = [objectEncoder encodeObject:object]; + [array addObject:encodedDict]; + } + return array; +} + +- (id)encodeWithObjectEncoder:(PFEncoder *)objectEncoder { + NSDictionary *addDict = nil; + NSDictionary *removeDict = nil; + if (self.relationsToAdd.count > 0) { + NSArray *array = [self _convertToArrayInSet:self.relationsToAdd withObjectEncoder:objectEncoder]; + addDict = @{ @"__op" : @"AddRelation", + @"objects" : array }; + } + if (self.relationsToRemove.count > 0) { + NSArray *array = [self _convertToArrayInSet:self.relationsToRemove withObjectEncoder:objectEncoder]; + removeDict = @{ @"__op" : @"RemoveRelation", + @"objects" : array }; + } + + if (addDict && removeDict) { + return @{ @"__op" : @"Batch", + @"ops" : @[ addDict, removeDict ] }; + } + + if (addDict) { + return addDict; + } + + if (removeDict) { + return removeDict; + } + + [NSException raise:NSInternalInconsistencyException format:@"A PFRelationOperation was created without any data."]; + return nil; +} + +- (PFFieldOperation *)mergeWithPrevious:(PFFieldOperation *)previous { + if (!previous) { + return self; + } + + PFConsistencyAssert(![previous isKindOfClass:[PFDeleteOperation class]], @"You can't modify a relation after deleting it"); + PFConsistencyAssert([previous isKindOfClass:[PFRelationOperation class]], @"Operation is invalid after previous operation"); + + PFRelationOperation *previousOperation = (PFRelationOperation *)previous; + + PFParameterAssert(!previousOperation.targetClass || [previousOperation.targetClass isEqualToString:self.targetClass], + @"Related object object must be of class %@, but %@ was passed in", + previousOperation.targetClass, self.targetClass); + + //TODO: (nlutsenko) This logic seems to be messed up. We should return a new operation here, also merging logic seems funky. + NSSet *newRelationsToAdd = [self.relationsToAdd copy]; + NSSet *newRelationsToRemove = [self.relationsToRemove copy]; + [self.relationsToAdd removeAllObjects]; + [self.relationsToRemove removeAllObjects]; + + for (NSString *objectId in previousOperation.relationsToAdd) { + [self.relationsToRemove removeObject:objectId]; + [self.relationsToAdd addObject:objectId]; + } + for (NSString *objectId in previousOperation.relationsToRemove) { + [self.relationsToRemove removeObject:objectId]; + [self.relationsToRemove addObject:objectId]; + } + + for (NSString *objectId in newRelationsToAdd) { + [self.relationsToRemove removeObject:objectId]; + [self.relationsToAdd addObject:objectId]; + } + for (NSString *objectId in newRelationsToRemove) { + [self.relationsToRemove removeObject:objectId]; + [self.relationsToRemove addObject:objectId]; + } + return self; +} + +- (id)applyToValue:(id)oldValue forKey:(NSString *)key { + PFRelation *relation = nil; + if (!oldValue) { + relation = [PFRelation relationWithTargetClass:self.targetClass]; + } else if ([oldValue isKindOfClass:[PFRelation class]]) { + relation = oldValue; + if (self.targetClass) { + if (relation.targetClass) { + PFParameterAssert([relation.targetClass isEqualToString:targetClass], + @"Related object object must be of class %@, but %@ was passed in", + relation.targetClass, self.targetClass); + } else { + relation.targetClass = self.targetClass; + } + } + } else { + [NSException raise:NSInternalInconsistencyException format:@"Operation is invalid after previous operation."]; + return nil; + } + + for (PFObject *object in self.relationsToAdd) { + [relation _addKnownObject:object]; + } + for (PFObject *object in self.relationsToRemove) { + [relation _removeKnownObject:object]; + } + + return relation; +} + +@end diff --git a/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.h b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.h new file mode 100644 index 0000000..831418a --- /dev/null +++ b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFDecoder; +@class PFFieldOperation; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFFieldOperationDecoder : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + ++ (instancetype)defaultDecoder; + +///-------------------------------------- +/// @name Decoding +///-------------------------------------- + +/*! + Converts a parsed JSON object into a PFFieldOperation. + + @param encoded An NSDictionary containing an __op field. + @returns An NSObject that conforms to PFFieldOperation. + */ +- (PFFieldOperation *)decode:(NSDictionary *)encoded withDecoder:(PFDecoder *)decoder; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.m b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.m new file mode 100644 index 0000000..1c5a303 --- /dev/null +++ b/Pods/Parse/Parse/Internal/FieldOperation/PFFieldOperationDecoder.m @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFieldOperationDecoder.h" + +#import "PFAssert.h" +#import "PFDecoder.h" +#import "PFFieldOperation.h" + +@interface PFFieldOperationDecoder () { + NSMutableDictionary *_operationDecoders; +} + +@end + +typedef PFFieldOperation * (^PFFieldOperationDecodingBlock_)(NSDictionary *encoded, PFDecoder *decoder); + +@implementation PFFieldOperationDecoder + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _operationDecoders = [NSMutableDictionary dictionary]; + [self _registerDefaultOperationDecoders]; + + return self; +} + ++ (instancetype)defaultDecoder { + static PFFieldOperationDecoder *decoder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + decoder = [[self alloc] init]; + }); + return decoder; +} + +///-------------------------------------- +#pragma mark - Setup +///-------------------------------------- + +- (void)_registerDecoderForOperationWithName:(NSString *)name block:(PFFieldOperationDecodingBlock_)block { + _operationDecoders[name] = [block copy]; +} + +- (void)_registerDefaultOperationDecoders { + @weakify(self); + [self _registerDecoderForOperationWithName:@"Batch" block:^(NSDictionary *encoded, PFDecoder *decoder) { + @strongify(self); + PFFieldOperation *op = nil; + NSArray *ops = encoded[@"ops"]; + for (id maybeEncodedNextOp in ops) { + PFFieldOperation *nextOp = nil; + if ([maybeEncodedNextOp isKindOfClass:[PFFieldOperation class]]) { + nextOp = maybeEncodedNextOp; + } else { + nextOp = [self decode:maybeEncodedNextOp withDecoder:decoder]; + } + op = [nextOp mergeWithPrevious:op]; + } + return op; + }]; + + [self _registerDecoderForOperationWithName:@"Delete" block:^(NSDictionary *encoded, PFDecoder *decoder) { + // Deleting has no state, so it can be a singleton. + static PFDeleteOperation *deleteOperation = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + deleteOperation = [[PFDeleteOperation alloc] init]; + }); + return deleteOperation; + }]; + + [self _registerDecoderForOperationWithName:@"Increment" block:^(NSDictionary *encoded, PFDecoder *decoder) { + return [PFIncrementOperation incrementWithAmount:encoded[@"amount"]]; + }]; + + [self _registerDecoderForOperationWithName:@"Add" block:^(NSDictionary *encoded, PFDecoder *decoder) { + NSArray *objects = [decoder decodeObject:encoded[@"objects"]]; + return [PFAddOperation addWithObjects:objects]; + }]; + + [self _registerDecoderForOperationWithName:@"AddUnique" block:^(NSDictionary *encoded, PFDecoder *decoder) { + NSArray *objects = [decoder decodeObject:encoded[@"objects"]]; + return [PFAddUniqueOperation addUniqueWithObjects:objects]; + }]; + + [self _registerDecoderForOperationWithName:@"Remove" block:^(NSDictionary *encoded, PFDecoder *decoder) { + NSArray *objects = [decoder decodeObject:encoded[@"objects"]]; + return [PFRemoveOperation removeWithObjects:objects]; + }]; + + [self _registerDecoderForOperationWithName:@"AddRelation" block:^(NSDictionary *encoded, PFDecoder *decoder) { + NSArray *objects = [decoder decodeObject:encoded[@"objects"]]; + return [PFRelationOperation addRelationToObjects:objects]; + }]; + + [self _registerDecoderForOperationWithName:@"RemoveRelation" block:^(NSDictionary *encoded, PFDecoder *decoder) { + NSArray *objects = [decoder decodeObject:encoded[@"objects"]]; + return [PFRelationOperation removeRelationToObjects:objects]; + }]; +} + +///-------------------------------------- +#pragma mark - Decoding +///-------------------------------------- + +- (PFFieldOperation *)decode:(NSDictionary *)encoded withDecoder:(PFDecoder *)decoder { + NSString *operationName = encoded[@"__op"]; + PFFieldOperationDecodingBlock_ block = _operationDecoders[operationName]; + PFConsistencyAssert(block, @"Unable to decode operation of type %@.", operationName); + return block(encoded, decoder); +} + +@end diff --git a/Pods/Parse/Parse/Internal/File/Controller/PFFileController.h b/Pods/Parse/Parse/Internal/File/Controller/PFFileController.h new file mode 100644 index 0000000..dd12a2c --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/Controller/PFFileController.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" +#import "PFMacros.h" + +@class BFCancellationToken; +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFFileState; +@class PFFileStagingController; + +@interface PFFileController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +@property (nonatomic, strong, readonly) PFFileStagingController *fileStagingController; + +@property (nonatomic, copy, readonly) NSString *cacheFilesDirectoryPath; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithDataSource:(id)dataSource; + + +///-------------------------------------- +/// @name Download +///-------------------------------------- + +/*! + Downloads a file asynchronously with a given state. + + @param fileState File state to download the file for. + @param cancellationToken Cancellation token. + @param progressBlock Progress block to call (optional). + + @returns `BFTask` with a result set to `nil`. + */ +- (BFTask *)downloadFileAsyncWithState:(PFFileState *)fileState + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock; + +/*! + Downloads a file asynchronously with a given state and yields a stream to the live download of that file. + + @param fileState File state to download the file for. + @param cancellationToken Cancellation token. + @param progressBlock Progress block to call (optional). + + @return `BFTask` with a result set to live `NSInputStream` of the file. + */ +- (BFTask *)downloadFileStreamAsyncWithState:(PFFileState *)fileState + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock; + +///-------------------------------------- +/// @name Upload +///-------------------------------------- + +/*! + Uploads a file asynchronously from file path for a given file state. + + @param fileState File state to upload the file for. + @param sourceFilePath Source file path. + @param sessionToken Session token to use. + @param cancellationToken Cancellation token. + @param progressBlock Progress block to call (optional). + + @returns `BFTask` with a result set to `PFFileState` of uploaded file. + */ +- (BFTask *)uploadFileAsyncWithState:(PFFileState *)fileState + sourceFilePath:(NSString *)sourceFilePath + sessionToken:(NSString *)sessionToken + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock; + +///-------------------------------------- +/// @name Cache +///-------------------------------------- + +- (BFTask *)clearFileCacheAsync; + +- (NSString *)cachedFilePathForFileState:(PFFileState *)fileState; + +@end diff --git a/Pods/Parse/Parse/Internal/File/Controller/PFFileController.m b/Pods/Parse/Parse/Internal/File/Controller/PFFileController.m new file mode 100644 index 0000000..d9b2a51 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/Controller/PFFileController.m @@ -0,0 +1,269 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileController.h" + +#import +#import + +#import "BFTask+Private.h" +#import "PFFileDataStream.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFFileManager.h" +#import "PFFileStagingController.h" +#import "PFFileState.h" +#import "PFHash.h" +#import "PFMacros.h" +#import "PFRESTFileCommand.h" + +static NSString *const PFFileControllerCacheDirectoryName_ = @"PFFileCache"; + +@interface PFFileController () { + NSMutableDictionary *_downloadTasks; // { "urlString" : BFTask } + NSMutableDictionary *_downloadProgressBlocks; // { "urlString" : [ block1, block2 ] } + dispatch_queue_t _downloadDataAccessQueue; + dispatch_queue_t _fileStagingControllerAccessQueue; +} + +@end + +@implementation PFFileController + +@synthesize fileStagingController = _fileStagingController; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + _downloadTasks = [NSMutableDictionary dictionary]; + _downloadProgressBlocks = [NSMutableDictionary dictionary]; + _downloadDataAccessQueue = dispatch_queue_create("com.parse.fileController.download", DISPATCH_QUEUE_SERIAL); + _fileStagingControllerAccessQueue = dispatch_queue_create("com.parse.filestaging.controller.access", DISPATCH_QUEUE_SERIAL); + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Properties +///-------------------------------------- + +- (PFFileStagingController *)fileStagingController { + __block PFFileStagingController *result = nil; + dispatch_sync(_fileStagingControllerAccessQueue, ^{ + if (!_fileStagingController) { + _fileStagingController = [PFFileStagingController controllerWithDataSource:self.dataSource]; + } + result = _fileStagingController; + }); + return result; +} + +///-------------------------------------- +#pragma mark - Download +///-------------------------------------- + +- (BFTask *)downloadFileAsyncWithState:(PFFileState *)fileState + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + [self _addFileDownloadProgressBlock:progressBlock forFileWithState:fileState]; + + BFTask *resultTask = [self _fileDownloadResultTaskForFileWithState:fileState]; + if (!resultTask) { + NSURL *url = [NSURL URLWithString:fileState.secureURLString]; + NSString *temporaryPath = [self _temporaryFileDownloadPathForFileState:fileState]; + + PFProgressBlock unifyingProgressBlock = [self _fileDownloadUnifyingProgressBlockForFileState:fileState]; + resultTask = [self.dataSource.commandRunner runFileDownloadCommandAsyncWithFileURL:url + targetFilePath:temporaryPath + cancellationToken:cancellationToken + progressBlock:unifyingProgressBlock]; + resultTask = [[resultTask continueWithSuccessBlock:^id(BFTask *task) { + return [[PFFileManager moveItemAsyncAtPath:temporaryPath + toPath:[self cachedFilePathForFileState:fileState]] continueWithBlock:^id(BFTask *task) { + // Ignore the error if file exists. + if (task.error && task.error.code == NSFileWriteFileExistsError) { + return nil; + } + return task; + }]; + }] continueWithBlock:^id(BFTask *task) { + dispatch_barrier_async(_downloadDataAccessQueue, ^{ + [_downloadTasks removeObjectForKey:fileState.secureURLString]; + [_downloadProgressBlocks removeObjectForKey:fileState.secureURLString]; + }); + return task; + }]; + dispatch_barrier_async(_downloadDataAccessQueue, ^{ + _downloadTasks[fileState.secureURLString] = resultTask; + }); + } + return resultTask; + }]; +} + +- (BFTask *)downloadFileStreamAsyncWithState:(PFFileState *)fileState + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; + NSString *filePath = [self _temporaryFileDownloadPathForFileState:fileState]; + PFFileDataStream *stream = [[PFFileDataStream alloc] initWithFileAtPath:filePath]; + [[self downloadFileAsyncWithState:fileState + cancellationToken:cancellationToken + progressBlock:^(int percentDone) { + [taskCompletionSource trySetResult:stream]; + + if (progressBlock) { + progressBlock(percentDone); + } + }] continueWithBlock:^id(BFTask *task) { + [stream stopBlocking]; + return task; + }]; + return taskCompletionSource.task; + }]; +} + +- (BFTask *)_fileDownloadResultTaskForFileWithState:(PFFileState *)state { + __block BFTask *resultTask = nil; + dispatch_sync(_downloadDataAccessQueue, ^{ + resultTask = _downloadTasks[state.secureURLString]; + }); + return resultTask; +} + +- (PFProgressBlock)_fileDownloadUnifyingProgressBlockForFileState:(PFFileState *)fileState { + return ^(int progress) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + __block NSArray *blocks = nil; + dispatch_sync(_downloadDataAccessQueue, ^{ + blocks = [_downloadProgressBlocks[fileState.secureURLString] copy]; + }); + if (blocks.count != 0) { + dispatch_async(dispatch_get_main_queue(), ^{ + for (PFProgressBlock block in blocks) { + block(progress); + } + }); + } + }); + }; +} + +- (void)_addFileDownloadProgressBlock:(PFProgressBlock)block forFileWithState:(PFFileState *)state { + if (!block) { + return; + } + + dispatch_barrier_async(_downloadDataAccessQueue, ^{ + NSMutableArray *progressBlocks = _downloadProgressBlocks[state.secureURLString]; + if (!progressBlocks) { + progressBlocks = [NSMutableArray arrayWithObject:block]; + _downloadProgressBlocks[state.secureURLString] = progressBlocks; + } else { + [progressBlocks addObject:block]; + } + }); +} + +- (NSString *)_temporaryFileDownloadPathForFileState:(PFFileState *)fileState { + return [NSTemporaryDirectory() stringByAppendingPathComponent:PFMD5HashFromString(fileState.secureURLString)]; +} + +///-------------------------------------- +#pragma mark - Upload +///-------------------------------------- + +- (BFTask *)uploadFileAsyncWithState:(PFFileState *)fileState + sourceFilePath:(NSString *)sourceFilePath + sessionToken:(NSString *)sessionToken + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock { + PFRESTFileCommand *command = [PFRESTFileCommand uploadCommandForFileWithName:fileState.name + sessionToken:sessionToken]; + + @weakify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + return [[[self.dataSource.commandRunner runFileUploadCommandAsync:command + withContentType:fileState.mimeType + contentSourceFilePath:sourceFilePath + options:PFCommandRunningOptionRetryIfFailed + cancellationToken:cancellationToken + progressBlock:progressBlock] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + PFFileState *fileState = [[PFFileState alloc] initWithName:result.result[@"name"] + urlString:result.result[@"url"] + mimeType:nil]; + return fileState; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + + NSString *finalPath = [self cachedFilePathForFileState:task.result]; + NSError *error = nil; + [[NSFileManager defaultManager] moveItemAtPath:sourceFilePath + toPath:finalPath + error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return task; + }]; +} + +///-------------------------------------- +#pragma mark - Cache +///-------------------------------------- + +- (NSString *)cachedFilePathForFileState:(PFFileState *)fileState { + if (!fileState.secureURLString) { + return nil; + } + + NSString *filename = [fileState.secureURLString lastPathComponent]; + NSString *path = [self.cacheFilesDirectoryPath stringByAppendingPathComponent:filename]; + return path; +} + +- (NSString *)cacheFilesDirectoryPath { + NSString *path = [self.dataSource.fileManager parseCacheItemPathForPathComponent:PFFileControllerCacheDirectoryName_]; + [[PFFileManager createDirectoryIfNeededAsyncAtPath:path] waitForResult:nil withMainThreadWarning:NO]; + return path; +} + +- (BFTask *)clearFileCacheAsync { + NSString *path = [self cacheFilesDirectoryPath]; + return [PFFileManager removeDirectoryContentsAsyncAtPath:path]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.h b/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.h new file mode 100644 index 0000000..15ee8d7 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFFileManagerProvider; + +@interface PFFileStagingController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +@property (nonatomic, copy, readonly) NSString *stagedFilesDirectoryPath; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Staging +///-------------------------------------- + +/*! + Moves a file from the specified path to the staging directory based off of the name and unique ID passed in. + + @param filePath The source path to stage + @param name The name of the file to stage + @param uniqueId A unique ID for this file to be used when differentiating between files with the same name. + + @return A task, which yields the path of the staged file on disk. + */ +- (BFTask *)stageFileAsyncAtPath:(NSString *)filePath name:(NSString *)name uniqueId:(uint64_t)uniqueId; + +/*! + Creates a file from the specified data and places it into the staging directory based off of the name and unique + ID passed in. + + @param fileData The data to stage + @param name The name of the file to stage + @param uniqueId The unique ID for this file to be used when differentiating between files with the same name. + + @return A task, which yields the path of the staged file on disk. + */ +- (BFTask *)stageFileAsyncWithData:(NSData *)fileData name:(NSString *)name uniqueId:(uint64_t)uniqueId; + +/*! + Get the staged directory path for a file with the specified name and unique ID. + + @param name The name of the staged file + @param uniqueId The unique ID of the staged file + + @return The path in the staged directory folder which contains the contents of the requested file. + */ +- (NSString *)stagedFilePathForFileWithName:(NSString *)name uniqueId:(uint64_t)uniqueId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.m b/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.m new file mode 100644 index 0000000..fb13749 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/Controller/PFFileStagingController.m @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileStagingController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFAsyncTaskQueue.h" +#import "PFDataProvider.h" +#import "PFFileManager.h" +#import "PFLogging.h" + +static NSString *const PFFileStagingControllerDirectoryName_ = @"PFFileStaging"; + +@implementation PFFileStagingController { + PFAsyncTaskQueue *_taskQueue; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + _taskQueue = [PFAsyncTaskQueue taskQueue]; + + [self _clearStagedFilesAsync]; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Properties +///-------------------------------------- + +- (NSString *)stagedFilesDirectoryPath { + NSString *folderPath = [self.dataSource.fileManager parseLocalSandboxDataDirectoryPath]; + return [folderPath stringByAppendingPathComponent:PFFileStagingControllerDirectoryName_]; +} + +///-------------------------------------- +#pragma mark - Staging +///-------------------------------------- + +- (BFTask *)stageFileAsyncAtPath:(NSString *)filePath name:(NSString *)name uniqueId:(uint64_t)uniqueId { + return [_taskQueue enqueue:^id(BFTask *task) { + return [[PFFileManager createDirectoryIfNeededAsyncAtPath:[self stagedFilesDirectoryPath]] continueWithBlock:^id(BFTask *task) { + NSString *destinationPath = [self stagedFilePathForFileWithName:name uniqueId:uniqueId]; + return [[PFFileManager copyItemAsyncAtPath:filePath toPath:destinationPath] continueWithSuccessResult:destinationPath]; + }]; + }]; +} + +- (BFTask *)stageFileAsyncWithData:(NSData *)fileData name:(NSString *)name uniqueId:(uint64_t)uniqueId { + return [_taskQueue enqueue:^id(BFTask *task) { + return [[PFFileManager createDirectoryIfNeededAsyncAtPath:[self stagedFilesDirectoryPath]] continueWithBlock:^id(BFTask *task) { + NSString *destinationPath = [self stagedFilePathForFileWithName:name uniqueId:uniqueId]; + return [[PFFileManager writeDataAsync:fileData toFile:destinationPath] continueWithSuccessResult:destinationPath]; + }]; + }]; +} + +- (NSString *)stagedFilePathForFileWithName:(NSString *)name uniqueId:(uint64_t)uniqueId { + NSString *fileName = [NSString stringWithFormat:@"%llX_%@", uniqueId, name]; + return [[self stagedFilesDirectoryPath] stringByAppendingPathComponent:fileName]; +} + +///-------------------------------------- +#pragma mark - Clearing +///-------------------------------------- + +- (BFTask *)_clearStagedFilesAsync { + return [_taskQueue enqueue:^id(BFTask *task) { + NSString *stagedFilesDirectoryPath = [self stagedFilesDirectoryPath]; + return [PFFileManager removeItemAtPathAsync:stagedFilesDirectoryPath]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.h b/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.h new file mode 100644 index 0000000..fa86bc6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! + PFFileDataStream is an NSStream proxy which won't read the last byte of a file until the downlaod has finished. + + When downloading a file stream via `-[PFFile getDataDownloadStreamInBackground]`, we need to be able to read and write + to the same file on disk concurrently. + + NSInputStream closes itself as soon as it hits EOF, so this class wraps an underlying NSInputStream and stops the + stream from closing until after writing has finished. + */ +@interface PFFileDataStream : NSProxy + +- (instancetype)initWithFileAtPath:(NSString *)path; + +- (void)stopBlocking; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.m b/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.m new file mode 100644 index 0000000..ac03501 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/FileDataStream/PFFileDataStream.m @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileDataStream.h" + +#import +#import + +@interface PFFileDataStream() { + NSString *_path; + NSInputStream *_inputStream; + + int _fd; + BOOL _finished; + + __weak id _delegate; +} + +@end + +@implementation PFFileDataStream + +- (instancetype)initWithFileAtPath:(NSString *)path { + _finished = NO; + + _path = path; + _inputStream = [NSInputStream inputStreamWithFileAtPath:path]; + _inputStream.delegate = self; + + return self; +} + +- (void)stopBlocking { + _finished = YES; + + [self stream:_inputStream handleEvent:NSStreamEventHasBytesAvailable]; +} + +///-------------------------------------- +#pragma mark - NSProxy methods +///-------------------------------------- + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { + return [_inputStream methodSignatureForSelector:sel]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + [invocation invokeWithTarget:_inputStream]; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + Method implementation = class_getInstanceMethod([self class], aSelector); + return implementation ? YES : [_inputStream respondsToSelector:aSelector]; +} + +///-------------------------------------- +#pragma mark - NSInputStream methods +///-------------------------------------- + +- (void)setDelegate:(id)delegate { + _delegate = delegate; +} + +- (id)delegate { + return _delegate; +} + +- (void)open { + _fd = open([_path UTF8String], O_RDONLY | O_NONBLOCK); + [_inputStream open]; +} + +- (void)close { + [_inputStream close]; + close(_fd); +} + +- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len { + if (!_finished) { + off_t currentOffset = [[_inputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; + off_t fileSize = lseek(_fd, 0, SEEK_END); + + len = (NSUInteger)MIN(len, ((fileSize - currentOffset) - 1)); + } + + // Reading 0 bytes from an NSInputStream causes this strange undocumented behavior: it marks the stream as 'at end', + // regardless of whether more bytes are available or not. lolwut? + if (len == 0) { + return 0; + } + + return [_inputStream read:buffer maxLength:len]; +} + +///-------------------------------------- +#pragma mark - NSStreamDelegate +///-------------------------------------- + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(stream:handleEvent:)]) { + [delegate stream:(NSInputStream *)self handleEvent:eventCode]; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/File/PFFile_Private.h b/Pods/Parse/Parse/Internal/File/PFFile_Private.h new file mode 100644 index 0000000..43fe6cc --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/PFFile_Private.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +#import "PFFileState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFFile (Private) + +@property (nonatomic, strong, readonly) PFFileState *state; + ++ (instancetype)fileWithName:(nullable NSString *)name url:(nullable NSString *)url; + +- (nullable NSString *)_cachedFilePath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/State/PFFileState.h b/Pods/Parse/Parse/Internal/File/State/PFFileState.h new file mode 100644 index 0000000..8088ae9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/State/PFFileState.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFBaseState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFFileState : PFBaseState + +@property (nonatomic, copy, readonly) NSString *name; +@property (nullable, nonatomic, copy, readonly) NSString *urlString; +@property (nullable, nonatomic, copy, readonly) NSString *secureURLString; + +@property (nullable, nonatomic, copy, readonly) NSString *mimeType; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithState:(PFFileState *)state; +- (instancetype)initWithName:(nullable NSString *)name + urlString:(nullable NSString *)urlString + mimeType:(nullable NSString *)mimeType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/State/PFFileState.m b/Pods/Parse/Parse/Internal/File/State/PFFileState.m new file mode 100644 index 0000000..3a0e4e8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/State/PFFileState.m @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileState.h" +#import "PFFileState_Private.h" + +#import "PFMutableFileState.h" +#import "PFPropertyInfo.h" + +static NSString *const _PFFileStateSecureDomain = @"files.parsetfss.com"; + +@interface PFFileState () + +@property (nonatomic, copy, readwrite) NSString *secureURLString; + +@end + +@implementation PFFileState + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + return @{ + @"name" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"urlString" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"mimeType" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + }; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithState:(PFFileState *)state { + return [super initWithState:state]; +} + +- (instancetype)initWithName:(NSString *)name urlString:(NSString *)urlString mimeType:(NSString *)mimeType { + self = [super init]; + if (!self) return nil; + + _name = (name ? [name copy] : @"file"); + _urlString = [urlString copy]; + _mimeType = [mimeType copy]; + + return self; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setUrlString:(NSString *)urlString { + if (self.urlString != urlString) { + _urlString = [urlString copy]; + _secureURLString = nil; // Invalidate variable cache + } +} + +- (NSString *)secureURLString { + if (_secureURLString) { + return _secureURLString; + } + + if (!self.urlString) { + return nil; + } + + NSURLComponents *components = [NSURLComponents componentsWithString:self.urlString]; + if (!components) { + return self.urlString; + } + + NSString *scheme = [components scheme]; + if (![scheme isEqualToString:@"http"]) { + return self.urlString; + } + + if ([[components host] isEqualToString:_PFFileStateSecureDomain]) { + components.scheme = @"https"; + } + _secureURLString = [[components URL] absoluteString]; + return _secureURLString; +} + +///-------------------------------------- +#pragma mark - Mutable Copying +///-------------------------------------- + +- (id)copyWithZone:(NSZone *)zone { + return [[PFFileState allocWithZone:zone] initWithState:self]; +} + +- (instancetype)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableFileState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/File/State/PFFileState_Private.h b/Pods/Parse/Parse/Internal/File/State/PFFileState_Private.h new file mode 100644 index 0000000..5114fef --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/State/PFFileState_Private.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFFileState () + +@property (nonatomic, copy, readwrite) NSString *name; +@property (nullable, nonatomic, copy, readwrite) NSString *urlString; +@property (nullable, nonatomic, copy, readwrite) NSString *mimeType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.h b/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.h new file mode 100644 index 0000000..1d33fa8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFMutableFileState : PFFileState + +@property (nonatomic, copy, readwrite) NSString *name; +@property (nullable, nonatomic, copy, readwrite) NSString *urlString; +@property (nullable, nonatomic, copy, readwrite) NSString *mimeType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.m b/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.m new file mode 100644 index 0000000..53db52c --- /dev/null +++ b/Pods/Parse/Parse/Internal/File/State/PFMutableFileState.m @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableFileState.h" + +@implementation PFMutableFileState + +@dynamic name; +@dynamic urlString; +@dynamic mimeType; + +@end diff --git a/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPRequest.h b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPRequest.h new file mode 100644 index 0000000..e6394e4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPRequest.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef Parse_PFHTTPRequest_h +#define Parse_PFHTTPRequest_h + +#import + +static NSString *const PFHTTPRequestMethodGET = @"GET"; +static NSString *const PFHTTPRequestMethodHEAD = @"HEAD"; +static NSString *const PFHTTPRequestMethodDELETE = @"DELETE"; +static NSString *const PFHTTPRequestMethodPOST = @"POST"; +static NSString *const PFHTTPRequestMethodPUT = @"PUT"; + +static NSString *const PFHTTPRequestHeaderNameContentType = @"Content-Type"; +static NSString *const PFHTTPRequestHeaderNameContentLength = @"Content-Length"; + +#endif diff --git a/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.h b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.h new file mode 100644 index 0000000..b3bfb70 --- /dev/null +++ b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFHTTPURLRequestConstructor : NSObject + ++ (NSMutableURLRequest *)urlRequestWithURL:(NSURL *)url + httpMethod:(NSString *)httpMethod + httpHeaders:(NSDictionary *)httpHeaders + parameters:(NSDictionary *)parameters; + +@end diff --git a/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.m b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.m new file mode 100644 index 0000000..e948c3d --- /dev/null +++ b/Pods/Parse/Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.m @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFHTTPURLRequestConstructor.h" + +#import "PFAssert.h" +#import "PFHTTPRequest.h" +#import "PFURLConstructor.h" + +static NSString *const PFHTTPURLRequestContentTypeJSON = @"application/json; charset=utf8"; + +@implementation PFHTTPURLRequestConstructor + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + ++ (NSMutableURLRequest *)urlRequestWithURL:(NSURL *)url + httpMethod:(NSString *)httpMethod + httpHeaders:(NSDictionary *)httpHeaders + parameters:(NSDictionary *)parameters { + NSParameterAssert(url != nil); + NSParameterAssert(httpMethod != nil); + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + + request.HTTPMethod = httpMethod; + request.allHTTPHeaderFields = httpHeaders; + + if (parameters != nil) { + PFConsistencyAssert([httpMethod isEqualToString:PFHTTPRequestMethodPOST] || + [httpMethod isEqualToString:PFHTTPRequestMethodPUT], + @"Can't create %@ request with json body.", httpMethod); + + [request setValue:PFHTTPURLRequestContentTypeJSON forHTTPHeaderField:PFHTTPRequestHeaderNameContentType]; + + NSError *error = nil; + [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters + options:(NSJSONWritingOptions)0 + error:&error]]; + PFConsistencyAssert(error == nil, @"Failed to serialize JSON with error = %@", error); + } + return request; +} + +@end diff --git a/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.h b/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.h new file mode 100644 index 0000000..19f7e26 --- /dev/null +++ b/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFURLConstructor : NSObject + ++ (NSURL *)URLFromAbsoluteString:(NSString *)string + path:(nullable NSString *)path + query:(nullable NSString *)query; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.m b/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.m new file mode 100644 index 0000000..7e0dd24 --- /dev/null +++ b/Pods/Parse/Parse/Internal/HTTPRequest/PFURLConstructor.m @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFURLConstructor.h" + +#import "PFAssert.h" + +@implementation PFURLConstructor + +///-------------------------------------- +#pragma mark - Basic +///-------------------------------------- + ++ (NSURL *)URLFromAbsoluteString:(NSString *)string + path:(nullable NSString *)path + query:(nullable NSString *)query { + NSURLComponents *components = [NSURLComponents componentsWithString:string]; + if (components.path) { + components.path = path; + } + if (query) { + components.query = query; + } + return components.URL; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.h b/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.h new file mode 100644 index 0000000..29ef544 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +extern NSString *const PFInstallationKeyParseVersion; +extern NSString *const PFInstallationKeyDeviceType; +extern NSString *const PFInstallationKeyInstallationId; +extern NSString *const PFInstallationKeyDeviceToken; +extern NSString *const PFInstallationKeyAppName; +extern NSString *const PFInstallationKeyAppVersion; +extern NSString *const PFInstallationKeyAppIdentifier; +extern NSString *const PFInstallationKeyTimeZone; +extern NSString *const PFInstallationKeyLocaleIdentifier; +extern NSString *const PFInstallationKeyBadge; +extern NSString *const PFInstallationKeyChannels; diff --git a/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.m b/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.m new file mode 100644 index 0000000..19f2565 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/Constants/PFInstallationConstants.m @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInstallationConstants.h" + +NSString *const PFInstallationKeyParseVersion = @"parseVersion"; +NSString *const PFInstallationKeyDeviceType = @"deviceType"; +NSString *const PFInstallationKeyInstallationId = @"installationId"; +NSString *const PFInstallationKeyDeviceToken = @"deviceToken"; +NSString *const PFInstallationKeyAppName = @"appName"; +NSString *const PFInstallationKeyAppVersion = @"appVersion"; +NSString *const PFInstallationKeyAppIdentifier = @"appIdentifier"; +NSString *const PFInstallationKeyTimeZone = @"timeZone"; +NSString *const PFInstallationKeyLocaleIdentifier = @"localeIdentifier"; +NSString *const PFInstallationKeyBadge = @"badge"; +NSString *const PFInstallationKeyChannels = @"channels"; diff --git a/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.h b/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.h new file mode 100644 index 0000000..75573b9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFCoreDataProvider.h" +#import "PFObjectControlling.h" + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFInstallationController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.m b/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.m new file mode 100644 index 0000000..66d09cc --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/Controller/PFInstallationController.m @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInstallationController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCurrentInstallationController.h" +#import "PFInstallationPrivate.h" +#import "PFObjectController.h" +#import "PFObjectPrivate.h" + +@implementation PFInstallationController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BFTask *)fetchObjectAsync:(PFInstallation *)object withSessionToken:(nullable NSString *)sessionToken { + @weakify(self); + return [[[self.objectController fetchObjectAsync:object + withSessionToken:sessionToken] continueWithBlock:^id(BFTask *task) { + @strongify(self); + + // Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed. + if (self.currentInstallationController.storageType == PFCurrentObjectStorageTypeOfflineStore) { + return task; + } + + if (task.faulted && task.error.code == kPFErrorObjectNotFound) { + @synchronized (object.lock) { + // Retry the fetch as a save operation because this Installation was deleted on the server. + // We always want [currentInstallation fetch] to succeed. + object.objectId = nil; + [object _markAllFieldsDirty]; + return [[object saveAsync:nil] continueWithSuccessResult:object]; + } + } + return task; + }] continueWithBlock:^id(BFTask *task) { + @strongify(self); + // Roll-forward the previous task. + return [[self.currentInstallationController saveCurrentObjectAsync:object] continueWithResult:task]; + }]; +} + +- (BFTask *)processFetchResultAsync:(NSDictionary *)result forObject:(PFInstallation *)object { + @weakify(self); + return [[self.objectController processFetchResultAsync:result forObject:object] continueWithBlock:^id(BFTask *task) { + @strongify(self); + // Roll-forward the previous task. + return [[self.currentInstallationController saveCurrentObjectAsync:object] continueWithResult:task]; + }]; +} + +///-------------------------------------- +#pragma mark - Delete +///-------------------------------------- + +- (BFTask *)deleteObjectAsync:(PFObject *)object withSessionToken:(nullable NSString *)sessionToken { + PFConsistencyAssert(NO, @"Installations cannot be deleted."); + return nil; +} + +- (BFTask *)processDeleteResultAsync:(nullable NSDictionary *)result forObject:(PFObject *)object { + PFConsistencyAssert(NO, @"Installations cannot be deleted."); + return nil; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (id)objectController { + return self.dataSource.objectController; +} + +- (PFCurrentInstallationController *)currentInstallationController { + return self.dataSource.currentInstallationController; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.h b/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.h new file mode 100644 index 0000000..b51f996 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFCoreDataProvider.h" +#import "PFCurrentObjectControlling.h" +#import "PFDataProvider.h" +#import "PFMacros.h" + +extern NSString *const PFCurrentInstallationFileName; +extern NSString *const PFCurrentInstallationPinName; + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFInstallation; + +PF_WATCH_UNAVAILABLE @interface PFCurrentInstallationController : NSObject + +@property (nonatomic, weak, readonly) id commonDataSource; +@property (nonatomic, weak, readonly) id coreDataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithStorageType:(PFCurrentObjectStorageType)dataStorageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; + ++ (instancetype)controllerWithStorageType:(PFCurrentObjectStorageType)dataStorageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; + +///-------------------------------------- +/// @name Installation +///-------------------------------------- + +@property (nonatomic, strong, readonly) PFInstallation *memoryCachedCurrentInstallation; + +- (BFTask *)clearCurrentInstallationAsync; +- (BFTask *)clearMemoryCachedCurrentInstallationAsync; + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.m b/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.m new file mode 100644 index 0000000..f716106 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.m @@ -0,0 +1,289 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCurrentInstallationController.h" + +#import "BFTask+Private.h" +#import "PFAsyncTaskQueue.h" +#import "PFFileManager.h" +#import "PFInstallationIdentifierStore.h" +#import "PFInstallationPrivate.h" +#import "PFMacros.h" +#import "PFObjectFilePersistenceController.h" +#import "PFObjectPrivate.h" +#import "PFPushPrivate.h" +#import "PFQuery.h" + +NSString *const PFCurrentInstallationFileName = @"currentInstallation"; +NSString *const PFCurrentInstallationPinName = @"_currentInstallation"; + +@interface PFCurrentInstallationController () { + dispatch_queue_t _dataQueue; + PFAsyncTaskQueue *_dataTaskQueue; +} + +@property (nonatomic, strong, readonly) PFFileManager *fileManager; +@property (nonatomic, strong, readonly) PFInstallationIdentifierStore *installationIdentifierStore; + +@property (nonatomic, strong) PFInstallation *currentInstallation; +@property (nonatomic, assign) BOOL currentInstallationMatchesDisk; + +@end + +@implementation PFCurrentInstallationController + +@synthesize storageType = _storageType; + +@synthesize currentInstallation = _currentInstallation; +@synthesize currentInstallationMatchesDisk = _currentInstallationMatchesDisk; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithStorageType:(PFCurrentObjectStorageType)storageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + self = [super init]; + if (!self) return nil; + + _dataQueue = dispatch_queue_create("com.parse.installation.current", DISPATCH_QUEUE_CONCURRENT); + _dataTaskQueue = [[PFAsyncTaskQueue alloc] init]; + + _storageType = storageType; + _commonDataSource = commonDataSource; + _coreDataSource = coreDataSource; + + return self; +} + ++ (instancetype)controllerWithStorageType:(PFCurrentObjectStorageType)storageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + return [[self alloc] initWithStorageType:storageType + commonDataSource:commonDataSource + coreDataSource:coreDataSource]; +} + +///-------------------------------------- +#pragma mark - PFCurrentObjectControlling +///-------------------------------------- + +- (BFTask *)getCurrentObjectAsync { + @weakify(self); + return [_dataTaskQueue enqueue:^BFTask *(BFTask *unused) { + return [[[BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id { + @strongify(self); + if (self.currentInstallation) { + return self.currentInstallation; + } + + if (!self.currentInstallationMatchesDisk) { + return [[self _loadCurrentInstallationFromDiskAsync] continueWithBlock:^id(BFTask *task) { + PFInstallation *installation = task.result; + if (installation) { + // If there is no objectId, but there is some data + // it means that the data wasn't yet saved to the server + // so we should mark everything as dirty + if (!installation.objectId && [[installation allKeys] count]) { + [installation _markAllFieldsDirty]; + } + } + return task; + }]; + } + return nil; + }] continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (task.faulted) { + return task; + } + + PFInstallation *installation = task.result; + NSString *installationId = self.installationIdentifierStore.installationIdentifier; + installationId = [installationId lowercaseString]; + if (!installation || ![installationId isEqualToString:installation.installationId]) { + // If there's no installation object, or the object's installation + // ID doesn't match this device's installation ID, create a new + // installation. Try to keep track of the previously stored device + // token: if there was an installation already stored just re-use + // its device token, otherwise try loading from the keychain (where + // old SDKs stored the token). Discard the old installation. + NSString *oldDeviceToken = nil; + if (installation) { + oldDeviceToken = installation.deviceToken; + } else { + oldDeviceToken = [[PFPush pushInternalUtilClass] getDeviceTokenFromKeychain]; + } + + installation = [PFInstallation object]; + installation.deviceType = kPFDeviceType; + installation.installationId = installationId; + if (oldDeviceToken) { + installation.deviceToken = oldDeviceToken; + } + } + + return installation; + }] continueWithBlock:^id(BFTask *task) { + dispatch_barrier_sync(_dataQueue, ^{ + _currentInstallation = task.result; + _currentInstallationMatchesDisk = !task.faulted; + }); + return task; + }]; + }]; +} + +- (BFTask *)saveCurrentObjectAsync:(PFInstallation *)installation { + @weakify(self); + return [_dataTaskQueue enqueue:^BFTask *(BFTask *unused) { + @strongify(self); + + if (installation != self.currentInstallation) { + return nil; + } + return [[self _saveCurrentInstallationToDiskAsync:installation] continueWithBlock:^id(BFTask *task) { + self.currentInstallationMatchesDisk = (!task.faulted && !task.cancelled); + return nil; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Installation +///-------------------------------------- + +- (PFInstallation *)memoryCachedCurrentInstallation { + return self.currentInstallation; +} + +- (BFTask *)clearCurrentInstallationAsync { + @weakify(self); + return [_dataTaskQueue enqueue:^BFTask *(BFTask *unused) { + @strongify(self); + + dispatch_barrier_sync(_dataQueue, ^{ + _currentInstallation = nil; + _currentInstallationMatchesDisk = NO; + }); + + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:2]; + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + BFTask *unpinTask = [PFObject unpinAllObjectsInBackgroundWithName:PFCurrentInstallationPinName]; + [tasks addObject:unpinTask]; + } + + NSString *path = [self.fileManager parseDataItemPathForPathComponent:PFCurrentInstallationFileName]; + BFTask *fileTask = [PFFileManager removeItemAtPathAsync:path]; + [tasks addObject:fileTask]; + + return [BFTask taskForCompletionOfAllTasks:tasks]; + }]; +} + +- (BFTask *)clearMemoryCachedCurrentInstallationAsync { + return [_dataTaskQueue enqueue:^BFTask *(BFTask *unused) { + self.currentInstallation = nil; + self.currentInstallationMatchesDisk = NO; + + return nil; + }]; +} + +///-------------------------------------- +#pragma mark - Data Storage +///-------------------------------------- + +- (BFTask *)_loadCurrentInstallationFromDiskAsync { + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + // Try loading from OfflineStore + PFQuery *query = [[[PFQuery queryWithClassName:[PFInstallation parseClassName]] + fromPinWithName:PFCurrentInstallationPinName] ignoreACLs]; + + return [[query findObjectsInBackground] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *results = task.result; + if ([results count] == 1) { + return [BFTask taskWithResult:[results firstObject]]; + } else if ([results count] != 0) { + return [[PFObject unpinAllObjectsInBackgroundWithName:PFCurrentInstallationPinName] + continueWithSuccessResult:nil]; + } + + // Backward compatibility if we previously have non-LDS currentInstallation. + return [PFObject _migrateObjectInBackgroundFromFile:PFCurrentInstallationFileName + toPin:PFCurrentInstallationPinName]; + }]; + } + + PFObjectFilePersistenceController *controller = self.objectFilePersistenceController; + return [controller loadPersistentObjectAsyncForKey:PFCurrentInstallationFileName]; +} + +- (BFTask *)_saveCurrentInstallationToDiskAsync:(PFInstallation *)installation { + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + BFTask *task = [PFObject unpinAllObjectsInBackgroundWithName:PFCurrentInstallationPinName]; + return [task continueWithBlock:^id(BFTask *task) { + // Make sure to not pin children of PFInstallation automatically, as it can create problems + // if any of the children are of Installation class. + return [installation _pinInBackgroundWithName:PFCurrentInstallationPinName includeChildren:NO]; + }]; + } + + PFObjectFilePersistenceController *controller = self.objectFilePersistenceController; + return [controller persistObjectAsync:installation forKey:PFCurrentInstallationFileName]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFFileManager *)fileManager { + return self.commonDataSource.fileManager; +} + +- (PFObjectFilePersistenceController *)objectFilePersistenceController { + return self.coreDataSource.objectFilePersistenceController; +} + +- (PFInstallationIdentifierStore *)installationIdentifierStore { + return self.commonDataSource.installationIdentifierStore; +} + +- (PFInstallation *)currentInstallation { + __block PFInstallation *installation = nil; + dispatch_sync(_dataQueue, ^{ + installation = _currentInstallation; + }); + return installation; +} + +- (void)setCurrentInstallation:(PFInstallation *)currentInstallation { + dispatch_barrier_sync(_dataQueue, ^{ + if (_currentInstallation != currentInstallation) { + _currentInstallation = currentInstallation; + } + }); +} + +- (BOOL)currentInstallationMatchesDisk { + __block BOOL matches = NO; + dispatch_sync(_dataQueue, ^{ + matches = _currentInstallationMatchesDisk; + }); + return matches; +} + +- (void)setCurrentInstallationMatchesDisk:(BOOL)currentInstallationMatchesDisk { + dispatch_barrier_sync(_dataQueue, ^{ + _currentInstallationMatchesDisk = currentInstallationMatchesDisk; + }); +} + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.h b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.h new file mode 100644 index 0000000..3e7becb --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFFileManager; + +@interface PFInstallationIdentifierStore : NSObject + +/*! + Returns a cached installationId or creates a new one, saves it to disk and returns it. + + @returns `NSString` representation of current installationId. + */ +@property (nonatomic, copy, readonly) NSString *installationIdentifier; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(PFFileManager *)fileManager NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Clear +///-------------------------------------- + +/*! + Clears installation identifier on disk and in-memory. + */ +- (void)clearInstallationIdentifier; + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.m b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.m new file mode 100644 index 0000000..af7fac5 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.m @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInstallationIdentifierStore.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFFileManager.h" +#import "PFInternalUtils.h" +#import "PFMacros.h" +#import "PFMultiProcessFileLockController.h" +#import "Parse_Private.h" + +static NSString *const PFInstallationIdentifierFileName = @"installationId"; + +@interface PFInstallationIdentifierStore () { + dispatch_queue_t _synchronizationQueue; + PFFileManager *_fileManager; +} + +@property (nonatomic, copy, readwrite) NSString *installationIdentifier; +@property (nonatomic, copy, readonly) NSString *installationIdentifierFilePath; + +@end + +@implementation PFInstallationIdentifierStore + +@synthesize installationIdentifier = _installationIdentifier; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithFileManager:(PFFileManager *)fileManager { + self = [super init]; + if (!self) return nil; + + _synchronizationQueue = dispatch_queue_create("com.parse.installationIdentifier", DISPATCH_QUEUE_SERIAL); + PFMarkDispatchQueue(_synchronizationQueue); + + _fileManager = fileManager; + + return self; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSString *)installationIdentifier { + __block NSString *identifier = nil; + dispatch_sync(_synchronizationQueue, ^{ + if (!_installationIdentifier) { + [self _loadInstallationIdentifier]; + } + + identifier = _installationIdentifier; + }); + return identifier; +} + +- (void)setInstallationIdentifier:(NSString *)installationIdentifier { + PFAssertIsOnDispatchQueue(_synchronizationQueue); + if (_installationIdentifier != installationIdentifier) { + _installationIdentifier = [installationIdentifier copy]; + } +} + +- (void)clearInstallationIdentifier { + dispatch_sync(_synchronizationQueue, ^{ + NSString *filePath = self.installationIdentifierFilePath; + [[PFFileManager removeItemAtPathAsync:filePath] waitForResult:nil withMainThreadWarning:NO]; + + self.installationIdentifier = nil; + }); +} + +///-------------------------------------- +#pragma mark - Disk Operations +///-------------------------------------- + +- (void)_loadInstallationIdentifier { + PFAssertIsOnDispatchQueue(_synchronizationQueue); + + NSString *filePath = self.installationIdentifierFilePath; + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:filePath]; + + NSString *identifier = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; + if (!identifier) { + identifier = [[[NSUUID UUID] UUIDString] lowercaseString]; + [[PFFileManager writeStringAsync:identifier toFile:filePath] waitForResult:nil withMainThreadWarning:NO]; + } + self.installationIdentifier = identifier; + + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:filePath]; +} + +- (void)_clearCachedInstallationIdentifier { + dispatch_sync(_synchronizationQueue, ^{ + self.installationIdentifier = nil; + }); +} + +- (NSString *)installationIdentifierFilePath { + return [_fileManager parseDataItemPathForPathComponent:PFInstallationIdentifierFileName]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore_Private.h b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore_Private.h new file mode 100644 index 0000000..f84b36b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore_Private.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInstallationIdentifierStore.h" + +@interface PFInstallationIdentifierStore (Private) + +/*! + Clears in-memory cached installation identifier, if any. + */ +- (void)_clearCachedInstallationIdentifier; + +@end diff --git a/Pods/Parse/Parse/Internal/Installation/PFInstallationPrivate.h b/Pods/Parse/Parse/Internal/Installation/PFInstallationPrivate.h new file mode 100644 index 0000000..0e24f86 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Installation/PFInstallationPrivate.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@interface PFInstallation (Private) + +- (void)_clearDeviceToken; +- (void)_markAllFieldsDirty; + +@end + +@interface PFInstallation () + +// Private read-write declarations of publicly-readonly fields. +@property (nonatomic, copy, readwrite) NSString *deviceType; +@property (nonatomic, copy, readwrite) NSString *installationId; +@property (nonatomic, copy, readwrite) NSString *timeZone; + +@end diff --git a/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.h b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.h new file mode 100644 index 0000000..e2518de --- /dev/null +++ b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFKeyValueCache : NSObject + +@property (nonatomic, copy, readonly) NSString *cacheDirectoryPath; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCacheDirectoryPath:(NSString *)path; + +///-------------------------------------- +/// @name Setting +///-------------------------------------- + +- (void)setObject:(NSString *)object forKey:(NSString *)key; +- (void)setObject:(NSString *)object forKeyedSubscript:(NSString *)key; + +///-------------------------------------- +/// @name Getting +///-------------------------------------- + +- (NSString *)objectForKey:(NSString *)key maxAge:(NSTimeInterval)age; + +///-------------------------------------- +/// @name Removing +///-------------------------------------- + +- (void)removeObjectForKey:(NSString *)key; +- (void)removeAllObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.m b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.m new file mode 100644 index 0000000..f2ee85f --- /dev/null +++ b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache.m @@ -0,0 +1,336 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFKeyValueCache_Private.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFConstants.h" +#import "PFFileManager.h" +#import "PFInternalUtils.h" +#import "PFLogging.h" + +static const NSUInteger PFKeyValueCacheDefaultDiskCacheSize = 10 << 20; +static const NSUInteger PFKeyValueCacheDefaultDiskCacheRecords = 1000; +static const NSUInteger PFKeyValueCacheDefaultMemoryCacheRecordSize = 1 << 20; +static const NSTimeInterval PFKeyValueCacheDiskCacheTimeResolution = 1; // HFS+ stores only second level accuracy. + +static NSString *const PFKeyValueCacheDiskCachePathKey = @"path"; + +@interface PFKeyValueCacheEntry () + +// We need to generate a setter that's atomic to safely clear the value. +@property (nullable, atomic, readwrite, copy) NSString *value; + +@end + +@implementation PFKeyValueCacheEntry + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithValue:(NSString *)value { + return [self initWithValue:value creationTime:[NSDate date]]; +} + +- (instancetype)initWithValue:(NSString *)value creationTime:(NSDate *)creationTime { + self = [super init]; + if (!self) return nil; + + _value = [value copy]; + _creationTime = creationTime; + + return self; +} + ++ (instancetype)cacheEntryWithValue:(NSString *)value { + return [[self alloc] initWithValue:value]; +} + ++ (instancetype)cacheEntryWithValue:(NSString *)value creationTime:(NSDate *)creationTime { + return [[self alloc] initWithValue:value creationTime:creationTime]; +} + +@end + +@implementation PFKeyValueCache { + NSURL *_cacheDirectoryURL; + dispatch_queue_t _diskCacheQueue; + + NSDate *_lastDiskCacheModDate; + NSUInteger _lastDiskCacheSize; + NSMutableArray *_lastDiskCacheAttributes; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCacheDirectoryPath:(NSString *)path { + return [self initWithCacheDirectoryURL:[NSURL fileURLWithPath:path] + fileManager:[NSFileManager defaultManager] + memoryCache:[[NSCache alloc] init]]; +} + +- (instancetype)initWithCacheDirectoryURL:(NSURL *)url + fileManager:(NSFileManager *)fileManager + memoryCache:(NSCache *)cache { + self = [super init]; + if (!self) return nil; + + _cacheDirectoryURL = url; + _fileManager = fileManager; + _memoryCache = cache; + + _diskCacheQueue = dispatch_queue_create("com.parse.keyvaluecache.disk", DISPATCH_QUEUE_SERIAL); + + _maxDiskCacheBytes = PFKeyValueCacheDefaultDiskCacheSize; + _maxDiskCacheRecords = PFKeyValueCacheDefaultDiskCacheRecords; + _maxMemoryCacheBytesPerRecord = PFKeyValueCacheDefaultMemoryCacheRecordSize; + + return self; +} + +///-------------------------------------- +#pragma mark - Property Accessors +///-------------------------------------- + +- (NSString *)cacheDirectoryPath { + [_fileManager createDirectoryAtURL:_cacheDirectoryURL withIntermediateDirectories:YES attributes:nil error:NULL]; + return _cacheDirectoryURL.path; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (void)setObject:(NSString *)object forKeyedSubscript:(NSString *)key { + [self setObject:object forKey:key]; +} + +- (void)setObject:(NSString *)value forKey:(NSString *)key { + NSUInteger keyBytes = [key maximumLengthOfBytesUsingEncoding:[key fastestEncoding]]; + NSUInteger valueBytes = [value maximumLengthOfBytesUsingEncoding:[value fastestEncoding]]; + + if ((keyBytes + valueBytes) < self.maxMemoryCacheBytesPerRecord) { + [self.memoryCache setObject:[PFKeyValueCacheEntry cacheEntryWithValue:value] forKey:key]; + } else { + [self.memoryCache removeObjectForKey:key]; + } + + dispatch_async(_diskCacheQueue, ^{ + [self _createDiskCacheEntry:value atURL:[self _cacheURLForKey:key]]; + [self _compactDiskCache]; + }); +} + +- (NSString *)objectForKey:(NSString *)key maxAge:(NSTimeInterval)maxAge { + NSURL *cacheURL = [self _cacheURLForKey:key]; + PFKeyValueCacheEntry *cacheEntry = [self.memoryCache objectForKey:key]; + + if (cacheEntry) { + if ([[NSDate date] timeIntervalSinceDate:cacheEntry.creationTime] > maxAge) { + // We know the cache to be too old in both copies. + // Save space, remove this key from disk, and it's value from the memory cache. + [self removeObjectForKey:key]; + return nil; + } + + dispatch_async(_diskCacheQueue, ^{ + [self _updateModificationDateAtURL:cacheURL]; + }); + + return cacheEntry.value; + } + + // Wait for all outstanding disk operations before continuing, as another thread could be in the process of + // Writing a value to disk right now. + __block NSString *value = nil; + dispatch_sync(_diskCacheQueue, ^{ + NSDate *modificationDate = [self _modificationDateOfCacheEntryAtURL:cacheURL]; + if ([[NSDate date] timeIntervalSinceDate:modificationDate] > maxAge) { + [self removeObjectForKey:key]; + return; + } + + // Cache misses here (e.g. creationDate and value are both nil) should still be put into the memory cache. + value = [self _diskCacheEntryForURL:cacheURL]; + [self.memoryCache setObject:[PFKeyValueCacheEntry cacheEntryWithValue:value creationTime:modificationDate] + forKey:key]; + }); + + return value; +} + +- (void)removeObjectForKey:(NSString *)key { + [self.memoryCache removeObjectForKey:key]; + + dispatch_async(_diskCacheQueue, ^{ + [self.fileManager removeItemAtURL:[self _cacheURLForKey:key] error:NULL]; + }); +} + +- (void)removeAllObjects { + [self.memoryCache removeAllObjects]; + + dispatch_sync(_diskCacheQueue, ^{ + // Directory will be automatically recreated the next time 'cacheDir' is accessed. + [self.fileManager removeItemAtURL:_cacheDirectoryURL error:NULL]; + }); +} + +- (void)waitForOutstandingOperations { + dispatch_sync(_diskCacheQueue, ^{ + // Wait, do nothing + }); +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (NSURL *)_cacheURLForKey:(NSString *)key { + return [_cacheDirectoryURL URLByAppendingPathComponent:key]; +} + +- (void)_updateModificationDateAtURL:(NSURL *)url { + [self.fileManager setAttributes:@{ NSFileModificationDate: [NSDate date] } ofItemAtPath:url.path error:NULL]; +} + +- (NSDate *)_modificationDateOfCacheEntryAtURL:(NSURL *)url { + return [self.fileManager attributesOfItemAtPath:url.path error:NULL][NSFileModificationDate]; +} + +///-------------------------------------- +#pragma mark - Disk Cache +///-------------------------------------- + +- (NSString *)_diskCacheEntryForURL:(NSURL *)url { + NSData *bytes = [self.fileManager contentsAtPath:[url path]]; + if (!bytes) { + return nil; + } + + [self _updateModificationDateAtURL:url]; + return [[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding]; +} + +- (void)_createDiskCacheEntry:(NSString *)value atURL:(NSURL *)url { + NSString *path = [url path]; + NSData *bytes = [value dataUsingEncoding:NSUTF8StringEncoding]; + NSDate *creationDate = [NSDate date]; + + BOOL isDirty = [self _isDiskCacheDirty]; + + [_fileManager createDirectoryAtURL:_cacheDirectoryURL withIntermediateDirectories:YES attributes:nil error:NULL]; + [self.fileManager createFileAtPath:path + contents:bytes + attributes:@{ NSFileCreationDate: creationDate, NSFileModificationDate: creationDate }]; + + if (!isDirty) { + _lastDiskCacheModDate = creationDate; + _lastDiskCacheSize += bytes.length; + + [self _addToDiskCacheDictionary:path + modificationDate:creationDate + size:bytes.length]; + } else { + [self _invalidateDiskCache]; + } +} + +- (BOOL)_isDiskCacheDirty { + if (!_lastDiskCacheModDate) { + return YES; + } + + NSDate *modificationDate = [self _modificationDateOfCacheEntryAtURL:_cacheDirectoryURL]; + NSTimeInterval knownInterval = [_lastDiskCacheModDate timeIntervalSinceReferenceDate]; + NSTimeInterval actualInterval = [modificationDate timeIntervalSinceReferenceDate]; + + // NOTE: Most file systems (HFS) can only store up to 1 second of precision, whereas NSDate is super high resolution + // Yes, this is actually really bad to have hard coded, as this does give some window where we can get unwanted + // entries in the cache. However, that chance of another process touching this directory is greatly outweighed by + // the performance gained by using this technique. Plus, in the case of concurrent modification, we will never over- + // agressively remove something from the cache, we just might go a little bit over our limit. + return (actualInterval - knownInterval) >= PFKeyValueCacheDiskCacheTimeResolution; +} + +- (void)_invalidateDiskCache { + _lastDiskCacheModDate = nil; + _lastDiskCacheSize = 0; + _lastDiskCacheAttributes = nil; +} + +- (void)_recreateDiskCache { + NSDictionary *cacheDirectoryAttributes = [self.fileManager attributesOfItemAtPath:_cacheDirectoryURL.path error:NULL]; + + _lastDiskCacheModDate = cacheDirectoryAttributes[NSFileModificationDate]; + _lastDiskCacheSize = 0; + _lastDiskCacheAttributes = [[NSMutableArray alloc] init]; + + NSDirectoryEnumerator *enumerator = [self.fileManager enumeratorAtPath:[_cacheDirectoryURL path]]; + NSString *path = nil; + + while ((path = [enumerator nextObject]) != nil) { + [enumerator skipDescendants]; + + NSDictionary *attributes = enumerator.fileAttributes; + NSUInteger size = [attributes[NSFileSize] unsignedIntegerValue]; + + _lastDiskCacheSize += size; + + // NOTE: Do not use -copy here, as fileAttributes are lazily-loaded, we would run into issues with a lot of + // syscalls all at once here. + [self _addToDiskCacheDictionary:path + modificationDate:attributes[NSFileModificationDate] + size:size]; + } +} + +- (void)_addToDiskCacheDictionary:(NSString *)path modificationDate:(NSDate *)modificationDate size:(NSUInteger)size { + NSDictionary *entry = @{ + PFKeyValueCacheDiskCachePathKey: path, + NSFileModificationDate: modificationDate, + NSFileSize: @(size) + }; + + NSInteger insertionIndex = [_lastDiskCacheAttributes indexOfObject:entry + inSortedRange:NSMakeRange(0, _lastDiskCacheAttributes.count) + options:NSBinarySearchingInsertionIndex + usingComparator:^NSComparisonResult(id obj1, id obj2) { + return [obj1[NSFileModificationDate] compare:obj2[NSFileModificationDate]]; + }]; + + [_lastDiskCacheAttributes insertObject:entry atIndex:insertionIndex]; +} + +- (void)_compactDiskCache { + if ([self _isDiskCacheDirty]) { + [self _recreateDiskCache]; + } + + while (_lastDiskCacheAttributes.count > _maxDiskCacheRecords || _lastDiskCacheSize > _maxDiskCacheBytes) { + NSDictionary *attributes = [_lastDiskCacheAttributes firstObject]; + NSString *toRemove = attributes[PFKeyValueCacheDiskCachePathKey]; + NSNumber *fileSize = attributes[NSFileSize]; + + [self.fileManager removeItemAtURL:[self _cacheURLForKey:toRemove] error:NULL]; + _lastDiskCacheSize -= [fileSize unsignedIntegerValue]; + + [_lastDiskCacheAttributes removeObjectAtIndex:0]; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache_Private.h b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache_Private.h new file mode 100644 index 0000000..6c45b6d --- /dev/null +++ b/Pods/Parse/Parse/Internal/KeyValueCache/PFKeyValueCache_Private.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFKeyValueCache.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFKeyValueCache () + +///-------------------------------------- +/// @name Properties +///-------------------------------------- + +@property (nullable, nonatomic, strong, readwrite) NSFileManager *fileManager; +@property (nullable, nonatomic, strong, readwrite) NSCache *memoryCache; + +@property (nonatomic, assign) NSUInteger maxDiskCacheBytes; +@property (nonatomic, assign) NSUInteger maxDiskCacheRecords; +@property (nonatomic, assign) NSUInteger maxMemoryCacheBytesPerRecord; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithCacheDirectoryURL:(nullable NSURL *)url + fileManager:(nullable NSFileManager *)fileManager + memoryCache:(nullable NSCache *)cache NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Waiting +///-------------------------------------- + +- (void)waitForOutstandingOperations; + +@end + +@interface PFKeyValueCacheEntry : NSObject + +///-------------------------------------- +/// @name Properties +///-------------------------------------- + +@property (atomic, copy, readonly) NSString *value; +@property (atomic, strong, readonly) NSDate *creationTime; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + ++ (instancetype)cacheEntryWithValue:(NSString *)value; ++ (instancetype)cacheEntryWithValue:(NSString *)value creationTime:(NSDate *)creationTime; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithValue:(NSString *)value; +- (instancetype)initWithValue:(NSString *)value + creationTime:(NSDate *)creationTime NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.h b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.h new file mode 100644 index 0000000..ff8bfa2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; +@class PFOfflineStore; +@class PFQueryState; +@class PFSQLiteDatabase; +@class PFUser; + +typedef BFTask PF_GENERIC(NSNumber *)* (^PFConstraintMatcherBlock)(PFObject *object, PFSQLiteDatabase *database); + +typedef NS_OPTIONS(uint8_t, PFOfflineQueryOption) { + PFOfflineQueryOptionOrder = 1 << 0, + PFOfflineQueryOptionLimit = 1 << 1, + PFOfflineQueryOptionSkip = 1 << 2, +}; + +@interface PFOfflineQueryLogic : NSObject + +/*! + Initialize an `PFOfflineQueryLogic` instance with `PFOfflineStore` instance. + `PFOfflineStore` is needed for subQuery, inQuery and fetch. + */ +- (instancetype)initWithOfflineStore:(PFOfflineStore *)offlineStore; + +/*! + @returns YES iff the object is visible based on its read ACL and the given user objectId. + */ ++ (BOOL)userHasReadAccess:(PFUser *)user ofObject:(PFObject *)object; + +/*! + @returns YES iff the object is visible based on its read ACL and the given user objectId. + */ ++ (BOOL)userHasWriteAccess:(PFUser *)user ofObject:(PFObject *)object; + +/*! + Returns a PFConstraintMatcherBlock that returns true iff the object matches the given + query's constraints. This takes in a PFSQLiteDatabase connection because SQLite is finicky + about nesting connections, so we want to reuse them whenever possible. + */ +- (PFConstraintMatcherBlock)createMatcherForQueryState:(PFQueryState *)queryState user:(PFUser *)user; + +/*! + Sort given array with given `PFQuery` constraint. + + @returns sorted result. + */ +- (NSArray *)resultsByApplyingOptions:(PFOfflineQueryOption)options + ofQueryState:(PFQueryState *)queryState + toResults:(NSArray *)results; + +/*! + Make sure all of the objects included by the given query get fetched. + */ +- (BFTask *)fetchIncludesAsyncForResults:(NSArray *)results + ofQueryState:(PFQueryState *)queryState + inDatabase:(PFSQLiteDatabase *)database; + +/*! + Make sure all of the objects included by the given query get fetched. + */ +- (BFTask *)fetchIncludesForObjectAsync:(PFObject *)object + queryState:(PFQueryState *)queryState + database:(PFSQLiteDatabase *)database; + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.m b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.m new file mode 100644 index 0000000..63168ce --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.m @@ -0,0 +1,918 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFOfflineQueryLogic.h" + +#import + +#import "PFACL.h" +#import "PFAssert.h" +#import "PFConstants.h" +#import "PFDateFormatter.h" +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFErrorUtilities.h" +#import "PFGeoPoint.h" +#import "PFOfflineStore.h" +#import "PFQueryPrivate.h" +#import "PFRelation.h" +#import "PFRelationPrivate.h" + +typedef BOOL (^PFComparatorDeciderBlock)(id value, id constraint); +typedef BOOL (^PFSubQueryMatcherBlock)(id object, NSArray *results); + +/*! + A query to be used in $inQuery, $notInQuery, $select and $dontSelect + */ +@interface PFSubQueryMatcher : NSObject + +@property (nonatomic, strong, readonly) PFQuery *subQuery; +@property (nonatomic, strong) BFTask *subQueryResults; +@property (nonatomic, strong, readonly) PFOfflineStore *offlineStore; + +@end + +@implementation PFSubQueryMatcher + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithSubQuery:(PFQuery *)query offlineStore:(PFOfflineStore *)offlineStore { + if ((self = [super init]) != nil) { + _subQuery = query; + _offlineStore = offlineStore; + } + + return self; +} + +///-------------------------------------- +#pragma mark - SubQuery Matcher Creator +///-------------------------------------- + +- (PFConstraintMatcherBlock)createMatcherWithSubQueryMatcherBlock:(PFSubQueryMatcherBlock)block user:(PFUser *)user { + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + if (self.subQueryResults == nil) { + self.subQueryResults = [self.offlineStore findAsyncForQueryState:self.subQuery.state + user:user + pin:nil + isCount:NO + database:database]; + } + return [self.subQueryResults continueWithSuccessBlock:^id(BFTask *task) { + return @(block(object, task.result)); + }]; + }; +} + +@end + +@interface PFOfflineQueryLogic () + +@property (nonatomic, weak) PFOfflineStore *offlineStore; + +@end + +@implementation PFOfflineQueryLogic + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithOfflineStore:(PFOfflineStore *)offlineStore { + if ((self = [super init]) != nil) { + _offlineStore = offlineStore; + } + return self; +} + +///-------------------------------------- +#pragma mark - Value Getter +///-------------------------------------- + +- (id)valueForContainer:(id)container + key:(NSString *)key { + return [self valueForContainer:container key:key depth:0]; +} + +- (id)valueForContainer:(id)container + key:(NSString *)key + depth:(int)depth { + if ([key rangeOfString:@"."].location != NSNotFound) { + NSArray *parts = [key componentsSeparatedByString:@"."]; + + NSString *firstKey = [parts firstObject]; + NSString *rest = nil; + if ([parts count] > 1) { + NSRange range = NSMakeRange(1, [parts count] - 1); + rest = [[parts subarrayWithRange:range] componentsJoinedByString:@"."]; + } + id value = [self valueForContainer:container key:firstKey depth:depth + 1]; + // Only NSDictionary can be dotted into for getting values, so we should reject + // anything like ParseObjects and arrays. + if (!(value == nil || [value isKindOfClass:[NSDictionary class]])) { + if (depth > 0) { + id restFormat = [[PFPointerObjectEncoder objectEncoder] encodeObject:value]; + if ([restFormat isKindOfClass:[NSDictionary class]]) { + return [self valueForContainer:restFormat key:rest depth:depth + 1]; + } + } + [NSException raise:NSInvalidArgumentException format:@"Key %@ is invalid", key]; + } + return [self valueForContainer:value key:rest depth:depth + 1]; + } + + if ([container isKindOfClass:[PFObject class]]) { + PFObject *object = (PFObject *)container; + + // The object needs to have been fetched already if we are going to sort by one of its field. + PFParameterAssert(object.isDataAvailable, @"Bad key %@", key); + + // Handle special keys for PFObject. + if ([key isEqualToString:@"objectId"]) { + return object.objectId; + } else if ([key isEqualToString:@"createdAt"] || [key isEqualToString:@"_created_at"]) { + return object.createdAt; + } else if ([key isEqualToString:@"updatedAt"] || [key isEqualToString:@"_updated_at"]) { + return object.updatedAt; + } else { + return object[key]; + } + } else if ([container isKindOfClass:[NSDictionary class]]) { + return ((NSDictionary *)container)[key]; + } else if (container == nil) { + return nil; + } else { + [NSException raise:NSInvalidArgumentException format:@"Bad key %@", key]; + // Shouldn't reach here. + return nil; + } +} + +///-------------------------------------- +#pragma mark - Matcher With Decider +///-------------------------------------- + +/*! + Returns YES if decider returns YES for any value in the given array. + */ ++ (BOOL)matchesArray:(NSArray *)array + constraint:(id)constraint + withDecider:(PFComparatorDeciderBlock)decider { + for (id value in array) { + if (decider(value, constraint)) { + return YES; + } + } + return NO; +} + +/*! + Returns YES if decider returns YES for any value in the given array. + */ ++ (BOOL)matchesValue:(id)value + constraint:(id)constraint + withDecider:(PFComparatorDeciderBlock)decider { + if ([value isKindOfClass:[NSArray class]]) { + return [self matchesArray:value constraint:constraint withDecider:decider]; + } else { + return decider(value, constraint); + } +} + +///-------------------------------------- +#pragma mark - Matcher +///-------------------------------------- + +/*! + Implements simple equality constraints. This emulates Mongo's behavior where "equals" can mean array containment. + */ ++ (BOOL)matchesValue:(id)value + equalTo:(id)constraint { + return [self matchesValue:value constraint:constraint withDecider:^BOOL (id value, id constraint) { + // Do custom matching for dates to make sure we have proper precision. + if ([value isKindOfClass:[NSDate class]] && + [constraint isKindOfClass:[NSDate class]]) { + PFDateFormatter *dateFormatter = [PFDateFormatter sharedFormatter]; + NSString *valueString = [dateFormatter preciseStringFromDate:value]; + NSString *constraintString = [dateFormatter preciseStringFromDate:constraint]; + return [valueString isEqual:constraintString]; + } + + if ([value isKindOfClass:[PFRelation class]]) { + return [value isEqual:constraint] || [value _hasKnownObject:constraint]; + } + + return [value isEqual:constraint]; + }]; +} + +/*! + Matches $ne constraints. + */ ++ (BOOL)matchesValue:(id)value + notEqualTo:(id)constraint { + return ![self matchesValue:value equalTo:constraint]; +} + +/*! + Matches $lt constraints. + */ ++ (BOOL)matchesValue:(id)value + lessThan:(id)constraint { + return [self matchesValue:value constraint:constraint withDecider:^BOOL (id value, id constraint) { + if (value == nil || value == [NSNull null]) { + return NO; + } + NSComparisonResult comparisonResult = [value compare:constraint]; + return comparisonResult == NSOrderedAscending; + }]; +} + +/*! + Matches $lte constraints. + */ ++ (BOOL)matchesValue:(id)value + lessThanOrEqualTo:(id)constraint { + return [self matchesValue:value constraint:constraint withDecider:^BOOL (id value, id constraint) { + if (value == nil || value == [NSNull null]) { + return NO; + } + NSComparisonResult comparisonResult = [value compare:constraint]; + return (comparisonResult == NSOrderedAscending) || (comparisonResult == NSOrderedSame); + }]; +} + +/*! + Matches $gt constraints. + */ ++ (BOOL)matchesValue:(id)value + greaterThan:(id)constraint { + return [self matchesValue:value constraint:constraint withDecider:^BOOL (id value, id constraint) { + if (value == nil || value == [NSNull null]) { + return NO; + } + NSComparisonResult comparisonResult = [value compare:constraint]; + return comparisonResult == NSOrderedDescending; + }]; +} + +/*! + Matches $gte constraints. + */ ++ (BOOL)matchesValue:(id)value +greaterThanOrEqualTo:(id)constraint { + return [self matchesValue:value constraint:constraint withDecider:^BOOL (id value, id constraint) { + if (value == nil || value == [NSNull null]) { + return NO; + } + NSComparisonResult comparisonResult = [value compare:constraint]; + return (comparisonResult == NSOrderedDescending) || (comparisonResult == NSOrderedSame); + }]; +} + +/*! + Matches $in constraints. + $in returns YES if the intersection of value and constraint is not an empty set. + */ ++ (BOOL)matchesValue:(id)value + containedIn:(id)constraint { + if (constraint == nil || constraint == [NSNull null]) { + return NO; + } + + PFParameterAssert([constraint isKindOfClass:[NSArray class]], @"Constraint type not supported for $in queries"); + + for (id requiredItem in (NSArray *)constraint) { + if ([self matchesValue:value equalTo:requiredItem]) { + return YES; + } + } + return NO; +} + +/*! + Matches $nin constraints. + */ ++ (BOOL)matchesValue:(id)value + notContainedIn:(id)constraint { + return ![self matchesValue:value containedIn:constraint]; +} + +/*! + Matches $all constraints. + */ ++ (BOOL)matchesValue:(id)value containsAllObjectsInArray:(id)constraints { + PFParameterAssert([constraints isKindOfClass:[NSArray class]], @"Constraint type not supported for $all queries"); + PFParameterAssert([value isKindOfClass:[NSArray class]], @"Value type not supported for $all queries"); + + for (id requiredItem in (NSArray *)constraints) { + if (![self matchesValue:value equalTo:requiredItem]) { + return NO; + } + } + return YES; +} + +/*! + Matches $regex constraints. + */ ++ (BOOL)matchesValue:(id)value + regex:(id)constraint + withOptions:(NSString *)options { + if (value == nil || value == [NSNull null]) { + return NO; + } + + if (options == nil) { + options = @""; + } + + PFParameterAssert([options rangeOfString:@"^[imxs]*$" options:NSRegularExpressionSearch].location != NSNotFound, + @"Invalid regex options %@", options); + + NSRegularExpressionOptions flags = 0; + if ([options rangeOfString:@"i"].location != NSNotFound) { + flags = flags | NSRegularExpressionCaseInsensitive; + } + if ([options rangeOfString:@"m"].location != NSNotFound) { + flags = flags | NSRegularExpressionAnchorsMatchLines; + } + if ([options rangeOfString:@"x"].location != NSNotFound) { + flags = flags | NSRegularExpressionAllowCommentsAndWhitespace; + } + if ([options rangeOfString:@"s"].location != NSNotFound) { + flags = flags | NSRegularExpressionDotMatchesLineSeparators; + } + + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:constraint + options:flags + error:&error]; + NSArray *matches = [regex matchesInString:value options:0 range:NSMakeRange(0, [value length])]; + return matches.count > 0; +} + +/*! + Matches $exists constraints. + */ ++ (BOOL)matchesValue:(id)value + exists:(id)constraint { + if (constraint != nil && [constraint boolValue]) { + return value != nil && value != [NSNull null]; + } + + return value == nil || value == [NSNull null]; +} + +/*! + Matches $nearSphere constraints. + */ ++ (BOOL)matchesValue:(id)value + nearSphere:(id)constraint + maxDistance:(NSNumber *)maxDistance { + if (value == nil || value == [NSNull null]) { + return NO; + } + if (maxDistance == nil) { + return YES; + } + PFGeoPoint *point1 = constraint; + PFGeoPoint *point2 = value; + return [point1 distanceInRadiansTo:point2] <= [maxDistance doubleValue]; +} + +/*! + Matches $within constraints. + */ ++ (BOOL)matchesValue:(id)value + within:(id)constraint { + NSDictionary *constraintDictionary = (NSDictionary *)constraint; + NSArray *box = constraintDictionary[PFQueryOptionKeyBox]; + PFGeoPoint *southWest = box[0]; + PFGeoPoint *northEast = box[1]; + PFGeoPoint *target = (PFGeoPoint *)value; + + PFParameterAssert(northEast.longitude >= southWest.longitude, + @"whereWithinGeoBox queries cannot cross the International Date Line."); + PFParameterAssert(northEast.latitude >= southWest.latitude, + @"The southwest corner of a geo box must be south of the northeast corner."); + PFParameterAssert((northEast.longitude - southWest.longitude) <= 180, + @"Geo box queries larger than 180 degrees in longitude are not supported." + @"Please check point order."); + + return (target.latitude >= southWest.latitude && + target.latitude <= northEast.latitude && + target.longitude >= southWest.longitude && + target.longitude <= northEast.longitude); +} + +/*! + Returns YES iff the given value matches the given operator and constraint. + Raise NSInvalidArgumentException if the operator is not one this function can handle + */ ++ (BOOL)matchesValue:(id)value + constraint:(id)constraint + operator:(NSString *)operator + allKeyConstraints:(NSDictionary *)allKeyConstraints { + if ([operator isEqualToString:PFQueryKeyNotEqualTo]) { + return [self matchesValue:value notEqualTo:constraint]; + } else if ([operator isEqualToString:PFQueryKeyLessThan]) { + return [self matchesValue:value lessThan:constraint]; + } else if ([operator isEqualToString:PFQueryKeyLessThanEqualTo]) { + return [self matchesValue:value lessThanOrEqualTo:constraint]; + } else if ([operator isEqualToString:PFQueryKeyGreaterThan]) { + return [self matchesValue:value greaterThan:constraint]; + } else if ([operator isEqualToString:PFQueryKeyGreaterThanOrEqualTo]) { + return [self matchesValue:value greaterThanOrEqualTo:constraint]; + } else if ([operator isEqualToString:PFQueryKeyContainedIn]) { + return [self matchesValue:value containedIn:constraint]; + } else if ([operator isEqualToString:PFQueryKeyNotContainedIn]) { + return [self matchesValue:value notContainedIn:constraint]; + } else if ([operator isEqualToString:PFQueryKeyContainsAll]) { + return [self matchesValue:value containsAllObjectsInArray:constraint]; + } else if ([operator isEqualToString:PFQueryKeyRegex]) { + return [self matchesValue:value regex:constraint withOptions:allKeyConstraints[PFQueryOptionKeyRegexOptions]]; + } else if ([operator isEqualToString:PFQueryOptionKeyRegexOptions]) { + // No need to do anything. This is handled by $regex. + return YES; + } else if ([operator isEqualToString:PFQueryKeyExists]) { + return [self matchesValue:value exists:constraint]; + } else if ([operator isEqualToString:PFQueryKeyNearSphere]) { + return [self matchesValue:value + nearSphere:constraint + maxDistance:allKeyConstraints[PFQueryOptionKeyMaxDistance]]; + } else if ([operator isEqualToString:PFQueryOptionKeyMaxDistance]) { + // No need to do anything. This is handled by $nearSphere. + return YES; + } else if ([operator isEqualToString:PFQueryKeyWithin]) { + return [self matchesValue:value within:constraint]; + } + + [NSException raise:NSInvalidArgumentException + format:@"The offline store does not yet support %@ operator.", operator]; + // Shouldn't reach here + return YES; +} + +/*! + Creates a matcher that handles $inQuery constraints. + */ +- (PFConstraintMatcherBlock)createMatcherForKey:(NSString *)key + inQuery:(id)constraints + user:(PFUser *)user { + PFQuery *query = (PFQuery *)constraints; + PFSubQueryMatcher *subQueryMatcher = [[PFSubQueryMatcher alloc] initWithSubQuery:query + offlineStore:self.offlineStore]; + return [subQueryMatcher createMatcherWithSubQueryMatcherBlock:^BOOL(id object, NSArray *results) { + id value = [self valueForContainer:object key:key]; + return [[self class] matchesValue:value containedIn:results]; + } user:user]; +} + +/*! + Creates a matcher that handles $notInQuery constraints. + */ +- (PFConstraintMatcherBlock)createMatcherForKey:(NSString *)key + notInQuery:(id)constraints + user:(PFUser *)user { + PFConstraintMatcherBlock inQueryMatcher = [self createMatcherForKey:key inQuery:constraints user:user]; + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + return [inQueryMatcher(object, database) continueWithSuccessBlock:^id(BFTask *task) { + return @(![task.result boolValue]); + }]; + }; +} + +/*! + Creates a matcher that handles $select constraints. + */ +- (PFConstraintMatcherBlock)createMatcherForKey:(NSString *)key + select:(id)constraints + user:(PFUser *)user { + NSDictionary *constraintDictionary = (NSDictionary *)constraints; + PFQuery *query = (PFQuery *)constraintDictionary[PFQueryKeyQuery]; + NSString *resultKey = (NSString *)constraintDictionary[PFQueryKeyKey]; + PFSubQueryMatcher *subQueryMatcher = [[PFSubQueryMatcher alloc] initWithSubQuery:query + offlineStore:self.offlineStore]; + return [subQueryMatcher createMatcherWithSubQueryMatcherBlock:^BOOL(id object, NSArray *results) { + id value = [self valueForContainer:object key:key]; + for (id result in results) { + id resultValue = [self valueForContainer:result key:resultKey]; + if ([[self class] matchesValue:resultValue equalTo:value]) { + return YES; + } + } + return NO; + } user:user]; +} + +/*! + Creates a matcher that handles $dontSelect constraints. + */ +- (PFConstraintMatcherBlock)createMatcherForKey:(NSString *)key + dontSelect:(id)constraints + user:(PFUser *)user { + PFConstraintMatcherBlock selectMatcher = [self createMatcherForKey:key select:constraints user:user]; + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + return [selectMatcher(object, database) continueWithSuccessBlock:^id(BFTask *task) { + return @(![task.result boolValue]); + }]; + }; +} + +/*! + Creates a matcher for a particular constraint operator. + */ +- (PFConstraintMatcherBlock)createMatcherWithOperator:(NSString *)operator + constraints:(id)constraint + key:(NSString *)key + allKeyConstraints:(NSDictionary *)allKeyConstraints + user:(PFUser *)user { + if ([operator isEqualToString:PFQueryKeyInQuery]) { + return [self createMatcherForKey:key inQuery:constraint user:user]; + } else if ([operator isEqualToString:PFQueryKeyNotInQuery]) { + return [self createMatcherForKey:key notInQuery:constraint user:user]; + } else if ([operator isEqualToString:PFQueryKeySelect]) { + return [self createMatcherForKey:key select:constraint user:user]; + } else if ([operator isEqualToString:PFQueryKeyDontSelect]) { + return [self createMatcherForKey:key dontSelect:constraint user:user]; + } else { + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + id value = [self valueForContainer:object key:key]; + BOOL matchesValue = [[self class] matchesValue:value + constraint:constraint + operator:operator + allKeyConstraints:allKeyConstraints]; + return [BFTask taskWithResult:@(matchesValue)]; + }; + } +} + +/*! + Handles $or queries. + */ +- (PFConstraintMatcherBlock)createOrMatcherForQueries:(NSArray *)queries user:(PFUser *)user { + NSMutableArray *matchers = [NSMutableArray array]; + for (PFQuery *query in queries) { + PFConstraintMatcherBlock matcher = [self createMatcherWithQueryConstraints:query.state.conditions user:user]; + [matchers addObject:matcher]; + } + + // Now OR together the constraints for each query. + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + BFTask *task = [BFTask taskWithResult:@NO]; + for (PFConstraintMatcherBlock matcher in matchers) { + task = [task continueWithSuccessBlock:^id(BFTask *task) { + if ([task.result boolValue]) { + return task; + } + return matcher(object, database); + }]; + } + return task; + }; +} + +/*! + Returns a PFConstraintMatcherBlock that return true iff the object matches queryConstraints. This + takes in a SQLiteDatabase connection because SQLite is finicky about nesting connections, so we + want to reuse them whenever possible. + */ +- (PFConstraintMatcherBlock)createMatcherWithQueryConstraints:(NSDictionary *)queryConstraints user:(PFUser *)user { + NSMutableArray *matchers = [[NSMutableArray alloc] init]; + [queryConstraints enumerateKeysAndObjectsUsingBlock:^(id key, id queryConstraintValue, BOOL *stop) { + if ([key isEqualToString:PFQueryKeyOr]) { + // A set of queries to be OR-ed together + PFConstraintMatcherBlock matcher = [self createOrMatcherForQueries:queryConstraintValue user:user]; + [matchers addObject:matcher]; + } else if ([key isEqualToString:PFQueryKeyRelatedTo]) { + PFConstraintMatcherBlock matcher = ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + PFObject *parent = queryConstraintValue[PFQueryKeyObject]; + NSString *relationKey = queryConstraintValue[PFQueryKeyKey]; + PFRelation *relation = parent[relationKey]; + + return [BFTask taskWithResult:@([relation _hasKnownObject:object])]; + }; + [matchers addObject:matcher]; + } else if ([queryConstraintValue isKindOfClass:[NSDictionary class]]) { + // If it's a set of constraints that should be AND-ed together + NSDictionary *keyConstraints = (NSDictionary *)queryConstraintValue; + [keyConstraints enumerateKeysAndObjectsUsingBlock:^(id operator, id keyConstraintValue, BOOL *stop) { + PFConstraintMatcherBlock matcher = [self createMatcherWithOperator:operator + constraints:keyConstraintValue + key:key + allKeyConstraints:keyConstraints + user:user]; + [matchers addObject:matcher]; + }]; + } else { + // It's not a set of constraints, so it's just a value to compare against. + PFConstraintMatcherBlock matcher = ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + id objectValue = [self valueForContainer:object key:key]; + BOOL matches = [[self class] matchesValue:objectValue equalTo:queryConstraintValue]; + return [BFTask taskWithResult:@(matches)]; + }; + [matchers addObject:matcher]; + } + }]; + + // Now AND together the constraints for each key + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + BFTask *task = [BFTask taskWithResult:@YES]; + for (PFConstraintMatcherBlock matcher in matchers) { + task = [task continueWithSuccessBlock:^id(BFTask *task) { + if (![task.result boolValue]) { + return task; + } + @try { + return matcher(object, database); + } @catch (NSException *exception) { + // Promote to error to keep the same behavior as online. + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorInvalidQuery + message:exception.reason + shouldLog:NO]; + return [BFTask taskWithError:error]; + } + }]; + } + return task; + }; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BFTask *)fetchIncludeAsync:(NSString *)include + container:(id)container + database:(PFSQLiteDatabase *)database { + if (container == nil) { + return [BFTask taskWithResult:nil]; + } + + if ([container isKindOfClass:[NSArray class]]) { + NSArray *array = (NSArray *)container; + // We do the fetches in series because it makes it easier to fail on the first error. + BFTask *task = [BFTask taskWithResult:nil]; + for (id item in array) { + task = [task continueWithSuccessBlock:^id(BFTask *task) { + return [self fetchIncludeAsync:include container:item database:database]; + }]; + } + return task; + } + + // If we've reached the end of include, then actually do the fetch. + if (include == nil) { + if ([container isKindOfClass:[PFObject class]]) { + PFObject *object = (PFObject *)container; + return [self.offlineStore fetchObjectLocallyAsync:object database:database]; + } else if (container == [NSNull null]) { + // Accept NSNull value in included field. We swallow it silently instead of + // throwing an exception. + return nil; + } + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorInvalidNestedKey + message:@"include is invalid for non-ParseObjects"]; + return [BFTask taskWithError:error]; + } + + // Descend into the container and try again + NSArray *parts = [include componentsSeparatedByString:@"."]; + + NSString *key = [parts firstObject]; + NSString *rest = nil; + if ([parts count] > 1) { + NSRange range = NSMakeRange(1, [parts count] - 1); + rest = [[parts subarrayWithRange:range] componentsJoinedByString:@"."]; + } + + return [[[BFTask taskWithResult:nil] continueWithBlock:^id(BFTask *task) { + if ([container isKindOfClass:[PFObject class]]) { + BFTask *fetchTask = [self fetchIncludeAsync:nil container:container database:database]; + return [fetchTask continueWithSuccessBlock:^id(BFTask *task) { + return ((PFObject *)container)[key]; + }]; + } else if ([container isKindOfClass:[NSDictionary class]]) { + return ((NSDictionary *)container)[key]; + } else if (container == [NSNull null]) { + // Accept NSNull value in included field. We swallow it silently instead of + // throwing an exception. + return nil; + } + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"include is invalid" + userInfo:nil]; + return [BFTask taskWithException:exception]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [self fetchIncludeAsync:rest container:task.result database:database]; + }]; +} + +///-------------------------------------- +#pragma mark - User Access +///-------------------------------------- + ++ (BOOL)userHasReadAccess:(PFUser *)user ofObject:(PFObject *)object { + if (user == object) { + return YES; + } + + PFACL *acl = [object ACL]; + if (acl == nil) { + return YES; + } + if ([acl getPublicReadAccess]) { + return YES; + } + if (user != nil && [acl getReadAccessForUser:user]) { + return YES; + } + + // TODO (hallucinogen): Implement roles + return NO; +} + ++ (BOOL)userHasWriteAccess:(PFUser *)user ofObject:(PFObject *)object { + if (user == object) { + return YES; + } + + PFACL *acl = [object ACL]; + if (acl == nil) { + return YES; + } + if ([acl getPublicWriteAccess]) { + return YES; + } + if (user != nil && [acl getWriteAccessForUser:user]) { + return YES; + } + + // TODO (hallucinogen): Implement roles + return NO; +} + +///-------------------------------------- +#pragma mark - Internal Public Methods +///-------------------------------------- + +- (PFConstraintMatcherBlock)createMatcherForQueryState:(PFQueryState *)queryState user:(PFUser *)user { + PFConstraintMatcherBlock constraintMatcher = [self createMatcherWithQueryConstraints:queryState.conditions + user:user]; + // Capture ignoreACLs before the block since it might be modified between matchings. + BOOL shouldIgnoreACLs = queryState.shouldIgnoreACLs; + + return ^BFTask *(PFObject *object, PFSQLiteDatabase *database) { + // TODO (hallucinogen): revisit this whether we should check query and object parseClassname equality + if (!shouldIgnoreACLs && ![[self class] userHasReadAccess:user ofObject:object]) { + return [BFTask taskWithResult:@NO]; + } + return constraintMatcher(object, database); + }; +} + +///-------------------------------------- +#pragma mark - Query Options +///-------------------------------------- + +- (NSArray *)resultsByApplyingOptions:(PFOfflineQueryOption)options + ofQueryState:(PFQueryState *)queryState + toResults:(NSArray *)results { + // No results or empty options. + if (results.count == 0 || options == 0) { + return results; + } + + NSMutableArray *mutableResults = [results mutableCopy]; + if (options & PFOfflineQueryOptionOrder) { + [self _sortResults:mutableResults ofQueryState:queryState]; + } + if (options & PFOfflineQueryOptionSkip) { + NSInteger skip = queryState.skip; + if (skip > 0) { + skip = MIN(skip, results.count); + [mutableResults removeObjectsInRange:NSMakeRange(0, skip)]; + } + } + if (options & PFOfflineQueryOptionLimit) { + NSInteger limit = queryState.limit; + if (limit >= 0 && mutableResults.count > limit) { + [mutableResults removeObjectsInRange:NSMakeRange(limit, mutableResults.count - limit)]; + } + } + + return [mutableResults copy]; +} + +- (void)_sortResults:(NSMutableArray *)results ofQueryState:(PFQueryState *)queryState { + NSArray *keys = queryState.sortKeys; + [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + NSString *key = (NSString *)obj; + if ([key rangeOfString:@"^-?[A-Za-z][A-Za-z0-9_]*$" options:NSRegularExpressionSearch].location == NSNotFound) { + PFConsistencyAssert([@"_created_at" isEqualToString:key] || [@"_updated_at" isEqualToString:key], + @"Invalid key name: %@", key); + } + }]; + + __block NSString *nearSphereKey = nil; + __block PFGeoPoint *nearSphereValue = nil; + [queryState.conditions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([obj isKindOfClass:[NSDictionary class]]) { + NSDictionary *keyConstraints = (NSDictionary *)obj; + if (keyConstraints[PFQueryKeyNearSphere]) { + nearSphereKey = [key copy]; + nearSphereValue = keyConstraints[PFQueryKeyNearSphere]; + } + } + }]; + + // If there's nothing to sort based on, then don't do anything. + if (keys.count == 0 && nearSphereKey == nil) { + return; + } + + [results sortUsingComparator:^NSComparisonResult(id lhs, id rhs) { + if (nearSphereKey != nil) { + PFGeoPoint *lhsPoint = [self valueForContainer:lhs key:nearSphereKey]; + PFGeoPoint *rhsPoint = [self valueForContainer:rhs key:nearSphereKey]; + + double lhsDistance = [lhsPoint distanceInRadiansTo:nearSphereValue]; + double rhsDistance = [rhsPoint distanceInRadiansTo:nearSphereValue]; + if (lhsDistance != rhsDistance) { + return (lhsDistance - rhsDistance < 0) ? NSOrderedAscending : NSOrderedDescending; + } + } + + for (int i = 0; i < keys.count; ++i) { + NSString *key = keys[i]; + BOOL descending = NO; + if ([key hasPrefix:@"-"]) { + descending = YES; + key = [key substringFromIndex:1]; + } + + id lhsValue = [self valueForContainer:lhs key:key]; + id rhsValue = [self valueForContainer:rhs key:key]; + + NSComparisonResult result = NSOrderedSame; + if (lhsValue != nil && rhsValue == nil) { + result = NSOrderedAscending; + } else if (lhsValue == nil && rhsValue != nil) { + result = NSOrderedDescending; + } else if (lhsValue == nil && rhsValue == nil) { + result = NSOrderedSame; + } else { + result = [lhsValue compare:rhsValue]; + } + + if (result != 0) { + return descending ? -result : result; + } + + } + + return NSOrderedSame; + }]; +} + +- (BFTask *)fetchIncludesAsyncForResults:(NSArray *)results + ofQueryState:(PFQueryState *)queryState + inDatabase:(PFSQLiteDatabase *)database { + BFTask *fetchTask = [BFTask taskWithResult:nil]; + for (PFObject *object in results) { + @weakify(self); + fetchTask = [fetchTask continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + return [self fetchIncludesForObjectAsync:object + queryState:queryState + database:database]; + }]; + } + return fetchTask; +} + +- (BFTask *)fetchIncludesForObjectAsync:(PFObject *)object + queryState:(PFQueryState *)queryState + database:(PFSQLiteDatabase *)database { + NSSet *includes = queryState.includedKeys; + // We do the fetches in series because it makes it easier to fail on first error. + BFTask *task = [BFTask taskWithResult:nil]; + for (NSString *include in includes) { + // We do the fetches in series because it makes it easier to fail on the first error. + task = [task continueWithSuccessBlock:^id(BFTask *task) { + return [self fetchIncludeAsync:include container:object database:database]; + }]; + } + return task; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.h b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.h new file mode 100644 index 0000000..b8f1c69 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.h @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFFileManager; +@class PFObject; +@class PFPin; +@class PFQueryState; +@class PFSQLiteDatabase; +@class PFUser; + +typedef NS_OPTIONS(uint8_t, PFOfflineStoreOptions) +{ + PFOfflineStoreOptionAlwaysFetchFromSQLite = 1 << 0, +}; + +//TODO: (nlutsenko) Bring this header up to standard with @name, method comments, etc... +@interface PFOfflineStore : NSObject + +@property (nonatomic, assign, readonly) PFOfflineStoreOptions options; +@property (nonatomic, strong, readonly) PFFileManager *fileManager; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(PFFileManager *)fileManager + options:(PFOfflineStoreOptions)options NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Fetch +///-------------------------------------- + +- (BFTask *)fetchObjectLocallyAsync:(PFObject *)object; + +/*! + Gets the data for the given object from the offline database. Returns a task that will be + completed if data for the object was available. If the object is not in the cache, the task + will be faulted, with a CACHE_MISS error. + + @param object The object to fetch. + @param database A database connection to use. + */ +- (BFTask *)fetchObjectLocallyAsync:(PFObject *)object database:(PFSQLiteDatabase *)database; + +///-------------------------------------- +/// @name Save +///-------------------------------------- + +//TODO: (nlutsenko) Remove `includChildren` method, replace with PFLocalStore that wraps OfflineStore + Pin. +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object includeChildren:(BOOL)includeChildren; +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object withChildren:(NSArray *)children; + +/*! + Stores an object (and optionally, every object it points to recursively) in the local database. + If any of the objects have not been fetched from Parse, they will not be stored. However, if + they have changed data, the data will be retained. To get the objects back later, you can use a + ParseQuery with a cache policy that uses the local cache, or you can create an unfetched + pointer with ParseObject.createWithoutData() and then call fetchFromLocalDatastore() on it. + If you modify the object after saving it locally, such as by fetching it or saving it, + those changes will automatically be applied to the cache. + + @param object The root of the objects to save. + @param children If non-empty - these children will be saved to LDS as well. + @param database A database connection to use. + */ +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object + withChildren:(NSArray *)children + database:(PFSQLiteDatabase *)database; + +///-------------------------------------- +/// @name Find +///-------------------------------------- + +/*! + Runs a PFQueryState against the store's contents. + + @returns The objects that match the query's constraint. + */ +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin; + +/*! + Runs a PFQueryState against the store's contents. + + @returns The count of objects that match the query's constraint. + */ +- (BFTask *)countAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin; + +/*! + Runs a PFQueryState against the store's contents. + + @returns The objects that match the query's constraint. + */ +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin + isCount:(BOOL)isCount; + +/*! + Runs a PFQueryState against the store's contents. May cause any instances of the object to get fetched from + offline database. (TODO (hallucinogen): should we consider objects in memory but not in Offline Store?) + + @param queryState The query. + @param user The user making the query. + @param pin (Optional) The pin we're querying across. If null, all pins. + @param isCount YES if we're doing count. + @param database The PFSQLiteDatabase + + @returns The objects that match the query's constraint. + */ +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin + isCount:(BOOL)isCount + database:(PFSQLiteDatabase *)database; + +///-------------------------------------- +/// @name Update Internal State +///-------------------------------------- + +/*! + Takes an object that has been fetched from the database before and updates it with whatever + data is in memory. This will only be used when data comes back from the server after a fetch + or a save. + */ +- (BFTask *)updateDataForObjectAsync:(PFObject *)object; + +///-------------------------------------- +/// @name Delete +///-------------------------------------- + +/*! + Deletes the given object from Offline Store's pins + */ +- (BFTask *)deleteDataForObjectAsync:(PFObject *)object; + +///-------------------------------------- +/// @name Unpin +///-------------------------------------- + +- (BFTask *)unpinObjectAsync:(PFObject *)object; + +///-------------------------------------- +/// @name Internal Helper Methods +///-------------------------------------- + +/*! + Gets the UUID for the given object, if it has one. Otherwise, creates a new UUID for the object + and adds a new row to the database for the object with no data. + */ +- (BFTask *)getOrCreateUUIDAsyncForObject:(PFObject *)object + database:(PFSQLiteDatabase *)database; + +/*! + This should only be called from `PFObject.objectWithoutDataWithClassName`. + + @returns an object from OfflineStore cache. If nil is returned the object is not found in the cache. + */ +- (PFObject *)getOrCreateObjectWithoutDataWithClassName:(NSString *)className + objectId:(NSString *)objectId; + +/*! + When an object is finished saving, it gets an objectId. Then it should call this method to + clean up the bookeeping around ids. + */ +- (void)updateObjectIdForObject:(PFObject *)object + oldObjectId:(NSString *)oldObjectId + newObjectId:(NSString *)newObjectId; + +///-------------------------------------- +/// @name Unit Test Helper Methods +///-------------------------------------- + +/*! + Used in unit testing only. Clears all in-memory caches so that data must be retrieved from disk. + */ +- (void)simulateReboot; + +/*! + Used in unit testing only. Clears the database on disk. + */ +- (void)clearDatabase; + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m new file mode 100644 index 0000000..d90e373 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m @@ -0,0 +1,1067 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFOfflineStore.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFErrorUtilities.h" +#import "PFFileManager.h" +#import "PFJSONSerialization.h" +#import "PFObjectPrivate.h" +#import "PFOfflineQueryLogic.h" +#import "PFPin.h" +#import "PFQueryPrivate.h" +#import "PFSQLiteDatabase.h" +#import "PFSQLiteDatabaseController.h" +#import "PFSQLiteDatabaseResult.h" +#import "PFUser.h" +#import "PFWeakValue.h" +#import "Parse_Private.h" + +typedef BFTask *(^PFOfflineStoreDatabaseExecutionBlock)(PFSQLiteDatabase *database); + +NSString *const PFOfflineStoreDatabaseName = @"ParseOfflineStore"; + +NSString *const PFOfflineStoreTableOfObjects = @"ParseObjects"; +NSString *const PFOfflineStoreKeyOfClassName = @"className"; +NSString *const PFOfflineStoreKeyOfIsDeletingEventually = @"isDeletingEventually"; +NSString *const PFOfflineStoreKeyOfJSON = @"json"; +NSString *const PFOfflineStoreKeyOfObjectId = @"objectId"; +NSString *const PFOfflineStoreKeyOfUUID = @"uuid"; + +NSString *const PFOfflineStoreTableOfDependencies = @"Dependencies"; +NSString *const PFOfflineStoreKeyOfKey = @"key"; + +int const PFOfflineStoreMaximumSQLVariablesCount = 999; + +@interface PFOfflineStore () + +@property (nonatomic, assign, readwrite) PFOfflineStoreOptions options; + +@property (nonatomic, strong, readonly) NSObject *lock; + +/*! + In-memory map of (className, objectId) to ParseObject. This is used so that we can + always return the same instance for a given object. Objects in this map may or may + not be in the database. + */ +@property (nonatomic, strong, readonly) NSMapTable *classNameAndObjectIdToObjectMap; + +/*! + In-memory set of ParseObjects that have been fetched from local database already. + If the object is in the map, a fetch of it has been started. If the value is a + finished task, then the fetch was completed. + */ +@property (nonatomic, strong, readonly) NSMapTable *fetchedObjects; + +/*! + In-memory map of ParseObject to UUID. This is used so that we can always return + the same instance for a given object. Objects in this map may or may not be in the + database. + */ +@property (nonatomic, strong, readonly) NSMapTable *objectToUUIDMap; + +/*! + In-memory map of UUID to ParseObject. This is used so we can always return + the same instance for a given object. The only objects in this map are ones that + are in database. + */ +@property (nonatomic, strong, readonly) NSMapTable *UUIDToObjectMap; + +@property (nonatomic, strong, readonly) PFOfflineQueryLogic *offlineQueryLogic; + +@property (nonatomic, strong, readonly) PFSQLiteDatabaseController *databaseController; + +@end + +@implementation PFOfflineStore + +@synthesize offlineQueryLogic = _offlineQueryLogic; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithFileManager:(PFFileManager *)fileManager options:(PFOfflineStoreOptions)options { + self = [super init]; + if (!self) return nil; + + _options = options; + _fileManager = fileManager; + _databaseController = [PFSQLiteDatabaseController controllerWithFileManager:_fileManager]; + _lock = [[NSObject alloc] init]; + _classNameAndObjectIdToObjectMap = [NSMapTable strongToWeakObjectsMapTable]; + _fetchedObjects = [NSMapTable weakToStrongObjectsMapTable]; + // This is a bit different from what we have in Android. The reason is because the object is quickly + // retained by the OS and we depend on this MapTable to fetch the `uuidTask` of the object. + _objectToUUIDMap = [NSMapTable weakToStrongObjectsMapTable]; + _UUIDToObjectMap = [NSMapTable strongToWeakObjectsMapTable]; + + [[self class] _initializeTablesInBackgroundWithDatabaseController:_databaseController]; + + return self; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BFTask *)fetchObjectLocallyAsync:(PFObject *)object { + __block BFTask *fetchTask = nil; + return [[self _performDatabaseOperationAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + // We need this to return the result of `fetchObjectLocallyAsync` instead of returning the + // result of `[database closeAsync]` + fetchTask = [self fetchObjectLocallyAsync:object database:database]; + return fetchTask; + }] continueWithBlock:^id(BFTask *task) { + return fetchTask; + }]; +} + +- (BFTask *)fetchObjectLocallyAsync:(PFObject *)object database:(PFSQLiteDatabase *)database { + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + BFTask *uuidTask = nil; + + @synchronized (self.lock) { + BFTask *fetchTask = [self.fetchedObjects objectForKey:object]; + if (fetchTask && !(self.options & PFOfflineStoreOptionAlwaysFetchFromSQLite)) { + // The object has been fetched from offline store, so any data that's in there + // is already reflected in the in-memory version. There's nothing more to do. + return [fetchTask continueWithBlock:^id(BFTask *task) { + return [BFTask taskWithResult:[task.result weakObject]]; + }]; + } + + // Put a placeholder so that anyone else who attempts to fetch this object will just + // wait for this call to finish doing it. + [self.fetchedObjects setObject:[tcs.task continueWithBlock:^id(BFTask *task) { + return [BFTask taskWithResult:[PFWeakValue valueWithWeakObject:task.result]]; + }] forKey:object]; + uuidTask = [self.objectToUUIDMap objectForKey:object]; + } + NSString *className = [object parseClassName]; + NSString *objectId = [object objectId]; + + // If this gets set, then it will contain data from offline store that need to be merged + // into existing object in memory + BFTask *jsonStringTask = [BFTask taskWithResult:nil]; + __block NSString *uuid = nil; + + if (objectId == nil) { + // This object has never been saved to Parse + if (uuidTask == nil) { + // This object was not pulled from the datastore or previously saved to it. + // There's nothing that can be fetched from it. This isn't an error, because it's + // really convenient to try to fetch objects from offline store just to make sure + // they're up-to-date, and we shouldn't force developers to specially handle this case. + } else { + // This object is a new ParseObject that is known to the datastore, but hasn't been + // fetched. The only way this could happen is if the object had previously been stored + // in the offline store, then the object was removed from memory (maybe by rebooting), + // and then an object with a pointer to it was fetched, so we only created the pointer. + // We need to pull the data out of the database using UUID. + + jsonStringTask = [[uuidTask continueWithSuccessBlock:^id(BFTask *task) { + uuid = task.result; + NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@ = ?;", + PFOfflineStoreKeyOfJSON, PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID]; + return [database executeQueryAsync:query + withArgumentsInArray:[NSArray arrayWithObjects:uuid, nil]]; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFSQLiteDatabaseResult *result = task.result; + if (![result next]) { + [result close]; + [NSException raise:NSInternalInconsistencyException + format:@"Attempted to find non-existent uuid %@.", uuid]; + } + NSString *jsonString = [result stringForColumnIndex:0]; + [result close]; + + return jsonString; + }]; + } + } else { + if (uuidTask && !(self.options & PFOfflineStoreOptionAlwaysFetchFromSQLite)) { + // This object is an existing ParseObject, and we must've already pulled its data + // out of the offline store, or else we wouldn't know its UUID. This should never happen. + NSString *message = @"Object must have already been fetched but isn't marked as fetched."; + [tcs setException:[NSException exceptionWithName:NSInternalInconsistencyException + reason:message + userInfo:nil]]; + + @synchronized (self.lock) { + [self.fetchedObjects removeObjectForKey:object]; + } + return tcs.task; + } + + // We've got a pointer to an existing ParseObject, but we've never pulled its data out of + // the offline store. Since fetching from the server forces a fetch from the offline + // store, that means this is a pointer. We need to try to find any existing entry for this + // object in the database. + NSString *query = [NSString stringWithFormat:@"SELECT %@, %@ FROM %@ WHERE %@ = ? AND %@ = ?;", + PFOfflineStoreKeyOfJSON, PFOfflineStoreKeyOfUUID, + PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfClassName, + PFOfflineStoreKeyOfObjectId]; + NSArray *args = @[ className, objectId ]; + jsonStringTask = [[database executeQueryAsync:query + withArgumentsInArray:args] continueWithSuccessBlock:^id(BFTask *task) { + PFSQLiteDatabaseResult *result = task.result; + if (![result next]) { + NSString *errorMessage = @"This object is not available in the offline cache."; + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCacheMiss + message:errorMessage + shouldLog:NO]; + [result close]; + return [BFTask taskWithError:error]; + } + + NSString *jsonString = [result stringForColumnIndex:0]; + NSString *newUUID = [result stringForColumnIndex:1]; + [result close]; + + @synchronized (self.lock) { + // It's okay to put this object into the uuid map. No one will try to fetch it, + // because it's already in the fetchedObjects map. And no one will try to save it + // without fetching it first, so everything should be fine. + [self.objectToUUIDMap setObject:[BFTask taskWithResult:newUUID] forKey:object]; + [self.UUIDToObjectMap setObject:object forKey:newUUID]; + } + return jsonString; + }]; + } + + return [[jsonStringTask continueWithSuccessBlock:^id(BFTask *task) { + NSString *jsonString = task.result; + if (jsonString == nil) { + // This means we tried to fetch from the database that was never actually saved + // locally. This probably means that its parent object was saved locally and we + // just created a pointer to this object. This should be considered cache miss. + + NSString *errorMessage = @"Attempted to fetch and object offline which was never " + @"saved to the offline cache"; + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCacheMiss + message:errorMessage + shouldLog:NO]; + return [BFTask taskWithError:error]; + } + id parsedJson = [PFJSONSerialization JSONObjectFromString:jsonString]; + NSMutableDictionary *offlineObjects = [[NSMutableDictionary alloc] init]; + [PFInternalUtils traverseObject:parsedJson usingBlock:^id(id object) { + // Omit root and PFObject + if ([object isKindOfClass:[NSDictionary class]] && + [((NSDictionary *)object)[@"__type"] isEqualToString:@"OfflineObject"] && + object != parsedJson) { + NSString *uuid = ((NSDictionary *)object)[@"uuid"]; + offlineObjects[uuid] = [self _getPointerAsyncWithUUID:uuid database:database]; + } + return object; + }]; + + NSArray *objectValues = [offlineObjects allValues]; + return [[BFTask taskForCompletionOfAllTasks:objectValues] continueWithSuccessBlock:^id(BFTask *task) { + PFDecoder *decoder = [PFOfflineDecoder decoderWithOfflineObjects:offlineObjects]; + [object mergeFromRESTDictionary:parsedJson withDecoder:decoder]; + return [BFTask taskWithResult:nil]; + }]; + }] continueWithBlock:^id(BFTask *task) { + if (task.isCancelled) { + [tcs cancel]; + } else if (task.error != nil) { + [tcs setError:task.error]; + } else if (task.exception != nil) { + [tcs setException:task.exception]; + } else { + [tcs setResult:object]; + } + return tcs.task; + }]; +} + +///-------------------------------------- +#pragma mark - Save +///-------------------------------------- + +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object includeChildren:(BOOL)includeChildren { + //TODO: (nlutsenko) Remove this method, replace with LocalStore implementation that wraps OfflineStore + Pin. + return [self _performDatabaseTransactionAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + return [self saveObjectLocallyAsync:object includeChildren:includeChildren database:database]; + }]; +} + +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object withChildren:(NSArray *)children { + return [self _performDatabaseTransactionAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + return [self saveObjectLocallyAsync:object withChildren:children database:database]; + }]; +} + +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object + includeChildren:(BOOL)includeChildren + database:(PFSQLiteDatabase *)database { + //TODO: (nlutsenko) Remove this method, replace with LocalStore implementation that wraps OfflineStore + Pin. + NSMutableArray *children = nil; + if (includeChildren) { + children = [NSMutableArray array]; + [PFInternalUtils traverseObject:object usingBlock:^id(id traversedObject) { + if ([traversedObject isKindOfClass:[PFObject class]]) { + [children addObject:traversedObject]; + } + return traversedObject; + }]; + } + return [self saveObjectLocallyAsync:object withChildren:children database:database]; +} + +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object + withChildren:(NSArray *)children + database:(PFSQLiteDatabase *)database { + //TODO (nlutsenko): Add assert that checks whether all children are actually children of an object. + NSMutableArray *objectsInTree = nil; + if (children == nil) { + objectsInTree = [NSMutableArray arrayWithObject:object]; + } else { + objectsInTree = [children mutableCopy]; + if (![objectsInTree containsObject:object]) { + [objectsInTree addObject:object]; + } + } + + // Call saveObjectLocallyAsync for each of them individually + NSMutableArray *tasks = [[NSMutableArray alloc] init]; + for (PFObject *objInTree in objectsInTree) { + [tasks addObject:[self fetchObjectLocallyAsync:objInTree database:database]]; + } + + return [[[[[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task) { + return [self.objectToUUIDMap objectForKey:object]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSString *uuid = task.result; + if (uuid == nil) { + // The root object was never stored in offline store, so nothing unpin. + return [BFTask taskWithResult:nil]; + } + + // Delete all objects locally corresponding to the key we're trying to use in case it was + // used before (overwrite) + return [self _unpinKeyAsync:uuid database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [self getOrCreateUUIDAsyncForObject:object database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSString *uuid = task.result; + + NSMutableArray *tasks = [[NSMutableArray alloc] init]; + for (PFObject *object in objectsInTree) { + [tasks addObject:[self saveObjectLocallyAsync:object key:uuid database:database]]; + } + + return [BFTask taskForCompletionOfAllTasks:tasks]; + }]; +} + +- (BFTask *)saveObjectLocallyAsync:(PFObject *)object + key:(NSString *)key + database:(PFSQLiteDatabase *)database { + if ([object objectId] != nil && ![object isDataAvailable] && + ![object _hasChanges] && ![object _hasOutstandingOperations]) { + return [BFTask taskWithResult:nil]; + } + + __block NSString *uuid = nil; + __block id encoded = nil; + return [[[[BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + // Make sure we have UUID for the object to be saved. + return [self getOrCreateUUIDAsyncForObject:object database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + uuid = task.result; + + // Encode the object, and wait for the UUIDs in its pointers to get encoded. + PFOfflineObjectEncoder *encoder = [PFOfflineObjectEncoder objectEncoderWithOfflineStore:self database:database]; + // We don't care about operationSetUUIDs here + NSArray *operationSetUUIDs = nil; + encoded = [object RESTDictionaryWithObjectEncoder:encoder operationSetUUIDs:&operationSetUUIDs]; + return [encoder encodeFinished]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // Time to actually save the object + NSString *className = [object parseClassName]; + NSString *objectId = [object objectId]; + NSString *encodedString = [PFJSONSerialization stringFromJSONObject:encoded]; + NSString *updateFields = nil; + NSArray *queryParams = nil; + + if (objectId != nil) { + updateFields = [NSString stringWithFormat:@"%@ = ?, %@ = ?, %@ = ?", + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfJSON, + PFOfflineStoreKeyOfObjectId]; + queryParams = @[className, encodedString, objectId, uuid]; + } else { + updateFields = [NSString stringWithFormat:@"%@ = ?, %@ = ?", + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfJSON]; + queryParams = @[className, encodedString, uuid]; + } + + NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?", + PFOfflineStoreTableOfObjects, updateFields, + PFOfflineStoreKeyOfUUID]; + return [database executeSQLAsync:sql withArgumentsInArray:queryParams]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSString *sql = [NSString stringWithFormat:@"INSERT OR IGNORE INTO %@(%@, %@) VALUES (?, ?)", + PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfKey, + PFOfflineStoreKeyOfUUID]; + return [database executeSQLAsync:sql withArgumentsInArray:@[key, uuid]]; + }]; +} + +///-------------------------------------- +#pragma mark - Find +///-------------------------------------- + +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin { + return [self findAsyncForQueryState:queryState user:user pin:pin isCount:NO]; +} + +- (BFTask *)countAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin { + return [[self findAsyncForQueryState:queryState + user:user + pin:pin + isCount:YES] continueWithSuccessBlock:^id(BFTask *task) { + if (!task.cancelled && !task.error && !task.exception) { + NSArray *result = task.result; + return @(result.count); + } + return task; + }]; +} + +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin + isCount:(BOOL)isCount { + __block BFTask *resultTask = nil; + return [[self _performDatabaseOperationAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + resultTask = [self findAsyncForQueryState:queryState user:user pin:pin isCount:isCount database:database]; + return resultTask; + }] continueWithBlock:^id(BFTask *ignored) { + // We need this to return the result of `findQuery` instead of returning the + // result of `[database closeAsync]` + return resultTask; + }]; +} + +- (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState + user:(PFUser *)user + pin:(PFPin *)pin + isCount:(BOOL)isCount + database:(PFSQLiteDatabase *)database { + __block NSMutableArray *mutableResults = [NSMutableArray array]; + BFTask *queryTask = nil; + BOOL includeIsDeletingEventually = queryState.shouldIncludeDeletingEventually; + + if (pin == nil) { + NSString *isDeletingEventuallyQuery = @""; + if (!includeIsDeletingEventually) { + isDeletingEventuallyQuery = [NSString stringWithFormat:@"AND %@ = 0", + PFOfflineStoreKeyOfIsDeletingEventually]; + } + NSString *queryString = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@ = ? %@;", + PFOfflineStoreKeyOfUUID, PFOfflineStoreTableOfObjects, + PFOfflineStoreKeyOfClassName, isDeletingEventuallyQuery]; + queryTask = [database executeQueryAsync:queryString withArgumentsInArray:@[ queryState.parseClassName ]]; + } else { + BFTask *uuidTask = [self.objectToUUIDMap objectForKey:pin]; + if (uuidTask == nil) { + // Pin was never saved locally, therefore there won't be any results. + return [BFTask taskWithResult:mutableResults]; + } + + queryTask = [uuidTask continueWithSuccessBlock:^id(BFTask *task) { + NSString *uuid = task.result; + NSString *isDeletingEventuallyQuery = @""; + if (!includeIsDeletingEventually) { + isDeletingEventuallyQuery = [NSString stringWithFormat:@"AND %@ = 0", + PFOfflineStoreKeyOfIsDeletingEventually]; + } + NSString *queryString = [NSString stringWithFormat:@"SELECT A.%@ FROM %@ A " + @"INNER JOIN %@ B ON A.%@ = B.%@ WHERE %@ = ? AND %@ = ? %@;", + PFOfflineStoreKeyOfUUID, PFOfflineStoreTableOfObjects, + PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfUUID, + PFOfflineStoreKeyOfUUID, PFOfflineStoreKeyOfClassName, + PFOfflineStoreKeyOfKey, isDeletingEventuallyQuery]; + + return [database executeQueryAsync:queryString + withArgumentsInArray:@[ queryState.parseClassName, uuid ]]; + }]; + } + + @weakify(self); + return [[queryTask continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFSQLiteDatabaseResult *result = task.result; + + PFConstraintMatcherBlock matcherBlock = [self.offlineQueryLogic createMatcherForQueryState:queryState + user:user]; + + BFTask *checkAllObjectsTask = [BFTask taskWithResult:nil]; + while ([result next]) { + NSString *uuid = [result stringForColumnIndex:0]; + __block PFObject *object = nil; + + checkAllObjectsTask = [[[[checkAllObjectsTask continueWithSuccessBlock:^id(BFTask *task) { + return [self _getPointerAsyncWithUUID:uuid database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + object = task.result; + return [self fetchObjectLocallyAsync:object database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + if (!object.isDataAvailable) { + return [BFTask taskWithResult:@NO]; + } + return matcherBlock(object, database); + }] continueWithSuccessBlock:^id(BFTask *task) { + if ([task.result boolValue]) { + [mutableResults addObject:object]; + } + return [BFTask taskWithResult:nil]; + }]; + } + [result close]; + + return checkAllObjectsTask; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + + // Sort, Apply Skip and Limit + + PFOfflineQueryOption queryOptions = 0; + if (!isCount) { + queryOptions = PFOfflineQueryOptionOrder | PFOfflineQueryOptionSkip | PFOfflineQueryOptionLimit; + } + NSArray *results = [self.offlineQueryLogic resultsByApplyingOptions:queryOptions + ofQueryState:queryState + toResults:mutableResults]; + + // Fetch includes + BFTask *fetchIncludesTask = [self.offlineQueryLogic fetchIncludesAsyncForResults:results + ofQueryState:queryState + inDatabase:database]; + + return [fetchIncludesTask continueWithSuccessBlock:^id(BFTask *task) { + return results; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Update +///-------------------------------------- + +- (BFTask *)updateDataForObjectAsync:(PFObject *)object { + BFTask *fetchTask = nil; + + @synchronized (self.lock) { + fetchTask = [self.fetchedObjects objectForKey:object]; + if (!fetchTask) { + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"An object cannot be updated if it wasn't fetched" + userInfo:nil]; + return [BFTask taskWithException:exception]; + } + } + return [fetchTask continueWithBlock:^id(BFTask *task) { + if (task.error != nil) { + // Catch CACHE_MISS errors and ignore them. + if (task.error.code == kPFErrorCacheMiss) { + return [BFTask taskWithResult:nil]; + } + return [BFTask taskWithResult:[task.result weakObject]]; + } + + return [self _performDatabaseTransactionAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + return [self _updateDataForObjectAsync:object inDatabase:database]; + }]; + }]; +} + +- (BFTask *)_updateDataForObjectAsync:(PFObject *)object inDatabase:(PFSQLiteDatabase *)database { + BFTask *uuidTask = nil; + @synchronized (self.lock) { + uuidTask = [self.objectToUUIDMap objectForKey:object]; + if (!uuidTask) { + // It was fetched, but it has no UUID. That must mean it isn't actually in the database. + return [BFTask taskWithResult:nil]; + } + } + + __block NSString *uuid = nil; + __block NSDictionary *dataDictionary = nil; + return [[uuidTask continueWithSuccessBlock:^id(BFTask *task) { + uuid = task.result; + + PFOfflineObjectEncoder *encoder = [PFOfflineObjectEncoder objectEncoderWithOfflineStore:self + database:database]; + NSArray *operationSetUUIDs = nil; + dataDictionary = [object RESTDictionaryWithObjectEncoder:encoder operationSetUUIDs:&operationSetUUIDs]; + return [encoder encodeFinished]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // Put it in database + NSString *className = object.parseClassName; + NSString *objectId = object.objectId; + NSString *encodedDataDictionary = [PFJSONSerialization stringFromJSONObject:dataDictionary]; + NSNumber *deletingEventually = dataDictionary[PFOfflineStoreKeyOfIsDeletingEventually]; + + NSString *updateParams = nil; + NSArray *updateArguments = nil; + if (objectId != nil) { + updateParams = [NSString stringWithFormat:@"%@ = ?, %@ = ?, %@ = ?, %@ = ?", + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfJSON, + PFOfflineStoreKeyOfIsDeletingEventually, PFOfflineStoreKeyOfObjectId]; + updateArguments = @[ className, encodedDataDictionary, deletingEventually, objectId, uuid ]; + } else { + updateParams = [NSString stringWithFormat:@"%@ = ?, %@ = ?, %@ = ?", + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfJSON, + PFOfflineStoreKeyOfIsDeletingEventually]; + updateArguments = @[ className, encodedDataDictionary, deletingEventually, uuid ]; + } + + NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?", + PFOfflineStoreTableOfObjects, updateParams, PFOfflineStoreKeyOfUUID]; + + return [database executeSQLAsync:sql withArgumentsInArray:updateArguments]; + }]; +} + +///-------------------------------------- +#pragma mark - Delete +///-------------------------------------- + +- (BFTask *)deleteDataForObjectAsync:(PFObject *)object { + return [self _performDatabaseTransactionAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + return [self deleteDataForObjectAsync:object database:database]; + }]; +} + +- (BFTask *)deleteDataForObjectAsync:(PFObject *)object database:(PFSQLiteDatabase *)database { + __block NSString *uuid = nil; + + // Make sure the object has a UUID. + BFTask *uuidTask = nil; + @synchronized (self.lock) { + uuidTask = [self.objectToUUIDMap objectForKey:object]; + if (!uuidTask) { + // It was fetched, but it has no UUID. That must mean it isn't actually in the database. + return [BFTask taskWithResult:nil]; + } + } + + uuidTask = [uuidTask continueWithSuccessBlock:^id(BFTask *task) { + uuid = task.result; + return task; + }]; + + // If the object was the root of a pin, unpin it. + BFTask *unpinTask = [[uuidTask continueWithSuccessBlock:^id(BFTask *task) { + // Find all the roots for this object. + NSString *sql = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@ = ?", + PFOfflineStoreKeyOfKey, PFOfflineStoreTableOfDependencies, + PFOfflineStoreKeyOfUUID]; + return [database executeQueryAsync:sql withArgumentsInArray:@[ uuid ]]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // Try to unpin this object from the pin label if it's a root of the PFPin. + PFSQLiteDatabaseResult *result = task.result; + NSMutableArray *tasks = [NSMutableArray array]; + + while (result.next) { + NSString *objectUUID = [result stringForColumnIndex:0]; + + BFTask *getPointerTask = [self _getPointerAsyncWithUUID:objectUUID database:database]; + BFTask *objectUnpinTask = [[getPointerTask continueWithSuccessBlock:^id(BFTask *task) { + PFPin *pin = task.result; + return [self fetchObjectLocallyAsync:pin database:database]; + }] continueWithBlock:^id(BFTask *task) { + PFPin *pin = task.result; + + NSMutableArray *modified = pin.objects; + if (modified == nil || ![modified containsObject:object]) { + return task; + } + + [modified removeObject:object]; + if (modified.count == 0) { + return [self _unpinKeyAsync:objectUUID database:database]; + } + pin.objects = modified; + + return [self saveObjectLocallyAsync:pin includeChildren:YES database:database]; + }]; + [tasks addObject:objectUnpinTask]; + } + [result close]; + + return [BFTask taskForCompletionOfAllTasks:tasks]; + }]; + + return [[[unpinTask continueWithSuccessBlock:^id(BFTask *task) { + NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?", + PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfUUID]; + return [database executeSQLAsync:sql withArgumentsInArray:@[ uuid ]]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?", + PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID]; + return [database executeSQLAsync:sql withArgumentsInArray:@[ uuid ]]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // Delete the object from memory cache. + // (or else `PFObject.objectWithoutDataWithClassName` will return a valid object) + @synchronized (self.lock) { + // TODO (hallucinogen): we should probably clean up UUIDToObjectMap and objectToUUIDMap + // but getting the uuid requires a task and things might get a little funky... + if (object.objectId != nil) { + NSString *key = [self _generateKeyForClassName:object.parseClassName objectId:object.objectId]; + [self.classNameAndObjectIdToObjectMap removeObjectForKey:key]; + } + [self.fetchedObjects removeObjectForKey:object]; + } + return task; + }]; +} + +///-------------------------------------- +#pragma mark - Unpin +///-------------------------------------- + +- (BFTask *)unpinObjectAsync:(PFObject *)object { + BFTask *uuidTask = [self.objectToUUIDMap objectForKey:object]; + return [uuidTask continueWithBlock:^id(BFTask *task) { + NSString *uuid = task.result; + if (!uuid) { + // The root object was never stored in the offline store, so nothing to unpin. + return [BFTask taskWithResult:nil]; + } + return [self _unpinKeyAsync:uuid]; + }]; +} + +- (BFTask *)_unpinKeyAsync:(NSString *)key { + return [self _performDatabaseTransactionAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + return [self _unpinKeyAsync:key database:database]; + }]; +} + +- (BFTask *)_unpinKeyAsync:(NSString *)key database:(PFSQLiteDatabase *)database { + NSMutableArray *uuidsToDelete = [NSMutableArray array]; + // Fetch all uuids from Dependencies for key=? grouped by uuid having a count of 1 + NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@ = ? AND %@ IN " + @"(SELECT %@ FROM %@ GROUP BY %@ HAVING COUNT(%@) = 1);", + PFOfflineStoreKeyOfUUID, PFOfflineStoreTableOfDependencies, + PFOfflineStoreKeyOfKey, PFOfflineStoreKeyOfUUID, PFOfflineStoreKeyOfUUID, + PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfUUID, + PFOfflineStoreKeyOfUUID]; + return [[[[database executeQueryAsync:query + withArgumentsInArray:@[ key ]] continueWithSuccessBlock:^id(BFTask *task) { + // DELETE FROM Objects + PFSQLiteDatabaseResult *result = task.result; + while (result.next) { + [uuidsToDelete addObject:[result stringForColumnIndex:0]]; + } + [result close]; + + return [self _deleteObjectsWithUUIDs:uuidsToDelete database:database]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // DELETE FROM Dependencies + NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?", + PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfKey]; + return [database executeSQLAsync:sql withArgumentsInArray:@[ key ]]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @synchronized (self.lock) { + // Remove uuids from memory + for (NSString *uuid in uuidsToDelete) { + PFObject *object = [self.UUIDToObjectMap objectForKey:uuid]; + if (object != nil) { + [self.objectToUUIDMap removeObjectForKey:object]; + [self.UUIDToObjectMap removeObjectForKey:uuid]; + } + } + } + return [BFTask taskWithResult:nil]; + }]; +} + +- (BFTask *)_deleteObjectsWithUUIDs:(NSArray *)uuids database:(PFSQLiteDatabase *)database { + if (uuids.count <= 0) { + return [BFTask taskWithResult:nil]; + } + + if (uuids.count > PFOfflineStoreMaximumSQLVariablesCount) { + NSRange range = NSMakeRange(0, PFOfflineStoreMaximumSQLVariablesCount); + return [[self _deleteObjectsWithUUIDs:[uuids subarrayWithRange:range] + database:database] continueWithSuccessBlock:^id(BFTask *task) { + unsigned long includedCount = uuids.count - PFOfflineStoreMaximumSQLVariablesCount; + NSRange range = NSMakeRange(PFOfflineStoreMaximumSQLVariablesCount, includedCount); + return [self _deleteObjectsWithUUIDs:[uuids subarrayWithRange:range] database:database]; + }]; + } + + NSMutableArray *placeholders = [NSMutableArray array]; + for (int i = 0; i < uuids.count; ++i) { + [placeholders addObject:@"?"]; + } + NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ IN (%@);", + PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID, + [placeholders componentsJoinedByString:@","]]; + return [database executeSQLAsync:sql withArgumentsInArray:uuids]; +} + +///-------------------------------------- +#pragma mark - Internal Helper Methods +///-------------------------------------- + +- (BFTask *)getOrCreateUUIDAsyncForObject:(PFObject *)object + database:(PFSQLiteDatabase *)database { + NSString *newUUID = [[NSUUID UUID] UUIDString]; + BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource]; + + @synchronized (self.lock) { + BFTask *uuidTask = [self.objectToUUIDMap objectForKey:object]; + if (uuidTask != nil) { + // Return existing task. + return uuidTask; + } + + // The object doesn't have UUID yet, so we're gonna have to make one + [self.objectToUUIDMap setObject:tcs.task forKey:object]; + [self.UUIDToObjectMap setObject:object forKey:newUUID]; + + __weak id weakObject = object; + [self.fetchedObjects setObject:[tcs.task continueWithSuccessBlock:^id(BFTask *task) { + return [PFWeakValue valueWithWeakObject:weakObject]; + }] forKey:object]; + } + + // We need to put a placeholder row in the database so that later on the save can be just + // an update. This could be a pointer to an object that itself never gets saved offline, + // in which case the consumer will just have to deal with that. + NSString *query = [NSString stringWithFormat:@"INSERT INTO %@(%@, %@) VALUES(?, ?);", + PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID, PFOfflineStoreKeyOfClassName]; + [[database executeSQLAsync:query + withArgumentsInArray:@[ newUUID, [object parseClassName]]] continueWithSuccessBlock:^id(BFTask *task) { + [tcs setResult:newUUID]; + return [BFTask taskWithResult:nil]; + }]; + + return tcs.task; +} + +/*! + Gets an unfetched pointer to an object in the database, based on its uuid. The object may or may + not be in memory, but it must be in database. If it is already in memory, the instance will be + returned. Since this is only for creating pointers to objects that are referenced by other objects + in the datastore, it's a fair assumption. + + @param uuid The UUID of the object to retrieve. + @param database The database instance to retrieve from. + @returns The object with that UUID. + */ +- (BFTask *)_getPointerAsyncWithUUID:(NSString *)uuid + database:(PFSQLiteDatabase *)database { + @synchronized (self.lock) { + PFObject *existing = [self.UUIDToObjectMap objectForKey:uuid]; + if (existing != nil) { + return [BFTask taskWithResult:existing]; + } + } + + // We only want the pointer, but we have to look in the database to know if there's something + // with this classname and object id already. + NSString *query = [NSString stringWithFormat:@"SELECT %@, %@ FROM %@ WHERE %@ = ?;", + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfObjectId, + PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID]; + return [[database executeQueryAsync:query + withArgumentsInArray:@[ uuid ]] continueWithSuccessBlock:^id(BFTask *task) { + PFSQLiteDatabaseResult *result = task.result; + if (![result next]) { + [result close]; + [NSException raise:NSInternalInconsistencyException + format:@"Attempted to find non-existent uuid %@", uuid]; + } + + @synchronized (self.lock) { + PFObject *existing = [self.UUIDToObjectMap objectForKey:uuid]; + if (existing != nil) { + [result close]; + return existing; + } + + NSString *className = [result stringForColumnIndex:0]; + NSString *objectId = [result stringForColumnIndex:1]; + [result close]; + + PFObject *pointer = [PFObject objectWithoutDataWithClassName:className objectId:objectId]; + + // If it doesn't have objectId, we don't really need the UUID, and this simplifies some + // other logic elsewhere if we only update the map for new objects. + if (objectId == nil) { + [self.UUIDToObjectMap setObject:pointer forKey:uuid]; + [self.objectToUUIDMap setObject:[BFTask taskWithResult:uuid] forKey:pointer]; + } + return pointer; + } + }]; +} + +- (PFObject *)getOrCreateObjectWithoutDataWithClassName:(NSString *)className + objectId:(NSString *)objectId { + PFParameterAssert(objectId, @"objectId cannot be nil."); + + PFObject *object = nil; + @synchronized (self.lock) { + NSString *key = [self _generateKeyForClassName:className objectId:objectId]; + object = [self.classNameAndObjectIdToObjectMap objectForKey:key]; + if (!object) { + object = [PFObject objectWithClassName:className objectId:objectId completeData:NO]; + [self updateObjectIdForObject:object oldObjectId:nil newObjectId:objectId]; + } + } + return object; +} + +- (void)updateObjectIdForObject:(PFObject *)object + oldObjectId:(NSString *)oldObjectId + newObjectId:(NSString *)newObjectId { + if (oldObjectId != nil) { + PFConsistencyAssert([oldObjectId isEqualToString:newObjectId], @"objectIds cannot be changed in offline mode."); + return; + } + + NSString *className = object.parseClassName; + NSString *key = [self _generateKeyForClassName:className objectId:newObjectId]; + + @synchronized (self.lock) { + // See if there's already an entry for new objectId. + PFObject *existing = [self.classNameAndObjectIdToObjectMap objectForKey:key]; + PFConsistencyAssert(existing == nil || existing == object, + @"Attempted to change an objectId to one that's already known to the OfflineStore."); + + // Okay, all clear to add the new reference. + [self.classNameAndObjectIdToObjectMap setObject:object forKey:key]; + } +} + +- (NSString *)_generateKeyForClassName:(NSString *)className + objectId:(NSString *)objectId { + return [NSString stringWithFormat:@"%@:%@", className, objectId]; +} + +// TODO (hallucinogen): is this the right way to store the schema? ++ (NSString *)PFOfflineStoreParseObjectsTableSchema { + return [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (" + @"%@ TEXT PRIMARY KEY, " + @"%@ TEXT NOT NULL, " + @"%@ TEXT, " + @"%@ TEXT, " + @"%@ INTEGER DEFAULT 0, " + @"UNIQUE(%@, %@));", PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID, + PFOfflineStoreKeyOfClassName, PFOfflineStoreKeyOfObjectId, PFOfflineStoreKeyOfJSON, + PFOfflineStoreKeyOfIsDeletingEventually, PFOfflineStoreKeyOfClassName, + PFOfflineStoreKeyOfObjectId]; +} + ++ (NSString *)PFOfflineStoreDependenciesTableSchema { + return [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (" + @"%@ TEXT NOT NULL, " + @"%@ TEXT NOT NULL, " + @"PRIMARY KEY(%@, %@));", PFOfflineStoreTableOfDependencies, PFOfflineStoreKeyOfKey, + PFOfflineStoreKeyOfUUID, PFOfflineStoreKeyOfKey, PFOfflineStoreKeyOfUUID]; +} + ++ (BFTask *)_initializeTablesInBackgroundWithDatabaseController:(PFSQLiteDatabaseController *)databaseController { + return [[databaseController openDatabaseWithNameAsync:PFOfflineStoreDatabaseName] continueWithBlock:^id(BFTask *task) { + PFSQLiteDatabase *database = task.result; + return [[[[[database beginTransactionAsync] continueWithSuccessBlock:^id(BFTask *task) { + return [database executeSQLAsync:[self PFOfflineStoreParseObjectsTableSchema] withArgumentsInArray:nil]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [database executeSQLAsync:[self PFOfflineStoreDependenciesTableSchema] withArgumentsInArray:nil]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [database commitAsync]; + }] continueWithBlock:^id(BFTask *task) { + return [database closeAsync]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Database Helpers +///-------------------------------------- + +- (BFTask *)_performDatabaseTransactionAsyncWithBlock:(PFOfflineStoreDatabaseExecutionBlock)block { + return [self _performDatabaseOperationAsyncWithBlock:^BFTask *(PFSQLiteDatabase *database) { + BFTask *task = [database beginTransactionAsync]; + task = [task continueWithSuccessBlock:^id(BFTask *task) { + return block(database); + }]; + return [task continueWithSuccessBlock:^id(BFTask *task) { + return [database commitAsync]; + }]; + }]; +} + +- (BFTask *)_performDatabaseOperationAsyncWithBlock:(PFOfflineStoreDatabaseExecutionBlock)block { + return [[self.databaseController openDatabaseWithNameAsync:PFOfflineStoreDatabaseName] continueWithBlock:^id(BFTask *task) { + PFSQLiteDatabase *database = task.result; + return [block(database) continueWithBlock:^id(BFTask *task) { + return [database closeAsync]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFOfflineQueryLogic *)offlineQueryLogic { + @synchronized (self.lock) { + if (!_offlineQueryLogic) { + _offlineQueryLogic = [[PFOfflineQueryLogic alloc] initWithOfflineStore:self]; + } + return _offlineQueryLogic; + } +} + +///-------------------------------------- +#pragma mark - Unit Test helper +///-------------------------------------- + +- (void)simulateReboot { + @synchronized (self.lock) { + [self.UUIDToObjectMap removeAllObjects]; + [self.objectToUUIDMap removeAllObjects]; + [self.classNameAndObjectIdToObjectMap removeAllObjects]; + [self.fetchedObjects removeAllObjects]; + } +} + +- (void)clearDatabase { + // Delete DB file + NSString *filePath = [self.fileManager parseDataItemPathForPathComponent:PFOfflineStoreDatabaseName]; + [[PFFileManager removeItemAtPathAsync:filePath] waitForResult:nil withMainThreadWarning:NO]; + + // Reinitialize tables + [PFOfflineStore _initializeTablesInBackgroundWithDatabaseController:self.databaseController]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.h b/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.h new file mode 100644 index 0000000..745baf6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +extern NSString *const PFPinKeyName; +extern NSString *const PFPinKeyObjects; + +/*! + PFPin represent internal pin implementation of PFObject's `pin`. + */ +@interface PFPin : PFObject + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, strong) NSMutableArray *objects; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithName:(NSString *)name; ++ (instancetype)pinWithName:(NSString *)name; + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.m b/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.m new file mode 100644 index 0000000..3519b47 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/Pin/PFPin.m @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPin.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFObject+Subclass.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFQueryPrivate.h" +#import "Parse_Private.h" + +NSString *const PFPinKeyName = @"_name"; +NSString *const PFPinKeyObjects = @"_objects"; + +@implementation PFPin + +///-------------------------------------- +#pragma mark - PFSubclassing +///-------------------------------------- + ++ (NSString *)parseClassName { + return @"_Pin"; +} + +// Validates a class name. We override this to only allow the pin class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[self parseClassName]], + @"Cannot initialize a PFPin with a custom class name."); +} + +- (BOOL)needsDefaultACL { + return NO; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithName:(NSString *)name { + self = [super init]; + if (!self) return nil; + + // Use property accessor, as there is no ivar here for `name`. + self.name = name; + + return self; +} + ++ (instancetype)pinWithName:(NSString *)name { + return [[self alloc] initWithName:name]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSString *)name { + return self[PFPinKeyName]; +} + +- (void)setName:(NSString *)name { + self[PFPinKeyName] = [name copy]; +} + +- (NSMutableArray *)objects { + return self[PFPinKeyObjects]; +} + +- (void)setObjects:(NSMutableArray *)objects { + self[PFPinKeyObjects] = [objects mutableCopy]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.h b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.h new file mode 100644 index 0000000..fc37993 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.h @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFFileManager; +@class PFSQLiteDatabaseResult; + +/*! + Argument count given in executeSQLAsync or executeQueryAsync is invalid. + */ +extern int const PFSQLiteDatabaseInvalidArgumenCountErrorCode; + +/*! + Method `executeSQL` cannot execute SELECT. Use `executeQuery` instead. + */ +extern int const PFSQLiteDatabaseInvalidSQL; + +/*! + Database is opened already. + */ +extern int const PFSQLiteDatabaseDatabaseAlreadyOpened; + +/*! + Database is closed already. + */ +extern int const PFSQLiteDatabaseDatabaseAlreadyClosed; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSQLiteDatabase : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithPath:(NSString *)path; + +///-------------------------------------- +/// @name Database Creation +///-------------------------------------- + ++ (instancetype)databaseWithPath:(NSString *)path; + +///-------------------------------------- +/// @name Connection +///-------------------------------------- + +/*! + @returns A `BFTask` that resolves to `YES` if the database is open. + */ +- (BFTask *)isOpenAsync; + +/*! + Opens database. Database is one time use. Open > Close > Open is forbidden. + */ +- (BFTask *)openAsync; + +/*! + Closes the database connection. + */ +- (BFTask *)closeAsync; + +///-------------------------------------- +/// @name Transaction +///-------------------------------------- + +/*! + Begins a database transaction in EXCLUSIVE mode. + */ +- (BFTask *)beginTransactionAsync; + +/*! + Commits running transaction. + */ +- (BFTask *)commitAsync; + +/*! + Rollbacks running transaction. + */ +- (BFTask *)rollbackAsync; + +///-------------------------------------- +/// @name Query Methods +///-------------------------------------- + +/*! + Runs a single SQL statement which return result (SELECT). + */ +- (BFTask *)executeQueryAsync:(NSString *)sql withArgumentsInArray:(nullable NSArray *)args; + +/*! + Runs a single SQL statement, while caching the resulting statement for future use. + */ +- (BFTask *)executeCachedQueryAsync:(NSString *)sql withArgumentsInArray:(nullable NSArray *)args; + +/*! + Runs a single SQL statement which doesn't return result (UPDATE/INSERT/DELETE). + */ +- (BFTask *)executeSQLAsync:(NSString *)sql withArgumentsInArray:(nullable NSArray *)args; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.m b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.m new file mode 100644 index 0000000..9c46fa3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.m @@ -0,0 +1,339 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSQLiteDatabase.h" +#import "PFSQLiteDatabase_Private.h" + +#import + +#import +#import + +#import "BFTask+Private.h" +#import "PFFileManager.h" +#import "PFInternalUtils.h" +#import "PFMacros.h" +#import "PFMultiProcessFileLockController.h" +#import "PFSQLiteDatabaseResult.h" +#import "PFSQLiteStatement.h" +#import "Parse_Private.h" + +NSString *const PFSQLiteDatabaseBeginExclusiveOperationCommand = @"BEGIN EXCLUSIVE"; +NSString *const PFSQLiteDatabaseCommitOperationCommand = @"COMMIT"; +NSString *const PFSQLiteDatabaseRollbackOperationCommand = @"ROLLBACK"; + +NSString *const PFSQLiteDatabaseErrorSQLiteDomain = @"SQLite"; +NSString *const PFSQLiteDatabaseErrorPFSQLiteDatabaseDomain = @"PFSQLiteDatabase"; + +int const PFSQLiteDatabaseInvalidArgumenCountErrorCode = 1; +int const PFSQLiteDatabaseInvalidSQL = 2; +int const PFSQLiteDatabaseDatabaseAlreadyOpened = 3; +int const PFSQLiteDatabaseDatabaseAlreadyClosed = 4; + +@interface PFSQLiteDatabase () { + BFTaskCompletionSource *_databaseClosedTaskCompletionSource; + dispatch_queue_t _databaseQueue; + BFExecutor *_databaseExecutor; + NSMutableDictionary *_cachedStatements; +} + +/*! + Database instance + */ +@property (nonatomic, assign) sqlite3 *database; + +/*! + Database path + */ +@property (nonatomic, copy) NSString *databasePath; + +@end + +@implementation PFSQLiteDatabase + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithPath:(NSString *)path { + self = [super init]; + if (!self) return nil; + + _databaseClosedTaskCompletionSource = [[BFTaskCompletionSource alloc] init]; + _databasePath = [path copy]; + _databaseQueue = dispatch_queue_create("com.parse.sqlite.db.queue", DISPATCH_QUEUE_SERIAL); + _databaseExecutor = [BFExecutor executorWithDispatchQueue:_databaseQueue]; + _cachedStatements = [[NSMutableDictionary alloc] init]; + + return self; +} + ++ (instancetype)databaseWithPath:(NSString *)path { + return [[self alloc] initWithPath:path]; +} + +///-------------------------------------- +#pragma mark - Connection +///-------------------------------------- + +- (BFTask *)isOpenAsync { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + return @(self.database != nil); + }]; +} + +- (BFTask *)openAsync { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + if (self.database) { + NSError *error = [self _errorWithErrorCode:PFSQLiteDatabaseDatabaseAlreadyOpened + errorMessage:@"Database is opened already." + domain:PFSQLiteDatabaseErrorPFSQLiteDatabaseDomain]; + return [BFTask taskWithError:error]; + } + + // Check if this database have already been opened before. + if (_databaseClosedTaskCompletionSource.task.completed) { + NSError *error = [self _errorWithErrorCode:PFSQLiteDatabaseDatabaseAlreadyClosed + errorMessage:@"Closed database cannot be reopen." + domain:PFSQLiteDatabaseErrorPFSQLiteDatabaseDomain]; + return [BFTask taskWithError:error]; + } + + // Lock the file to avoid multi-process access. + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:self.databasePath]; + + sqlite3 *db; + int resultCode = sqlite3_open([self.databasePath UTF8String], &db); + if (resultCode != SQLITE_OK) { + return [BFTask taskWithError:[self _errorWithErrorCode:resultCode]]; + } + + self.database = db; + return [BFTask taskWithResult:nil]; + }]; +} + +- (BFTask *)closeAsync { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + if (!self.database) { + NSError *error = [self _errorWithErrorCode:PFSQLiteDatabaseDatabaseAlreadyClosed + errorMessage:@"Database is closed already." + domain:PFSQLiteDatabaseErrorPFSQLiteDatabaseDomain]; + return [BFTask taskWithError:error]; + } + + [self _clearCachedStatements]; + int resultCode = sqlite3_close(self.database); + + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:self.databasePath]; + + if (resultCode == SQLITE_OK) { + + self.database = nil; + [_databaseClosedTaskCompletionSource setResult:nil]; + } else { + // Returns error + [_databaseClosedTaskCompletionSource setError:[self _errorWithErrorCode:resultCode]]; + } + return _databaseClosedTaskCompletionSource.task; + }]; +} + +///-------------------------------------- +#pragma mark - Transaction +///-------------------------------------- + +- (BFTask *)beginTransactionAsync { + return [self executeSQLAsync:PFSQLiteDatabaseBeginExclusiveOperationCommand + withArgumentsInArray:nil]; +} + +- (BFTask *)commitAsync { + return [self executeSQLAsync:PFSQLiteDatabaseCommitOperationCommand + withArgumentsInArray:nil]; +} + +- (BFTask *)rollbackAsync { + return [self executeSQLAsync:PFSQLiteDatabaseRollbackOperationCommand + withArgumentsInArray:nil]; +} + +///-------------------------------------- +#pragma mark - Query Methods +///-------------------------------------- + +- (BFTask *)_executeQueryAsync:(NSString *)sql withArgumentsInArray:(NSArray *)args cachingEnabled:(BOOL)enableCaching { + int resultCode = 0; + PFSQLiteStatement *statement = enableCaching ? [self _cachedStatementForQuery:sql] : nil; + if (!statement) { + sqlite3_stmt *sqliteStatement = nil; + resultCode = sqlite3_prepare_v2(self.database, [sql UTF8String], -1, &sqliteStatement, 0); + if (resultCode != SQLITE_OK) { + sqlite3_finalize(sqliteStatement); + return [BFTask taskWithError:[self _errorWithErrorCode:resultCode]]; + } + statement = [[PFSQLiteStatement alloc] initWithStatement:sqliteStatement]; + + if (enableCaching) { + [self _cacheStatement:statement forQuery:sql]; + } + } else { + [statement reset]; + } + + // Make parameter + int queryCount = sqlite3_bind_parameter_count([statement sqliteStatement]); + int argumentCount = (int)[args count]; + if (queryCount != argumentCount) { + if (!enableCaching) { + [statement close]; + } + + NSError *error = [self _errorWithErrorCode:PFSQLiteDatabaseInvalidArgumenCountErrorCode + errorMessage:@"Statement arguments count doesn't match " + @"given arguments count." + domain:NSStringFromClass([self class])]; + return [BFTask taskWithError:error]; + } + + for (int idx = 0; idx < queryCount; ++idx) { + [self _bindObject:args[idx] toColumn:(idx + 1) inStatement:statement]; + } + + PFSQLiteDatabaseResult *result = [[PFSQLiteDatabaseResult alloc] initWithStatement:statement]; + return [BFTask taskWithResult:result]; +} + +- (BFTask *)executeCachedQueryAsync:(NSString *)sql withArgumentsInArray:(NSArray *)args { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + return [self _executeQueryAsync:sql withArgumentsInArray:args cachingEnabled:YES]; + }]; +} + +- (BFTask *)executeQueryAsync:(NSString *)sql withArgumentsInArray:(NSArray *)args { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + return [self _executeQueryAsync:sql withArgumentsInArray:args cachingEnabled:NO]; + }]; +} + +- (BFTask *)executeSQLAsync:(NSString *)sql withArgumentsInArray:(NSArray *)args { + return [BFTask taskFromExecutor:_databaseExecutor withBlock:^id { + return [[self _executeQueryAsync:sql + withArgumentsInArray:args + cachingEnabled:NO] continueWithExecutor:[BFExecutor immediateExecutor] withSuccessBlock:^id(BFTask *task) { + PFSQLiteDatabaseResult *databaseResult = task.result; + int sqliteResultCode = [databaseResult step]; + [databaseResult close]; + + switch (sqliteResultCode) { + case SQLITE_DONE: { + return [BFTask taskWithResult:nil]; + } + case SQLITE_ROW: { + NSError *error = [self _errorWithErrorCode:PFSQLiteDatabaseInvalidSQL + errorMessage:@"Cannot SELECT on executeSQLAsync." + @"Please use executeQueryAsync." + domain:NSStringFromClass([self class])]; + return [BFTask taskWithError:error]; + } + default: { + return [BFTask taskWithError:[self _errorWithErrorCode:sqliteResultCode]]; + } + } + }]; + }]; +} + +/*! + bindObject will bind any object supported by PFSQLiteDatabase to query statement. + Note: sqlite3 query index binding is one-based, while querying result is zero-based. + */ +- (void)_bindObject:(id)obj toColumn:(int)idx inStatement:(PFSQLiteStatement *)statement { + if ((!obj) || ((NSNull *)obj == [NSNull null])) { + sqlite3_bind_null([statement sqliteStatement], idx); + } else if ([obj isKindOfClass:[NSData class]]) { + const void *bytes = [obj bytes]; + if (!bytes) { + // It's an empty NSData object, aka [NSData data]. + // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. + bytes = ""; + } + sqlite3_bind_blob([statement sqliteStatement], idx, bytes, (int)[obj length], SQLITE_TRANSIENT); + } else if ([obj isKindOfClass:[NSDate class]]) { + sqlite3_bind_double([statement sqliteStatement], idx, [obj timeIntervalSince1970]); + } else if ([obj isKindOfClass:[NSNumber class]]) { + if (CFNumberIsFloatType((__bridge CFNumberRef)obj)) { + sqlite3_bind_double([statement sqliteStatement], idx, [obj doubleValue]); + } else { + sqlite3_bind_int64([statement sqliteStatement], idx, [obj longLongValue]); + } + } else { + sqlite3_bind_text([statement sqliteStatement], idx, [[obj description] UTF8String], -1, SQLITE_TRANSIENT); + } +} + +///-------------------------------------- +#pragma mark - Cached Statements +///-------------------------------------- + +- (void)_clearCachedStatements { + for (PFSQLiteStatement *statement in [_cachedStatements allValues]) { + [statement close]; + } + + [_cachedStatements removeAllObjects]; +} + +- (PFSQLiteStatement *)_cachedStatementForQuery:(NSString *)query { + return _cachedStatements[query]; +} + +- (void)_cacheStatement:(PFSQLiteStatement *)statement forQuery:(NSString *)query { + _cachedStatements[query] = statement; +} + +///-------------------------------------- +#pragma mark - Errors +///-------------------------------------- + +/*! + Generates SQLite error. The details of the error code can be seen in: www.sqlite.org/c3ref/errcode.html + */ +- (NSError *)_errorWithErrorCode:(int)errorCode { + return [self _errorWithErrorCode:errorCode + errorMessage:[NSString stringWithUTF8String:sqlite3_errmsg(self.database)]]; +} + +- (NSError *)_errorWithErrorCode:(int)errorCode errorMessage:(NSString *)errorMessage { + return [self _errorWithErrorCode:errorCode + errorMessage:errorMessage + domain:PFSQLiteDatabaseErrorSQLiteDomain]; +} + +/*! + Generates SQLite/PFSQLiteDatabase error. + */ +- (NSError *)_errorWithErrorCode:(int)errorCode + errorMessage:(NSString *)errorMessage + domain:(NSString *)domain { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + result[@"code"] = @(errorCode); + result[@"error"] = errorMessage; + return [[NSError alloc] initWithDomain:domain code:errorCode userInfo:result]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (BFTask *)databaseClosedTask { + return _databaseClosedTaskCompletionSource.task; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.h b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.h new file mode 100644 index 0000000..d1a0f8c --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFFileManager; +@class PFSQLiteDatabase; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSQLiteDatabaseController : NSObject + +@property (nonatomic, strong, readonly) PFFileManager *fileManager; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(PFFileManager *)fileManager NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithFileManager:(PFFileManager *)fileManager; + +///-------------------------------------- +/// @name Opening +///-------------------------------------- + +/*! + @abstract Asynchronously opens a database connection to the database with the name specified. + @note Only one database can be actively open at a time. + + @param name The name of the database to open. + + @return A task, which yields a `PFSQLiteDatabase`, with the open database. + When the database is closed, a new database connection can be opened. + */ +- (BFTask *)openDatabaseWithNameAsync:(NSString *)name; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.m b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.m new file mode 100644 index 0000000..d971ea4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.m @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSQLiteDatabaseController.h" + +#import +#import + +#import "PFAssert.h" +#import "PFAsyncTaskQueue.h" +#import "PFFileManager.h" +#import "PFSQLiteDatabase_Private.h" + +@implementation PFSQLiteDatabaseController { + PFAsyncTaskQueue *_openDatabaseQueue; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithFileManager:(PFFileManager *)fileManager { + self = [super init]; + if (!self) return nil; + + _fileManager = fileManager; + _openDatabaseQueue = [[PFAsyncTaskQueue alloc] init]; + + return self; +} + ++ (instancetype)controllerWithFileManager:(PFFileManager *)fileManager { + return [[self alloc] initWithFileManager:fileManager]; +} + +///-------------------------------------- +#pragma mark - Opening +///-------------------------------------- + +// TODO: (richardross) Implement connection pooling using NSCache or similar mechanism. +- (BFTask *)openDatabaseWithNameAsync:(NSString *)name { + BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; + [_openDatabaseQueue enqueue:^id(BFTask *task) { + NSString *databasePath = [self.fileManager parseDataItemPathForPathComponent:name]; + PFSQLiteDatabase *sqliteDatabase = [PFSQLiteDatabase databaseWithPath:databasePath]; + [[sqliteDatabase openAsync] continueWithBlock:^id(BFTask *task) { + if (task.faulted) { + NSError *error = task.error; + if (error) { + [taskCompletionSource trySetError:error]; + } else { + [taskCompletionSource trySetException:task.exception]; + } + } else if (task.cancelled) { + [taskCompletionSource trySetCancelled]; + } else { + [taskCompletionSource trySetResult:sqliteDatabase]; + } + + return nil; + }]; + + return sqliteDatabase.databaseClosedTask; + }]; + + return taskCompletionSource.task; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.h b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.h new file mode 100644 index 0000000..d63d36f --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFSQLiteStatement; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSQLiteDatabaseResult : NSObject + +- (instancetype)initWithStatement:(PFSQLiteStatement *)statement; + +/*! + Move current result to next row. Returns true if next result exists. False if current result + is the end of result set. + */ +- (BOOL)next; + +/*! + Move the current result to next row, and returns the raw SQLite return code for the cursor. + Useful for detecting end of cursor vs. error. + */ +- (int)step; + +/*! + Closes the database result. + */ +- (BOOL)close; + +///-------------------------------------- +/// @name Get Column Value +///-------------------------------------- + +- (int)intForColumn:(NSString *)columnName; +- (int)intForColumnIndex:(int)columnIndex; + +- (long)longForColumn:(NSString *)columnName; +- (long)longForColumnIndex:(int)columnIndex; + +- (BOOL)boolForColumn:(NSString *)columnName; +- (BOOL)boolForColumnIndex:(int)columnIndex; + +- (double)doubleForColumn:(NSString *)columnName; +- (double)doubleForColumnIndex:(int)columnIndex; + +- (nullable NSString *)stringForColumn:(NSString *)columnName; +- (nullable NSString *)stringForColumnIndex:(int)columnIndex; + +- (nullable NSDate *)dateForColumn:(NSString *)columnName; +- (nullable NSDate *)dateForColumnIndex:(int)columnIndex; + +- (nullable NSData *)dataForColumn:(NSString *)columnName; +- (nullable NSData *)dataForColumnIndex:(int)columnIndex; + +- (nullable id)objectForColumn:(NSString *)columnName; +- (nullable id)objectForColumnIndex:(int)columnIndex; + +- (BOOL)columnIsNull:(NSString *)columnName; +- (BOOL)columnIndexIsNull:(int)columnIndex; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.m b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.m new file mode 100644 index 0000000..cdbeb0e --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.m @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSQLiteDatabaseResult.h" + +#import + +#import "PFSQLiteStatement.h" + +@interface PFSQLiteDatabaseResult () + +@property (nonatomic, copy, readonly) NSDictionary *columnNameToIndexMap; +@property (nonatomic, strong, readonly) PFSQLiteStatement *statement; + +@end + +@implementation PFSQLiteDatabaseResult + +@synthesize columnNameToIndexMap = _columnNameToIndexMap; + +- (instancetype)initWithStatement:(PFSQLiteStatement *)stmt { + if ((self = [super init])) { + _statement = stmt; + } + return self; +} + +- (BOOL)next { + return [self step] == SQLITE_ROW; +} + +- (int)step { + return sqlite3_step([self.statement sqliteStatement]); +} + +- (BOOL)close { + return [self.statement close]; +} + +- (int)intForColumn:(NSString *)columnName { + return [self intForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (int)intForColumnIndex:(int)columnIndex { + return sqlite3_column_int([self.statement sqliteStatement], columnIndex); +} + +- (long)longForColumn:(NSString *)columnName { + return [self longForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (long)longForColumnIndex:(int)columnIndex { + return (long)sqlite3_column_int64([self.statement sqliteStatement], columnIndex); +} + +- (BOOL)boolForColumn:(NSString *)columnName { + return [self boolForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (BOOL)boolForColumnIndex:(int)columnIndex { + return ([self intForColumnIndex:columnIndex] != 0); +} + +- (double)doubleForColumn:(NSString *)columnName { + return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (double)doubleForColumnIndex:(int)columnIndex { + return sqlite3_column_double([self.statement sqliteStatement], columnIndex); +} + +- (NSString *)stringForColumn:(NSString *)columnName { + return [self stringForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (NSString *)stringForColumnIndex:(int)columnIndex { + if ([self columnIndexIsNull:columnIndex]) { + return nil; + } + + const char *str = (const char *)sqlite3_column_text([self.statement sqliteStatement], columnIndex); + if (!str) { + return nil; + } + return [NSString stringWithUTF8String:str]; +} + +- (NSDate *)dateForColumn:(NSString *)columnName { + return [self dateForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (NSDate *)dateForColumnIndex:(int)columnIndex { + // TODO: (nlutsenko) probably use formatter + return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIndex]]; +} + +- (NSData *)dataForColumn:(NSString *)columnName { + return [self dataForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (NSData *)dataForColumnIndex:(int)columnIndex { + if ([self columnIndexIsNull:columnIndex]) { + return nil; + } + + int size = sqlite3_column_bytes([self.statement sqliteStatement], columnIndex); + const char *buffer = sqlite3_column_blob([self.statement sqliteStatement], columnIndex); + if (buffer == nil) { + return nil; + } + return [NSData dataWithBytes:buffer length:size]; +} + +- (id)objectForColumn:(NSString *)columnName { + return [self objectForColumnIndex:[self columnIndexForName:columnName]]; +} + +- (id)objectForColumnIndex:(int)columnIndex { + int columnType = sqlite3_column_type([self.statement sqliteStatement], columnIndex); + switch (columnType) { + case SQLITE_INTEGER: + return @([self longForColumnIndex:columnIndex]); + case SQLITE_FLOAT: + return @([self doubleForColumnIndex:columnIndex]); + case SQLITE_BLOB: + return [self dataForColumnIndex:columnIndex]; + default: + return [self stringForColumnIndex:columnIndex]; + } +} + +- (BOOL)columnIsNull:(NSString *)columnName { + return [self columnIndexIsNull:[self columnIndexForName:columnName]]; +} + +- (BOOL)columnIndexIsNull:(int)columnIndex { + return (sqlite3_column_type([self.statement sqliteStatement], columnIndex) == SQLITE_NULL); +} + +- (int)columnIndexForName:(NSString *)columnName { + NSNumber *index = self.columnNameToIndexMap[[columnName lowercaseString]]; + if (index) { + return [index intValue]; + } + // not found + return -1; +} + +- (NSDictionary *)columnNameToIndexMap { + if (!_columnNameToIndexMap) { + int columnCount = sqlite3_column_count([self.statement sqliteStatement]); + NSMutableDictionary *mutableColumnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:columnCount]; + for (int i = 0; i < columnCount; ++i) { + NSString *key = [NSString stringWithUTF8String:sqlite3_column_name([self.statement sqliteStatement], i)]; + mutableColumnNameToIndexMap[[key lowercaseString]] = @(i); + } + _columnNameToIndexMap = mutableColumnNameToIndexMap; + } + return _columnNameToIndexMap; +} + +@end diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase_Private.h b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase_Private.h new file mode 100644 index 0000000..5db0366 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase_Private.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSQLiteDatabase.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSQLiteDatabase () + +@property (nonatomic, strong, readonly) BFTask *databaseClosedTask; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.h b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.h new file mode 100644 index 0000000..04e181c --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! + PFSQLiteStatement is sqlite3_stmt wrapper class. + */ +typedef struct sqlite3_stmt sqlite3_stmt; + +@interface PFSQLiteStatement : NSObject + +@property (atomic, assign, readonly) sqlite3_stmt *sqliteStatement; + +- (instancetype)initWithStatement:(sqlite3_stmt *)stmt; + +- (BOOL)close; +- (BOOL)reset; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.m b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.m new file mode 100644 index 0000000..03f31b6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.m @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSQLiteStatement.h" + +#import + +@implementation PFSQLiteStatement + +- (instancetype)initWithStatement:(sqlite3_stmt *)stmt { + self = [super init]; + if (!stmt || !self) return nil; + + _sqliteStatement = stmt; + + return self; +} + +- (void)dealloc { + [self close]; +} + +- (BOOL)close { + if (!_sqliteStatement) { + return YES; + } + + int resultCode = sqlite3_finalize(_sqliteStatement); + _sqliteStatement = nil; + + return (resultCode == SQLITE_OK || resultCode == SQLITE_DONE); +} + +- (BOOL)reset { + if (!_sqliteStatement) { + return YES; + } + + int resultCode = sqlite3_reset(_sqliteStatement); + return (resultCode == SQLITE_OK || resultCode == SQLITE_DONE); +} + +@end diff --git a/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.h b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.h new file mode 100644 index 0000000..da1ad89 --- /dev/null +++ b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +//TODO: (nlutsenko) Add unit tests for this class. +@interface PFMultiProcessFileLock : NSObject + +@property (nonatomic, copy, readonly) NSString *filePath; +@property (nonatomic, copy, readonly) NSString *lockFilePath; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initForFileWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER; ++ (instancetype)lockForFileWithPath:(NSString *)path; + +@end diff --git a/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.m b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.m new file mode 100644 index 0000000..2c02cea --- /dev/null +++ b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.m @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMultiProcessFileLock.h" + +#import "PFAssert.h" +#import "PFMacros.h" + +static const NSTimeInterval PFMultiProcessLockAttemptsDelay = 0.001; + +@interface PFMultiProcessFileLock () { + dispatch_queue_t _synchronizationQueue; + int _fileDescriptor; +} + +@property (nonatomic, copy, readwrite) NSString *filePath; +@property (nonatomic, copy, readwrite) NSString *lockFilePath; + +@end + +@implementation PFMultiProcessFileLock + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initForFileWithPath:(NSString *)path { + self = [super init]; + if (!self) return nil; + + _filePath = [path copy]; + _lockFilePath = [path stringByAppendingPathExtension:@"pflock"]; + + NSString *queueName = [NSString stringWithFormat:@"com.parse.multiprocess.%@", + [[path lastPathComponent] stringByDeletingPathExtension]]; + _synchronizationQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); + + return self; +} + ++ (instancetype)lockForFileWithPath:(NSString *)path { + return [[self alloc] initForFileWithPath:path]; +} + +- (void)dealloc { + [self unlock]; +} + +///-------------------------------------- +#pragma mark - NSLocking +///-------------------------------------- + +- (void)lock { + dispatch_sync(_synchronizationQueue, ^{ + // Greater than zero means that the lock was already succesfully acquired. + if (_fileDescriptor > 0) { + return; + } + + BOOL locked = NO; + while (!locked) @autoreleasepool { + locked = [self _tryLock]; + if (!locked) { + [NSThread sleepForTimeInterval:PFMultiProcessLockAttemptsDelay]; + } + } + }); +} + +- (void)unlock { + dispatch_sync(_synchronizationQueue, ^{ + // Only descriptor that is greater than zero is going to be open. + if (_fileDescriptor <= 0) { + return; + } + + close(_fileDescriptor); + _fileDescriptor = 0; + }); +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (BOOL)_tryLock { + const char *filePath = [self.lockFilePath fileSystemRepresentation]; + + // Atomically create a lock file if it doesn't exist and acquire the lock. + _fileDescriptor = open(filePath, (O_RDWR | O_CREAT | O_EXLOCK), + ((S_IRUSR | S_IWUSR | S_IXUSR) | (S_IRGRP | S_IWGRP | S_IXGRP) | (S_IROTH | S_IWOTH | S_IXOTH))); + return (_fileDescriptor > 0); +} + +@end diff --git a/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.h b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.h new file mode 100644 index 0000000..098b01a --- /dev/null +++ b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +//TODO: (nlutsenko) Add unit tests for this class. +@interface PFMultiProcessFileLockController : NSObject + +//TODO: (nlutsenko) Re-consider using singleton here. ++ (instancetype)sharedController; + +/*! + Increments the content access counter by 1. + If the count was 0 - this will try to acquire the file lock first. + + @param filePath Path to a file to lock access to. + */ +- (void)beginLockedContentAccessForFileAtPath:(NSString *)filePath; + +/*! + Decrements the content access counter by 1. + If the count reaches 0 - the lock is going to be released. + + @param filePath Path to a file to lock access to. + */ +- (void)endLockedContentAccessForFileAtPath:(NSString *)filePath; + +- (NSUInteger)lockedContentAccessCountForFileAtPath:(NSString *)filePath; + +@end diff --git a/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.m b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.m new file mode 100644 index 0000000..be10da9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.m @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMultiProcessFileLockController.h" + +#import "PFMultiProcessFileLock.h" + +@interface PFMultiProcessFileLockController () { + dispatch_queue_t _synchronizationQueue; + NSMutableDictionary *_locksDictionary; + NSMutableDictionary *_contentAccessDictionary; +} + +@end + +@implementation PFMultiProcessFileLockController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _synchronizationQueue = dispatch_queue_create("com.parse.multiprocesslock.controller", DISPATCH_QUEUE_CONCURRENT); + + _locksDictionary = [NSMutableDictionary dictionary]; + _contentAccessDictionary = [NSMutableDictionary dictionary]; + + return self; +} + ++ (instancetype)sharedController { + static PFMultiProcessFileLockController *controller; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + controller = [[self alloc] init]; + }); + return controller; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (void)beginLockedContentAccessForFileAtPath:(NSString *)filePath { + dispatch_barrier_sync(_synchronizationQueue, ^{ + PFMultiProcessFileLock *fileLock = _locksDictionary[filePath]; + if (!fileLock) { + fileLock = [PFMultiProcessFileLock lockForFileWithPath:filePath]; + _locksDictionary[filePath] = fileLock; + } + + [fileLock lock]; + + NSUInteger contentAccess = [_contentAccessDictionary[filePath] unsignedIntegerValue]; + _contentAccessDictionary[filePath] = @(contentAccess + 1); + }); +} + +- (void)endLockedContentAccessForFileAtPath:(NSString *)filePath { + dispatch_barrier_sync(_synchronizationQueue, ^{ + PFMultiProcessFileLock *fileLock = _locksDictionary[filePath]; + [fileLock unlock]; + + if (fileLock && [_contentAccessDictionary[filePath] unsignedIntegerValue] == 0) { + [_locksDictionary removeObjectForKey:filePath]; + [_contentAccessDictionary removeObjectForKey:filePath]; + } + }); +} + +- (NSUInteger)lockedContentAccessCountForFileAtPath:(NSString *)filePath { + __block NSUInteger value = 0; + dispatch_sync(_synchronizationQueue, ^{ + value = [_contentAccessDictionary[filePath] unsignedIntegerValue]; + }); + return value; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.h b/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.h new file mode 100644 index 0000000..a513f6d --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFObjectBatchController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Fetch +///-------------------------------------- + +- (BFTask *)fetchObjectsAsync:(nullable NSArray *)objects withSessionToken:(nullable NSString *)sessionToken; + +///-------------------------------------- +/// @name Delete +///-------------------------------------- + +- (BFTask *)deleteObjectsAsync:(nullable NSArray *)objects withSessionToken:(nullable NSString *)sessionToken; + +///-------------------------------------- +/// @name Utilities +///-------------------------------------- + ++ (nullable NSArray *)uniqueObjectsArrayFromArray:(nullable NSArray *)objects omitObjectsWithData:(BOOL)omitFetched; ++ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects usingFilter:(BOOL (^)(PFObject *object))filter; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.m b/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.m new file mode 100644 index 0000000..5ad34ca --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/BatchController/PFObjectBatchController.m @@ -0,0 +1,229 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectBatchController.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFErrorUtilities.h" +#import "PFMacros.h" +#import "PFObjectController.h" +#import "PFObjectPrivate.h" +#import "PFQueryPrivate.h" +#import "PFRESTQueryCommand.h" +#import "PFRESTObjectCommand.h" +#import "PFRESTObjectBatchCommand.h" + +@implementation PFObjectBatchController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BFTask *)fetchObjectsAsync:(NSArray *)objects withSessionToken:(NSString *)sessionToken { + if (objects.count == 0) { + return [BFTask taskWithResult:objects]; + } + + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [self _fetchCommandForObjects:objects withSessionToken:sessionToken]; + return [self.dataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFCommandResult *result = task.result; + return [self _processFetchResultAsync:result.result forObjects:objects]; + }]; +} + +- (PFRESTCommand *)_fetchCommandForObjects:(NSArray *)objects withSessionToken:(NSString *)sessionToken { + NSArray *objectIds = [objects valueForKey:@keypath(PFObject, objectId)]; + PFQuery *query = [PFQuery queryWithClassName:[objects.firstObject parseClassName]]; + [query whereKey:@keypath(PFObject, objectId) containedIn:objectIds]; + query.limit = objectIds.count; + return [PFRESTQueryCommand findCommandForQueryState:query.state withSessionToken:sessionToken]; +} + +- (BFTask *)_processFetchResultAsync:(NSDictionary *)result forObjects:(NSArray *)objects { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSArray *results = result[@"results"]; // TODO: (nlutsenko) Move this logic into command itself? + NSArray *objectIds = [results valueForKey:@keypath(PFObject, objectId)]; + NSDictionary *objectResults = [NSDictionary dictionaryWithObjects:results forKeys:objectIds]; + + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:objects.count]; + for (PFObject *object in objects) { + PFObjectController *controller = [[object class] objectController]; + NSDictionary *objectResult = objectResults[object.objectId]; + + BFTask *task = nil; + if (objectResult) { + task = [controller processFetchResultAsync:objectResult forObject:object]; + } else { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorObjectNotFound + message:@"Object not found on the server."]; + task = [BFTask taskWithError:error]; + } + [tasks addObject:task]; + } + return [BFTask taskForCompletionOfAllTasks:tasks]; + }]; +} + +///-------------------------------------- +#pragma mark - Delete +///-------------------------------------- + +- (BFTask *)deleteObjectsAsync:(NSArray *)objects withSessionToken:(NSString *)sessionToken { + if (objects.count == 0) { + return [BFTask taskWithResult:objects]; + } + + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + NSArray *objectBatches = [PFInternalUtils arrayBySplittingArray:objects + withMaximumComponentsPerSegment:PFRESTObjectBatchCommandSubcommandsLimit]; + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:objectBatches.count]; + for (NSArray *batch in objectBatches) { + PFRESTCommand *command = [self _deleteCommandForObjects:batch withSessionToken:sessionToken]; + BFTask *task = [[self.dataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + return [self _processDeleteResultsAsync:[result result] forObjects:batch]; + }]; + [tasks addObject:task]; + } + return [[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task) { + NSError *taskError = task.error; + if (taskError && [taskError.domain isEqualToString:BFTaskErrorDomain]) { + NSArray *taskErrors = taskError.userInfo[@"errors"]; + NSMutableArray *errors = [NSMutableArray array]; + for (NSError *error in taskErrors) { + if ([error.domain isEqualToString:BFTaskErrorDomain]) { + [errors addObjectsFromArray:error.userInfo[@"errors"]]; + } else { + [errors addObject:error]; + } + } + return [BFTask taskWithError:[NSError errorWithDomain:BFTaskErrorDomain + code:kBFMultipleErrorsError + userInfo:@{ @"errors" : errors }]]; + } + return task; + }]; + }] continueWithSuccessResult:objects]; +} + +- (PFRESTCommand *)_deleteCommandForObjects:(NSArray *)objects withSessionToken:(NSString *)sessionToken { + NSMutableArray *commands = [NSMutableArray arrayWithCapacity:objects.count]; + for (PFObject *object in objects) { + PFRESTCommand *deleteCommand = [PFRESTObjectCommand deleteObjectCommandForObjectState:object._state + withSessionToken:sessionToken]; + [commands addObject:deleteCommand]; + } + return [PFRESTObjectBatchCommand batchCommandWithCommands:commands sessionToken:sessionToken]; +} + +- (BFTask *)_processDeleteResultsAsync:(NSArray *)results forObjects:(NSArray *)objects { + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:results.count]; + [results enumerateObjectsUsingBlock:^(NSDictionary *result, NSUInteger idx, BOOL *stop) { + PFObject *object = objects[idx]; + NSDictionary *errorResult = result[@"error"]; + NSDictionary *successResult = result[@"success"]; + + id controller = [[object class] objectController]; + BFTask *task = [controller processDeleteResultAsync:successResult forObject:object]; + if (errorResult) { + task = [task continueWithBlock:^id(BFTask *task) { + return [BFTask taskWithError:[PFErrorUtilities errorFromResult:errorResult]]; + }]; + } + [tasks addObject:task]; + }]; + return [BFTask taskForCompletionOfAllTasks:tasks]; +} + +///-------------------------------------- +#pragma mark - Utilities +///-------------------------------------- + +//TODO: (nlutsenko) Convert to use `uniqueObjectsArrayFromArray:usingFilter:` ++ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects omitObjectsWithData:(BOOL)omitFetched { + if (objects.count == 0) { + return objects; + } + + NSMutableSet *set = [NSMutableSet setWithCapacity:[objects count]]; + NSString *className = [objects.firstObject parseClassName]; + for (PFObject *object in objects) { + @synchronized (object.lock) { + if (omitFetched && [object isDataAvailable]) { + continue; + } + + //TODO: (nlutsenko) Convert to using errors instead of assertions. + PFParameterAssert([className isEqualToString:object.parseClassName], + @"All object should be in the same class."); + PFParameterAssert(object.objectId != nil, + @"All objects must exist on the server."); + + [set addObject:object]; + } + } + return [set allObjects]; +} + ++ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects usingFilter:(BOOL (^)(PFObject *object))filter { + if (objects.count == 0) { + return objects; + } + + NSMutableDictionary *uniqueObjects = [NSMutableDictionary dictionary]; + for (PFObject *object in objects) { + if (!filter(object)) { + continue; + } + + // Use stringWithFormat: in case objectId or parseClassName are nil. + NSString *objectIdentifier = [NSString stringWithFormat:@"%@%@", object.parseClassName, object.objectId]; + if (!uniqueObjects[objectIdentifier]) { + uniqueObjects[objectIdentifier] = object; + } + } + return [uniqueObjects allValues]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.h b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.h new file mode 100644 index 0000000..371acc1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFDecoder; +@class PFEncoder; +@class PFObject; + +NS_ASSUME_NONNULL_BEGIN + +/*! + Handles encoding/decoding of `PFObject`s into a /2 JSON format. + /2 format is only used for persisting `currentUser`, `currentInstallation` to disk when LDS is not enabled. + */ +@interface PFObjectFileCoder : NSObject + +///-------------------------------------- +/// @name Encode +///-------------------------------------- + ++ (NSData *)dataFromObject:(PFObject *)object usingEncoder:(PFEncoder *)encoder; + +///-------------------------------------- +/// @name Decode +///-------------------------------------- + ++ (PFObject *)objectFromData:(NSData *)data usingDecoder:(PFDecoder *)decoder; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.m b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.m new file mode 100644 index 0000000..75bc197 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCoder.m @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectFileCoder.h" + +#import "PFJSONSerialization.h" +#import "PFObjectFileCodingLogic.h" +#import "PFObjectPrivate.h" +#import "PFObjectState.h" + +@implementation PFObjectFileCoder + +///-------------------------------------- +#pragma mark - Encode +///-------------------------------------- + ++ (NSData *)dataFromObject:(PFObject *)object usingEncoder:(PFEncoder *)encoder { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + result[@"classname"] = object._state.parseClassName; + result[@"data"] = [object._state dictionaryRepresentationWithObjectEncoder:encoder]; + return [PFJSONSerialization dataFromJSONObject:result]; +} + +///-------------------------------------- +#pragma mark - Decode +///-------------------------------------- + ++ (PFObject *)objectFromData:(NSData *)data usingDecoder:(PFDecoder *)decoder { + NSDictionary *dictionary = [PFJSONSerialization JSONObjectFromData:data]; + NSString *className = dictionary[@"classname"] ?: dictionary[@"className"]; + NSString *objectId = dictionary[@"data"][@"objectId"] ?: dictionary[@"id"]; + + PFObject *object = [PFObject objectWithoutDataWithClassName:className objectId:objectId]; + [[[object class] objectFileCodingLogic] updateObject:object fromDictionary:dictionary usingDecoder:decoder]; + return object; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.h b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.h new file mode 100644 index 0000000..c9ce66c --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFDecoder; +@class PFObject; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFObjectFileCodingLogic : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + ++ (instancetype)codingLogic; + +///-------------------------------------- +/// @name Logic +///-------------------------------------- + +- (void)updateObject:(PFObject *)object fromDictionary:(NSDictionary *)dictionary usingDecoder:(PFDecoder *)decoder; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.m b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.m new file mode 100644 index 0000000..f5955f1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.m @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectFileCodingLogic.h" + +#import "PFMutableObjectState.h" +#import "PFObjectPrivate.h" + +@implementation PFObjectFileCodingLogic + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)codingLogic { + return [[self alloc] init]; +} + +///-------------------------------------- +#pragma mark - Logic +///-------------------------------------- + +- (void)updateObject:(PFObject *)object fromDictionary:(NSDictionary *)dictionary usingDecoder:(PFDecoder *)decoder { + PFMutableObjectState *state = [object._state mutableCopy]; + NSString *newObjectId = dictionary[@"id"]; + if (newObjectId) { + state.objectId = newObjectId; + } + NSString *createdAtString = dictionary[@"created_at"]; + if (createdAtString) { + [state setCreatedAtFromString:createdAtString]; + } + NSString *updatedAtString = dictionary[@"updated_at"]; + if (updatedAtString) { + [state setUpdatedAtFromString:updatedAtString]; + } + object._state = state; + + NSDictionary *newPointers = dictionary[@"pointers"]; + NSMutableDictionary *pointersDictionary = [NSMutableDictionary dictionaryWithCapacity:newPointers.count]; + [newPointers enumerateKeysAndObjectsUsingBlock:^(id key, NSArray *pointerArray, BOOL *stop) { + PFObject *pointer = [PFObject objectWithoutDataWithClassName:[pointerArray firstObject] + objectId:[pointerArray lastObject]]; + pointersDictionary[key] = pointer; + }]; + + NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary[@"data"]]; + [dataDictionary addEntriesFromDictionary:pointersDictionary]; + [object _mergeAfterFetchWithResult:dataDictionary decoder:decoder completeData:YES]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.h b/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.h new file mode 100644 index 0000000..524078c --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +// REST Key magic strings +extern NSString *const PFObjectCompleteRESTKey; +extern NSString *const PFObjectOperationsRESTKey; +extern NSString *const PFObjectTypeRESTKey; +extern NSString *const PFObjectObjectIdRESTKey; +extern NSString *const PFObjectUpdatedAtRESTKey; +extern NSString *const PFObjectCreatedAtRESTKey; +extern NSString *const PFObjectIsDeletingEventuallyRESTKey; +extern NSString *const PFObjectClassNameRESTKey; +extern NSString *const PFObjectACLRESTKey; + +extern NSString *const PFObjectDefaultPin; diff --git a/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.m b/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.m new file mode 100644 index 0000000..76047ec --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Constants/PFObjectConstants.m @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectConstants.h" + +NSString *const PFObjectCompleteRESTKey = @"__complete"; +NSString *const PFObjectOperationsRESTKey = @"__operations"; +NSString *const PFObjectTypeRESTKey = @"__type"; +NSString *const PFObjectObjectIdRESTKey = @"objectId"; +NSString *const PFObjectUpdatedAtRESTKey = @"updatedAt"; +NSString *const PFObjectCreatedAtRESTKey = @"createdAt"; +NSString *const PFObjectIsDeletingEventuallyRESTKey = @"isDeletingEventually"; +NSString *const PFObjectClassNameRESTKey = @"className"; +NSString *const PFObjectACLRESTKey = @"ACL"; + +NSString *const PFObjectDefaultPin = @"_default"; diff --git a/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.h b/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.h new file mode 100644 index 0000000..27dd6dd --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFOfflineObjectController : PFObjectController + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.m b/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.m new file mode 100644 index 0000000..e9cefc3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.m @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFOfflineObjectController.h" + +#import "BFTask+Private.h" +#import "PFMacros.h" +#import "PFObjectController_Private.h" +#import "PFObjectPrivate.h" +#import "PFObjectState.h" +#import "PFOfflineStore.h" + +@interface PFOfflineObjectController () + +@property (nonatomic, strong, readonly) PFOfflineStore *offlineStore; + +@end + +@implementation PFOfflineObjectController + +@dynamic dataSource; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource { + return [super initWithDataSource:dataSource]; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [super controllerWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - PFObjectController +///-------------------------------------- + +- (BFTask *)processFetchResultAsync:(NSDictionary *)result forObject:(PFObject *)object { + return [[[[self.offlineStore fetchObjectLocallyAsync:object] continueWithBlock:^id(BFTask *task) { + // Catch CacheMiss error and ignore it. + if ([task.error.domain isEqualToString:PFParseErrorDomain] && + task.error.code == kPFErrorCacheMiss) { + return nil; + } + return task; + }] continueWithBlock:^id(BFTask *task) { + return [super processFetchResultAsync:result forObject:object]; + }] continueWithBlock:^id(BFTask *task) { + return [[self.offlineStore updateDataForObjectAsync:object] continueWithBlock:^id(BFTask *task) { + // Catch CACHE_MISS and ignore it. + if ([task.error.domain isEqualToString:PFParseErrorDomain] && + task.error.code == kPFErrorCacheMiss) { + return [BFTask taskWithResult:nil]; + } + return task; + }]; + }]; +} + +- (BFTask *)processDeleteResultAsync:(nullable NSDictionary *)result forObject:(PFObject *)object { + @weakify(self); + return [[super processDeleteResultAsync:result forObject:object] continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (object._state.deleted) { + return [self.offlineStore deleteDataForObjectAsync:object]; + } + return [self.offlineStore updateDataForObjectAsync:object]; + }]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFOfflineStore *)offlineStore { + return self.dataSource.offlineStore; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.h b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.h new file mode 100644 index 0000000..44a8ac6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFDataProvider.h" +#import "PFObjectControlling.h" + + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFObjectController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.m b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.m new file mode 100644 index 0000000..ec5d45b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController.m @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectController.h" +#import "PFObjectController_Private.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFErrorUtilities.h" +#import "PFMacros.h" +#import "PFObjectPrivate.h" +#import "PFObjectState.h" +#import "PFRESTObjectCommand.h" +#import "PFTaskQueue.h" + +@implementation PFObjectController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - PFObjectControlling +///-------------------------------------- + +#pragma mark Fetch + +- (BFTask *)fetchObjectAsync:(PFObject *)object withSessionToken:(NSString *)sessionToken { + @weakify(self); + return [[[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + return [object _validateFetchAsync]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFRESTCommand *command = [PFRESTObjectCommand fetchObjectCommandForObjectState:[object._state copy] + withSessionToken:sessionToken]; + return [self _runFetchCommand:command forObject:object]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFCommandResult *result = task.result; + return [self processFetchResultAsync:result.result forObject:object]; + }] continueWithSuccessResult:object]; +} + +- (BFTask *)_runFetchCommand:(PFRESTCommand *)command forObject:(PFObject *)object { + return [self.dataSource.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed]; +} + +- (BFTask *)processFetchResultAsync:(NSDictionary *)result forObject:(PFObject *)object { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSDictionary *fetchedObjects = [object _collectFetchedObjects]; + @synchronized (object.lock) { + PFKnownParseObjectDecoder *decoder = [PFKnownParseObjectDecoder decoderWithFetchedObjects:fetchedObjects]; + [object _mergeAfterFetchWithResult:result decoder:decoder completeData:YES]; + } + return nil; + }]; +} + +#pragma mark Delete + +- (BFTask *)deleteObjectAsync:(PFObject *)object withSessionToken:(nullable NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + return [object _validateDeleteAsync]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFObjectState *state = [object._state copy]; + if (!state.objectId) { + return nil; + } + + PFRESTCommand *command = [PFRESTObjectCommand deleteObjectCommandForObjectState:state + withSessionToken:sessionToken]; + return [[self _runDeleteCommand:command forObject:object] continueWithBlock:^id(BFTask *fetchTask) { + @strongify(self); + PFCommandResult *result = fetchTask.result; + return [[self processDeleteResultAsync:result.result forObject:object] continueWithBlock:^id(BFTask *task) { + // Propagate the result of network task if it's faulted, cancelled. + if (fetchTask.faulted || fetchTask.cancelled) { + return fetchTask; + } + // Propagate the result of processDeleteResult otherwise. + return task; + }]; + }]; + }]; +} + +- (BFTask *)_runDeleteCommand:(PFRESTCommand *)command forObject:(PFObject *)object { + return [self.dataSource.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed]; +} + +- (BFTask *)processDeleteResultAsync:(NSDictionary *)result forObject:(PFObject *)object { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + BOOL deleted = (result != nil); + [object _setDeleted:deleted]; + return nil; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController_Private.h b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController_Private.h new file mode 100644 index 0000000..7ca5abc --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectController_Private.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectController.h" + +@class PFRESTCommand; + +@interface PFObjectController () + +///-------------------------------------- +/// @name Fetch +///-------------------------------------- + +- (BFTask *)_runFetchCommand:(PFRESTCommand *)command forObject:(PFObject *)object; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Controller/PFObjectControlling.h b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectControlling.h new file mode 100644 index 0000000..e57a645 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Controller/PFObjectControlling.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFObjectControlling + +///-------------------------------------- +/// @name Fetch +///-------------------------------------- + +/*! + Fetches an object asynchronously. + + @param object Object to fetch. + @param sessionToken Session token to use. + + @returns `BFTask` with result set to `PFObject`. + */ +- (BFTask *)fetchObjectAsync:(PFObject *)object withSessionToken:(nullable NSString *)sessionToken; + +- (BFTask *)processFetchResultAsync:(NSDictionary *)result forObject:(PFObject *)object; + +///-------------------------------------- +/// @name Delete +///-------------------------------------- + +/*! + Deletes an object asynchronously. + + @param object Object to fetch. + @param sessionToken Session token to use. + + @returns `BFTask` with result set to `nil`. + */ +- (BFTask *)deleteObjectAsync:(PFObject *)object withSessionToken:(nullable NSString *)sessionToken; + +//TODO: (nlutsenko) This needs removal, figure out how to kill it. +- (BFTask *)processDeleteResultAsync:(nullable NSDictionary *)result forObject:(PFObject *)object; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/CurrentController/PFCurrentObjectControlling.h b/Pods/Parse/Parse/Internal/Object/CurrentController/PFCurrentObjectControlling.h new file mode 100644 index 0000000..9cbd1b0 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/CurrentController/PFCurrentObjectControlling.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +typedef NS_ENUM(NSUInteger, PFCurrentObjectStorageType) { + PFCurrentObjectStorageTypeFile = 1, + PFCurrentObjectStorageTypeOfflineStore, +}; + +@protocol PFCurrentObjectControlling + +@property (nonatomic, assign, readonly) PFCurrentObjectStorageType storageType; + +///-------------------------------------- +/// @name Current +///-------------------------------------- + +- (BFTask *)getCurrentObjectAsync; +- (BFTask *)saveCurrentObjectAsync:(PFObject *)object; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.h b/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.h new file mode 100644 index 0000000..b507e8a --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFFieldOperation; +@class PFOperationSet; + +@interface PFObjectEstimatedData : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithServerData:(NSDictionary *)serverData + operationSetQueue:(NSArray *)operationSetQueue; ++ (instancetype)estimatedDataFromServerData:(NSDictionary *)serverData + operationSetQueue:(NSArray *)operationSetQueue; + +///-------------------------------------- +/// @name Read +///-------------------------------------- + +- (id)objectForKey:(NSString *)key; +- (id)objectForKeyedSubscript:(NSString *)keyedSubscript; + +- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(NSString *key, id obj, BOOL *stop))block; + +@property (nonatomic, copy, readonly) NSArray *allKeys; +@property (nonatomic, copy, readonly) NSDictionary *dictionaryRepresentation; + +///-------------------------------------- +/// @name Write +///-------------------------------------- + +- (id)applyFieldOperation:(PFFieldOperation *)operation forKey:(NSString *)key; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.m b/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.m new file mode 100644 index 0000000..7fb5df5 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.m @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectEstimatedData.h" + +#import "PFObjectUtilities.h" + +@interface PFObjectEstimatedData () { + NSMutableDictionary *_dataDictionary; +} + +@end + +@implementation PFObjectEstimatedData + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _dataDictionary = [NSMutableDictionary dictionary]; + + return self; +} + +- (instancetype)initWithServerData:(NSDictionary *)serverData + operationSetQueue:(NSArray *)operationSetQueue { + self = [super init]; + if (!self) return nil; + + // Don't use mutableCopy to make sure we never initialize _dataDictionary to `nil`. + _dataDictionary = [NSMutableDictionary dictionaryWithDictionary:serverData]; + for (PFOperationSet *operationSet in operationSetQueue) { + [PFObjectUtilities applyOperationSet:operationSet toDictionary:_dataDictionary]; + } + + return self; +} + ++ (instancetype)estimatedDataFromServerData:(NSDictionary *)serverData + operationSetQueue:(NSArray *)operationSetQueue { + return [[self alloc] initWithServerData:serverData operationSetQueue:operationSetQueue]; +} + +///-------------------------------------- +#pragma mark - Read +///-------------------------------------- + +- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(NSString *key, id obj, BOOL *stop))block { + [_dataDictionary enumerateKeysAndObjectsUsingBlock:block]; +} + +- (id)objectForKey:(NSString *)key { + return [_dataDictionary objectForKey:key]; +} + +- (id)objectForKeyedSubscript:(NSString *)keyedSubscript { + return [_dataDictionary objectForKeyedSubscript:keyedSubscript]; +} + +- (NSArray *)allKeys { + return [_dataDictionary allKeys]; +} + +- (NSDictionary *)dictionaryRepresentation { + return [_dataDictionary copy]; +} + +///-------------------------------------- +#pragma mark - Write +///-------------------------------------- + +- (id)applyFieldOperation:(PFFieldOperation *)operation forKey:(NSString *)key { + return [PFObjectUtilities newValueByApplyingFieldOperation:operation toDictionary:_dataDictionary forKey:key]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.h b/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.h new file mode 100644 index 0000000..8537ddf --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +@interface PFObjectFilePersistenceController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Objects +///-------------------------------------- + +/*! + Loads and creates a PFObject from file. + + @param key File name to use. + + @returns `BFTask` with `PFObject` or `nil` result. + */ +- (BFTask *)loadPersistentObjectAsyncForKey:(NSString *)key; + +/*! + Saves a given object to a file with name. + + @param object Object to save. + @param key File name to use. + + @returns `BFTask` with `nil` result. + */ +- (BFTask *)persistObjectAsync:(PFObject *)object forKey:(NSString *)key; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.m b/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.m new file mode 100644 index 0000000..38fdcd1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.m @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectFilePersistenceController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFFileManager.h" +#import "PFJSONSerialization.h" +#import "PFMacros.h" +#import "PFMultiProcessFileLockController.h" +#import "PFObjectFileCoder.h" +#import "PFObjectPrivate.h" + +@implementation PFObjectFilePersistenceController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Objects +///-------------------------------------- + +- (BFTask *)loadPersistentObjectAsyncForKey:(NSString *)key { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + + NSString *path = [self.dataSource.fileManager parseDataItemPathForPathComponent:key]; + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:path]; + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:path]; + return nil; + } + + NSError *error = nil; + NSData *jsonData = [NSData dataWithContentsOfFile:path + options:NSDataReadingMappedIfSafe + error:&error]; + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:path]; + + if (error) { + return [BFTask taskWithError:error]; + } + return jsonData; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSData *jsonData = task.result; + if (jsonData) { + PFObject *object = [PFObjectFileCoder objectFromData:jsonData usingDecoder:[PFDecoder objectDecoder]]; + return object; + } + + return nil; + }]; +} + +- (BFTask *)persistObjectAsync:(PFObject *)object forKey:(NSString *)key { + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + + NSData *data = [PFObjectFileCoder dataFromObject:object usingEncoder:[PFPointerObjectEncoder objectEncoder]]; + + NSString *filePath = [self.dataSource.fileManager parseDataItemPathForPathComponent:key]; + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:filePath]; + + return [[PFFileManager writeDataAsync:data toFile:filePath] continueWithBlock:^id(BFTask *task) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:filePath]; + return nil; + }]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.h b/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.h new file mode 100644 index 0000000..541056b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFDataProvider.h" + +/*! + A disk-based map of local ids to global Parse objectIds. Every entry in this + map has a retain count, and the entry will be removed from the map if the + retain count reaches 0. Every time a localId is written out to disk, its retain + count should be incremented. When the reference on disk is deleted, it should + be decremented. Some entries in this map may not have an object id yet. + This class is thread-safe. + */ +@interface PFObjectLocalIdStore : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)storeWithDataSource:(id)dataSource; + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSString *)createLocalId; +- (void)retainLocalIdOnDisk:(NSString *)localId; +- (void)releaseLocalIdOnDisk:(NSString *)localId; + +- (void)setObjectId:(NSString *)objectId forLocalId:(NSString *)localId; +- (NSString *)objectIdForLocalId:(NSString *)localId; + +// For testing only. +- (BOOL)clear; +- (void)clearInMemoryCache; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.m b/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.m new file mode 100644 index 0000000..7c88c3e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.m @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectLocalIdStore.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFFileManager.h" +#import "PFInternalUtils.h" +#import "PFJSONSerialization.h" +#import "PFLogging.h" +#import "Parse_Private.h" + +static NSString *const _PFObjectLocalIdStoreDiskFolderPath = @"LocalId"; + +///-------------------------------------- +#pragma mark - PFObjectLocalIdStoreMapEntry +///-------------------------------------- + +/*! + * Internal class representing all the information we know about a local id. + */ +@interface PFObjectLocalIdStoreMapEntry : NSObject + +@property (nonatomic, strong) NSString *objectId; +@property (atomic, assign) int referenceCount; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFile:(NSString *)filePath; + +@end + +@implementation PFObjectLocalIdStoreMapEntry + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithFile:(NSString *)filePath { + self = [self init]; + if (!self) return nil; + + NSData *jsonData = [NSData dataWithContentsOfFile:filePath]; + NSDictionary *dictionary = [PFJSONSerialization JSONObjectFromData:jsonData]; + + _objectId = [dictionary[@"objectId"] copy]; + _referenceCount = [dictionary[@"referenceCount"] intValue]; + + return self; +} + +- (void)writeToFile:(NSString *)filePath { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + dictionary[@"referenceCount"] = @(self.referenceCount); + if (self.objectId) { + dictionary[@"objectId"] = self.objectId; + } + + NSData *jsonData = [PFJSONSerialization dataFromJSONObject:dictionary]; + [[PFFileManager writeDataAsync:jsonData toFile:filePath] waitForResult:nil withMainThreadWarning:NO]; +} + +@end + +///-------------------------------------- +#pragma mark - PFObjectLocalIdStore +///-------------------------------------- + +@interface PFObjectLocalIdStore () { + NSString *_diskPath; + NSObject *_lock; + NSMutableDictionary *_inMemoryCache; +} + +@end + +@implementation PFObjectLocalIdStore + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +/*! + * Creates a new LocalIdManager with default options. + */ +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + _lock = [[NSObject alloc] init]; + _inMemoryCache = [NSMutableDictionary dictionary]; + + // Construct the path to the disk storage directory. + _diskPath = [[Parse _currentManager].fileManager parseDataItemPathForPathComponent:_PFObjectLocalIdStoreDiskFolderPath]; + + NSError *error = nil; + [[PFFileManager createDirectoryIfNeededAsyncAtPath:_diskPath] waitForResult:&error withMainThreadWarning:NO]; + if (error) { + PFLogError(PFLoggingTagCommon, @"Unable to create directories for local id storage with error: %@", error); + } + + return self; +} + ++ (instancetype)storeWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +/*! + * Returns Yes if localId has the right basic format for a local id. + */ ++ (BOOL)isLocalId:(NSString *)localId { + if ([localId length] != 22U) { + return NO; + } + if (![localId hasPrefix:@"local_"]) { + return NO; + } + for (int i = 6; i < [localId length]; ++i) { + if (!ishexnumber([localId characterAtIndex:i])) { + return NO; + } + } + return YES; +} + +/*! + * Grabs one entry in the local id map off the disk. + */ +- (PFObjectLocalIdStoreMapEntry *)getMapEntry:(NSString *)localId { + PFConsistencyAssert([[self class] isLocalId:localId], @"Tried to get invalid local id: \"%@\".", localId); + + PFObjectLocalIdStoreMapEntry *entry = nil; + + NSString *file = [_diskPath stringByAppendingPathComponent:localId]; + if (![[NSFileManager defaultManager] isReadableFileAtPath:file]) { + entry = [[PFObjectLocalIdStoreMapEntry alloc] init]; + } else { + entry = [[PFObjectLocalIdStoreMapEntry alloc] initWithFile:file]; + } + + // If there's an objectId in memory, make sure it matches the one in the + // file. This is in case the id was retained on disk *after* it was resolved. + if (!entry.objectId) { + NSString *objectId = [_inMemoryCache objectForKey:localId]; + if (objectId) { + entry.objectId = objectId; + if (entry.referenceCount > 0) { + [self putMapEntry:entry forLocalId:localId]; + } + } + } + + return entry; +} + +/*! + * Writes one entry to the local id map on disk. + */ +- (void)putMapEntry:(PFObjectLocalIdStoreMapEntry *)entry forLocalId:(NSString *)localId { + PFConsistencyAssert([[self class] isLocalId:localId], @"Tried to get invalid local id: \"%@\".", localId); + + NSString *file = [_diskPath stringByAppendingPathComponent:localId]; + [entry writeToFile:file]; +} + +/*! + * Removes an entry from the local id map on disk. + */ +- (void)removeMapEntry:(NSString *)localId { + PFConsistencyAssert([[self class] isLocalId:localId], @"Tried to get invalid local id: \"%@\".", localId); + + NSString *file = [_diskPath stringByAppendingPathComponent:localId]; + [[NSFileManager defaultManager] removeItemAtPath:file error:nil]; +} + +/*! + * Creates a new local id in the map. + */ +- (NSString *)createLocalId { + @synchronized (_lock) { + // Generate a new random string of upper and lower case letters. + + // Start by generating a number. It will be the localId as a base-52 number. + // It has to be a uint64_t because log256(52^10) ~= 7.13 bytes. + uint64_t localIdNumber = (((uint64_t)arc4random()) << 32) | ((uint64_t)arc4random()); + NSString *localId = [NSString stringWithFormat:@"local_%016llx", localIdNumber]; + + PFConsistencyAssert([[self class] isLocalId:localId], @"Generated an invalid local id: \"%@\".", localId); + + return localId; + } +} + +/*! + * Increments the retain count of a local id on disk. + */ +- (void)retainLocalIdOnDisk:(NSString *)localId { + @synchronized (_lock) { + PFObjectLocalIdStoreMapEntry *entry = [self getMapEntry:localId]; + entry.referenceCount++; + [self putMapEntry:entry forLocalId:localId]; + } +} + +/*! + * Decrements the retain count of a local id on disk. + * If the retain count hits zero, the id is forgotten forever. + */ +- (void)releaseLocalIdOnDisk:(NSString *)localId { + @synchronized (_lock) { + PFObjectLocalIdStoreMapEntry *entry = [self getMapEntry:localId]; + if (--entry.referenceCount > 0) { + [self putMapEntry:entry forLocalId:localId]; + } else { + [self removeMapEntry:localId]; + } + } +} + +/*! + * Sets the objectId associated with a given local id. + */ +- (void)setObjectId:(NSString *)objectId forLocalId:(NSString *)localId { + @synchronized (_lock) { + PFObjectLocalIdStoreMapEntry *entry = [self getMapEntry:localId]; + if (entry.referenceCount > 0) { + entry.objectId = objectId; + [self putMapEntry:entry forLocalId:localId]; + } + [_inMemoryCache setObject:objectId forKey:localId]; + } +} + +/*! + * Returns the objectId associated with a given local id. + * Returns nil if no objectId is yet known for the lcoal id. + */ +- (NSString *)objectIdForLocalId:(NSString *)localId { + @synchronized (_lock) { + NSString *objectId = [_inMemoryCache objectForKey:localId]; + if (objectId) { + return objectId; + } + + PFObjectLocalIdStoreMapEntry *entry = [self getMapEntry:localId]; + return entry.objectId; + } +} + +/*! + * Removes all local ids from the disk and memory caches. + */ +- (BOOL)clear { + @synchronized (_lock) { + [self clearInMemoryCache]; + + BOOL empty = ([[[[NSFileManager defaultManager] enumeratorAtPath:_diskPath] allObjects] count] == 0); + + [[NSFileManager defaultManager] removeItemAtPath:_diskPath error:nil]; + + [[NSFileManager defaultManager] createDirectoryAtPath:_diskPath + withIntermediateDirectories:YES + attributes:nil + error:nil]; + return !empty; + } +} + +/*! + * Removes all local ids from the memory cache. + */ +- (void)clearInMemoryCache { + @synchronized (_lock) { + [_inMemoryCache removeAllObjects]; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.h b/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.h new file mode 100644 index 0000000..5456ffa --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFDecoder; +@class PFEncoder; +@class PFFieldOperation; + +/*! + A set of field-level operations that can be performed on an object, corresponding to one + command. For example, all the data for a single call to save() will be packaged here. It is + assumed that the PFObject that owns the operations handles thread-safety. + */ +@interface PFOperationSet : NSObject + +/*! + Returns true if this set corresponds to a call to saveEventually. + */ +@property (nonatomic, assign, getter=isSaveEventually) BOOL saveEventually; + +/*! + A unique id for this operation set. + */ +@property (nonatomic, copy, readonly) NSString *uuid; + +@property (nonatomic, copy) NSDate *updatedAt; + +/*! + Merges the changes from the given operation set into this one. Most typically, this is what + happens when a save fails and changes need to be rolled into the next save. + */ +- (void)mergeOperationSet:(PFOperationSet *)other; + +/*! + Converts this operation set into its REST format for serializing to the pinning store + */ +- (NSDictionary *)RESTDictionaryUsingObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs; + +/*! + The inverse of RESTDictionaryUsingObjectEncoder. + Creates a new OperationSet from the given NSDictionary + */ ++ (PFOperationSet *)operationSetFromRESTDictionary:(NSDictionary *)data + usingDecoder:(PFDecoder *)decoder; + +///-------------------------------------- +/// @name Accessors +///-------------------------------------- + +- (id)objectForKey:(id)aKey; +- (id)objectForKeyedSubscript:(id)aKey; +- (NSUInteger)count; +- (NSEnumerator *)keyEnumerator; + +- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(NSString *key, PFFieldOperation *operation, BOOL *stop))block; + +- (void)setObject:(id)anObject forKey:(id)aKey; +- (void)setObject:(id)anObject forKeyedSubscript:(id)aKey; +- (void)removeObjectForKey:(id)aKey; +- (void)removeAllObjects; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.m b/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.m new file mode 100644 index 0000000..f1df813 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/OperationSet/PFOperationSet.m @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFOperationSet.h" + +#import "PFACL.h" +#import "PFACLPrivate.h" +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFFieldOperation.h" +#import "PFInternalUtils.h" + +NSString *const PFOperationSetKeyUUID = @"__uuid"; +NSString *const PFOperationSetKeyIsSaveEventually = @"__isSaveEventually"; +NSString *const PFOperationSetKeyUpdatedAt = @"__updatedAt"; +NSString *const PFOperationSetKeyACL = @"ACL"; + +@interface PFOperationSet() + +@property (nonatomic, strong) NSMutableDictionary *dictionary; + +@end + +@implementation PFOperationSet + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + return [self initWithUUID:[[NSUUID UUID] UUIDString]]; +} + +- (instancetype)initWithUUID:(NSString *)uuid { + self = [super init]; + if (!self) return nil; + + _dictionary = [NSMutableDictionary dictionary]; + _uuid = [uuid copy]; + + _updatedAt = [NSDate date]; + + return self; +} + +///-------------------------------------- +#pragma mark - Merge +///-------------------------------------- + +- (void)mergeOperationSet:(PFOperationSet *)other { + [other.dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + PFFieldOperation *localOperation = self.dictionary[key]; + PFFieldOperation *remoteOperation = other.dictionary[key]; + if (localOperation != nil) { + localOperation = [localOperation mergeWithPrevious:remoteOperation]; + self.dictionary[key] = localOperation; + } else { + self.dictionary[key] = remoteOperation; + } + }]; + self.updatedAt = [NSDate date]; +} + +///-------------------------------------- +#pragma mark - Encoding +///-------------------------------------- + +- (NSDictionary *)RESTDictionaryUsingObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs { + NSMutableDictionary *operationSetResult = [[NSMutableDictionary alloc] init]; + [self.dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + operationSetResult[key] = [obj encodeWithObjectEncoder:objectEncoder]; + }]; + + operationSetResult[PFOperationSetKeyUUID] = self.uuid; + operationSetResult[PFOperationSetKeyUpdatedAt] = [objectEncoder encodeObject:self.updatedAt]; + + if (self.saveEventually) { + operationSetResult[PFOperationSetKeyIsSaveEventually] = @YES; + } + *operationSetUUIDs = @[ self.uuid ]; + return operationSetResult; +} + ++ (PFOperationSet *)operationSetFromRESTDictionary:(NSDictionary *)data + usingDecoder:(PFDecoder *)decoder { + NSMutableDictionary *mutableData = [data mutableCopy]; + NSString *inputUUID = mutableData[PFOperationSetKeyUUID]; + [mutableData removeObjectForKey:PFOperationSetKeyUUID]; + PFOperationSet *operationSet = nil; + if (inputUUID == nil) { + operationSet = [[PFOperationSet alloc] init]; + } else { + operationSet = [[PFOperationSet alloc] initWithUUID:inputUUID]; + } + + NSNumber *saveEventuallyFlag = mutableData[PFOperationSetKeyIsSaveEventually]; + if (saveEventuallyFlag) { + operationSet.saveEventually = [saveEventuallyFlag boolValue]; + [mutableData removeObjectForKey:PFOperationSetKeyIsSaveEventually]; + } + + NSDate *updatedAt = mutableData[PFOperationSetKeyUpdatedAt]; + [mutableData removeObjectForKey:PFOperationSetKeyUpdatedAt]; + + [mutableData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + id value = [decoder decodeObject:obj]; + PFFieldOperation *fieldOperation = nil; + if ([key isEqualToString:PFOperationSetKeyACL]) { + // TODO (hallucinogen): where to use the decoder? + value = [PFACL ACLWithDictionary:obj]; + } + if ([value isKindOfClass:[PFFieldOperation class]]) { + fieldOperation = value; + } else { + fieldOperation = [PFSetOperation setWithValue:value]; + } + operationSet[key] = fieldOperation; + }]; + operationSet.updatedAt = updatedAt ? [decoder decodeObject:updatedAt] : nil; + + return operationSet; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (id)objectForKey:(id)aKey { + return self.dictionary[aKey]; +} + +- (id)objectForKeyedSubscript:(id)aKey { + return [self objectForKey:aKey]; +} + +- (NSUInteger)count { + return [self.dictionary count]; +} + +- (NSEnumerator *)keyEnumerator { + return [self.dictionary keyEnumerator]; +} + +- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(NSString *key, PFFieldOperation *operation, BOOL *stop))block { + [self.dictionary enumerateKeysAndObjectsUsingBlock:block]; +} + +- (void)setObject:(id)anObject forKey:(id)aKey { + self.dictionary[aKey] = anObject; + self.updatedAt = [NSDate date]; +} + +- (void)setObject:(id)anObject forKeyedSubscript:(id)key { + [self setObject:anObject forKey:key]; +} + +- (void)removeObjectForKey:(id)key { + [self.dictionary removeObjectForKey:key]; + self.updatedAt = [NSDate date]; +} + +- (void)removeAllObjects { + [self.dictionary removeAllObjects]; + self.updatedAt = [NSDate date]; +} + +///-------------------------------------- +#pragma mark - NSFastEnumeration +///-------------------------------------- + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained [])buffer + count:(NSUInteger)len { + return [self.dictionary countByEnumeratingWithState:state objects:buffer count:len]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + PFOperationSet *operationSet = [[[self class] allocWithZone:zone] initWithUUID:self.uuid]; + operationSet.dictionary = [self.dictionary mutableCopy]; + operationSet.updatedAt = [self.updatedAt copy]; + operationSet.saveEventually = self.saveEventually; + return operationSet; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/PFObjectPrivate.h b/Pods/Parse/Parse/Internal/Object/PFObjectPrivate.h new file mode 100644 index 0000000..949e589 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/PFObjectPrivate.h @@ -0,0 +1,306 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import + +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFMulticastDelegate.h" +#import "PFObjectControlling.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFCurrentUserController; +@class PFFieldOperation; +@class PFJSONCacheItem; +@class PFMultiCommand; +@class PFObjectEstimatedData; +@class PFObjectFileCodingLogic; +@class PFObjectState; +@class PFObjectSubclassingController; +@class PFOperationSet; +@class PFPinningObjectStore; +@class PFRESTCommand; +@class PFTaskQueue; + +///-------------------------------------- +#pragma mark - PFObjectPrivateSubclass +///-------------------------------------- + +@protocol PFObjectPrivateSubclass + +@required + +///-------------------------------------- +/// @name State +///-------------------------------------- + ++ (PFObjectState *)_newObjectStateWithParseClassName:(NSString *)className + objectId:(NSString *)objectId + isComplete:(BOOL)complete; + +@optional + +///-------------------------------------- +/// @name Before Save +///-------------------------------------- + +/*! + Called before an object is going to be saved. Called in a context of object lock. + Subclasses can override this method to do any custom updates before an object gets saved. + */ +- (void)_objectWillSave; + +@end + +///-------------------------------------- +#pragma mark - PFObject +///-------------------------------------- + +// Extension for property methods. +@interface PFObject () + +/*! + @returns Current object state. + */ +@property (nonatomic, copy) PFObjectState *_state; +@property (nonatomic, copy) NSMutableSet *_availableKeys; + +- (instancetype)initWithObjectState:(PFObjectState *)state; ++ (instancetype)objectWithClassName:(NSString *)className + objectId:(NSString *)objectid + completeData:(BOOL)completeData; ++ (instancetype)objectWithoutDataWithClassName:(NSString *)className localId:(NSString *)localId; + +- (PFTaskQueue *)taskQueue; + +- (PFObjectEstimatedData *)_estimatedData; + +#if PARSE_OSX_ONLY +// Not available publicly, but available for testing + +- (instancetype)refresh; +- (instancetype)refresh:(NSError **)error; +- (void)refreshInBackgroundWithBlock:(PFObjectResultBlock)block; +- (void)refreshInBackgroundWithTarget:(id)target selector:(SEL)selector; + +#endif + +///-------------------------------------- +/// @name Validation +///-------------------------------------- + +- (BFTask PF_GENERIC(PFVoid) *)_validateFetchAsync NS_REQUIRES_SUPER; +- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync NS_REQUIRES_SUPER; + +/*! + Validate the save eventually operation with the current state. + The result of this task is ignored. The error/cancellation/exception will prevent `saveEventually`. + + @returns Task that encapsulates the validation. + */ +- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync NS_REQUIRES_SUPER; + +///-------------------------------------- +/// @name Pin +///-------------------------------------- +- (BFTask *)_pinInBackgroundWithName:(NSString *)name includeChildren:(BOOL)includeChildren; ++ (BFTask *)_pinAllInBackground:(NSArray *)objects withName:(NSString *)name includeChildren:(BOOL)includeChildren; + ++ (PFPinningObjectStore *)pinningObjectStore; ++ (id)objectController; ++ (PFObjectFileCodingLogic *)objectFileCodingLogic; ++ (PFCurrentUserController *)currentUserController; + +///-------------------------------------- +#pragma mark - Subclassing +///-------------------------------------- + ++ (PFObjectSubclassingController *)subclassingController; + +@end + +@interface PFObject (Private) + +/*! + Returns the object that should be used to synchronize all internal data access. + */ +- (NSObject *)lock; + +/*! + Blocks until all outstanding operations have completed. + */ +- (void)waitUntilFinished; + +- (NSDictionary *)_collectFetchedObjects; + +///-------------------------------------- +#pragma mark - Static methods for Subclassing +///-------------------------------------- + +/*! + Unregisters a class registered using registerSubclass: + If we ever expose thsi method publicly, we must change the underlying implementation + to have stack behavior. Currently unregistering a custom class for a built-in will + leave the built-in unregistered as well. + @param subclass the subclass + */ ++ (void)unregisterSubclass:(Class)subclass; + +///-------------------------------------- +#pragma mark - Children helpers +///-------------------------------------- +- (BFTask *)_saveChildrenInBackgroundWithCurrentUser:(PFUser *)currentUser sessionToken:(NSString *)sessionToken; + +///-------------------------------------- +#pragma mark - Dirtiness helpers +///-------------------------------------- +- (BOOL)isDirty:(BOOL)considerChildren; +- (void)_setDirty:(BOOL)dirty; + +- (void)performOperation:(PFFieldOperation *)operation forKey:(NSString *)key; +- (void)setHasBeenFetched:(BOOL)fetched; +- (void)_setDeleted:(BOOL)deleted; + +- (BOOL)isDataAvailableForKey:(NSString *)key; + +- (BOOL)_hasChanges; +- (BOOL)_hasOutstandingOperations; +- (PFOperationSet *)unsavedChanges; + +///-------------------------------------- +#pragma mark - Validations +///-------------------------------------- +- (void)_checkSaveParametersWithCurrentUser:(PFUser *)currentUser; +/*! + Checks if Parse class name could be used to initialize a given instance of PFObject or it's subclass. + */ ++ (void)_assertValidInstanceClassName:(NSString *)className; + +///-------------------------------------- +#pragma mark - Serialization helpers +///-------------------------------------- +- (NSString *)getOrCreateLocalId; +- (void)resolveLocalId; + ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + completeData:(BOOL)completeData; + ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + selectedKeys:(NSArray *)selectedKeys; + ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + completeData:(BOOL)completeData + decoder:(PFDecoder *)decoder; ++ (BFTask *)_migrateObjectInBackgroundFromFile:(NSString *)fileName toPin:(NSString *)pinName; ++ (BFTask *)_migrateObjectInBackgroundFromFile:(NSString *)fileName + toPin:(NSString *)pinName + usingMigrationBlock:(BFContinuationBlock)block; + +- (NSMutableDictionary *)_convertToDictionaryForSaving:(PFOperationSet *)changes + withObjectEncoder:(PFEncoder *)encoder; + +///-------------------------------------- +#pragma mark - REST operations +///-------------------------------------- +- (NSDictionary *)RESTDictionaryWithObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs; +- (NSDictionary *)RESTDictionaryWithObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs + state:(PFObjectState *)state + operationSetQueue:(NSArray *)operationSetQueue; + +- (void)mergeFromRESTDictionary:(NSDictionary *)object + withDecoder:(PFDecoder *)decoder; + +///-------------------------------------- +#pragma mark - Data helpers +///-------------------------------------- +- (void)checkForChangesToMutableContainers; +- (void)rebuildEstimatedData; + +///-------------------------------------- +#pragma mark - Command handlers +///-------------------------------------- +- (PFObject *)mergeFromObject:(PFObject *)other; + +- (void)_mergeAfterSaveWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder; +- (void)_mergeAfterFetchWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder completeData:(BOOL)completeData; +- (void)_mergeFromServerWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder completeData:(BOOL)completeData; + +- (BFTask *)handleSaveResultAsync:(NSDictionary *)result; + +///-------------------------------------- +#pragma mark - Asynchronous operations +///-------------------------------------- +- (void)startSave; +- (BFTask *)_enqueueSaveEventuallyWithChildren:(BOOL)saveChildren; +- (BFTask *)saveAsync:(BFTask *)toAwait; +- (BFTask *)fetchAsync:(BFTask *)toAwait; +- (BFTask *)deleteAsync:(BFTask *)toAwait; + +///-------------------------------------- +#pragma mark - Command constructors +///-------------------------------------- +- (PFRESTCommand *)_constructSaveCommandForChanges:(PFOperationSet *)changes + sessionToken:(NSString *)sessionToken + objectEncoder:(PFEncoder *)encoder; +- (PFRESTCommand *)_currentDeleteCommandWithSessionToken:(NSString *)sessionToken; + +///-------------------------------------- +#pragma mark - Misc helpers +///-------------------------------------- +- (NSString *)displayClassName; +- (NSString *)displayObjectId; + +- (void)registerSaveListener:(void (^)(id result, NSError *error))callback; +- (void)unregisterSaveListener:(void (^)(id result, NSError *error))callback; +- (PFACL *)ACLWithoutCopying; + +///-------------------------------------- +#pragma mark - Get and set +///-------------------------------------- + +- (void)_setObject:(id)object + forKey:(NSString *)key + onlyIfDifferent:(BOOL)onlyIfDifferent; + +///-------------------------------------- +#pragma mark - Subclass Helpers +///-------------------------------------- + +/*! + This method is called by -[PFObject init]; changes made to the object during this + method will not mark the object as dirty. PFObject uses this method to to apply the + default ACL; subclasses which override this method shold be sure to call the super + implementation if they want to honor the default ACL. + */ +- (void)setDefaultValues; + +/*! + This method allows subclasses to determine whether a default ACL should be applied + to new instances. + */ +- (BOOL)needsDefaultACL; + +@end + +@interface PFObject () { + PFMulticastDelegate *saveDelegate; +} + +@property (nonatomic, strong) PFMulticastDelegate *saveDelegate; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.h b/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.h new file mode 100644 index 0000000..66950c7 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.h @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFPin; + +@interface PFPinningObjectStore : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)storeWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Pin +///-------------------------------------- + +/*! + Gets pin with name equals to given name. + + @param name Pin Name. + + @returns `BFTask` with `PFPin` result if pinning succeeds. + */ +- (BFTask *)fetchPinAsyncWithName:(NSString *)name; + +/*! + Pins given objects to the pin. Creates new pin if the pin with such name is not found. + + @param objects Array of `PFObject`s to pin. + @param name Pin Name. + @param includeChildren Whether children of `objects` should be pinned as well. + + @returns `BFTask` with `@YES` result. + */ +- (BFTask *)pinObjectsAsync:(nullable NSArray *)objects + withPinName:(NSString *)name + includeChildren:(BOOL)includeChildren; + +///-------------------------------------- +/// @name Unpin +///-------------------------------------- + +/*! + Unpins given array of objects from the pin. + + @param objects Objects to unpin. + @param name Pin name. + + @returns `BFTask` with `@YES` result. + */ +- (BFTask *)unpinObjectsAsync:(nullable NSArray *)objects withPinName:(NSString *)name; + +/*! + Unpins all objects from the pin. + + @param name Pin name. + + @returns `BFTask` with `YES` result. + */ +- (BFTask *)unpinAllObjectsAsyncWithPinName:(NSString *)name; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.m b/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.m new file mode 100644 index 0000000..b731df1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/PinningStore/PFPinningObjectStore.m @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPinningObjectStore.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFMacros.h" +#import "PFOfflineStore.h" +#import "PFPin.h" +#import "PFQueryPrivate.h" + +@interface PFPinningObjectStore () { + NSMapTable *_pinCacheTable; + dispatch_queue_t _pinCacheAccessQueue; + BFExecutor *_pinCacheAccessExecutor; +} + +@end + +@implementation PFPinningObjectStore + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _pinCacheTable = [NSMapTable strongToWeakObjectsMapTable]; + _pinCacheAccessQueue = dispatch_queue_create("com.parse.object.pin.cache", DISPATCH_QUEUE_SERIAL); + _pinCacheAccessExecutor = [BFExecutor executorWithDispatchQueue:_pinCacheAccessQueue]; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)storeWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Pin +///-------------------------------------- + +- (BFTask *)fetchPinAsyncWithName:(NSString *)name { + @weakify(self); + return [BFTask taskFromExecutor:_pinCacheAccessExecutor withBlock:^id{ + BFTask *cachedTask = [_pinCacheTable objectForKey:name] ?: [BFTask taskWithResult:nil]; + // We need to call directly to OfflineStore since we don't want/need a user to query for ParsePins + cachedTask = [cachedTask continueWithBlock:^id(BFTask *task) { + @strongify(self); + PFQuery *query = [[PFPin query] whereKey:PFPinKeyName equalTo:name]; + PFOfflineStore *store = self.dataSource.offlineStore; + return [[store findAsyncForQueryState:query.state + user:nil + pin:nil] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *result = task.result; + // TODO (hallucinogen): What do we do if there are more than 1 result? + PFPin *pin = (result.count != 0 ? result.firstObject : [PFPin pinWithName:name]); + return pin; + }]; + }]; + // Put the task back into the cache. + [_pinCacheTable setObject:cachedTask forKey:name]; + return cachedTask; + }]; +} + +- (BFTask *)pinObjectsAsync:(NSArray *)objects withPinName:(NSString *)name includeChildren:(BOOL)includeChildren { + if (objects.count == 0) { + return [BFTask taskWithResult:@YES]; + } + + @weakify(self); + return [[[self fetchPinAsyncWithName:name] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFPin *pin = task.result; + PFOfflineStore *store = self.dataSource.offlineStore; + //TODO (hallucinogen): some stuff @grantland mentioned can't be done maybe needs to be done here + //TODO (grantland): change to use relations. currently the related PO are only getting saved + //TODO (grantland): can't add and then remove + + // Hack to store collection in a pin + NSMutableArray *modified = pin.objects; + if (modified == nil) { + modified = [objects mutableCopy]; + } else { + for (PFObject *object in objects) { + if (![modified containsObject:object]) { + [modified addObject:object]; + } + } + } + pin.objects = modified; + + BFTask *saveTask = nil; + if (includeChildren) { + saveTask = [store saveObjectLocallyAsync:pin includeChildren:YES]; + } else { + saveTask = [store saveObjectLocallyAsync:pin withChildren:pin.objects]; + } + return saveTask; + }] continueWithSuccessResult:@YES]; +} + +///-------------------------------------- +#pragma mark - Unpin +///-------------------------------------- + +- (BFTask *)unpinObjectsAsync:(NSArray *)objects withPinName:(NSString *)name { + if (objects.count == 0) { + return [BFTask taskWithResult:@YES]; + } + + @weakify(self); + return [[[self fetchPinAsyncWithName:name] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFPin *pin = task.result; + NSMutableArray *modified = pin.objects; + if (!modified) { + // Nothing to unpin + return task; + } + + //TODO (hallucinogen): some stuff @grantland mentioned can't be done maybe needs to be done here + //TODO (grantland): change to use relations. currently the related PO are only getting saved + //TODO (grantland): can't add and then remove + + PFOfflineStore *store = self.dataSource.offlineStore; + + [modified removeObjectsInArray:objects]; + if (modified.count == 0) { + return [store unpinObjectAsync:pin]; + } + pin.objects = modified; + + return [store saveObjectLocallyAsync:pin includeChildren:YES]; + }] continueWithSuccessResult:@YES]; +} + +- (BFTask *)unpinAllObjectsAsyncWithPinName:(NSString *)name { + @weakify(self); + return [[self fetchPinAsyncWithName:name] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFPin *pin = task.result; + return [[self.dataSource.offlineStore unpinObjectAsync:pin] continueWithSuccessResult:@YES]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.h b/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.h new file mode 100644 index 0000000..70cae09 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectState.h" + +@class PFOperationSet; + +@interface PFMutableObjectState : PFObjectState + +@property (nonatomic, copy, readwrite) NSString *parseClassName; +@property (nonatomic, copy, readwrite) NSString *objectId; + +@property (nonatomic, strong, readwrite) NSDate *createdAt; +@property (nonatomic, strong, readwrite) NSDate *updatedAt; + +@property (nonatomic, copy, readwrite) NSDictionary *serverData; + +@property (nonatomic, assign, readwrite, getter=isComplete) BOOL complete; +@property (nonatomic, assign, readwrite, getter=isDeleted) BOOL deleted; + +///-------------------------------------- +/// @name Accessors +///-------------------------------------- + +- (void)setServerDataObject:(id)object forKey:(NSString *)key; +- (void)removeServerDataObjectForKey:(NSString *)key; +- (void)removeServerDataObjectsForKeys:(NSArray *)keys; + +- (void)setCreatedAtFromString:(NSString *)string; +- (void)setUpdatedAtFromString:(NSString *)string; + +///-------------------------------------- +/// @name Apply +///-------------------------------------- + +- (void)applyState:(PFObjectState *)state; +- (void)applyOperationSet:(PFOperationSet *)operationSet; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.m b/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.m new file mode 100644 index 0000000..edfd652 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/State/PFMutableObjectState.m @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableObjectState.h" + +#import "PFDateFormatter.h" +#import "PFObjectState_Private.h" + +@implementation PFMutableObjectState + +@dynamic parseClassName; +@dynamic objectId; +@dynamic createdAt; +@dynamic updatedAt; +@dynamic serverData; +@dynamic complete; +@dynamic deleted; + +///-------------------------------------- +#pragma mark - PFMutableObjectState +///-------------------------------------- + +#pragma mark Accessors + +- (void)setServerDataObject:(id)object forKey:(NSString *)key { + [super setServerDataObject:object forKey:key]; +} + +- (void)removeServerDataObjectForKey:(NSString *)key { + [super removeServerDataObjectForKey:key]; +} + +- (void)removeServerDataObjectsForKeys:(NSArray *)keys { + [super removeServerDataObjectsForKeys:keys]; +} + +- (void)setCreatedAtFromString:(NSString *)string { + [super setCreatedAtFromString:string]; +} + +- (void)setUpdatedAtFromString:(NSString *)string { + [super setUpdatedAtFromString:string]; +} + +#pragma mark Apply + +- (void)applyState:(PFObjectState *)state { + [super applyState:state]; +} + +- (void)applyOperationSet:(PFOperationSet *)operationSet { + [super applyOperationSet:operationSet]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/State/PFObjectState.h b/Pods/Parse/Parse/Internal/Object/State/PFObjectState.h new file mode 100644 index 0000000..a7d9744 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/State/PFObjectState.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFEncoder; + +@interface PFObjectState : NSObject + +@property (nonatomic, copy, readonly) NSString *parseClassName; +@property (nonatomic, copy, readonly) NSString *objectId; + +@property (nonatomic, strong, readonly) NSDate *createdAt; +@property (nonatomic, strong, readonly) NSDate *updatedAt; + +@property (nonatomic, copy, readonly) NSDictionary *serverData; + +@property (nonatomic, assign, readonly, getter=isComplete) BOOL complete; +@property (nonatomic, assign, readonly, getter=isDeleted) BOOL deleted; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithState:(PFObjectState *)state NS_REQUIRES_SUPER; +- (instancetype)initWithParseClassName:(NSString *)parseClassName; +- (instancetype)initWithParseClassName:(NSString *)parseClassName + objectId:(NSString *)objectId + isComplete:(BOOL)complete; + ++ (instancetype)stateWithState:(PFObjectState *)state NS_REQUIRES_SUPER; ++ (instancetype)stateWithParseClassName:(NSString *)parseClassName; ++ (instancetype)stateWithParseClassName:(NSString *)parseClassName + objectId:(NSString *)objectId + isComplete:(BOOL)complete; + +///-------------------------------------- +/// @name Coding +///-------------------------------------- + +/*! + Encodes all fields in `serverData`, `objectId`, `createdAt` and `updatedAt` into objects suitable for JSON/Persistence. + + @note `parseClassName` isn't automatically added to the dictionary. + + @param objectEncoder Encoder to use to encode custom objects. + + @returns `NSDictionary` instance representing object state. + */ +- (NSDictionary *)dictionaryRepresentationWithObjectEncoder:(PFEncoder *)objectEncoder NS_REQUIRES_SUPER; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/State/PFObjectState.m b/Pods/Parse/Parse/Internal/Object/State/PFObjectState.m new file mode 100644 index 0000000..fec73f4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/State/PFObjectState.m @@ -0,0 +1,179 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectState.h" +#import "PFObjectState_Private.h" + +#import "PFDateFormatter.h" +#import "PFEncoder.h" +#import "PFMutableObjectState.h" +#import "PFObjectConstants.h" +#import "PFObjectUtilities.h" + +@implementation PFObjectState + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _serverData = [NSMutableDictionary dictionary]; + + return [super init]; +} + +- (instancetype)initWithState:(PFObjectState *)state { + self = [self init]; + if (!self) return nil; + + _parseClassName = [state.parseClassName copy]; + _objectId = [state.objectId copy]; + + _updatedAt = state.updatedAt; + _createdAt = state.createdAt; + + _serverData = [state.serverData mutableCopy] ?: [NSMutableDictionary dictionary]; + + _complete = state.complete; + _deleted = state.deleted; + + return self; +} + +- (instancetype)initWithParseClassName:(NSString *)parseClassName { + return [self initWithParseClassName:parseClassName objectId:nil isComplete:NO]; +} + +- (instancetype)initWithParseClassName:(NSString *)parseClassName + objectId:(NSString *)objectId + isComplete:(BOOL)complete { + self = [self init]; + if (!self) return nil; + + _parseClassName = [parseClassName copy]; + _objectId = [objectId copy]; + _complete = complete; + + return self; +} + ++ (instancetype)stateWithState:(PFObjectState *)state { + return [[self alloc] initWithState:state]; +} + ++ (instancetype)stateWithParseClassName:(NSString *)parseClassName { + return [[self alloc] initWithParseClassName:parseClassName]; +} + ++ (instancetype)stateWithParseClassName:(NSString *)parseClassName + objectId:(NSString *)objectId + isComplete:(BOOL)complete { + return [[self alloc] initWithParseClassName:parseClassName + objectId:objectId + isComplete:complete]; +} + +///-------------------------------------- +#pragma mark - Accessors +///--------------------------------------s + +- (void)setServerData:(NSDictionary *)serverData { + if (self.serverData != serverData) { + _serverData = [serverData mutableCopy]; + } +} + +///-------------------------------------- +#pragma mark - Coding +///-------------------------------------- + +- (NSDictionary *)dictionaryRepresentationWithObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + if (self.objectId) { + result[PFObjectObjectIdRESTKey] = self.objectId; + } + if (self.createdAt) { + result[PFObjectCreatedAtRESTKey] = [[PFDateFormatter sharedFormatter] preciseStringFromDate:self.createdAt]; + } + if (self.updatedAt) { + result[PFObjectUpdatedAtRESTKey] = [[PFDateFormatter sharedFormatter] preciseStringFromDate:self.updatedAt]; + } + [self.serverData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + result[key] = [objectEncoder encodeObject:obj]; + }]; + return [result copy]; +} + +///-------------------------------------- +#pragma mark - PFObjectState (Mutable) +///-------------------------------------- + +#pragma mark Accessors + +- (void)setServerDataObject:(id)object forKey:(NSString *)key { + _serverData[key] = object; +} + +- (void)removeServerDataObjectForKey:(NSString *)key { + [_serverData removeObjectForKey:key]; +} + +- (void)removeServerDataObjectsForKeys:(NSArray *)keys { + [_serverData removeObjectsForKeys:keys]; +} + +- (void)setCreatedAtFromString:(NSString *)string { + self.createdAt = [[PFDateFormatter sharedFormatter] dateFromString:string]; +} + +- (void)setUpdatedAtFromString:(NSString *)string { + self.updatedAt = [[PFDateFormatter sharedFormatter] dateFromString:string]; +} + +#pragma mark Apply + +- (void)applyState:(PFObjectState *)state { + if (state.objectId) { + self.objectId = state.objectId; + } + if (state.createdAt) { + self.createdAt = state.createdAt; + } + if (state.updatedAt) { + self.updatedAt = state.updatedAt; + } + [_serverData addEntriesFromDictionary:state.serverData]; + + self.complete |= state.complete; +} + +- (void)applyOperationSet:(PFOperationSet *)operationSet { + [PFObjectUtilities applyOperationSet:operationSet toDictionary:_serverData]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (id)copyWithZone:(NSZone *)zone { + return [[PFObjectState allocWithZone:zone] initWithState:self]; +} + +///-------------------------------------- +#pragma mark - NSMutableCopying +///-------------------------------------- + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableObjectState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/State/PFObjectState_Private.h b/Pods/Parse/Parse/Internal/Object/State/PFObjectState_Private.h new file mode 100644 index 0000000..d5f8d63 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/State/PFObjectState_Private.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectState.h" + +@class PFOperationSet; + +@interface PFObjectState () { +@protected + NSString *_parseClassName; + NSString *_objectId; + NSDate *_createdAt; + NSDate *_updatedAt; + NSMutableDictionary *_serverData; + + BOOL _complete; + BOOL _deleted; +} + +@property (nonatomic, copy, readwrite) NSString *parseClassName; +@property (nonatomic, copy, readwrite) NSString *objectId; +@property (nonatomic, strong, readwrite) NSDate *createdAt; +@property (nonatomic, strong, readwrite) NSDate *updatedAt; +@property (nonatomic, copy, readwrite) NSMutableDictionary *serverData; + +@property (nonatomic, assign, readwrite, getter=isComplete) BOOL complete; +@property (nonatomic, assign, readwrite, getter=isDeleted) BOOL deleted; + +@end + +@interface PFObjectState (Mutable) + +///-------------------------------------- +/// @name Accessors +///-------------------------------------- + +- (void)setServerDataObject:(id)object forKey:(NSString *)key; +- (void)removeServerDataObjectForKey:(NSString *)key; +- (void)removeServerDataObjectsForKeys:(NSArray *)keys; + +- (void)setCreatedAtFromString:(NSString *)string; +- (void)setUpdatedAtFromString:(NSString *)string; + +///-------------------------------------- +/// @name Apply +///-------------------------------------- + +- (void)applyState:(PFObjectState *)state NS_REQUIRES_SUPER; +- (void)applyOperationSet:(PFOperationSet *)operationSet NS_REQUIRES_SUPER; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.h b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.h new file mode 100644 index 0000000..3db23d7 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFPropertyInfo; + +@interface PFObjectSubclassInfo : NSObject + +@property (atomic, strong) Class subclass; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSubclass:(Class)kls NS_DESIGNATED_INITIALIZER; ++ (instancetype)subclassInfoWithSubclass:(Class)kls; + +- (PFPropertyInfo *)propertyInfoForSelector:(SEL)cmd isSetter:(BOOL *)isSetter; +- (NSMethodSignature *)forwardingMethodSignatureForSelector:(SEL)cmd; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.m b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.m new file mode 100644 index 0000000..8005b1a --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.m @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectSubclassInfo.h" + +#import + +#import "PFAssert.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFPropertyInfo_Private.h" + +///-------------------------------------- +#pragma mark - Helper +///-------------------------------------- + +static BOOL startsWith(const char *string, const char *prefix) { + // Keep iterating in lockstep. If we run out of prefix letters first, + // this is a valid prefix. + for (; *string && *prefix && *prefix == *string; ++string, ++prefix) + ; + return !*prefix; +} + +// This method helps us get our bearings regardless of whether we were passed +// setFoo: or foo. We'll always exit this method by setting outPair to +// [accessor, mutator] and returns the property they correspond to. If the +// property cannot be found, returns NULL and outPair is undefined. +// An objc_property_t is an opaque struct pointer containing a SEL name and char * +// type information which follows a DSL explained in the Objective-C Runtime Reference. +static objc_property_t getAccessorMutatorPair(Class klass, SEL sel, SEL outPair[2]) { + const char *selName = sel_getName(sel); + ptrdiff_t selNameByteLen = strlen(selName) + 1; + char temp[selNameByteLen + 4]; + + if (startsWith(selName, "set")) { + outPair[1] = sel; + memcpy(temp, selName + 3, selNameByteLen - 3); + temp[0] -= 'A' - 'a'; + + temp[selNameByteLen - 5] = 0; // drop ':' + outPair[0] = sel_registerName(temp); + } else { + outPair[0] = sel; + sprintf(temp, "set%s:", selName); + if (selName[0] >= 'a' && selName[0] <= 'z') { + temp[3] += 'A' - 'a'; + } + outPair[1] = sel_registerName(temp); + } + + const char *propName = sel_getName(outPair[0]); + objc_property_t property = class_getProperty(klass, propName); + if (!property) { + // The user could have broken convention and declared an upper case property. + memcpy(temp, propName, strlen(propName) + 1); + temp[0] += 'A' - 'a'; + outPair[0] = sel_registerName(temp); + property = class_getProperty(klass, temp); + } + return property; +} + +@implementation PFObjectSubclassInfo { + dispatch_queue_t _dataAccessQueue; + NSMutableDictionary *_knownProperties; + NSMutableDictionary *_knownMethodSignatures; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithSubclass:(Class)kls { + self = [super init]; + if (!self) return nil; + + _dataAccessQueue = dispatch_queue_create("com.parse.object.subclassing.data.access", DISPATCH_QUEUE_SERIAL); + _subclass = kls; + + _knownProperties = [NSMutableDictionary dictionary]; + _knownMethodSignatures = [NSMutableDictionary dictionary]; + + return self; +} + ++ (instancetype)subclassInfoWithSubclass:(Class)kls { + return [[self alloc] initWithSubclass:kls]; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (PFPropertyInfo *)propertyInfoForSelector:(SEL)cmd isSetter:(BOOL *)isSetter { + __block PFPropertyInfo *result = nil; + dispatch_sync(_dataAccessQueue, ^{ + result = [self _rawPropertyInfoForSelector:cmd]; + }); + + if (isSetter) { + *isSetter = (cmd == result.setterSelector); + } + + return result; +} + +- (NSMethodSignature *)forwardingMethodSignatureForSelector:(SEL)cmd { + __block NSMethodSignature *result = nil; + NSString *selectorString = NSStringFromSelector(cmd); + + // NSMethodSignature can be fairly heavyweight, so let's agressively cache this here. + dispatch_sync(_dataAccessQueue, ^{ + result = _knownMethodSignatures[selectorString]; + if (result) { + return; + } + + PFPropertyInfo *propertyInfo = [self _rawPropertyInfoForSelector:cmd]; + if (!propertyInfo) { + return; + } + + BOOL isSetter = (cmd == propertyInfo.setterSelector); + NSString *typeEncoding = propertyInfo.typeEncoding; + + // Property type encoding includes the class name as well. + // This is fine, except for the fact that NSMethodSignature hates that. + NSUInteger startLocation = [typeEncoding rangeOfString:@"\"" options:0].location; + NSUInteger endLocation = [typeEncoding rangeOfString:@"\"" + options:NSBackwardsSearch | NSAnchoredSearch].location; + + if (startLocation != NSNotFound && endLocation != NSNotFound) { + typeEncoding = [typeEncoding substringToIndex:startLocation]; + } + + NSString *objcTypes = ([NSString stringWithFormat:(isSetter ? @"v@:%@" : @"%@@:"), typeEncoding]); + result = [NSMethodSignature signatureWithObjCTypes:[objcTypes UTF8String]]; + + _knownMethodSignatures[selectorString] = result; + }); + + return result; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (PFPropertyInfo *)_rawPropertyInfoForSelector:(SEL)cmd { + PFPropertyInfo *result = nil; + NSString *selectorString = NSStringFromSelector(cmd); + result = _knownProperties[selectorString]; + if (result) { + return result; + } + + SEL propertySelectors[2]; + objc_property_t property = getAccessorMutatorPair(self.subclass, cmd, propertySelectors); + if (!property) { + return nil; + } + + // Check if we've registered this property with a different name. + NSString *propertyName = @(property_getName(property)); + result = _knownProperties[propertyName]; + if (result) { + // Re-register it with the name we just searched for for faster future lookup. + _knownProperties[selectorString] = result; + return result; + } + + const char *attributes = property_getAttributes(property); + if (strstr(attributes, "T@\"PFRelation\",") == attributes && !strstr(attributes, ",R")) { + PFLogWarning(PFLoggingTagCommon, + @"PFRelation properties are always readonly, but %@.%@ was declared otherwise.", + self.subclass, selectorString); + } + + result = [PFPropertyInfo propertyInfoWithClass:self.subclass name:propertyName]; + + _knownProperties[result.name] = result; + if (result.getterSelector) { + _knownProperties[NSStringFromSelector(result.getterSelector)] = result; + } + if (result.setterSelector) { + _knownProperties[NSStringFromSelector(result.setterSelector)] = result; + } + + return result; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.h b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.h new file mode 100644 index 0000000..ef7977b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFObject; +@protocol PFSubclassing; + +@interface PFObjectSubclassingController : NSObject + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +//TODO: (nlutsenko, richardross) Make it not terrible aka don't have singletons. ++ (instancetype)defaultController; ++ (void)clearDefaultController; + +///-------------------------------------- +/// @name Registration +///-------------------------------------- + +- (Class)subclassForParseClassName:(NSString *)parseClassName; +- (void)registerSubclass:(Class)kls; +- (void)unregisterSubclass:(Class)kls; + +///-------------------------------------- +/// @name Forwarding +///-------------------------------------- + +- (NSMethodSignature *)forwardingMethodSignatureForSelector:(SEL)cmd ofClass:(Class)kls; +- (BOOL)forwardObjectInvocation:(NSInvocation *)invocation withObject:(PFObject *)object; + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.m b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.m new file mode 100644 index 0000000..878a816 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Subclassing/PFObjectSubclassingController.m @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectSubclassingController.h" + +#import + +#import "PFAssert.h" +#import "PFMacros.h" +#import "PFObject.h" +#import "PFObjectSubclassInfo.h" +#import "PFPropertyInfo_Private.h" +#import "PFPropertyInfo_Runtime.h" +#import "PFSubclassing.h" + +// CFNumber does not use number type 0, we take advantage of that here. +#define kCFNumberTypeUnknown 0 + +static CFNumberType PFNumberTypeForObjCType(const char *encodedType) { +// To save anyone in the future from some major headaches, sanity check here. +#if kCFNumberTypeMax > UINT8_MAX +#error kCFNumberTypeMax has been changed! This solution will no longer work. +#endif + + // Organizing the table this way makes it nicely fit into two cache lines. This makes lookups nearly free, even more + // so if repeated. + static uint8_t types[128] = { + // Core types. + ['c'] = kCFNumberCharType, + ['i'] = kCFNumberIntType, + ['s'] = kCFNumberShortType, + ['l'] = kCFNumberLongType, + ['q'] = kCFNumberLongLongType, + + // CFNumber (and NSNumber, actually) does not store unsigned types. + // This may cause some strange issues when dealing with values near the max for that type. + // We should investigate this if it becomes a problem. + ['C'] = kCFNumberCharType, + ['I'] = kCFNumberIntType, + ['S'] = kCFNumberShortType, + ['L'] = kCFNumberLongType, + ['Q'] = kCFNumberLongLongType, + + // Floating point + ['f'] = kCFNumberFloatType, + ['d'] = kCFNumberDoubleType, + + // C99 & CXX boolean. We are keeping this here for decoding, as you can safely use CFNumberGetBytes on a + // CFBoolean, and extract it into a char. + ['B'] = kCFNumberCharType, + }; + + return (CFNumberType)types[encodedType[0]]; +} + +static NSNumber *PFNumberCreateSafe(const char *typeEncoding, const void *bytes) { + // NOTE: This is required because NSJSONSerialization treats all NSNumbers with the 'char' type as numbers, not + // booleans. As such, we must treat any and all boolean type encodings as explicit booleans, otherwise we will + // send '1' and '0' to the api server rather than 'true' and 'false'. + // + // TODO (richardross): When we drop support for 10.9/iOS 7, remove the 'c' encoding and only use the new 'B' + // encoding. + if (typeEncoding[0] == 'B' || typeEncoding[0] == 'c') { + return [NSNumber numberWithBool:*(BOOL *)bytes]; + } + + CFNumberType numberType = PFNumberTypeForObjCType(typeEncoding); + PFConsistencyAssert(numberType != kCFNumberTypeUnknown, @"Unsupported type encoding %s!", typeEncoding); + return (__bridge_transfer NSNumber *)CFNumberCreate(NULL, numberType, bytes); +} + +@implementation PFObjectSubclassingController { + dispatch_queue_t _registeredSubclassesAccessQueue; + NSMutableDictionary *_registeredSubclasses; + NSMutableDictionary *_unregisteredSubclasses; +} + +static PFObjectSubclassingController *defaultController_; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _registeredSubclassesAccessQueue = dispatch_queue_create("com.parse.object.subclassing", DISPATCH_QUEUE_SERIAL); + _registeredSubclasses = [NSMutableDictionary dictionary]; + _unregisteredSubclasses = [NSMutableDictionary dictionary]; + + return self; +} + ++ (instancetype)defaultController { + if (!defaultController_) { + defaultController_ = [[PFObjectSubclassingController alloc] init]; + } + return defaultController_; +} + ++ (void)clearDefaultController { + defaultController_ = nil; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (Class)subclassForParseClassName:(NSString *)parseClassName { + __block Class result = nil; + pf_sync_with_throw(_registeredSubclassesAccessQueue, ^{ + result = [_registeredSubclasses[parseClassName] subclass]; + }); + return result; +} + +- (void)registerSubclass:(Class)kls { + pf_sync_with_throw(_registeredSubclassesAccessQueue, ^{ + [self _rawRegisterSubclass:kls]; + }); +} + +- (void)unregisterSubclass:(Class)class { + pf_sync_with_throw(_registeredSubclassesAccessQueue, ^{ + NSString *parseClassName = [class parseClassName]; + Class registeredClass = [_registeredSubclasses[parseClassName] subclass]; + + // Make it a no-op if the class itself is not registered or + // if there is another class registered under the same name. + if (registeredClass == nil || + ![registeredClass isEqual:class]) { + return; + } + + [_registeredSubclasses removeObjectForKey:parseClassName]; + }); +} + +- (BOOL)forwardObjectInvocation:(NSInvocation *)invocation withObject:(PFObject *)object { + PFObjectSubclassInfo *subclassInfo = [self _subclassInfoForClass:[object class]]; + + BOOL isSetter = NO; + PFPropertyInfo *propertyInfo = [subclassInfo propertyInfoForSelector:invocation.selector isSetter:&isSetter]; + if (!propertyInfo) { + return NO; + } + + if (isSetter) { + [self _forwardSetterInvocation:invocation forProperty:propertyInfo withObject:object]; + } else { + [self _forwardGetterInvocation:invocation forProperty:propertyInfo withObject:object]; + } + return YES; +} + +- (NSMethodSignature *)forwardingMethodSignatureForSelector:(SEL)cmd ofClass:(Class)kls { + PFObjectSubclassInfo *subclassInfo = [self _subclassInfoForClass:kls]; + return [subclassInfo forwardingMethodSignatureForSelector:cmd]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (void)_forwardGetterInvocation:(NSInvocation *)invocation + forProperty:(PFPropertyInfo *)propertyInfo + withObject:(PFObject *)object { + PFConsistencyAssert(invocation.methodSignature.numberOfArguments == 2, @"Getter should take no arguments!"); + PFConsistencyAssert(invocation.methodSignature.methodReturnType[0] != 'v', @"A getter cannot return void!"); + + const char *methodReturnType = [invocation.methodSignature methodReturnType]; + void *returnValueBytes = alloca([invocation.methodSignature methodReturnLength]); + + if (propertyInfo.ivar) { + object_getIvarValue_safe(object, propertyInfo.ivar, returnValueBytes, propertyInfo.associationType); + } else { + __autoreleasing id dictionaryValue = nil; + if ([propertyInfo.typeEncoding isEqualToString:@"@\"PFRelation\""]) { + dictionaryValue = [object relationForKey:propertyInfo.name]; + } else { + dictionaryValue = object[propertyInfo.name]; + + // TODO: (richardross) Investigate why we were orignally copying the result of -objectForKey, + // as this doens't seem right. + if (propertyInfo.associationType == PFPropertyInfoAssociationTypeCopy) { + dictionaryValue = [dictionaryValue copy]; + } + } + + if (dictionaryValue == nil || [dictionaryValue isKindOfClass:[NSNull class]]) { + memset(returnValueBytes, 0, invocation.methodSignature.methodReturnLength); + } else if (methodReturnType[0] == '@') { + memcpy(returnValueBytes, (void *) &dictionaryValue, sizeof(id)); + } else if ([dictionaryValue isKindOfClass:[NSNumber class]]) { + CFNumberGetValue((__bridge CFNumberRef) dictionaryValue, + PFNumberTypeForObjCType(methodReturnType), + returnValueBytes); + } else { + // TODO:(richardross)Support C-style structs that automatically convert to JSON via NSValue? + PFConsistencyAssert(false, @"Unsupported type encoding %s!", methodReturnType); + } + } + + [invocation setReturnValue:returnValueBytes]; +} + +- (void)_forwardSetterInvocation:(NSInvocation *)invocation + forProperty:(PFPropertyInfo *)propertyInfo + withObject:(PFObject *)object { + PFConsistencyAssert(invocation.methodSignature.numberOfArguments == 3, @"Setter should only take 1 argument!"); + + PFObject *sourceObject = object; + const char *argumentType = [invocation.methodSignature getArgumentTypeAtIndex:2]; + + NSUInteger argumentValueSize = 0; + NSGetSizeAndAlignment(argumentType, &argumentValueSize, NULL); + + void *argumentValueBytes = alloca(argumentValueSize); + [invocation getArgument:argumentValueBytes atIndex:2]; + + if (propertyInfo.ivar) { + object_setIvarValue_safe(sourceObject, propertyInfo.ivar, argumentValueBytes, propertyInfo.associationType); + } else { + id dictionaryValue = nil; + + if (argumentType[0] == '@') { + dictionaryValue = *(__unsafe_unretained id *)argumentValueBytes; + + if (propertyInfo.associationType == PFPropertyInfoAssociationTypeCopy) { + dictionaryValue = [dictionaryValue copy]; + } + } else { + dictionaryValue = PFNumberCreateSafe(argumentType, argumentValueBytes); + } + + if (dictionaryValue == nil) { + [sourceObject removeObjectForKey:propertyInfo.name]; + } else { + sourceObject[propertyInfo.name] = dictionaryValue; + } + } +} + +- (PFObjectSubclassInfo *)_subclassInfoForClass:(Class)kls { + __block PFObjectSubclassInfo *result = nil; + pf_sync_with_throw(_registeredSubclassesAccessQueue, ^{ + if (class_respondsToSelector(object_getClass(kls), @selector(parseClassName))) { + result = _registeredSubclasses[[kls parseClassName]]; + } + + // TODO: (nlutsenko, richardross) Don't let unregistered subclasses have dynamic property resolution. + if (!result) { + result = [PFObjectSubclassInfo subclassInfoWithSubclass:kls]; + _unregisteredSubclasses[NSStringFromClass(kls)] = result; + } + }); + return result; +} + +// Reverse compatibility note: many people may have built PFObject subclasses before +// we officially supported them. Our implementation can do cool stuff, but requires +// the parseClassName class method. +- (void)_rawRegisterSubclass:(Class)kls { + PFConsistencyAssert([kls conformsToProtocol:@protocol(PFSubclassing)], + @"Can only call +registerSubclass on subclasses conforming to PFSubclassing."); + + NSString *parseClassName = [kls parseClassName]; + + // Bug detection: don't allow subclasses of subclasses (i.e. custom user classes) + // to change the value of +parseClassName + if ([kls superclass] != [PFObject class]) { + // We compare Method definitions against the PFObject version witout invoking it + // because that Method could throw on an intermediary class which is + // not meant for direct use. + Method baseImpl = class_getClassMethod([PFObject class], @selector(parseClassName)); + Method superImpl = class_getClassMethod([kls superclass], @selector(parseClassName)); + + PFConsistencyAssert(superImpl == baseImpl || + [parseClassName isEqualToString:[[kls superclass] parseClassName]], + @"Subclasses of subclasses may not have separate +parseClassName " + "definitions. %@ should inherit +parseClassName from %@.", + kls, [kls superclass]); + } + + Class current = [_registeredSubclasses[parseClassName] subclass]; + if (current && current != kls) { + // We've already registered a more specific subclass (i.e. we're calling + // registerSubclass:PFUser after MYUser + if ([current isSubclassOfClass:kls]) { + return; + } + + PFConsistencyAssert([kls isSubclassOfClass:current], + @"Tried to register both %@ and %@ as the native PFObject subclass " + "of %@. Cannot determine the right class to use because neither " + "inherits from the other.", current, kls, parseClassName); + } + + // Move the subclass info from unregisteredSubclasses dictionary to registered ones, or create if it doesn't exist. + NSString *className = NSStringFromClass(kls); + PFObjectSubclassInfo *subclassInfo = _unregisteredSubclasses[className]; + if (subclassInfo) { + [_unregisteredSubclasses removeObjectForKey:className]; + } else { + subclassInfo = [PFObjectSubclassInfo subclassInfoWithSubclass:kls]; + } + _registeredSubclasses[[kls parseClassName]] = subclassInfo; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.h b/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.h new file mode 100644 index 0000000..a94952d --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class PFFieldOperation; +@class PFOperationSet; + +@interface PFObjectUtilities : NSObject + +///-------------------------------------- +/// @name Operations +///-------------------------------------- + ++ (id)newValueByApplyingFieldOperation:(PFFieldOperation *)operation + toDictionary:(NSMutableDictionary *)dictionary + forKey:(NSString *)key; ++ (void)applyOperationSet:(PFOperationSet *)operationSet toDictionary:(NSMutableDictionary *)dictionary; + +///-------------------------------------- +/// @name Equality +///-------------------------------------- + ++ (BOOL)isObject:(nullable id)objectA equalToObject:(nullable id)objectB; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.m b/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.m new file mode 100644 index 0000000..9b5f1e1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Object/Utilities/PFObjectUtilities.m @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectUtilities.h" + +#import "PFFieldOperation.h" +#import "PFOperationSet.h" + +@implementation PFObjectUtilities + +///-------------------------------------- +#pragma mark - Operations +///-------------------------------------- + ++ (id)newValueByApplyingFieldOperation:(PFFieldOperation *)operation + toDictionary:(NSMutableDictionary *)dictionary + forKey:(NSString *)key { + id oldValue = dictionary[key]; + id newValue = [operation applyToValue:oldValue forKey:key]; + if (newValue) { + dictionary[key] = newValue; + } else { + [dictionary removeObjectForKey:key]; + } + return newValue; +} + ++ (void)applyOperationSet:(PFOperationSet *)operationSet toDictionary:(NSMutableDictionary *)dictionary { + [operationSet enumerateKeysAndObjectsUsingBlock:^(NSString *key, PFFieldOperation *obj, BOOL *stop) { + [self newValueByApplyingFieldOperation:obj toDictionary:dictionary forKey:key]; + }]; +} + +///-------------------------------------- +#pragma mark - Equality +///-------------------------------------- + ++ (BOOL)isObject:(id)objectA equalToObject:(id)objectB { + return (objectA == objectB || (objectA != nil && [objectA isEqual:objectB])); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFAlertView.h b/Pods/Parse/Parse/Internal/PFAlertView.h new file mode 100644 index 0000000..bddaf30 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFAlertView.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +typedef void(^PFAlertViewCompletion)(NSUInteger selectedOtherButtonIndex); + +@interface PFAlertView : NSObject + ++ (void)showAlertWithTitle:(NSString *)title + message:(NSString *)message + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSArray *)otherButtonTitles + completion:(PFAlertViewCompletion)completion NS_EXTENSION_UNAVAILABLE_IOS(""); + +@end diff --git a/Pods/Parse/Parse/Internal/PFAlertView.m b/Pods/Parse/Parse/Internal/PFAlertView.m new file mode 100644 index 0000000..e17fa6d --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFAlertView.m @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAlertView.h" + +@interface PFAlertView () + +@property (nonatomic, copy) PFAlertViewCompletion completion; + +@end + +@implementation PFAlertView + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (void)showAlertWithTitle:(NSString *)title + message:(NSString *)message + cancelButtonTitle:(NSString *)cancelButtonTitle + otherButtonTitles:(NSArray *)otherButtonTitles + completion:(PFAlertViewCompletion)completion { + if ([UIAlertController class] != nil) { + __block UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + void (^alertActionHandler)(UIAlertAction *) = [^(UIAlertAction *action) { + if (completion) { + // This block intentionally retains alertController, and releases it afterwards. + if (action.style == UIAlertActionStyleCancel) { + completion(NSNotFound); + } else { + NSUInteger index = [alertController.actions indexOfObject:action]; + completion(index - 1); + } + } + alertController = nil; + } copy]; + + [alertController addAction:[UIAlertAction actionWithTitle:cancelButtonTitle + style:UIAlertActionStyleCancel + handler:alertActionHandler]]; + + for (NSString *buttonTitle in otherButtonTitles) { + [alertController addAction:[UIAlertAction actionWithTitle:buttonTitle + style:UIAlertActionStyleDefault + handler:alertActionHandler]]; + } + + UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; + UIViewController *viewController = keyWindow.rootViewController; + while (viewController.presentedViewController) { + viewController = viewController.presentedViewController; + } + + [viewController presentViewController:alertController animated:YES completion:nil]; + } else { +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 + __block PFAlertView *pfAlertView = [[self alloc] init]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title + message:message + delegate:nil + cancelButtonTitle:cancelButtonTitle + otherButtonTitles:nil]; + + for (NSString *buttonTitle in otherButtonTitles) { + [alertView addButtonWithTitle:buttonTitle]; + } + + pfAlertView.completion = ^(NSUInteger index) { + if (completion) { + completion(index); + } + + pfAlertView = nil; + }; + + alertView.delegate = pfAlertView; + [alertView show]; +#endif + } +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 + +///-------------------------------------- +#pragma mark - UIAlertViewDelegate +///-------------------------------------- + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { + if (self.completion) { + if (buttonIndex == alertView.cancelButtonIndex) { + self.completion(NSNotFound); + } else { + self.completion(buttonIndex - 1); + } + } +} + +#endif + +@end diff --git a/Pods/Parse/Parse/Internal/PFApplication.h b/Pods/Parse/Parse/Internal/PFApplication.h new file mode 100644 index 0000000..7bf1c24 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFApplication.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_WATCH +@class UIApplication; +#elif TARGET_OS_MAC +#import +@compatibility_alias UIApplication NSApplication; +#endif + +/*! + `PFApplication` class provides a centralized way to get the information about the current application, + or the environment it's running in. Please note, that all device specific things - should go to . + */ +@interface PFApplication : NSObject + +@property (nonatomic, strong, readonly) UIApplication *systemApplication; + +@property (nonatomic, assign, readonly, getter=isAppStoreEnvironment) BOOL appStoreEnvironment; +@property (nonatomic, assign, readonly, getter=isExtensionEnvironment) BOOL extensionEnvironment; + +@property (nonatomic, assign) NSInteger iconBadgeNumber; + ++ (instancetype)currentApplication; + +@end diff --git a/Pods/Parse/Parse/Internal/PFApplication.m b/Pods/Parse/Parse/Internal/PFApplication.m new file mode 100644 index 0000000..a602fa1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFApplication.m @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFApplication.h" + +#if TARGET_OS_IOS +#import +#elif !TARGET_OS_WATCH && TARGET_OS_MAC +#import +#endif + +@implementation PFApplication + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)currentApplication { + static PFApplication *application; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + application = [[self alloc] init]; + }); + return application; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (BOOL)isAppStoreEnvironment { +#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR + return ([[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"] == nil); +#endif + + return NO; +} + +- (BOOL)isExtensionEnvironment { + return [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]; +} + +- (NSInteger)iconBadgeNumber { +#if TARGET_OS_WATCH + return 0; +#elif TARGET_OS_IOS + return self.systemApplication.applicationIconBadgeNumber; +#elif TARGET_OS_MAC + // Make sure not to use `NSApp` here, because it doesn't work sometimes, + // `NSApplication +sharedApplication` does though. + NSString *badgeLabel = [[NSApplication sharedApplication] dockTile].badgeLabel; + if (badgeLabel.length == 0) { + return 0; + } + + NSScanner *scanner = [NSScanner localizedScannerWithString:badgeLabel]; + + NSInteger number = 0; + [scanner scanInteger:&number]; + if (scanner.scanLocation != badgeLabel.length) { + return 0; + } + + return number; +#endif +} + +- (void)setIconBadgeNumber:(NSInteger)iconBadgeNumber { + if (self.iconBadgeNumber != iconBadgeNumber) { +#if TARGET_OS_IOS + self.systemApplication.applicationIconBadgeNumber = iconBadgeNumber; +#elif !TARGET_OS_WATCH + [[NSApplication sharedApplication] dockTile].badgeLabel = [@(iconBadgeNumber) stringValue]; +#endif + } +} + +- (UIApplication *)systemApplication { +#if TARGET_OS_WATCH + return nil; +#else + // Workaround to make `sharedApplication` still be called even if compiling for App Extensions or WatchKit apps. + return [UIApplication performSelector:@selector(sharedApplication)]; +#endif +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFAssert.h b/Pods/Parse/Parse/Internal/PFAssert.h new file mode 100644 index 0000000..35277e0 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFAssert.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMacros.h" + +#ifndef Parse_PFAssert_h +#define Parse_PFAssert_h + +/*! + Raises an `NSInvalidArgumentException` if the `condition` does not pass. + Use `description` to supply the way to fix the exception. + */ +#define PFParameterAssert(condition, description, ...) \ + do {\ + if (!(condition)) { \ + [NSException raise:NSInvalidArgumentException \ + format:description, ##__VA_ARGS__]; \ + } \ + } while(0) + +/*! + Raises an `NSRangeException` if the `condition` does not pass. + Use `description` to supply the way to fix the exception. + */ +#define PFRangeAssert(condition, description, ...) \ + do {\ + if (!(condition)) { \ + [NSException raise:NSRangeException \ + format:description, ##__VA_ARGS__]; \ + } \ +} while(0) + +/*! + Raises an `NSInternalInconsistencyException` if the `condition` does not pass. + Use `description` to supply the way to fix the exception. + */ +#define PFConsistencyAssert(condition, description, ...) \ + do { \ + if (!(condition)) { \ + [NSException raise:NSInternalInconsistencyException \ + format:description, ##__VA_ARGS__]; \ + } \ + } while(0) + +/*! + Always raises `NSInternalInconsistencyException` with details + about the method used and class that received the message + */ +#define PFNotDesignatedInitializer() \ +do { \ + PFConsistencyAssert(NO, \ + @"%@ is not the designated initializer for instances of %@.", \ + NSStringFromSelector(_cmd), \ + NSStringFromClass([self class])); \ + return nil; \ +} while (0) + +/*! + Raises `NSInternalInconsistencyException` if current thread is not main thread. + */ +#define PFAssertMainThread() \ +do { \ + PFConsistencyAssert([NSThread isMainThread], @"This method must be called on the main thread."); \ +} while (0) + +/*! + Raises `NSInternalInconsistencyException` if current thread is not the required one. + */ +#define PFAssertIsOnThread(thread) \ +do { \ + PFConsistencyAssert([NSThread currentThread] == thread, \ + @"This method must be called only on thread: %@.", thread); \ +} while (0) + +/*! + Raises `NSInternalInconsistencyException` if the current queue + is not the same as the queue provided. + Make sure you mark the queue first via `PFMarkDispatchQueue` + */ +#define PFAssertIsOnDispatchQueue(queue) \ +do { \ + void *mark = PFOSObjectPointer(queue); \ + PFConsistencyAssert(dispatch_get_specific(mark) == mark, \ + @"%s must be executed on %s", \ + __PRETTY_FUNCTION__, dispatch_queue_get_label(queue)); \ +} while (0) + +#endif diff --git a/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.h b/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.h new file mode 100644 index 0000000..45da5dc --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFAsyncTaskQueue : NSObject + ++ (instancetype)taskQueue; + +- (BFTask *)enqueue:(BFContinuationBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.m b/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.m new file mode 100644 index 0000000..2dae167 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFAsyncTaskQueue.m @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAsyncTaskQueue.h" + +#import + +#import "BFTask+Private.h" + +@interface PFAsyncTaskQueue() + +@property (nonatomic, strong) dispatch_queue_t syncQueue; +@property (nonatomic, strong) BFTask *tail; + +@end + +@implementation PFAsyncTaskQueue + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _tail = [BFTask taskWithResult:nil]; + _syncQueue = dispatch_queue_create("com.parse.asynctaskqueue.sync", DISPATCH_QUEUE_SERIAL); + + return self; +} + ++ (instancetype)taskQueue { + return [[self alloc] init]; +} + +///-------------------------------------- +#pragma mark - Enqueue +///-------------------------------------- + +- (BFTask *)enqueue:(BFContinuationBlock)block { + BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource]; + dispatch_async(_syncQueue, ^{ + _tail = [_tail continueAsyncWithBlock:block]; + [_tail continueAsyncWithBlock:^id(BFTask *task) { + if (task.faulted) { + NSError *error = task.error; + if (error) { + [source trySetError:error]; + } else { + [source trySetException:task.exception]; + } + } else if (task.cancelled) { + [source trySetCancelled]; + } else { + [source trySetResult:task.result]; + } + return task; + }]; + }); + return source.task; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFBase64Encoder.h b/Pods/Parse/Parse/Internal/PFBase64Encoder.h new file mode 100644 index 0000000..4a7d44f --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFBase64Encoder.h @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFBase64Encoder : NSObject + ++ (NSData *)dataFromBase64String:(NSString *)string; ++ (NSString *)base64StringFromData:(NSData *)data; + +@end diff --git a/Pods/Parse/Parse/Internal/PFBase64Encoder.m b/Pods/Parse/Parse/Internal/PFBase64Encoder.m new file mode 100644 index 0000000..07fb5d0 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFBase64Encoder.m @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFBase64Encoder.h" + +@implementation PFBase64Encoder + ++ (NSData *)dataFromBase64String:(NSString *)string { + if (!string) { + return [NSData data]; + } + return [[NSData alloc] initWithBase64EncodedString:string options:NSDataBase64DecodingIgnoreUnknownCharacters]; +} + ++ (NSString *)base64StringFromData:(NSData *)data { + if (!data) { + return [NSString string]; + } + return [data base64EncodedStringWithOptions:0]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFBaseState.h b/Pods/Parse/Parse/Internal/PFBaseState.h new file mode 100644 index 0000000..535403c --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFBaseState.h @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +typedef NS_ENUM(uint8_t, PFPropertyInfoAssociationType) { + PFPropertyInfoAssociationTypeDefault, // Assign for c-types, strong for objc-types. + PFPropertyInfoAssociationTypeAssign, + PFPropertyInfoAssociationTypeStrong, + PFPropertyInfoAssociationTypeWeak, + PFPropertyInfoAssociationTypeCopy, + PFPropertyInfoAssociationTypeMutableCopy, +}; + +@interface PFPropertyAttributes : NSObject + +@property (nonatomic, assign, readonly) PFPropertyInfoAssociationType associationType; + +- (instancetype)initWithAssociationType:(PFPropertyInfoAssociationType)associationType NS_DESIGNATED_INITIALIZER; + ++ (instancetype)attributes; ++ (instancetype)attributesWithAssociationType:(PFPropertyInfoAssociationType)associationType; + +@end + +@protocol PFBaseStateSubclass + +/*! + This is the list of properties that should be used automatically for the methods implemented by PFBaseState. + + It should be a dictionary in the format of @{ @"<#property name#>": [PFPropertyAttributes attributes] } + This will be automatically cached by PFBaseState, no need for you to cache it yourself. + + @return a dictionary of property attributes + */ ++ (NSDictionary *)propertyAttributes; + +@end + +/*! + Shared base class for all state objects. + Implements -init, -description, -debugDescription, -hash, -isEqual:, -compareTo:, etc. for you. + */ +@interface PFBaseState : NSObject + +- (instancetype)initWithState:(PFBaseState *)otherState; ++ (instancetype)stateWithState:(PFBaseState *)otherState; + +- (NSComparisonResult)compare:(PFBaseState *)other; + +/*! + Returns a dictionary representation of this object. + + Essentially, it takes the values for the keys of this object, and stuffs them in the dictionary. + It will call -dictionaryRepresentation on any objects it contains, in order to handle base states + contained in this base state. + + If a value is `nil`, it will be replaced with [NSNull null], to ensure all keys exist in the dictionary. + + If you don't like this behavior, you can overwrite the method + -nilValueForProperty:(NSString *) property + to return either nil to skip the key, or a value to use in it's place. + + @return A dictionary representation of this object state. + */ +- (NSDictionary *)dictionaryRepresentation; + +- (id)debugQuickLookObject; + +@end diff --git a/Pods/Parse/Parse/Internal/PFBaseState.m b/Pods/Parse/Parse/Internal/PFBaseState.m new file mode 100644 index 0000000..dc6af22 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFBaseState.m @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFBaseState.h" + +#import +#import + +#import "PFAssert.h" +#import "PFHash.h" +#import "PFMacros.h" +#import "PFPropertyInfo.h" + +///-------------------------------------- +#pragma mark - Helpers +///-------------------------------------- + +@implementation PFPropertyAttributes + +- (instancetype)init { + return [self initWithAssociationType:PFPropertyInfoAssociationTypeDefault]; +} + +- (instancetype)initWithAssociationType:(PFPropertyInfoAssociationType)associationType { + self = [super init]; + if (!self) return nil; + + _associationType = associationType; + + return self; +} + ++ (instancetype)attributes { + return [[self alloc] init]; +} + ++ (instancetype)attributesWithAssociationType:(PFPropertyInfoAssociationType)associationType { + return [[self alloc] initWithAssociationType:associationType]; +} + +@end + +@interface PFBaseState () { + BOOL _initializing; +} + +@end + +@implementation PFBaseState + +///-------------------------------------- +#pragma mark - Property Info +///-------------------------------------- + ++ (NSSet *)_propertyInfo { + static void *_propertyMapKey = &_propertyMapKey; + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("com.parse.basestate.propertyinfo", DISPATCH_QUEUE_SERIAL); + }); + + __block NSMutableSet *results = nil; + dispatch_sync(queue, ^{ + results = objc_getAssociatedObject(self, _propertyMapKey); + if (results) { + return; + } + + NSDictionary *attributesMap = [(id)self propertyAttributes]; + results = [[NSMutableSet alloc] initWithCapacity:attributesMap.count]; + + [attributesMap enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [results addObject:[PFPropertyInfo propertyInfoWithClass:self + name:key + associationType:[obj associationType]]]; + }]; + + objc_setAssociatedObject(self, _propertyMapKey, results, OBJC_ASSOCIATION_RETAIN); + }); + + return results; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + // To prevent a recursive init function. + if (_initializing) { + return [super init]; + } + + _initializing = YES; + return [self initWithState:nil]; +} + +- (instancetype)initWithState:(id)otherState { + if (!_initializing) { + _initializing = YES; + + self = [self init]; + if (!self) return nil; + } + + NSSet *ourProperties = [[self class] _propertyInfo]; + NSSet *theirProperties = [[otherState class] _propertyInfo]; + + NSMutableSet *shared = [ourProperties mutableCopy]; + [shared intersectSet:theirProperties]; + + for (PFPropertyInfo *property in shared) { + [property takeValueFrom:otherState toObject:self]; + } + + return self; +} + ++ (instancetype)stateWithState:(PFBaseState *)otherState { + return [[self alloc] initWithState:otherState]; +} + +///-------------------------------------- +#pragma mark - Hashing +///-------------------------------------- + +- (NSUInteger)hash { + NSUInteger result = 0; + + for (PFPropertyInfo *property in [[self class] _propertyInfo]) { + result = PFIntegerPairHash(result, [[property getWrappedValueFrom:self] hash]); + } + + return result; +} + +///-------------------------------------- +#pragma mark - Comparison +///-------------------------------------- + +- (NSComparisonResult)compare:(PFBaseState *)other { + PFParameterAssert([other isKindOfClass:[PFBaseState class]], + @"Cannot compatre to an object that isn't a PFBaseState"); + + NSSet *ourProperties = [[self class] _propertyInfo]; + NSSet *theirProperties = [[other class] _propertyInfo]; + + NSMutableSet *shared = [ourProperties mutableCopy]; + [shared intersectSet:theirProperties]; + + for (PFPropertyInfo *info in shared) { + id ourValue = [info getWrappedValueFrom:self]; + id theirValue = [info getWrappedValueFrom:other]; + + if (![ourValue respondsToSelector:@selector(compare:)]) { + continue; + } + + NSComparisonResult result = [ourValue compare:theirValue]; + if (result != NSOrderedSame) { + return result; + } + } + + return NSOrderedSame; +} + +///-------------------------------------- +#pragma mark - Equality +///-------------------------------------- + +- (BOOL)isEqual:(id)other { + if (self == other) { + return YES; + } + + if (![other isKindOfClass:[PFBaseState class]]) { + return NO; + } + + NSSet *ourProperties = [[self class] _propertyInfo]; + NSSet *theirProperties = [[other class] _propertyInfo]; + + NSMutableSet *shared = [ourProperties mutableCopy]; + [shared intersectSet:theirProperties]; + + for (PFPropertyInfo *info in shared) { + id ourValue = [info getWrappedValueFrom:self]; + id theirValue = [info getWrappedValueFrom:other]; + + if (ourValue != theirValue && ![ourValue isEqual:theirValue]) { + return NO; + } + } + + return YES; +} + +///-------------------------------------- +#pragma mark - Description +///-------------------------------------- + +// This allows us to easily use the same implementation for description and debugDescription +- (NSString *)descriptionWithValueSelector:(SEL)toPerform { + NSMutableString *results = [NSMutableString stringWithFormat:@"<%@: %p", [self class], self]; + + for (PFPropertyInfo *property in [[self class] _propertyInfo]) { + id propertyValue = [property getWrappedValueFrom:self]; + NSString *propertyDescription = objc_msgSend_safe(NSString *)(propertyValue, toPerform); + + [results appendFormat:@", %@: %@", property.name, propertyDescription]; + } + + [results appendString:@">"]; + return results; +} + +- (NSString *)description { + return [self descriptionWithValueSelector:_cmd]; +} + +- (NSString *)debugDescription { + return [self descriptionWithValueSelector:_cmd]; +} + +///-------------------------------------- +#pragma mark - Dictionary/QuickLook representation +///-------------------------------------- + +- (id)nilValueForProperty:(NSString *)propertyName { + return [NSNull null]; +} + +// Implementation detail - this returns a mutable dictionary with mutable leaves. +- (NSDictionary *)dictionaryRepresentation { + NSSet *properties = [[self class] _propertyInfo]; + NSMutableDictionary *results = [[NSMutableDictionary alloc] initWithCapacity:properties.count]; + + for (PFPropertyInfo *info in properties) { + id value = [info getWrappedValueFrom:self]; + + if (value == nil) { + value = [self nilValueForProperty:info.name]; + + if (value == nil) { + continue; + } + } + + results[info.name] = value; + } + + return results; +} + +- (id)debugQuickLookObject { + return [[self dictionaryRepresentation] description]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFCategoryLoader.h b/Pods/Parse/Parse/Internal/PFCategoryLoader.h new file mode 100644 index 0000000..9c298e2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCategoryLoader.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFCategoryLoader : NSObject + ++ (void)loadPrivateCategories; + +@end diff --git a/Pods/Parse/Parse/Internal/PFCategoryLoader.m b/Pods/Parse/Parse/Internal/PFCategoryLoader.m new file mode 100644 index 0000000..1a38f40 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCategoryLoader.m @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCategoryLoader.h" + +#import "BFTask+Private.h" + +@implementation PFCategoryLoader + ++ (void)loadPrivateCategories { + forceLoadCategory_BFTask_Private(); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFCommandCache.h b/Pods/Parse/Parse/Internal/PFCommandCache.h new file mode 100644 index 0000000..4d780cf --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCommandCache.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFEventuallyQueue.h" + +@class PFCommandCacheTestHelper; +@class PFObject; + +/*! + ParseCommandCache manages an on-disk cache of commands to be executed, and a thread with a standard run loop + that executes the commands. There should only ever be one instance of this class, because multiple instances + would be running separate threads trying to read and execute the same commands. + */ +@interface PFCommandCache : PFEventuallyQueue + +@property (nonatomic, copy, readonly) NSString *diskCachePath; +@property (nonatomic, assign, readonly) unsigned long long diskCacheSize; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +/*! + Creates the command cache object for all ParseObjects with default configuration. + This command cache is used to locally store save commands created by the [PFObject saveEventually]. + When a PFCommandCache is instantiated, it will begin running its run loop, + which will start by processing any commands already stored in the on-disk queue. + */ ++ (instancetype)newDefaultCommandCacheWithCommandRunner:(id)commandRunner + cacheFolderPath:(NSString *)cacheFolderPath; + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval NS_UNAVAILABLE; + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval + diskCachePath:(NSString *)diskCachePath + diskCacheSize:(unsigned long long)diskCacheSize NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Pods/Parse/Parse/Internal/PFCommandCache.m b/Pods/Parse/Parse/Internal/PFCommandCache.m new file mode 100644 index 0000000..6889964 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCommandCache.m @@ -0,0 +1,330 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandCache.h" + +#include +#include + +#import +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCoreManager.h" +#import "PFErrorUtilities.h" +#import "PFEventuallyQueue_Private.h" +#import "PFFileManager.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFMultiProcessFileLockController.h" +#import "PFObject.h" +#import "PFObjectLocalIdStore.h" +#import "PFObjectPrivate.h" +#import "PFRESTCommand.h" +#import "Parse_Private.h" + +static NSString *const _PFCommandCacheDiskCacheDirectoryName = @"Command Cache"; + +static const NSString *PFCommandCachePrefixString = @"Command"; +static unsigned long long const PFCommandCacheDefaultDiskCacheSize = 10 * 1024 * 1024; // 10 MB + +@interface PFCommandCache () { + unsigned int _fileCounter; +} + +@property (nonatomic, assign, readwrite, setter=_setDiskCacheSize:) unsigned long long diskCacheSize; + +@end + +@implementation PFCommandCache + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)newDefaultCommandCacheWithCommandRunner:(id)commandRunner + cacheFolderPath:(NSString *)cacheFolderPath { + NSString *diskCachePath = [cacheFolderPath stringByAppendingPathComponent:_PFCommandCacheDiskCacheDirectoryName]; + diskCachePath = [diskCachePath stringByStandardizingPath]; + PFCommandCache *cache = [[PFCommandCache alloc] initWithCommandRunner:commandRunner + maxAttemptsCount:PFEventuallyQueueDefaultMaxAttemptsCount + retryInterval:PFEventuallyQueueDefaultTimeoutRetryInterval + diskCachePath:diskCachePath + diskCacheSize:PFCommandCacheDefaultDiskCacheSize]; + [cache start]; + return cache; +} + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval + diskCachePath:(NSString *)diskCachePath + diskCacheSize:(unsigned long long)diskCacheSize { + self = [super initWithCommandRunner:commandRunner maxAttemptsCount:attemptsCount retryInterval:retryInterval]; + if (!self) return nil; + + _diskCachePath = diskCachePath; + _diskCacheSize = diskCacheSize; + _fileCounter = 0; + + [self _createDiskCachePathIfNeeded]; + + return self; +} + +///-------------------------------------- +#pragma mark - Controlling Queue +///-------------------------------------- + +- (void)removeAllCommands { + [self pause]; + + [super removeAllCommands]; + + NSArray *commandIdentifiers = [self _pendingCommandIdentifiers]; + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:[commandIdentifiers count]]; + + for (NSString *identifier in commandIdentifiers) { + BFTask *task = [self _removeFileForCommandWithIdentifier:identifier]; + [tasks addObject:task]; + } + + [[BFTask taskForCompletionOfAllTasks:tasks] waitUntilFinished]; + + [self resume]; +} + +///-------------------------------------- +#pragma mark - PFEventuallyQueue +///-------------------------------------- + +- (void)_simulateReboot { + [super _simulateReboot]; + [self _createDiskCachePathIfNeeded]; +} + +///-------------------------------------- +#pragma mark - PFEventuallyQueueSubclass +///-------------------------------------- + +- (NSString *)_newIdentifierForCommand:(id)command { + // Start with current time - so we can sort identifiers and get the oldest one first. + return [NSString stringWithFormat:@"%@-%016qx-%08x-%@", + PFCommandCachePrefixString, + (unsigned long long)[NSDate timeIntervalSinceReferenceDate], + _fileCounter++, + [[NSUUID UUID] UUIDString]]; +} + +- (NSArray *)_pendingCommandIdentifiers { + NSArray *result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.diskCachePath error:nil]; + // Only accept files that starts with "Command" since sometimes the directory is filled with garbage + // e.g.: https://phab.parse.com/file/info/PHID-FILE-qgbwk7sm7kcyaks6n4j7/ + result = [result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH %@", PFCommandCachePrefixString]]; + + return [result sortedArrayUsingSelector:@selector(compare:)]; +} + +- (id)_commandWithIdentifier:(NSString *)identifier error:(NSError **)error { + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:self.diskCachePath]; + + NSError *innerError = nil; + NSData *jsonData = [NSData dataWithContentsOfFile:[self _filePathForCommandWithIdentifier:identifier] + options:NSDataReadingUncached + error:&innerError]; + + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:self.diskCachePath]; + + if (innerError || !jsonData) { + NSString *message = [NSString stringWithFormat:@"Failed to read command from cache. %@", + innerError ? [innerError localizedDescription] : @""]; + innerError = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:message]; + if (error) { + *error = innerError; + } + return nil; + } + + id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData + options:0 + error:&innerError]; + if (innerError) { + NSString *message = [NSString stringWithFormat:@"Failed to deserialiaze command from cache. %@", + [innerError localizedDescription]]; + innerError = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:message]; + } else { + if ([PFRESTCommand isValidDictionaryRepresentation:jsonObject]) { + return [PFRESTCommand commandFromDictionaryRepresentation:jsonObject]; + } + innerError = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:@"Failed to construct eventually command from cache." + shouldLog:NO]; + } + if (innerError && error) { + *error = innerError; + } + + return nil; +} + +- (BFTask *)_enqueueCommandInBackground:(id)command + object:(PFObject *)object + identifier:(NSString *)identifier { + return [self _saveCommandToCacheInBackground:command object:object identifier:identifier]; +} + +- (BFTask *)_didFinishRunningCommand:(id)command + withIdentifier:(NSString *)identifier + resultTask:(BFTask *)resultTask { + // Get the new objectId and mark the new localId so it can be resolved. + if (command.localId) { + NSDictionary *dictionaryResult = nil; + if ([resultTask.result isKindOfClass:[NSDictionary class]]) { + dictionaryResult = resultTask.result; + } else if ([resultTask.result isKindOfClass:[PFCommandResult class]]) { + PFCommandResult *commandResult = resultTask.result; + dictionaryResult = commandResult.result; + } + + if (dictionaryResult != nil) { + NSString *objectId = dictionaryResult[@"objectId"]; + if (objectId) { + [[Parse _currentManager].coreManager.objectLocalIdStore setObjectId:objectId forLocalId:command.localId]; + } + } + } + + [[self _removeFileForCommandWithIdentifier:identifier] waitUntilFinished]; + return [super _didFinishRunningCommand:command withIdentifier:identifier resultTask:resultTask]; +} + +- (BFTask *)_waitForOperationSet:(PFOperationSet *)operationSet eventuallyPin:(PFEventuallyPin *)eventuallyPin { + // Do nothing. This is only relevant in PFPinningEventuallyQueue. Looks super hacky you said? Yes it is! + return [BFTask taskWithResult:nil]; +} + +///-------------------------------------- +#pragma mark - Disk Cache +///-------------------------------------- + +- (BFTask *)_cleanupDiskCacheWithRequiredFreeSize:(NSUInteger)requiredSize { + return [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + NSUInteger size = requiredSize; + + NSMutableDictionary *commandSizes = [NSMutableDictionary dictionary]; + + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:self.diskCachePath]; + NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath]; + + NSString *identifier = nil; + while ((identifier = [enumerator nextObject])) { + NSNumber *fileSize = [enumerator fileAttributes][NSFileSize]; + if (fileSize) { + commandSizes[identifier] = fileSize; + size += [fileSize unsignedIntegerValue]; + } + } + + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:self.diskCachePath]; + + if (size > self.diskCacheSize) { + // Get identifiers and sort them to remove oldest commands first + NSArray *identifiers = [[commandSizes allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *identifier in identifiers) @autoreleasepool { + [self _removeFileForCommandWithIdentifier:identifier]; + size -= [commandSizes[identifier] unsignedIntegerValue]; + + if (size <= self.diskCacheSize) { + break; + } + [commandSizes removeObjectForKey:identifier]; + } + } + + return [BFTask taskWithResult:nil]; + }]; +} + +- (void)_setDiskCacheSize:(unsigned long long)diskCacheSize { + _diskCacheSize = diskCacheSize; +} + +///-------------------------------------- +#pragma mark - Files +///-------------------------------------- + +- (BFTask *)_saveCommandToCacheInBackground:(id)command + object:(PFObject *)object + identifier:(NSString *)identifier { + if (object != nil && object.objectId == nil) { + command.localId = [object getOrCreateLocalId]; + } + + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:[command dictionaryRepresentation] + options:0 + error:&error]; + NSUInteger commandSize = [data length]; + if (commandSize > self.diskCacheSize) { + error = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:@"Failed to run command, because it's too big."]; + } else if (commandSize == 0) { + error = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:@"Failed to run command, because it's empty."]; + } + + if (error) { + return [BFTask taskWithError:error]; + } + + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:self.diskCachePath]; + return [[[self _cleanupDiskCacheWithRequiredFreeSize:commandSize] continueWithBlock:^id(BFTask *task) { + NSString *filePath = [self _filePathForCommandWithIdentifier:identifier]; + return [PFFileManager writeDataAsync:data toFile:filePath]; + }] continueWithBlock:^id(BFTask *task) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:self.diskCachePath]; + return task; + }]; + }]; +} + +- (BFTask *)_removeFileForCommandWithIdentifier:(NSString *)identifier { + NSString *filePath = [self _filePathForCommandWithIdentifier:identifier]; + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:self.diskCachePath]; + return [PFFileManager removeItemAtPathAsync:filePath withFileLock:NO]; + }] continueWithBlock:^id(BFTask *task) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:self.diskCachePath]; + return task; // Roll-forward the previous task. + }]; +} + +- (NSString *)_filePathForCommandWithIdentifier:(NSString *)identifier { + return [self.diskCachePath stringByAppendingPathComponent:identifier]; +} + +- (void)_createDiskCachePathIfNeeded { + [[PFFileManager createDirectoryIfNeededAsyncAtPath:_diskCachePath] waitForResult:nil withMainThreadWarning:NO]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFCommandCache_Private.h b/Pods/Parse/Parse/Internal/PFCommandCache_Private.h new file mode 100644 index 0000000..869ce66 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCommandCache_Private.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandCache.h" + +@interface PFCommandCache () + +- (void)_setDiskCacheSize:(unsigned long long)diskCacheSize; + +@end; diff --git a/Pods/Parse/Parse/Internal/PFCommandResult.h b/Pods/Parse/Parse/Internal/PFCommandResult.h new file mode 100644 index 0000000..a87d667 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCommandResult.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFCommandResult : NSObject + +@property (nonatomic, strong, readonly) id result; +@property (nullable, nonatomic, copy, readonly) NSString *resultString; +@property (nullable, nonatomic, strong, readonly) NSHTTPURLResponse *httpResponse; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithResult:(NSDictionary *)result + resultString:(nullable NSString *)resultString + httpResponse:(nullable NSHTTPURLResponse *)response NS_DESIGNATED_INITIALIZER; ++ (instancetype)commandResultWithResult:(NSDictionary *)result + resultString:(nullable NSString *)resultString + httpResponse:(nullable NSHTTPURLResponse *)response; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFCommandResult.m b/Pods/Parse/Parse/Internal/PFCommandResult.m new file mode 100644 index 0000000..e83f2c3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCommandResult.m @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCommandResult.h" + +#import "PFAssert.h" + +@implementation PFCommandResult + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithResult:(NSDictionary *)result + resultString:(NSString *)resultString + httpResponse:(NSHTTPURLResponse *)response { + self = [super init]; + if (!self) return nil; + + _result = result; + _resultString = [resultString copy]; + _httpResponse = response; + + return self; +} + ++ (instancetype)commandResultWithResult:(NSDictionary *)result + resultString:(NSString *)resultString + httpResponse:(NSHTTPURLResponse *)response { + return [[self alloc] initWithResult:result resultString:resultString httpResponse:response]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFCoreDataProvider.h b/Pods/Parse/Parse/Internal/PFCoreDataProvider.h new file mode 100644 index 0000000..021e974 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCoreDataProvider.h @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef Parse_PFCoreDataProvider_h +#define Parse_PFCoreDataProvider_h + +NS_ASSUME_NONNULL_BEGIN + +///-------------------------------------- +/// @name Object +///-------------------------------------- + +@class PFObjectController; + +@protocol PFObjectControllerProvider + +@property (nonatomic, strong) PFObjectController *objectController; + +@end + +@class PFObjectBatchController; + +@protocol PFObjectBatchController + +@property (nonatomic, strong, readonly) PFObjectBatchController *objectBatchController; + +@end + +@class PFObjectFilePersistenceController; + +@protocol PFObjectFilePersistenceControllerProvider + +@property (nonatomic, strong, readonly) PFObjectFilePersistenceController *objectFilePersistenceController; + +@end + +@class PFObjectLocalIdStore; + +@protocol PFObjectLocalIdStoreProvider + +@property (nonatomic, strong) PFObjectLocalIdStore *objectLocalIdStore; + +@end + +///-------------------------------------- +/// @name User +///-------------------------------------- + +@class PFUserAuthenticationController; + +@protocol PFUserAuthenticationControllerProvider + +@property (nonatomic, strong) PFUserAuthenticationController *userAuthenticationController; + +@end + +@class PFCurrentUserController; + +@protocol PFCurrentUserControllerProvider + +@property (nonatomic, strong) PFCurrentUserController *currentUserController; + +@end + +@class PFUserController; + +@protocol PFUserControllerProvider + +@property (nonatomic, strong) PFUserController *userController; + +@end + +///-------------------------------------- +/// @name Installation +///-------------------------------------- + +@class PFCurrentInstallationController; + +@protocol PFCurrentInstallationControllerProvider + +@property (nonatomic, strong) PFCurrentInstallationController *currentInstallationController; + +@end + +@class PFInstallationController; + +@protocol PFInstallationControllerProvider + +@property (nonatomic, strong) PFInstallationController *installationController; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFCoreManager.h b/Pods/Parse/Parse/Internal/PFCoreManager.h new file mode 100644 index 0000000..19e2fca --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCoreManager.h @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFCoreDataProvider.h" +#import "PFDataProvider.h" + +@class PFInstallationIdentifierStore; + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFCoreManagerDataSource + + +@property (nonatomic, strong, readonly) PFInstallationIdentifierStore *installationIdentifierStore; + +@end + +@class PFCloudCodeController; +@class PFConfigController; +@class PFFileController; +@class PFObjectFilePersistenceController; +@class PFObjectSubclassingController; +@class PFPinningObjectStore; +@class PFQueryController; +@class PFSessionController; + +@interface PFCoreManager : NSObject + + +@property (nonatomic, weak, readonly) id dataSource; + +@property (nonatomic, strong) PFQueryController *queryController; +@property (nonatomic, strong) PFFileController *fileController; +@property (nonatomic, strong) PFCloudCodeController *cloudCodeController; +@property (nonatomic, strong) PFConfigController *configController; +@property (nonatomic, strong) PFSessionController *sessionController; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)managerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name ObjectFilePersistenceController +///-------------------------------------- + +- (void)unloadObjectFilePersistenceController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFCoreManager.m b/Pods/Parse/Parse/Internal/PFCoreManager.m new file mode 100644 index 0000000..c2b02dc --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFCoreManager.m @@ -0,0 +1,447 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCoreManager.h" + +#import "PFAssert.h" +#import "PFCachedQueryController.h" +#import "PFCloudCodeController.h" +#import "PFConfigController.h" +#import "PFCurrentInstallationController.h" +#import "PFCurrentUserController.h" +#import "PFFileController.h" +#import "PFInstallationController.h" +#import "PFLocationManager.h" +#import "PFMacros.h" +#import "PFObjectBatchController.h" +#import "PFObjectController.h" +#import "PFObjectFilePersistenceController.h" +#import "PFObjectLocalIdStore.h" +#import "PFObjectSubclassingController.h" +#import "PFOfflineObjectController.h" +#import "PFOfflineQueryController.h" +#import "PFPinningObjectStore.h" +#import "PFSessionController.h" +#import "PFUserAuthenticationController.h" +#import "PFUserController.h" + +@interface PFCoreManager () { + dispatch_queue_t _locationManagerAccessQueue; + dispatch_queue_t _controllerAccessQueue; + dispatch_queue_t _objectLocalIdStoreAccessQueue; +} + +@end + +@implementation PFCoreManager + +@synthesize locationManager = _locationManager; + +@synthesize queryController = _queryController; +@synthesize fileController = _fileController; +@synthesize cloudCodeController = _cloudCodeController; +@synthesize configController = _configController; +@synthesize objectController = _objectController; +@synthesize objectBatchController = _objectBatchController; +@synthesize objectFilePersistenceController = _objectFilePersistenceController; +@synthesize objectLocalIdStore = _objectLocalIdStore; +@synthesize pinningObjectStore = _pinningObjectStore; +@synthesize userAuthenticationController = _userAuthenticationController; +@synthesize sessionController = _sessionController; +@synthesize currentInstallationController = _currentInstallationController; +@synthesize currentUserController = _currentUserController; +@synthesize userController = _userController; +@synthesize installationController = _installationController; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + _locationManagerAccessQueue = dispatch_queue_create("com.parse.core.locationManager", DISPATCH_QUEUE_SERIAL); + _controllerAccessQueue = dispatch_queue_create("com.parse.core.controller.accessQueue", DISPATCH_QUEUE_SERIAL); + _objectLocalIdStoreAccessQueue = dispatch_queue_create("com.parse.core.object.localIdStore", DISPATCH_QUEUE_SERIAL); + + return self; +} + ++ (instancetype)managerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - LocationManager +///-------------------------------------- + +- (PFLocationManager *)locationManager { + __block PFLocationManager *manager; + dispatch_sync(_locationManagerAccessQueue, ^{ + if (!_locationManager) { + _locationManager = [[PFLocationManager alloc] init]; + } + manager = _locationManager; + }); + return manager; +} + +///-------------------------------------- +#pragma mark - QueryController +///-------------------------------------- + +- (PFQueryController *)queryController { + __block PFQueryController *queryController; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_queryController) { + id dataSource = self.dataSource; + if (dataSource.offlineStoreLoaded) { + _queryController = [PFOfflineQueryController controllerWithCommonDataSource:dataSource + coreDataSource:self]; + } else { + _queryController = [PFCachedQueryController controllerWithCommonDataSource:dataSource]; + } + } + queryController = _queryController; + }); + return queryController; +} + +- (void)setQueryController:(PFQueryController *)queryController { + dispatch_sync(_controllerAccessQueue, ^{ + _queryController = queryController; + }); +} + +///-------------------------------------- +#pragma mark - FileController +///-------------------------------------- + +- (PFFileController *)fileController { + __block PFFileController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_fileController) { + _fileController = [PFFileController controllerWithDataSource:self.dataSource]; + } + controller = _fileController; + }); + return controller; +} + +- (void)setFileController:(PFFileController *)fileController { + dispatch_sync(_controllerAccessQueue, ^{ + _fileController = fileController; + }); +} + +///-------------------------------------- +#pragma mark - CloudCodeController +///-------------------------------------- + +- (PFCloudCodeController *)cloudCodeController { + __block PFCloudCodeController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_cloudCodeController) { + _cloudCodeController = [[PFCloudCodeController alloc] initWithCommandRunner:self.dataSource.commandRunner]; + } + controller = _cloudCodeController; + }); + return controller; +} + +- (void)setCloudCodeController:(PFCloudCodeController *)cloudCodeController { + dispatch_sync(_controllerAccessQueue, ^{ + _cloudCodeController = cloudCodeController; + }); +} + +///-------------------------------------- +#pragma mark - ConfigController +///-------------------------------------- + +- (PFConfigController *)configController { + __block PFConfigController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_configController) { + id dataSource = self.dataSource; + _configController = [[PFConfigController alloc] initWithFileManager:dataSource.fileManager + commandRunner:dataSource.commandRunner]; + } + controller = _configController; + }); + return controller; +} + +- (void)setConfigController:(PFConfigController *)configController { + dispatch_sync(_controllerAccessQueue, ^{ + _configController = configController; + }); +} + +///-------------------------------------- +#pragma mark - ObjectController +///-------------------------------------- + +- (PFObjectController *)objectController { + __block PFObjectController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_objectController) { + id dataSource = self.dataSource; + if (dataSource.offlineStoreLoaded) { + _objectController = [PFOfflineObjectController controllerWithDataSource:dataSource]; + } else { + _objectController = [PFObjectController controllerWithDataSource:dataSource]; + } + } + controller = _objectController; + }); + return controller; +} + +- (void)setObjectController:(PFObjectController *)controller { + dispatch_sync(_controllerAccessQueue, ^{ + _objectController = controller; + }); +} + +///-------------------------------------- +#pragma mark - ObjectBatchController +///-------------------------------------- + +- (PFObjectBatchController *)objectBatchController { + __block PFObjectBatchController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_objectBatchController) { + _objectBatchController = [PFObjectBatchController controllerWithDataSource:self.dataSource]; + } + controller = _objectBatchController; + }); + return controller; +} + +///-------------------------------------- +#pragma mark - ObjectFilePersistenceController +///-------------------------------------- + +- (PFObjectFilePersistenceController *)objectFilePersistenceController { + __block PFObjectFilePersistenceController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_objectFilePersistenceController) { + _objectFilePersistenceController = [PFObjectFilePersistenceController controllerWithDataSource:self.dataSource]; + } + controller = _objectFilePersistenceController; + }); + return controller; +} + +- (void)unloadObjectFilePersistenceController { + dispatch_sync(_controllerAccessQueue, ^{ + _objectFilePersistenceController = nil; + }); +} + +///-------------------------------------- +#pragma mark - Pinning Object Store +///-------------------------------------- + +- (PFPinningObjectStore *)pinningObjectStore { + __block PFPinningObjectStore *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_pinningObjectStore) { + _pinningObjectStore = [PFPinningObjectStore storeWithDataSource:self.dataSource]; + } + controller = _pinningObjectStore; + }); + return controller; +} + +- (void)setPinningObjectStore:(PFPinningObjectStore *)pinningObjectStore { + dispatch_sync(_controllerAccessQueue, ^{ + _pinningObjectStore = pinningObjectStore; + }); +} + +///-------------------------------------- +#pragma mark - Object LocalId Store +///-------------------------------------- + +- (PFObjectLocalIdStore *)objectLocalIdStore { + __block PFObjectLocalIdStore *store = nil; + @weakify(self); + dispatch_sync(_objectLocalIdStoreAccessQueue, ^{ + @strongify(self); + if (!_objectLocalIdStore) { + _objectLocalIdStore = [[PFObjectLocalIdStore alloc] initWithDataSource:self.dataSource]; + } + store = _objectLocalIdStore; + }); + return store; +} + +- (void)setObjectLocalIdStore:(PFObjectLocalIdStore *)objectLocalIdStore { + dispatch_sync(_objectLocalIdStoreAccessQueue, ^{ + _objectLocalIdStore = objectLocalIdStore; + }); +} + +///-------------------------------------- +#pragma mark - UserAuthenticationController +///-------------------------------------- + +- (PFUserAuthenticationController *)userAuthenticationController { + __block PFUserAuthenticationController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_userAuthenticationController) { + _userAuthenticationController = [PFUserAuthenticationController controllerWithDataSource:self]; + } + controller = _userAuthenticationController; + }); + return controller; +} + +- (void)setUserAuthenticationController:(PFUserAuthenticationController *)userAuthenticationController { + dispatch_sync(_controllerAccessQueue, ^{ + _userAuthenticationController = userAuthenticationController; + }); +} + +///-------------------------------------- +#pragma mark - SessionController +///-------------------------------------- + +- (PFSessionController *)sessionController { + __block PFSessionController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_sessionController) { + _sessionController = [PFSessionController controllerWithDataSource:self.dataSource]; + } + controller = _sessionController; + }); + return controller; +} + +- (void)setSessionController:(PFSessionController *)sessionController { + dispatch_sync(_controllerAccessQueue, ^{ + _sessionController = sessionController; + }); +} + +#if !TARGET_OS_WATCH + +///-------------------------------------- +#pragma mark - Current Installation Controller +///-------------------------------------- + +- (PFCurrentInstallationController *)currentInstallationController { + __block PFCurrentInstallationController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_currentInstallationController) { + id dataSource = self.dataSource; + PFCurrentObjectStorageType storageType = (dataSource.offlineStore ? + PFCurrentObjectStorageTypeOfflineStore : + PFCurrentObjectStorageTypeFile); + _currentInstallationController = [PFCurrentInstallationController controllerWithStorageType:storageType + commonDataSource:dataSource + coreDataSource:self]; + } + controller = _currentInstallationController; + }); + return controller; +} + +- (void)setCurrentInstallationController:(PFCurrentInstallationController *)controller { + dispatch_sync(_controllerAccessQueue, ^{ + _currentInstallationController = controller; + }); +} + +#endif + +///-------------------------------------- +#pragma mark - Current User Controller +///-------------------------------------- + +- (PFCurrentUserController *)currentUserController { + __block PFCurrentUserController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_currentUserController) { + id dataSource = self.dataSource; + PFCurrentObjectStorageType storageType = (dataSource.offlineStore ? + PFCurrentObjectStorageTypeOfflineStore : + PFCurrentObjectStorageTypeFile); + _currentUserController = [PFCurrentUserController controllerWithStorageType:storageType + commonDataSource:dataSource + coreDataSource:self]; + } + controller = _currentUserController; + }); + return controller; +} + +- (void)setCurrentUserController:(PFCurrentUserController *)currentUserController { + dispatch_sync(_controllerAccessQueue, ^{ + _currentUserController = currentUserController; + }); +} + +#if !TARGET_OS_WATCH + +///-------------------------------------- +#pragma mark - Installation Controller +///-------------------------------------- + +- (PFInstallationController *)installationController { + __block PFInstallationController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_installationController) { + _installationController = [PFInstallationController controllerWithDataSource:self]; + } + controller = _installationController; + }); + return controller; +} + +- (void)setInstallationController:(PFInstallationController *)installationController { + dispatch_sync(_controllerAccessQueue, ^{ + _installationController = installationController; + }); +} + +#endif + +///-------------------------------------- +#pragma mark - User Controller +///-------------------------------------- + +- (PFUserController *)userController { + __block PFUserController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_userController) { + _userController = [PFUserController controllerWithCommonDataSource:self.dataSource + coreDataSource:self]; + } + controller = _userController; + }); + return controller; +} + +- (void)setUserController:(PFUserController *)userController { + dispatch_sync(_controllerAccessQueue, ^{ + _userController = userController; + }); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFDataProvider.h b/Pods/Parse/Parse/Internal/PFDataProvider.h new file mode 100644 index 0000000..3813c4d --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDataProvider.h @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef Parse_PFDataProviders_h +#define Parse_PFDataProviders_h + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFCommandRunning; + +@protocol PFCommandRunnerProvider + +@property (nonatomic, strong, readonly) id commandRunner; + +@end + +@class PFFileManager; + +@protocol PFFileManagerProvider + +@property (nonatomic, strong, readonly) PFFileManager *fileManager; + +@end + +@class PFOfflineStore; + +@protocol PFOfflineStoreProvider + +@property (nullable, nonatomic, strong) PFOfflineStore *offlineStore; +@property (nonatomic, assign, readonly, getter=isOfflineStoreLoaded) BOOL offlineStoreLoaded; + +@end + +@class PFEventuallyQueue; + +@protocol PFEventuallyQueueProvider + +@property (nonatomic, strong, readonly) PFEventuallyQueue *eventuallyQueue; + +@end + +@class PFKeychainStore; + +@protocol PFKeychainStoreProvider + +@property (nonatomic, strong, readonly) PFKeychainStore *keychainStore; + +@end + +@class PFKeyValueCache; + +@protocol PFKeyValueCacheProvider + +@property (nonatomic, strong, readonly) PFKeyValueCache *keyValueCache; + +@end + +@class PFLocationManager; + +@protocol PFLocationManagerProvider + +@property (nonatomic, strong, readonly) PFLocationManager *locationManager; + +@end + +@class PFPinningObjectStore; + +@protocol PFPinningObjectStoreProvider + +@property (nonatomic, strong) PFPinningObjectStore *pinningObjectStore; + +@end + +@class PFInstallationIdentifierStore; + +@protocol PFInstallationIdentifierStoreProvider + +@property (nonatomic, strong, readonly) PFInstallationIdentifierStore *installationIdentifierStore; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFDateFormatter.h b/Pods/Parse/Parse/Internal/PFDateFormatter.h new file mode 100644 index 0000000..d258e76 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDateFormatter.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFDateFormatter : NSObject + ++ (instancetype)sharedFormatter; + +///-------------------------------------- +/// @name String from Date +///-------------------------------------- + +/*! + Converts `NSDate` into `NSString` representation using the following format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z' + + @param date `NSDate` to convert. + + @returns Formatted `NSString` representation. + */ +- (NSString *)preciseStringFromDate:(NSDate *)date; + +///-------------------------------------- +/// @name Date from String +///-------------------------------------- + +/*! + Converts `NSString` representation of a date into `NSDate` object. + + @discussion Following date formats are supported: + YYYY-MM-DD + YYYY-MM-DD HH:MM'Z' + YYYY-MM-DD HH:MM:SS'Z' + YYYY-MM-DD HH:MM:SS.SSS'Z' + YYYY-MM-DDTHH:MM'Z' + YYYY-MM-DDTHH:MM:SS'Z' + YYYY-MM-DDTHH:MM:SS.SSS'Z' + + @param string `NSString` representation to convert. + + @returns `NSDate` incapsulating the date. + */ +- (NSDate *)dateFromString:(NSString *)string; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFDateFormatter.m b/Pods/Parse/Parse/Internal/PFDateFormatter.m new file mode 100644 index 0000000..c4a6ba2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDateFormatter.m @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFDateFormatter.h" + +#import + +@interface PFDateFormatter () { + dispatch_queue_t _synchronizationQueue; + + sqlite3 *_sqliteDatabase; + sqlite3_stmt *_stringToDateStatement; +} + +@property (nonatomic, strong, readonly) NSDateFormatter *preciseDateFormatter; + +@end + +@implementation PFDateFormatter + +@synthesize preciseDateFormatter = _preciseDateFormatter; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)sharedFormatter { + static PFDateFormatter *formatter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [[self alloc] init]; + }); + return formatter; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _synchronizationQueue = dispatch_queue_create("com.parse.dateFormatter", DISPATCH_QUEUE_SERIAL); + + //TODO: (nlutsenko) Check for error here. + sqlite3_open(":memory:", &_sqliteDatabase); + sqlite3_prepare_v2(_sqliteDatabase, + "SELECT strftime('%s', ?), strftime('%f', ?);", + -1, + &_stringToDateStatement, + NULL); + + return self; +} + +- (void)dealloc { + sqlite3_finalize(_stringToDateStatement); + sqlite3_close(_sqliteDatabase); +} + +///-------------------------------------- +#pragma mark - Date Formatters +///-------------------------------------- + +- (NSDateFormatter *)preciseDateFormatter { + if (!_preciseDateFormatter) { + _preciseDateFormatter = [[NSDateFormatter alloc] init]; + _preciseDateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + _preciseDateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + _preciseDateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + } + return _preciseDateFormatter; +} + +///-------------------------------------- +#pragma mark - String from Date +///-------------------------------------- + +- (NSString *)preciseStringFromDate:(NSDate *)date { + __block NSString *string = nil; + dispatch_sync(_synchronizationQueue, ^{ + string = [self.preciseDateFormatter stringFromDate:date]; + }); + return string; +} + +///-------------------------------------- +#pragma mark - Date from String +///-------------------------------------- + +- (NSDate *)dateFromString:(NSString *)string { + __block sqlite3_int64 interval = 0; + __block double seconds = 0.0; + dispatch_sync(_synchronizationQueue, ^{ + const char *utf8String = [string UTF8String]; + + sqlite3_bind_text(_stringToDateStatement, 1, utf8String, -1, SQLITE_STATIC); + sqlite3_bind_text(_stringToDateStatement, 2, utf8String, -1, SQLITE_STATIC); + + if (sqlite3_step(_stringToDateStatement) == SQLITE_ROW) { + interval = sqlite3_column_int64(_stringToDateStatement, 0); + seconds = sqlite3_column_double(_stringToDateStatement, 1); + } + + sqlite3_reset(_stringToDateStatement); + sqlite3_clear_bindings(_stringToDateStatement); + }); + // Extract the fraction component of the seconds + double sintegral = 0.0; + double sfraction = modf(seconds, &sintegral); + + return [NSDate dateWithTimeIntervalSince1970:(double)interval + sfraction]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFDecoder.h b/Pods/Parse/Parse/Internal/PFDecoder.h new file mode 100644 index 0000000..b0e5074 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDecoder.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFDecoder : NSObject + +/*! + Globally available shared instance of PFDecoder. + */ ++ (PFDecoder *)objectDecoder; + +/*! + Takes a complex object that was deserialized and converts encoded + dictionaries into the proper Parse types. This is the inverse of + encodeObject:allowUnsaved:allowObjects:seenObjects:. + */ +- (nullable id)decodeObject:(nullable id)object; + +@end + +/*! + Extends the normal JSON to PFObject decoding to also deal with placeholders for new objects + that have been saved offline. + */ +@interface PFOfflineDecoder : PFDecoder + ++ (instancetype)decoderWithOfflineObjects:(nullable NSDictionary *)offlineObjects; + +@end + +/*! + A subclass of PFDecoder which can keep PFObject that has been fetched instead of creating a new instance. + */ +@interface PFKnownParseObjectDecoder : PFDecoder + ++ (instancetype)decoderWithFetchedObjects:(nullable NSDictionary *)fetchedObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFDecoder.m b/Pods/Parse/Parse/Internal/PFDecoder.m new file mode 100644 index 0000000..9cd045a --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDecoder.m @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFDecoder.h" + +#import "PFBase64Encoder.h" +#import "PFDateFormatter.h" +#import "PFFieldOperation.h" +#import "PFFieldOperationDecoder.h" +#import "PFFile_Private.h" +#import "PFGeoPointPrivate.h" +#import "PFInternalUtils.h" +#import "PFMacros.h" +#import "PFObjectPrivate.h" +#import "PFRelationPrivate.h" + +///-------------------------------------- +#pragma mark - PFDecoder +///-------------------------------------- + +@implementation PFDecoder + +#pragma mark Init + ++ (PFDecoder *)objectDecoder { + static PFDecoder *decoder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + decoder = [[PFDecoder alloc] init]; + }); + return decoder; +} + +#pragma mark Decode + +- (id)decodeDictionary:(NSDictionary *)dictionary { + NSString *op = dictionary[@"__op"]; + if (op) { + return [[PFFieldOperationDecoder defaultDecoder] decode:dictionary withDecoder:self]; + } + + NSString *type = dictionary[@"__type"]; + if (type) { + if ([type isEqualToString:@"Date"]) { + return [[PFDateFormatter sharedFormatter] dateFromString:dictionary[@"iso"]]; + + } else if ([type isEqualToString:@"Bytes"]) { + return [PFBase64Encoder dataFromBase64String:dictionary[@"base64"]]; + + } else if ([type isEqualToString:@"GeoPoint"]) { + return [PFGeoPoint geoPointWithDictionary:dictionary]; + + } else if ([type isEqualToString:@"Relation"]) { + return [PFRelation relationFromDictionary:dictionary withDecoder:self]; + + } else if ([type isEqualToString:@"File"]) { + return [PFFile fileWithName:dictionary[@"name"] + url:dictionary[@"url"]]; + + } else if ([type isEqualToString:@"Pointer"]) { + NSString *objectId = dictionary[@"objectId"]; + NSString *localId = dictionary[@"localId"]; + NSString *className = dictionary[@"className"]; + if (localId) { + // This is a PFObject deserialized off the local disk, which has a localId + // that will need to be resolved before the object can be sent over the network. + // Its localId should be known to PFObjectLocalIdStore. + return [self _decodePointerForClassName:className localId:localId]; + } else { + return [self _decodePointerForClassName:className objectId:objectId]; + } + + } else if ([type isEqualToString:@"Object"]) { + NSString *className = dictionary[@"className"]; + + NSMutableDictionary *data = [dictionary mutableCopy]; + [data removeObjectForKey:@"__type"]; + [data removeObjectForKey:@"className"]; + NSDictionary *result = [self decodeDictionary:data]; + + return [PFObject _objectFromDictionary:result + defaultClassName:className + completeData:YES + decoder:self]; + + } else { + // We don't know how to decode this, so just leave it as a dictionary. + return dictionary; + } + } + + NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithCapacity:[dictionary count]]; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + newDictionary[key] = [self decodeObject:obj]; + }]; + return newDictionary; +} + +- (id)_decodePointerForClassName:(NSString *)className objectId:(NSString *)objectId { + return [PFObject objectWithoutDataWithClassName:className objectId:objectId]; +} + +- (id)_decodePointerForClassName:(NSString *)className localId:(NSString *)localId { + return [PFObject objectWithoutDataWithClassName:className localId:localId]; +} + +- (id)decodeArray:(NSArray *)array { + NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; + for (id value in array) { + [newArray addObject:[self decodeObject:value]]; + } + return newArray; +} + +- (id)decodeObject:(id)object { + if ([object isKindOfClass:[NSDictionary class]]) { + return [self decodeDictionary:object]; + } else if ([object isKindOfClass:[NSArray class]]) { + return [self decodeArray:object]; + } + return object; +} + +@end + +///-------------------------------------- +#pragma mark - PFOfflineDecoder +///-------------------------------------- + +@interface PFOfflineDecoder () + +/*! + A map of UUID to Task that will be finished once the given PFObject is loaded. + The Tasks should all be finished before decode is called. + */ +@property (nonatomic, copy) NSDictionary *offlineObjects; + +@end + +@implementation PFOfflineDecoder + ++ (instancetype)decoderWithOfflineObjects:(NSDictionary *)offlineObjects { + PFOfflineDecoder *decoder = [[self alloc] init]; + decoder.offlineObjects = offlineObjects; + return decoder; +} + +#pragma mark PFDecoder + +- (id)decodeObject:(id)object { + if ([object isKindOfClass:[NSDictionary class]] && + [((NSDictionary *)object)[@"__type"] isEqualToString:@"OfflineObject"]) { + NSString *uuid = ((NSDictionary *)object)[@"uuid"]; + return ((BFTask *)self.offlineObjects[uuid]).result; + } + + // Embedded objects can't show up here, because we never stored them that way offline. + return [super decodeObject:object]; +} + +@end + +///-------------------------------------- +#pragma mark - PFKnownParseObjectDecoder +///-------------------------------------- + +@interface PFKnownParseObjectDecoder () + +@property (nonatomic, copy) NSDictionary *fetchedObjects; + +@end + +@implementation PFKnownParseObjectDecoder + ++ (instancetype)decoderWithFetchedObjects:(NSDictionary *)fetchedObjects { + PFKnownParseObjectDecoder *decoder = [[self alloc] init]; + decoder.fetchedObjects = fetchedObjects; + return decoder; +} + +- (id)_decodePointerForClassName:(NSString *)className objectId:(NSString *)objectId { + if (_fetchedObjects != nil && _fetchedObjects[objectId]) { + return _fetchedObjects[objectId]; + } + return [super _decodePointerForClassName:className objectId:objectId]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFDevice.h b/Pods/Parse/Parse/Internal/PFDevice.h new file mode 100644 index 0000000..6e62a3c --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDevice.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFDevice : NSObject + +@property (nonatomic, copy, readonly) NSString *detailedModel; + +@property (nonatomic, copy, readonly) NSString *operatingSystemFullVersion; +@property (nonatomic, copy, readonly) NSString *operatingSystemVersion; +@property (nonatomic, copy, readonly) NSString *operatingSystemBuild; + +@property (nonatomic, assign, readonly, getter=isJailbroken) BOOL jailbroken; + ++ (instancetype)currentDevice; + +@end diff --git a/Pods/Parse/Parse/Internal/PFDevice.m b/Pods/Parse/Parse/Internal/PFDevice.m new file mode 100644 index 0000000..b918012 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFDevice.m @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFDevice.h" + +#import + +#if TARGET_OS_WATCH +#import +#elif TARGET_OS_IOS +#import +#elif TARGET_OS_MAC +#import +#endif + +#include +#include +#include + +static NSString *PFDeviceSysctlByName(NSString *name) { + const char *charName = [name UTF8String]; + + size_t size; + sysctlbyname(charName, NULL, &size, NULL, 0); + char *answer = (char*)malloc(size); + + if (answer == NULL) { + return nil; + } + + sysctlbyname(charName, answer, &size, NULL, 0); + NSString *string = [NSString stringWithUTF8String:answer]; + free(answer); + + return string; +} + +@implementation PFDevice + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)currentDevice { + static PFDevice *device; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + device = [[self alloc] init]; + }); + return device; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSString *)detailedModel { + NSString *name = PFDeviceSysctlByName(@"hw.machine"); + if (!name) { +#if TARGET_OS_WATCH + name = [WKInterfaceDevice currentDevice].model; +#elif TARGET_OS_IOS + name = [UIDevice currentDevice].model; +#elif TARGET_OS_MAC + name = @"Mac"; +#endif + } + return name; +} + +- (NSString *)operatingSystemFullVersion { + NSString *version = self.operatingSystemVersion; + NSString *build = self.operatingSystemBuild; + if (build.length) { + version = [version stringByAppendingFormat:@" (%@)", build]; + } + return version; +} +- (NSString *)operatingSystemVersion { +#if TARGET_OS_IOS + return [UIDevice currentDevice].systemVersion; +#elif TARGET_OS_WATCH + NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion; + return [NSString stringWithFormat:@"%d.%d.%d", + (int)version.majorVersion, + (int)version.minorVersion, + (int)version.patchVersion]; +#elif TARGET_OS_MAC + NSProcessInfo *info = [NSProcessInfo processInfo]; + if ([info respondsToSelector:@selector(operatingSystemVersion)]) { + NSOperatingSystemVersion version = info.operatingSystemVersion; + return [NSString stringWithFormat:@"%d.%d.%d", + (int)version.majorVersion, + (int)version.minorVersion, + (int)version.patchVersion]; + } else { + // TODO: (nlutsenko) Remove usage of this method, when we drop support for OSX 10.9 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + SInt32 major, minor, bugfix; + if (Gestalt(gestaltSystemVersionMajor, &major) == noErr && + Gestalt(gestaltSystemVersionMinor, &minor) == noErr && + Gestalt(gestaltSystemVersionBugFix, &bugfix) == noErr) { + return [NSString stringWithFormat:@"%d.%d.%d", major, minor, bugfix]; + } +#pragma clang diagnostic pop + return [[NSProcessInfo processInfo] operatingSystemVersionString]; + } +#endif +} + +- (NSString *)operatingSystemBuild { + return PFDeviceSysctlByName(@"kern.osversion"); +} + +- (BOOL)isJailbroken { + BOOL jailbroken = NO; +#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR + DIR *dir = opendir("/"); + if (dir != NULL) { + jailbroken = YES; + closedir(dir); + } +#endif + return jailbroken; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFEncoder.h b/Pods/Parse/Parse/Internal/PFEncoder.h new file mode 100644 index 0000000..9482755 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEncoder.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; +@class PFOfflineStore; +@class PFSQLiteDatabase; + +///-------------------------------------- +/// @name Encoders +///-------------------------------------- + +@interface PFEncoder : NSObject + ++ (instancetype)objectEncoder; + +- (id)encodeObject:(id)object; +- (id)encodeParseObject:(PFObject *)object; + +@end + +/*! + Encoding strategy that rejects PFObject. + */ +@interface PFNoObjectEncoder : PFEncoder + +@end + +/*! + Encoding strategy that encodes PFObject to PFPointer with objectId or with localId. + */ +@interface PFPointerOrLocalIdObjectEncoder : PFEncoder + +@end + +/*! + Encoding strategy that encodes PFObject to PFPointer with objectId and rejects + unsaved PFObject. + */ +@interface PFPointerObjectEncoder : PFPointerOrLocalIdObjectEncoder + +@end + +/*! + Encoding strategy that can encode objects that are available offline. After using this encoder, + you must call encodeFinished and wait for its result to be finished before the results of the + encoding will be valid. + */ +@interface PFOfflineObjectEncoder : PFEncoder + ++ (instancetype)objectEncoderWithOfflineStore:(PFOfflineStore *)store database:(PFSQLiteDatabase *)database; + +- (BFTask *)encodeFinished; + +@end diff --git a/Pods/Parse/Parse/Internal/PFEncoder.m b/Pods/Parse/Parse/Internal/PFEncoder.m new file mode 100644 index 0000000..6f9ce64 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEncoder.m @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFEncoder.h" + +#import "PFACLPrivate.h" +#import "PFAssert.h" +#import "PFBase64Encoder.h" +#import "PFDateFormatter.h" +#import "PFFieldOperation.h" +#import "PFFile_Private.h" +#import "PFGeoPointPrivate.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFRelationPrivate.h" + +@implementation PFEncoder + ++ (instancetype)objectEncoder { + static PFEncoder *encoder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + encoder = [[PFEncoder alloc] init]; + }); + return encoder; +} + +- (id)encodeObject:(id)object { + if ([object isKindOfClass:[PFObject class]]) { + return [self encodeParseObject:object]; + } else if ([object isKindOfClass:[NSData class]]) { + return @{ + @"__type" : @"Bytes", + @"base64" : [PFBase64Encoder base64StringFromData:object] + }; + + } else if ([object isKindOfClass:[NSDate class]]) { + return @{ + @"__type" : @"Date", + @"iso" : [[PFDateFormatter sharedFormatter] preciseStringFromDate:object] + }; + + } else if ([object isKindOfClass:[PFFile class]]) { + if (((PFFile *)object).isDirty) { + // TODO: (nlutsenko) Figure out what to do with things like an unsaved file + // in a mutable container, where we don't normally want to allow serializing + // such a thing inside an object. + // + // Returning this empty object is strictly wrong, but we have to have *something* + // to put into an object's mutable container cache, and this is just about the + // best we can do right now. + // + // [NSException raise:NSInternalInconsistencyException + // format:@"Tried to serialize an unsaved file."]; + return @{ @"__type" : @"File" }; + } + return @{ + @"__type" : @"File", + @"url" : ((PFFile *)object).state.urlString, + @"name" : ((PFFile *)object).name + }; + + } else if ([object isKindOfClass:[PFFieldOperation class]]) { + // Always encode PFFieldOperation with PFPointerOrLocalId + return [object encodeWithObjectEncoder:[PFPointerOrLocalIdObjectEncoder objectEncoder]]; + } else if ([object isKindOfClass:[PFACL class]]) { + // TODO (hallucinogen): pass object encoder here + return [object encodeIntoDictionary]; + + } else if ([object isKindOfClass:[PFGeoPoint class]]) { + // TODO (hallucinogen): pass object encoder here + return [object encodeIntoDictionary]; + + } else if ([object isKindOfClass:[PFRelation class]]) { + // TODO (hallucinogen): pass object encoder here + return [object encodeIntoDictionary]; + + } else if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[object count]]; + for (id elem in object) { + [newArray addObject:[self encodeObject:elem]]; + } + return newArray; + + } else if ([object isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:[object count]]; + [object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + dict[key] = [self encodeObject:obj]; + }]; + return dict; + } + + return object; +} + +- (id)encodeParseObject:(PFObject *)object { + // Do nothing here + return nil; +} + +@end + +///-------------------------------------- +#pragma mark - PFNoObjectEncoder +///-------------------------------------- + +@implementation PFNoObjectEncoder + ++ (instancetype)objectEncoder { + static PFNoObjectEncoder *encoder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + encoder = [[PFNoObjectEncoder alloc] init]; + }); + return encoder; +} + +- (id)encodeParseObject:(PFObject *)object { + [NSException raise:NSInternalInconsistencyException format:@"PFObjects are not allowed here."]; + return nil; +} + +@end + +///-------------------------------------- +#pragma mark - PFPointerOrLocalIdObjectEncoder +///-------------------------------------- + +@implementation PFPointerOrLocalIdObjectEncoder + ++ (instancetype)objectEncoder { + static PFPointerOrLocalIdObjectEncoder *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[PFPointerOrLocalIdObjectEncoder alloc] init]; + }); + return instance; +} + +- (id)encodeParseObject:(PFObject *)object { + if (object.objectId) { + return @{ + @"__type" : @"Pointer", + @"objectId" : object.objectId, + @"className" : object.parseClassName + }; + } + return @{ + @"__type" : @"Pointer", + @"localId" : [object getOrCreateLocalId], + @"className" : object.parseClassName + }; +} + +@end + +///-------------------------------------- +#pragma mark - PFPointerObjectEncoder +///-------------------------------------- + +@implementation PFPointerObjectEncoder + ++ (instancetype)objectEncoder { + static PFPointerObjectEncoder *encoder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + encoder = [[self alloc] init]; + }); + return encoder; +} + +- (id)encodeParseObject:(PFObject *)object { + PFConsistencyAssert(object.objectId, @"Tried to save an object with a new, unsaved child."); + return [super encodeParseObject:object]; +} + +@end + +///-------------------------------------- +#pragma mark - PFOfflineObjectEncoder +///-------------------------------------- + +@interface PFOfflineObjectEncoder () + +@property (nonatomic, assign) PFOfflineStore *store; +@property (nonatomic, assign) PFSQLiteDatabase *database; +@property (nonatomic, strong) NSMutableArray *tasks; +@property (nonatomic, strong) NSObject *tasksLock; // TODO: (nlutsenko) Avoid using @synchronized + +@end + +@implementation PFOfflineObjectEncoder + ++ (instancetype)objectEncoder { + PFNotDesignatedInitializer(); + return nil; +} + ++ (instancetype)objectEncoderWithOfflineStore:(PFOfflineStore *)store database:(PFSQLiteDatabase *)database { + PFOfflineObjectEncoder *encoder = [[self alloc] init]; + encoder.store = store; + encoder.database = database; + encoder.tasks = [NSMutableArray array]; + encoder.tasksLock = [[NSObject alloc] init]; + return encoder; +} + +- (id)encodeParseObject:(PFObject *)object { + if (object.objectId) { + return @{ + @"__type" : @"Pointer", + @"objectId" : object.objectId, + @"className" : object.parseClassName + }; + } else { + NSMutableDictionary *result = [@{ @"__type" : @"OfflineObject" } mutableCopy]; + @synchronized(self.tasksLock) { + BFTask *uuidTask = [self.store getOrCreateUUIDAsyncForObject:object database:self.database]; + [uuidTask continueWithSuccessBlock:^id(BFTask *task) { + result[@"uuid"] = task.result; + return nil; + }]; + [self.tasks addObject:uuidTask]; + } + return result; + } +} + +- (BFTask *)encodeFinished { + return [[BFTask taskForCompletionOfAllTasks:self.tasks] continueWithBlock:^id(BFTask *ignore) { + @synchronized (self.tasksLock) { + // TODO (hallucinogen) It might be better to return an aggregate error here + for (BFTask *task in self.tasks) { + if (task.cancelled || task.error != nil) { + return task; + } + } + [self.tasks removeAllObjects]; + return [BFTask taskWithResult:nil]; + } + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFErrorUtilities.h b/Pods/Parse/Parse/Internal/PFErrorUtilities.h new file mode 100644 index 0000000..a5656f6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFErrorUtilities.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFErrorUtilities : NSObject + +/*! + Construct an error object from a code and a message. + + @description Note that this logs all errors given to it. + You should use `errorWithCode:message:shouldLog:` to explicitly control whether it logs. + + @param code Parse Error Code + @param message Error description + + @return Instance of `NSError` or `nil`. + */ ++ (nullable NSError *)errorWithCode:(NSInteger)code message:(NSString *)message; ++ (nullable NSError *)errorWithCode:(NSInteger)code message:(NSString *)message shouldLog:(BOOL)shouldLog; + +/*! + Construct an error object from a result dictionary the API returned. + + @description Note that this logs all errors given to it. + You should use `errorFromResult:shouldLog:` to explicitly control whether it logs. + + @param result Network command result. + + @return Instance of `NSError` or `nil`. + */ ++ (nullable NSError *)errorFromResult:(NSDictionary *)result; ++ (nullable NSError *)errorFromResult:(NSDictionary *)result shouldLog:(BOOL)shouldLog; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFErrorUtilities.m b/Pods/Parse/Parse/Internal/PFErrorUtilities.m new file mode 100644 index 0000000..a3f060d --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFErrorUtilities.m @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFErrorUtilities.h" + +#import "PFConstants.h" +#import "PFLogging.h" + +@implementation PFErrorUtilities + ++ (NSError *)errorWithCode:(NSInteger)code message:(NSString *)message { + return [self errorWithCode:code message:message shouldLog:YES]; +} + ++ (NSError *)errorWithCode:(NSInteger)code message:(NSString *)message shouldLog:(BOOL)shouldLog { + NSDictionary *result = @{ @"code" : @(code), + @"error" : message }; + return [self errorFromResult:result shouldLog:shouldLog]; +} + ++ (NSError *)errorFromResult:(NSDictionary *)result { + return [self errorFromResult:result shouldLog:YES]; +} + ++ (NSError *)errorFromResult:(NSDictionary *)result shouldLog:(BOOL)shouldLog { + NSInteger errorCode = [[result objectForKey:@"code"] integerValue]; + + NSString *errorExplanation = [result objectForKey:@"error"]; + + if (shouldLog) { + PFLogError(PFLoggingTagCommon, + @"%@ (Code: %ld, Version: %@)", errorExplanation, (long)errorCode, PARSE_VERSION); + } + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:result]; + if (errorExplanation) { + userInfo[NSLocalizedDescriptionKey] = errorExplanation; + } + return [NSError errorWithDomain:PFParseErrorDomain code:errorCode userInfo:userInfo]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFEventuallyPin.h b/Pods/Parse/Parse/Internal/PFEventuallyPin.h new file mode 100644 index 0000000..de6e754 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEventuallyPin.h @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFObject.h" +#import "PFSubclassing.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@protocol PFNetworkCommand; + +extern NSString *const PFEventuallyPinPinName; + +// Cache policies +typedef NS_ENUM(NSUInteger, PFEventuallyPinType) { + PFEventuallyPinTypeSave = 1, + PFEventuallyPinTypeDelete, + PFEventuallyPinTypeCommand +}; + +/*! + PFEventuallyPin represents PFCommand that's save locally so that it can be executed eventually. + + Properties of PFEventuallyPin: + - time + Used for sort order when querying for all EventuallyPins. + - type + PFEventuallyPinTypeSave or PFEventuallyPinTypeDelete. + - object + The object the operation should notify when complete. + - operationSetUUID + The operationSet to be completed. + - sessionToken + The user that instantiated the operation. + */ +@interface PFEventuallyPin : PFObject + +@property (nonatomic, copy, readonly) NSString *uuid; + +@property (nonatomic, assign, readonly) PFEventuallyPinType type; + +@property (nonatomic, strong, readonly) PFObject *object; + +@property (nonatomic, copy, readonly) NSString *operationSetUUID; + +@property (nonatomic, copy, readonly) NSString *sessionToken; + +@property (nonatomic, strong, readonly) id command; + +///-------------------------------------- +#pragma mark - Eventually Pin +///-------------------------------------- + +/*! + Wrap given PFObject and PFCommand in a PFEventuallyPin with auto-generated UUID. + */ ++ (BFTask *)pinEventually:(PFObject *)object forCommand:(id)command; + +/*! + Wrap given PFObject and PFCommand in a PFEventuallyPin with given UUID. + */ ++ (BFTask *)pinEventually:(PFObject *)object forCommand:(id)command withUUID:(NSString *)uuid; + ++ (BFTask *)findAllEventuallyPin; + ++ (BFTask *)findAllEventuallyPinWithExcludeUUIDs:(NSArray *)excludeUUIDs; + +@end diff --git a/Pods/Parse/Parse/Internal/PFEventuallyPin.m b/Pods/Parse/Parse/Internal/PFEventuallyPin.m new file mode 100644 index 0000000..aa9e0d4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEventuallyPin.m @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFEventuallyPin.h" + +#import + +#import "PFAssert.h" +#import "PFHTTPRequest.h" +#import "PFInternalUtils.h" +#import "PFObject+Subclass.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFQuery.h" +#import "PFRESTCommand.h" + +NSString *const PFEventuallyPinPinName = @"_eventuallyPin"; + +NSString *const PFEventuallyPinKeyUUID = @"uuid"; +NSString *const PFEventuallyPinKeyTime = @"time"; +NSString *const PFEventuallyPinKeyType = @"type"; +NSString *const PFEventuallyPinKeyObject = @"object"; +NSString *const PFEventuallyPinKeyOperationSetUUID = @"operationSetUUID"; +NSString *const PFEventuallyPinKeySessionToken = @"sessionToken"; +NSString *const PFEventuallyPinKeyCommand = @"command"; + +@implementation PFEventuallyPin + +///-------------------------------------- +#pragma mark - PFSubclassing +///-------------------------------------- + ++ (NSString *)parseClassName { + return @"_EventuallyPin"; +} + +// Validates a class name. We override this to only allow the pin class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[self parseClassName]], + @"Cannot initialize a PFEventuallyPin with a custom class name."); +} + +- (BOOL)needsDefaultACL { + return NO; +} + +///-------------------------------------- +#pragma mark - Getter +///-------------------------------------- + +- (NSString *)uuid { + return self[PFEventuallyPinKeyUUID]; +} + +- (PFEventuallyPinType)type { + return [self[PFEventuallyPinKeyType] intValue]; +} + +- (PFObject *)object { + return self[PFEventuallyPinKeyObject]; +} + +- (NSString *)operationSetUUID { + return self[PFEventuallyPinKeyOperationSetUUID]; +} + +- (NSString *)sessionToken { + return self[PFEventuallyPinKeySessionToken]; +} + +- (id)command { + NSDictionary *dictionary = self[PFEventuallyPinKeyCommand]; + if ([PFRESTCommand isValidDictionaryRepresentation:dictionary]) { + return [PFRESTCommand commandFromDictionaryRepresentation:dictionary]; + } + return nil; +} + +///-------------------------------------- +#pragma mark - Eventually Pin +///-------------------------------------- + ++ (BFTask *)pinEventually:(PFObject *)object forCommand:(id)command { + return [self pinEventually:object forCommand:command withUUID:[[NSUUID UUID] UUIDString]]; +} + ++ (BFTask *)pinEventually:(PFObject *)object forCommand:(id)command withUUID:(NSString *)uuid { + PFEventuallyPinType type = [self _pinTypeForCommand:command]; + NSDictionary *commandDictionary = (type == PFEventuallyPinTypeCommand ? [command dictionaryRepresentation] : nil); + return [self _pinEventually:object + type:type + uuid:uuid + operationSetUUID:command.operationSetUUID + sessionToken:command.sessionToken + commandDictionary:commandDictionary]; +} + ++ (BFTask *)findAllEventuallyPin { + return [self findAllEventuallyPinWithExcludeUUIDs:nil]; +} + ++ (BFTask *)findAllEventuallyPinWithExcludeUUIDs:(NSArray *)excludeUUIDs { + PFQuery *query = [PFQuery queryWithClassName:self.parseClassName]; + [query fromPinWithName:PFEventuallyPinPinName]; + [query orderByAscending:PFEventuallyPinKeyTime]; + + if (excludeUUIDs != nil) { + [query whereKey:PFEventuallyPinKeyUUID notContainedIn:excludeUUIDs]; + } + + return [[query findObjectsInBackground] continueWithBlock:^id(BFTask *task) { + NSArray *pins = task.result; + NSMutableArray *fetchTasks = [NSMutableArray array]; + + for (PFEventuallyPin *pin in pins) { + PFObject *object = pin.object; + if (object != nil) { + [fetchTasks addObject:[object fetchFromLocalDatastoreInBackground]]; + } + } + + return [[BFTask taskForCompletionOfAllTasks:fetchTasks] continueWithBlock:^id(BFTask *task) { + return [BFTask taskWithResult:pins]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + ++ (BFTask *)_pinEventually:(PFObject *)object + type:(PFEventuallyPinType)type + uuid:(NSString *)uuid + operationSetUUID:(NSString *)operationSetUUID + sessionToken:(NSString *)sessionToken + commandDictionary:(NSDictionary *)commandDictionary { + PFEventuallyPin *pin = [[PFEventuallyPin alloc] init]; + pin[PFEventuallyPinKeyUUID] = uuid; + pin[PFEventuallyPinKeyTime] = [NSDate date]; + pin[PFEventuallyPinKeyType] = @(type); + if (object != nil) { + pin[PFEventuallyPinKeyObject] = object; + } + if (operationSetUUID != nil) { + pin[PFEventuallyPinKeyOperationSetUUID] = operationSetUUID; + } + if (sessionToken != nil) { + pin[PFEventuallyPinKeySessionToken] = sessionToken; + } + if (commandDictionary != nil) { + pin[PFEventuallyPinKeyCommand] = commandDictionary; + } + + // NOTE: This is needed otherwise ARC releases the pins before we have a chance to persist the new ones to disk, + // Which means we'd lose any columns on objects in eventually pins not currently in memory. + __block NSArray *existingPins = nil; + return [[[self findAllEventuallyPin] continueWithSuccessBlock:^id(BFTask *task) { + existingPins = task.result; + return [pin pinInBackgroundWithName:PFEventuallyPinPinName]; + }] continueWithSuccessBlock:^id(BFTask *task) { + existingPins = nil; + return pin; + }]; +} + ++ (PFEventuallyPinType)_pinTypeForCommand:(id)command { + PFEventuallyPinType type = PFEventuallyPinTypeCommand; + NSString *path = [(PFRESTCommand *)command httpPath]; + NSString *method = [(PFRESTCommand *)command httpMethod]; + if ([path hasPrefix:@"classes"]) { + if ([method isEqualToString:PFHTTPRequestMethodPOST] || + [method isEqualToString:PFHTTPRequestMethodPUT]) { + type = PFEventuallyPinTypeSave; + } else if ([method isEqualToString:PFHTTPRequestMethodDELETE]) { + type = PFEventuallyPinTypeDelete; + } + } + return type; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFEventuallyQueue.h b/Pods/Parse/Parse/Internal/PFEventuallyQueue.h new file mode 100644 index 0000000..0ac4ea1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEventuallyQueue.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" +#import "PFNetworkCommand.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFEventuallyPin; +@class PFEventuallyQueueTestHelper; +@class PFObject; +@protocol PFCommandRunning; + +extern NSUInteger const PFEventuallyQueueDefaultMaxAttemptsCount; +extern NSTimeInterval const PFEventuallyQueueDefaultTimeoutRetryInterval; + +@interface PFEventuallyQueue : NSObject + +@property (nonatomic, strong, readonly) id commandRunner; + +@property (nonatomic, assign, readonly) NSUInteger maxAttemptsCount; +@property (nonatomic, assign, readonly) NSTimeInterval retryInterval; + +@property (nonatomic, assign, readonly) NSUInteger commandCount; + +/*! + Controls whether the queue should monitor network reachability and pause itself when there is no connection. + Default: `YES`. + */ +@property (atomic, assign, readonly) BOOL monitorsReachability PF_WATCH_UNAVAILABLE; +@property (nonatomic, assign, readonly, getter=isConnected) BOOL connected; + +// Gets notifications of various events happening in the command cache, so that tests can be synchronized. +@property (nonatomic, strong, readonly) PFEventuallyQueueTestHelper *testHelper; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Running Commands +///-------------------------------------- + +- (BFTask *)enqueueCommandInBackground:(id)command; +- (BFTask *)enqueueCommandInBackground:(id)command withObject:(PFObject *)object; + +///-------------------------------------- +/// @name Controlling Queue +///-------------------------------------- + +- (void)start NS_REQUIRES_SUPER; +- (void)resume NS_REQUIRES_SUPER; +- (void)pause NS_REQUIRES_SUPER; + +- (void)removeAllCommands NS_REQUIRES_SUPER; + +@end + +typedef enum { + PFEventuallyQueueEventCommandEnqueued, // A command was placed into the queue. + PFEventuallyQueueEventCommandNotEnqueued, // A command could not be placed into the queue. + + PFEventuallyQueueEventCommandSucceded, // A command has successfully running on the server. + PFEventuallyQueueEventCommandFailed, // A command has failed on the server. + + PFEventuallyQueueEventObjectUpdated, // An object's data was updated after a command completed. + PFEventuallyQueueEventObjectRemoved, // An object was removed because it was deleted before creation. + + PFEventuallyQueueEventCount // The total number of items in this enum. +} PFEventuallyQueueTestHelperEvent; + +@interface PFEventuallyQueueTestHelper : NSObject { + dispatch_semaphore_t events[PFEventuallyQueueEventCount]; +} + +- (void)clear; +- (void)notify:(PFEventuallyQueueTestHelperEvent)event; +- (BOOL)waitFor:(PFEventuallyQueueTestHelperEvent)event; + +@end diff --git a/Pods/Parse/Parse/Internal/PFEventuallyQueue.m b/Pods/Parse/Parse/Internal/PFEventuallyQueue.m new file mode 100644 index 0000000..80e025d --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEventuallyQueue.m @@ -0,0 +1,503 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFEventuallyQueue.h" +#import "PFEventuallyQueue_Private.h" + +#import +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFErrorUtilities.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFRESTCommand.h" +#import "PFReachability.h" +#import "PFTaskQueue.h" + +NSUInteger const PFEventuallyQueueDefaultMaxAttemptsCount = 5; +NSTimeInterval const PFEventuallyQueueDefaultTimeoutRetryInterval = 600.0f; + +@interface PFEventuallyQueue () +#if !TARGET_OS_WATCH + +#endif + +@property (atomic, assign, readwrite) BOOL monitorsReachability; +@property (atomic, assign, getter=isRunning) BOOL running; + +@end + +@implementation PFEventuallyQueue + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval { + self = [super init]; + if (!self) return nil; + + _commandRunner = commandRunner; + _maxAttemptsCount = attemptsCount; + _retryInterval = retryInterval; + + // Set up all the queues + NSString *queueBaseLabel = [NSString stringWithFormat:@"com.parse.%@", NSStringFromClass([self class])]; + + _synchronizationQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.synchronization", + queueBaseLabel] UTF8String], + DISPATCH_QUEUE_SERIAL); + PFMarkDispatchQueue(_synchronizationQueue); + _synchronizationExecutor = [BFExecutor executorWithDispatchQueue:_synchronizationQueue]; + + _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.processing", + queueBaseLabel] UTF8String], + DISPATCH_QUEUE_SERIAL); + PFMarkDispatchQueue(_processingQueue); + + _processingQueueSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, _processingQueue); + + _commandEnqueueTaskQueue = [[PFTaskQueue alloc] init]; + + _taskCompletionSources = [NSMutableDictionary dictionary]; + _testHelper = [[PFEventuallyQueueTestHelper alloc] init]; + + [self _startMonitoringNetworkReachability]; + + return self; +} + +- (void)dealloc { + [self _stopMonitoringNetworkReachability]; +} + +///-------------------------------------- +#pragma mark - Enqueueing Commands +///-------------------------------------- + +- (BFTask *)enqueueCommandInBackground:(id)command { + return [self enqueueCommandInBackground:command withObject:nil]; +} + +- (BFTask *)enqueueCommandInBackground:(id)command withObject:(PFObject *)object { + PFParameterAssert(command, @"Cannot enqueue nil command."); + + BFTaskCompletionSource *taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; + + @weakify(self); + [_commandEnqueueTaskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + @strongify(self); + + NSString *identifier = [self _newIdentifierForCommand:command]; + return [[[self _enqueueCommandInBackground:command + object:object + identifier:identifier] continueWithBlock:^id(BFTask *task) { + if (task.error || task.exception || task.cancelled) { + [self.testHelper notify:PFEventuallyQueueEventCommandNotEnqueued]; + if (task.error) { + taskCompletionSource.error = task.error; + } else if (task.exception) { + taskCompletionSource.exception = task.exception; + } else if (task.cancelled) { + [taskCompletionSource cancel]; + } + } else { + [self.testHelper notify:PFEventuallyQueueEventCommandEnqueued]; + } + + return task; + }] continueWithExecutor:_synchronizationExecutor withSuccessBlock:^id(BFTask *task) { + [self _didEnqueueCommand:command withIdentifier:identifier taskCompletionSource:taskCompletionSource]; + return nil; + }]; + }]; + }]; + + return taskCompletionSource.task; +} + +- (BFTask *)_enqueueCommandInBackground:(id)command + object:(PFObject *)object + identifier:(NSString *)identifier { + // This enforces implementing this method in subclasses + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void)_didEnqueueCommand:(id)command + withIdentifier:(NSString *)identifier + taskCompletionSource:(BFTaskCompletionSource *)taskCompletionSource { + PFAssertIsOnDispatchQueue(_synchronizationQueue); + + _taskCompletionSources[identifier] = taskCompletionSource; + dispatch_source_merge_data(_processingQueueSource, 1); + + if (_retryingSemaphore) { + dispatch_semaphore_signal(_retryingSemaphore); + } +} + +///-------------------------------------- +#pragma mark - Pending Commands +///-------------------------------------- + +- (NSArray *)_pendingCommandIdentifiers { + return nil; +} + +- (id)_commandWithIdentifier:(NSString *)identifier error:(NSError **)error { + return nil; +} + +- (NSString *)_newIdentifierForCommand:(id)command { + return nil; +} + +- (NSUInteger)commandCount { + return [[self _pendingCommandIdentifiers] count]; +} + +///-------------------------------------- +#pragma mark - Controlling Queue +///-------------------------------------- + +- (void)start { + dispatch_source_set_event_handler(_processingQueueSource, ^{ + [self _runCommands]; + }); + [self resume]; +} + +- (void)resume { + if (self.running) { + return; + } + self.running = YES; + dispatch_resume(_processingQueueSource); + dispatch_source_merge_data(_processingQueueSource, 1); +} + +- (void)pause { + if (!self.running) { + return; + } + self.running = NO; + dispatch_suspend(_processingQueueSource); +} + +- (void)removeAllCommands { + dispatch_sync(_synchronizationQueue, ^{ + [_taskCompletionSources removeAllObjects]; + }); +} + +///-------------------------------------- +#pragma mark - Running Commands +///-------------------------------------- + +- (void)_runCommands { + PFAssertIsOnDispatchQueue(_processingQueue); + + [self _runCommandsWithRetriesCount:self.maxAttemptsCount]; +} + +- (void)_runCommandsWithRetriesCount:(NSUInteger)retriesCount { + PFAssertIsOnDispatchQueue(_processingQueue); + + if (!self.running || !self.connected) { + return; + } + + // Expect sorted result from _pendingCommandIdentifiers + NSArray *commandIdentifiers = [self _pendingCommandIdentifiers]; + BOOL shouldRetry = NO; + for (NSString *identifier in commandIdentifiers) { + NSError *error = nil; + id command = [self _commandWithIdentifier:identifier error:&error]; + if (!command || error) { + if (!error) { + error = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:@"Failed to dequeue an eventually command." + shouldLog:NO]; + } + BFTask *task = [BFTask taskWithError:error]; + [self _didFinishRunningCommand:command withIdentifier:identifier resultTask:task]; + continue; + } + + __block BFTaskCompletionSource *taskCompletionSource = nil; + dispatch_sync(_synchronizationQueue, ^{ + taskCompletionSource = _taskCompletionSources[identifier]; + }); + + BFTask *resultTask = nil; + PFCommandResult *result = nil; + @try { + resultTask = [self _runCommand:command withIdentifier:identifier]; + result = [resultTask waitForResult:&error]; + } + @catch (NSException *exception) { + error = [NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorInvalidPointer + userInfo:@{ @"message" : @"Failed to run an eventually command.", + @"exception" : exception }]; + resultTask = [BFTask taskWithError:error]; + } + + if (error) { + BOOL permanent = (![error.userInfo[@"temporary"] boolValue] && + ([[error domain] isEqualToString:PFParseErrorDomain] || + [error code] != kPFErrorConnectionFailed)); + + if (!permanent) { + PFLogWarning(PFLoggingTagCommon, + @"Attempt at runEventually command timed out. Waiting %f seconds. %d retries remaining.", + self.retryInterval, + (int)retriesCount); + + __block dispatch_semaphore_t semaphore = NULL; + dispatch_sync(_synchronizationQueue, ^{ + _retryingSemaphore = dispatch_semaphore_create(0); + semaphore = _retryingSemaphore; + }); + + dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, + (int64_t)(self.retryInterval * NSEC_PER_SEC)); + + long waitResult = dispatch_semaphore_wait(semaphore, timeoutTime); + dispatch_sync(_synchronizationQueue, ^{ + _retryingSemaphore = NULL; + }); + + if (waitResult == 0) { + // We haven't waited long enough, but if we lost the connection, or should stop, just quit. + return; + } + + // We need to go out of the loop. + if (retriesCount > 0) { + shouldRetry = YES; + break; + } + } + + PFLogError(PFLoggingTagCommon, @"Failed to run command eventually with error: %@", error); + } + + // Post processing shouldn't make the queue retry the command. + resultTask = [self _didFinishRunningCommand:command withIdentifier:identifier resultTask:resultTask]; + [resultTask waitForResult:nil]; + + // Notify anyone waiting that the operation is completed. + if (resultTask.error) { + taskCompletionSource.error = resultTask.error; + } else if (resultTask.exception) { + taskCompletionSource.exception = resultTask.exception; + } else if (resultTask.cancelled) { + [taskCompletionSource cancel]; + } else { + taskCompletionSource.result = resultTask.result; + } + } + + // Retry here so that we're in cleaner state. + if (shouldRetry) { + return [self _runCommandsWithRetriesCount:(retriesCount - 1)]; + } +} + +- (BFTask *)_runCommand:(id)command withIdentifier:(NSString *)identifier { + if ([command isKindOfClass:[PFRESTCommand class]]) { + return [self.commandRunner runCommandAsync:(PFRESTCommand *)command withOptions:0]; + } + + NSString *reason = [NSString stringWithFormat:@"Can't find a compatible runner for command %@.", command]; + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:reason + userInfo:nil]; + return [BFTask taskWithException:exception]; +} + +- (BFTask *)_didFinishRunningCommand:(id)command + withIdentifier:(NSString *)identifier + resultTask:(BFTask *)resultTask { + PFConsistencyAssert(resultTask.completed, @"Task should be completed."); + + dispatch_sync(_synchronizationQueue, ^{ + [_taskCompletionSources removeObjectForKey:identifier]; + }); + + if (resultTask.exception || resultTask.error || resultTask.cancelled) { + [self.testHelper notify:PFEventuallyQueueEventCommandFailed]; + } else { + [self.testHelper notify:PFEventuallyQueueEventCommandSucceded]; + } + + return resultTask; +} + +- (BFTask *)_waitForOperationSet:(PFOperationSet *)operationSet + eventuallyPin:(PFEventuallyPin *)eventuallyPin { + return [BFTask taskWithResult:nil]; +} + +///-------------------------------------- +#pragma mark - Reachability +///-------------------------------------- + +- (void)_startMonitoringNetworkReachability { +#if TARGET_OS_WATCH + self.connected = YES; +#else + if (self.monitorsReachability) { + return; + } + self.monitorsReachability = YES; + + [[PFReachability sharedParseReachability] addListener:self]; + + // Set the initial connected status + self.connected = ([PFReachability sharedParseReachability].currentState != PFReachabilityStateNotReachable); +#endif +} + +- (void)_stopMonitoringNetworkReachability { +#if !TARGET_OS_WATCH + if (!self.monitorsReachability) { + return; + } + + [[PFReachability sharedParseReachability] removeListener:self]; + + self.monitorsReachability = NO; + self.connected = YES; +#endif +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +/*! Manually sets the network connection status. */ +- (void)setConnected:(BOOL)connected { + BFTaskCompletionSource *barrier = [BFTaskCompletionSource taskCompletionSource]; + dispatch_async(_processingQueue, ^{ + dispatch_sync(_synchronizationQueue, ^{ + if (self.connected != connected) { + _connected = connected; + if (connected) { + dispatch_source_merge_data(_processingQueueSource, 1); + } + } + }); + barrier.result = nil; + }); + if (connected) { + dispatch_async(_synchronizationQueue, ^{ + if (_retryingSemaphore) { + dispatch_semaphore_signal(_retryingSemaphore); + } + }); + } + [barrier.task waitForResult:nil]; +} + +///-------------------------------------- +#pragma mark - Test Helper Method +///-------------------------------------- + +/*! Makes this command cache forget all the state it keeps during a single run of the app. */ +- (void)_simulateReboot { + // Make sure there is no command pending enqueuing + [[[[_commandEnqueueTaskQueue enqueue:^BFTask *(BFTask *toAwait) { + return toAwait; + }] continueWithExecutor:_synchronizationExecutor withBlock:^id(BFTask *task) { + // Remove all state task completion sources + [_taskCompletionSources removeAllObjects]; + return nil; + }] continueWithExecutor:[BFExecutor executorWithDispatchQueue:_processingQueue] withBlock:^id(BFTask *task) { + // Let all operations in the queue run at least once + return nil; + }] waitUntilFinished]; +} + +/*! Test helper to return how many commands are being retained in memory by the cache. */ +- (int)_commandsInMemory { + return (int)[_taskCompletionSources count]; +} + +/*! Called by PFObject whenever an object has been updated after a saveEventually. */ +- (void)_notifyTestHelperObjectUpdated { + [self.testHelper notify:PFEventuallyQueueEventObjectUpdated]; +} + +- (void)_setMaxAttemptsCount:(NSUInteger)attemptsCount { + _maxAttemptsCount = attemptsCount; +} + +- (void)_setRetryInterval:(NSTimeInterval)retryInterval { + _retryInterval = retryInterval; +} + +#if !TARGET_OS_WATCH + +///-------------------------------------- +#pragma mark - Reachability +///-------------------------------------- + +- (void)reachability:(PFReachability *)reachability didChangeReachabilityState:(PFReachabilityState)state { + if (self.monitorsReachability) { + self.connected = (state != PFReachabilityStateNotReachable); + } +} + +#endif + +@end + +// PFEventuallyQueueTestHelper gets notifications of various events happening in the command cache, +// so that tests can be synchronized. See CommandTests.m for examples of how to use this. + +@implementation PFEventuallyQueueTestHelper + +- (instancetype)init { + self = [super init]; + if (self) { + [self clear]; + } + return self; +} + +- (void)clear { + for (int i = 0; i < PFEventuallyQueueEventCount; ++i) { + events[i] = dispatch_semaphore_create(0); + } +} + +- (void)notify:(PFEventuallyQueueTestHelperEvent)event { + dispatch_semaphore_signal(events[event]); +} + +- (BOOL)waitFor:(PFEventuallyQueueTestHelperEvent)event { + // Wait 1 second for a permit from the semaphore. + return (dispatch_semaphore_wait(events[event], dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFEventuallyQueue_Private.h b/Pods/Parse/Parse/Internal/PFEventuallyQueue_Private.h new file mode 100644 index 0000000..3e576f3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFEventuallyQueue_Private.h @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFEventuallyQueue.h" + +@class BFExecutor; +@class PFEventuallyPin; +@class PFObject; +@class PFOperationSet; +@class PFTaskQueue; + +extern NSUInteger const PFEventuallyQueueDefaultMaxAttemptsCount; +extern NSTimeInterval const PFEventuallyQueueDefaultTimeoutRetryInterval; + +@class BFTaskCompletionSource; + +@interface PFEventuallyQueue () +{ +@protected + BFExecutor *_synchronizationExecutor; + dispatch_queue_t _synchronizationQueue; + +@private + dispatch_queue_t _processingQueue; + dispatch_source_t _processingQueueSource; + + dispatch_semaphore_t _retryingSemaphore; + + NSMutableDictionary *_taskCompletionSources; + + /*! + Task queue that will enqueue command enqueueing task so that we enqueue the command + one at a time. + */ + PFTaskQueue *_commandEnqueueTaskQueue; +} + +@property (nonatomic, assign, readwrite, getter=isConnected) BOOL connected; + +/*! + This method is used to do some work after the command is finished running and + either succeeded or dropped from queue with error/exception. + + @param command Command that was run. + @param identifier Unique identifier of the command + @param resultTask Task that represents the result of running a command. + @returns A continuation task in case the EventuallyQueue need to do something. + Typically this will return back given resultTask. + */ +- (BFTask *)_didFinishRunningCommand:(id)command + withIdentifier:(NSString *)identifier + resultTask:(BFTask *)resultTask; + +///-------------------------------------- +/// @name Reachability +///-------------------------------------- + +- (void)_startMonitoringNetworkReachability; +- (void)_stopMonitoringNetworkReachability; + +///-------------------------------------- +/// @name Test Helper +///-------------------------------------- + +- (void)_setMaxAttemptsCount:(NSUInteger)attemptsCount; + +- (void)_setRetryInterval:(NSTimeInterval)retryInterval; + +- (void)_simulateReboot NS_REQUIRES_SUPER; + +- (int)_commandsInMemory; + +- (void)_notifyTestHelperObjectUpdated; + +@end + +@protocol PFEventuallyQueueSubclass + +///-------------------------------------- +/// @name Pending Commands +///-------------------------------------- + + +/*! + Generates a new identifier for a command so that it can be sorted later by this identifier. + */ +- (NSString *)_newIdentifierForCommand:(id)command; + +/*! + This method is triggered on batch processing of the queue. + It will capture the identifiers and use them to execute commands. + + @returns An array of identifiers of all commands that are pending sorted by the order they're enqueued. + */ +- (NSArray *)_pendingCommandIdentifiers; + +/*! + This method should return a command for a given identifier. + + @param identifier An identifier of a command, that was in array returned by <_pendingCommandIdentifiers> + @param error Pointer to `NSError *` that should be set if the method failed to construct/retrieve a command. + + @returns A command that needs to be run, or `nil` if there was an error. + */ +- (id)_commandWithIdentifier:(NSString *)identifier error:(NSError **)error; + +///-------------------------------------- +/// @name Running Commands +///-------------------------------------- + +/*! + This method serves as a way to do any kind of work to enqueue a command properly. + If the task fails with an error/exception or is cancelled - execution won't start. + + @param command Command that needs to be enqueued + @param object The object on which the command is run against. + @param identifier Unique identifier used to represent a command. + @returns Task that is resolved when the command is complete enqueueing. + */ +- (BFTask *)_enqueueCommandInBackground:(id)command + object:(PFObject *)object + identifier:(NSString *)identifier; + +- (BFTask *)_waitForOperationSet:(PFOperationSet *)operationSet + eventuallyPin:(PFEventuallyPin *)eventuallyPin; + +@end diff --git a/Pods/Parse/Parse/Internal/PFFileManager.h b/Pods/Parse/Parse/Internal/PFFileManager.h new file mode 100644 index 0000000..91e5700 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFFileManager.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFExecutor; +@class BFTask PF_GENERIC(__covariant BFGenericType); + +typedef NS_OPTIONS(uint8_t, PFFileManagerOptions) { + PFFileManagerOptionSkipBackup = 1 << 0, +}; + +@interface PFFileManager : NSObject + +///-------------------------------------- +/// @name Class +///-------------------------------------- + ++ (BOOL)isApplicationGroupContainerReachableForGroupIdentifier:(NSString *)applicationGroup; + ++ (BFTask *)createDirectoryIfNeededAsyncAtPath:(NSString *)path; ++ (BFTask *)createDirectoryIfNeededAsyncAtPath:(NSString *)path + withOptions:(PFFileManagerOptions)options + executor:(BFExecutor *)executor; + ++ (BFTask *)writeStringAsync:(NSString *)string toFile:(NSString *)filePath; ++ (BFTask *)writeDataAsync:(NSData *)data toFile:(NSString *)filePath; + ++ (BFTask *)copyItemAsyncAtPath:(NSString *)fromPath toPath:(NSString *)toPath; ++ (BFTask *)moveItemAsyncAtPath:(NSString *)fromPath toPath:(NSString *)toPath; + ++ (BFTask *)moveContentsOfDirectoryAsyncAtPath:(NSString *)fromPath + toDirectoryAtPath:(NSString *)toPath + executor:(BFExecutor *)executor; + ++ (BFTask *)removeItemAtPathAsync:(NSString *)path; ++ (BFTask *)removeItemAtPathAsync:(NSString *)path withFileLock:(BOOL)useFileLock; ++ (BFTask *)removeDirectoryContentsAsyncAtPath:(NSString *)path; + +///-------------------------------------- +/// @name Instance +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithApplicationIdentifier:(NSString *)applicationIdentifier + applicationGroupIdentifier:(NSString *)applicationGroupIdentifier NS_DESIGNATED_INITIALIZER; + +/*! + Returns /Library/Private Documents/Parse + for non-user generated data that shouldn't be deleted by iOS, such as "offline data". + + See https://developer.apple.com/library/ios/#qa/qa1699/_index.html + */ +- (NSString *)parseDefaultDataDirectoryPath; +- (NSString *)parseLocalSandboxDataDirectoryPath; +- (NSString *)parseDataDirectoryPath_DEPRECATED; + +/*! + The path including directories that we save data to for a given filename. + If the file isn't found in the new "Private Documents" location, but is in the old "Documents" location, + moves it and returns the new location. + */ +- (NSString *)parseDataItemPathForPathComponent:(NSString *)pathComponent; + +- (NSString *)parseCacheItemPathForPathComponent:(NSString *)component; + +@end diff --git a/Pods/Parse/Parse/Internal/PFFileManager.m b/Pods/Parse/Parse/Internal/PFFileManager.m new file mode 100644 index 0000000..a48a6c8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFFileManager.m @@ -0,0 +1,367 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFileManager.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFLogging.h" +#import "PFMultiProcessFileLockController.h" + +static NSString *const _PFFileManagerParseDirectoryName = @"Parse"; + +static NSDictionary *_PFFileManagerDefaultDirectoryFileAttributes() { +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + return @{ NSFileProtectionKey : NSFileProtectionCompleteUntilFirstUserAuthentication }; +#else + return nil; +#endif +} + +static NSDataWritingOptions _PFFileManagerDefaultDataWritingOptions() { + NSDataWritingOptions options = NSDataWritingAtomic; +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + options |= NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication; +#endif + return options; +} + +@interface PFFileManager () + +@property (nonatomic, copy) NSString *applicationIdentifier; +@property (nonatomic, copy) NSString *applicationGroupIdentifier; + +@end + +@implementation PFFileManager + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (BOOL)isApplicationGroupContainerReachableForGroupIdentifier:(NSString *)applicationGroup { + return ([[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:applicationGroup] != nil); +} + ++ (BFTask *)writeStringAsync:(NSString *)string toFile:(NSString *)filePath { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; + return [self writeDataAsync:data toFile:filePath]; + }]; +} + ++ (BFTask *)writeDataAsync:(NSData *)data toFile:(NSString *)filePath { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSError *error = nil; + [data writeToFile:filePath + options:_PFFileManagerDefaultDataWritingOptions() + error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + ++ (BFTask *)createDirectoryIfNeededAsyncAtPath:(NSString *)path { + return [self createDirectoryIfNeededAsyncAtPath:path + withOptions:PFFileManagerOptionSkipBackup + executor:[BFExecutor defaultPriorityBackgroundExecutor]]; +} + ++ (BFTask *)createDirectoryIfNeededAsyncAtPath:(NSString *)path + withOptions:(PFFileManagerOptions)options + executor:(BFExecutor *)executor { + return [BFTask taskFromExecutor:executor withBlock:^id{ + if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { + NSError *error = nil; + [[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:_PFFileManagerDefaultDirectoryFileAttributes() + error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + } + +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + if (options & PFFileManagerOptionSkipBackup) { + [self _skipBackupOnPath:path]; + } +#endif + return nil; + }]; +} + ++ (BFTask *)copyItemAsyncAtPath:(NSString *)fromPath toPath:(NSString *)toPath { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSError *error = nil; + [[NSFileManager defaultManager] copyItemAtPath:fromPath toPath:toPath error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + ++ (BFTask *)moveItemAsyncAtPath:(NSString *)fromPath toPath:(NSString *)toPath { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSError *error = nil; + [[NSFileManager defaultManager] moveItemAtPath:fromPath toPath:toPath error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + ++ (BFTask *)moveContentsOfDirectoryAsyncAtPath:(NSString *)fromPath + toDirectoryAtPath:(NSString *)toPath + executor:(BFExecutor *)executor { + if ([fromPath isEqualToString:toPath]) { + return [BFTask taskWithResult:nil]; + } + + return [[[self createDirectoryIfNeededAsyncAtPath:toPath + withOptions:PFFileManagerOptionSkipBackup + executor:executor] continueWithSuccessBlock:^id(BFTask *task) { + NSError *error = nil; + NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fromPath + error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return contents; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *contents = task.result; + if ([contents count] == 0) { + return nil; + } + + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:[contents count]]; + for (NSString *path in contents) { + BFTask *task = [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSError *error = nil; + NSString *fromFilePath = [fromPath stringByAppendingPathComponent:path]; + NSString *toFilePath = [toPath stringByAppendingPathComponent:path]; + [[NSFileManager defaultManager] moveItemAtPath:fromFilePath + toPath:toFilePath + error:&error]; + if (error) { + return [BFTask taskWithError:error]; + } + return nil; + }]; + [tasks addObject:task]; + } + return [BFTask taskForCompletionOfAllTasks:tasks]; + }]; +} + ++ (BFTask *)removeDirectoryContentsAsyncAtPath:(NSString *)path { + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:path]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSError *error = nil; + NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:path error:&error]; + if (error) { + PFLogError(PFLoggingTagCommon, @"Failed to list directory: %@", path); + return [BFTask taskWithError:error]; + } + + NSMutableArray *fileTasks = [NSMutableArray array]; + for (NSString *fileName in fileNames) { + NSString *filePath = [path stringByAppendingPathComponent:fileName]; + BFTask *fileTask = [[self removeItemAtPathAsync:filePath withFileLock:NO] continueWithBlock:^id(BFTask *task) { + if (task.faulted) { + PFLogError(PFLoggingTagCommon, @"Failed to delete file: %@ with error: %@", filePath, task.error); + } + return task; + }]; + [fileTasks addObject:fileTask]; + } + return [BFTask taskForCompletionOfAllTasks:fileTasks]; + }] continueWithBlock:^id(BFTask *task) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:path]; + return task; + }]; +} + ++ (BFTask *)removeItemAtPathAsync:(NSString *)path { + return [self removeItemAtPathAsync:path withFileLock:YES]; +} + ++ (BFTask *)removeItemAtPathAsync:(NSString *)path withFileLock:(BOOL)useFileLock { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + if (useFileLock) { + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:path]; + } + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:path]) { + NSError *error = nil; + [[NSFileManager defaultManager] removeItemAtPath:path error:&error]; + if (error) { + if (useFileLock) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:path]; + } + return [BFTask taskWithError:error]; + } + } + if (useFileLock) { + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:path]; + } + return nil; + }]; +} + +///-------------------------------------- +#pragma mark - Instance +///-------------------------------------- + +#pragma mark Init + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithApplicationIdentifier:(NSString *)applicationIdentifier + applicationGroupIdentifier:(NSString *)applicationGroupIdentifier { + self = [super init]; + if (!self) return nil; + + _applicationIdentifier = [applicationIdentifier copy]; + _applicationGroupIdentifier = [applicationGroupIdentifier copy]; + + return self; +} + +#pragma mark Public + +- (NSString *)parseDefaultDataDirectoryPath { + // NSHomeDirectory: Returns the path to either the user's or application's + // home directory, depending on the platform. Sandboxed by default on iOS. +#if PARSE_IOS_ONLY + NSString *directoryPath = nil; + if (self.applicationGroupIdentifier) { + NSURL *containerPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:self.applicationGroupIdentifier]; + directoryPath = [[containerPath path] stringByAppendingPathComponent:_PFFileManagerParseDirectoryName]; + directoryPath = [directoryPath stringByAppendingPathComponent:self.applicationIdentifier]; + } else { + return [self parseLocalSandboxDataDirectoryPath]; + } +#else + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *directoryPath = [paths firstObject]; + directoryPath = [directoryPath stringByAppendingPathComponent:_PFFileManagerParseDirectoryName]; + directoryPath = [directoryPath stringByAppendingPathComponent:self.applicationIdentifier]; +#endif + + BFTask *createDirectoryTask = [[self class] createDirectoryIfNeededAsyncAtPath:directoryPath + withOptions:PFFileManagerOptionSkipBackup + executor:[BFExecutor immediateExecutor]]; + [createDirectoryTask waitForResult:nil withMainThreadWarning:NO]; + + return directoryPath; +} + +- (NSString *)parseLocalSandboxDataDirectoryPath { +#if TARGET_OS_IPHONE + NSString *library = [NSHomeDirectory() stringByAppendingPathComponent:@"Library"]; + NSString *privateDocuments = [library stringByAppendingPathComponent:@"Private Documents"]; + NSString *directoryPath = [privateDocuments stringByAppendingPathComponent:_PFFileManagerParseDirectoryName]; + BFTask *createDirectoryTask = [[self class] createDirectoryIfNeededAsyncAtPath:directoryPath + withOptions:PFFileManagerOptionSkipBackup + executor:[BFExecutor immediateExecutor]]; + [createDirectoryTask waitForResult:nil withMainThreadWarning:NO]; + + return directoryPath; +#else + return [self parseDefaultDataDirectoryPath]; +#endif +} + +- (NSString *)parseDataDirectoryPath_DEPRECATED { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder + NSString *parseDirPath = [documentsDirectory stringByAppendingPathComponent:_PFFileManagerParseDirectoryName]; + + // If this old directory is still on disk, but empty, delete it. + if ([[NSFileManager defaultManager] fileExistsAtPath:parseDirPath]) { + NSError *error = nil; + NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:parseDirPath error:&error]; + if (error == nil && [contents count] == 0) { + [[NSFileManager defaultManager] removeItemAtPath:parseDirPath error:nil]; + } + } + + return parseDirPath; +} + +- (NSString *)parseDataItemPathForPathComponent:(NSString *)pathComponent { + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSString *currentLocation = [[self parseDefaultDataDirectoryPath] stringByAppendingPathComponent:pathComponent]; + if (![fileManager fileExistsAtPath:currentLocation]) { + NSString *deprecatedDir = [self parseDataDirectoryPath_DEPRECATED]; + NSString *deprecatedLocation = [deprecatedDir stringByAppendingPathComponent:pathComponent]; + if ([fileManager fileExistsAtPath:deprecatedLocation]) { + [fileManager moveItemAtPath:deprecatedLocation toPath:currentLocation error:nil]; + // If the deprecated dir is still on disk, delete it. + if ([fileManager fileExistsAtPath:deprecatedDir]) { + NSError *error = nil; + NSArray *contents = [fileManager contentsOfDirectoryAtPath:deprecatedDir error:&error]; + if (!error && [contents count] == 0) { + [fileManager removeItemAtPath:deprecatedDir error:nil]; + } + } + } + } + return currentLocation; +} + +- (NSString *)parseCacheItemPathForPathComponent:(NSString *)component { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + NSString *folderPath = [paths firstObject]; + folderPath = [folderPath stringByAppendingPathComponent:_PFFileManagerParseDirectoryName]; +#if !TARGET_OS_IPHONE + // We append the applicationId in case the OS X application isn't sandboxed, + // to avoid collisions in the generic ~/Library/Caches/Parse/------ dir. + folderPath = [folderPath stringByAppendingPathComponent:self.applicationIdentifier]; +#endif + folderPath = [folderPath stringByAppendingPathComponent:component]; + return [folderPath stringByStandardizingPath]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +// Skips all backups on the provided path. ++ (BOOL)_skipBackupOnPath:(NSString *)path { + if (path == nil) { + return NO; + } + + NSError *error = nil; + + NSURL *url = [NSURL fileURLWithPath:path]; + BOOL success = [url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&error]; + if (!success) { + PFLogError(PFLoggingTagCommon, + @"Unable to exclude %@ from backup with error: %@", [url lastPathComponent], error); + } + + return success; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFGeoPointPrivate.h b/Pods/Parse/Parse/Internal/PFGeoPointPrivate.h new file mode 100644 index 0000000..7883ef9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFGeoPointPrivate.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +# import + +extern const double EARTH_RADIUS_MILES; +extern const double EARTH_RADIUS_KILOMETERS; + +@class PFGeoPoint; + +@interface PFGeoPoint (Private) + +// Internal commands + +/* + Gets the encoded format for an GeoPoint. + */ +- (NSDictionary *)encodeIntoDictionary; + +/*! + Creates an GeoPoint from its encoded format. + */ ++ (instancetype)geoPointWithDictionary:(NSDictionary *)dictionary; + +@end diff --git a/Pods/Parse/Parse/Internal/PFHash.h b/Pods/Parse/Parse/Internal/PFHash.h new file mode 100644 index 0000000..e97af69 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFHash.h @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +extern NSUInteger PFIntegerPairHash(NSUInteger a, NSUInteger b); + +extern NSUInteger PFDoublePairHash(double a, double b); + +extern NSUInteger PFDoubleHash(double d); + +extern NSUInteger PFLongHash(unsigned long long l); + +extern NSString *PFMD5HashFromData(NSData *data); +extern NSString *PFMD5HashFromString(NSString *string); diff --git a/Pods/Parse/Parse/Internal/PFHash.m b/Pods/Parse/Parse/Internal/PFHash.m new file mode 100644 index 0000000..4fb33f8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFHash.m @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFHash.h" + +#import + +// +// Thank you Thomas Wang for 32/64 bit mix hash +// http://www.concentric.net/~Ttwang/tech/inthash.htm +// +// Above link is now dead, please visit +// http://web.archive.org/web/20121102023700/http://www.concentric.net/~Ttwang/tech/inthash.htm +// + +extern NSUInteger PFIntegerPairHash(NSUInteger a, NSUInteger b) { + return PFLongHash(((unsigned long long)a) << 32 | b); +} + +extern NSUInteger PFDoublePairHash(double a, double b) { + return PFIntegerPairHash(PFDoubleHash(a), PFDoubleHash(b)); +} + +extern NSUInteger PFDoubleHash(double d) { + union { + double key; + uint64_t bits; + } u; + u.key = d; + return PFLongHash(u.bits); +} + +extern NSUInteger PFLongHash(unsigned long long l) { + l = (~l) + (l << 18); // key = (key << 18) - key - 1; + l ^= (l >> 31); + l *= 21; // key = (key + (key << 2)) + (key << 4); + l ^= (l >> 11); + l += (l << 6); + l ^= (l >> 22); + return (NSUInteger)l; +} + +extern NSString *PFMD5HashFromData(NSData *data) { + unsigned char md[CC_MD5_DIGEST_LENGTH]; + + // NOTE: `__block` variables of a struct type seem to be bugged. The compiler emits instructions to read + // from the stack past where they're supposed to exist. This fixes that, by only using a traditional pointer. + CC_MD5_CTX ctx_val = { 0 }; + CC_MD5_CTX *ctx_ptr = &ctx_val; + CC_MD5_Init(ctx_ptr); + [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { + CC_MD5_Update(ctx_ptr , bytes, (CC_LONG)byteRange.length); + }]; + CC_MD5_Final(md, ctx_ptr); + + NSString *string = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + md[0], md[1], + md[2], md[3], + md[4], md[5], + md[6], md[7], + md[8], md[9], + md[10], md[11], + md[12], md[13], + md[14], md[15]]; + return string; +} + +extern NSString *PFMD5HashFromString(NSString *string) { + return PFMD5HashFromData([string dataUsingEncoding:NSUTF8StringEncoding]); +} diff --git a/Pods/Parse/Parse/Internal/PFInternalUtils.h b/Pods/Parse/Parse/Internal/PFInternalUtils.h new file mode 100644 index 0000000..eedf09e --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFInternalUtils.h @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFEncoder.h" + +@class PFFileManager; +@class PFKeychainStore; +@class PFNetworkCommand; + +@interface PFInternalUtils : NSObject + ++ (NSString *)parseServerURLString; ++ (void)setParseServer:(NSString *)server; + ++ (NSNumber *)fileSizeOfFileAtPath:(NSString *)filePath error:(NSError **)error; + +/** + Clears system time zone cache, gets the name of the time zone + and caches it. This method is completely thread-safe. + */ ++ (NSString *)currentSystemTimeZoneName; + +/** + * Performs selector on the target, only if the target and selector are non-nil, + * as well as target responds to selector + */ ++ (void)safePerformSelector:(SEL)selector withTarget:(id)target object:(id)object object:(id)anotherObject; + ++ (NSNumber *)addNumber:(NSNumber *)first withNumber:(NSNumber *)second; + +// +// Given an NSDictionary/NSArray/NSNumber/NSString even nested ones +// Generates a cache key that can be used to identify this object ++ (NSString *)cacheKeyForObject:(id)object; + +/**! + * Does a deep traversal of every item in object, calling block on every one. + * @param object The object or array to traverse deeply. + * @param block The block to call for every item. It will be passed the item + * as an argument. If it returns a truthy value, that value will replace the + * item in its parent container. + * @return The result of calling block on the top-level object itself. + **/ ++ (id)traverseObject:(id)object usingBlock:(id (^)(id object))block; + +/*! + This method will split an array into multiple arrays, each with up to maximum components count. + + @param array Array to split. + @param components Number of components that should be used as a max per each subarray. + + @return Array of arrays constructed by splitting the array. + */ ++ (NSArray *)arrayBySplittingArray:(NSArray *)array withMaximumComponentsPerSegment:(NSUInteger)components; + ++ (id)_stringWithFormat:(NSString *)format arguments:(NSArray *)arguments; +@end + +@interface PFJSONCacheItem : NSObject + +@property (nonatomic, copy, readonly) NSString *hashValue; + +- (instancetype)initWithObject:(id)object; ++ (PFJSONCacheItem *)cacheFromObject:(id)object; + +@end diff --git a/Pods/Parse/Parse/Internal/PFInternalUtils.m b/Pods/Parse/Parse/Internal/PFInternalUtils.m new file mode 100644 index 0000000..0b585bb --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFInternalUtils.m @@ -0,0 +1,298 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInternalUtils.h" + +#include +#include + +#import + +#import "PFACLPrivate.h" +#import "PFAssert.h" +#import "PFDateFormatter.h" +#import "BFTask+Private.h" +#import "PFFieldOperation.h" +#import "PFFile_Private.h" +#import "PFGeoPointPrivate.h" +#import "PFKeyValueCache.h" +#import "PFKeychainStore.h" +#import "PFLogging.h" +#import "PFEncoder.h" +#import "PFObjectPrivate.h" +#import "PFRelationPrivate.h" +#import "PFUserPrivate.h" +#import "Parse.h" +#import "PFFileManager.h" +#import "PFJSONSerialization.h" +#import "PFMultiProcessFileLockController.h" +#import "PFHash.h" + +#if TARGET_OS_IOS +#import "PFProduct.h" +#endif + +static NSString *parseServer_; + +@implementation PFInternalUtils + ++ (void)initialize { + if (self == [PFInternalUtils class]) { + [self setParseServer:kPFParseServer]; + } +} + ++ (NSString *)parseServerURLString { + return parseServer_; +} + +// Useful for testing. +// Beware of race conditions if you call setParseServer while something else may be using +// httpClient. ++ (void)setParseServer:(NSString *)server { + parseServer_ = [server copy]; +} + ++ (NSString *)currentSystemTimeZoneName { + static NSLock *methodLock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + methodLock = [[NSLock alloc] init]; + }); + + [methodLock lock]; + [NSTimeZone resetSystemTimeZone]; + NSString *systemTimeZoneName = [[NSTimeZone systemTimeZone].name copy]; + [methodLock unlock]; + + return systemTimeZoneName; +} + ++ (void)safePerformSelector:(SEL)selector withTarget:(id)target object:(id)object object:(id)anotherObject { + if (target == nil || selector == nil || ![target respondsToSelector:selector]) { + return; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [target performSelector:selector withObject:object withObject:anotherObject]; +#pragma clang diagnostic pop +} + ++ (NSNumber *)fileSizeOfFileAtPath:(NSString *)filePath error:(NSError **)error { + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath + error:error]; + return attributes[NSFileSize]; +} + +///-------------------------------------- +#pragma mark - Serialization +///-------------------------------------- + ++ (NSNumber *)addNumber:(NSNumber *)first withNumber:(NSNumber *)second { + const char *objcType = [first objCType]; + + if (strcmp(objcType, @encode(BOOL)) == 0) { + return @([first boolValue] + [second boolValue]); + } else if (strcmp(objcType, @encode(char)) == 0) { + return @([first charValue] + [second charValue]); + } else if (strcmp(objcType, @encode(double)) == 0) { + return @([first doubleValue] + [second doubleValue]); + } else if (strcmp(objcType, @encode(float)) == 0) { + return @([first floatValue] + [second floatValue]); + } else if (strcmp(objcType, @encode(int)) == 0) { + return @([first intValue] + [second intValue]); + } else if (strcmp(objcType, @encode(long)) == 0) { + return @([first longValue] + [second longValue]); + } else if (strcmp(objcType, @encode(long long)) == 0) { + return @([first longLongValue] + [second longLongValue]); + } else if (strcmp(objcType, @encode(short)) == 0) { + return @([first shortValue] + [second shortValue]); + } else if (strcmp(objcType, @encode(unsigned char)) == 0) { + return @([first unsignedCharValue] + [second unsignedCharValue]); + } else if (strcmp(objcType, @encode(unsigned int)) == 0) { + return @([first unsignedIntValue] + [second unsignedIntValue]); + } else if (strcmp(objcType, @encode(unsigned long)) == 0) { + return @([first unsignedLongValue] + [second unsignedLongValue]); + } else if (strcmp(objcType, @encode(unsigned long long)) == 0) { + return @([first unsignedLongLongValue] + [second unsignedLongLongValue]); + } else if (strcmp(objcType, @encode(unsigned short)) == 0) { + return @([first unsignedShortValue] + [second unsignedShortValue]); + } + + // Fall back to int? + return @([first intValue] + [second intValue]); +} + +///-------------------------------------- +#pragma mark - CacheKey +///-------------------------------------- + +#pragma mark Public + ++ (NSString *)cacheKeyForObject:(id)object { + NSMutableString *string = [NSMutableString string]; + [self appendObject:object toString:string]; + return string; +} + +#pragma mark Private + ++ (void)appendObject:(id)object toString:(NSMutableString *)string { + if ([object isKindOfClass:[NSDictionary class]]) { + [self appendDictionary:object toString:string]; + } else if ([object isKindOfClass:[NSArray class]]) { + [self appendArray:object toString:string]; + } else if ([object isKindOfClass:[NSString class]]) { + [string appendFormat:@"\"%@\"", object]; + } else if ([object isKindOfClass:[NSNumber class]]) { + [self appendNumber:object toString:string]; + } else if ([object isKindOfClass:[NSNull class]]) { + [self appendNullToString:string]; + } else { + [NSException raise:NSInvalidArgumentException + format:@"Couldn't create cache key from %@", object]; + } +} + ++ (void)appendDictionary:(NSDictionary *)dictionary toString:(NSMutableString *)string { + [string appendString:@"{"]; + + NSArray *keys = [[dictionary allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *key in keys) { + [string appendFormat:@"%@:", key]; + + id value = [dictionary objectForKey:key]; + [self appendObject:value toString:string]; + + [string appendString:@","]; + } + + [string appendString:@"}"]; +} + ++ (void)appendArray:(NSArray *)array toString:(NSMutableString *)string { + [string appendString:@"["]; + for (id object in array) { + [self appendObject:object toString:string]; + [string appendString:@","]; + } + [string appendString:@"]"]; +} + ++ (void)appendNumber:(NSNumber *)number toString:(NSMutableString *)string { + [string appendFormat:@"%@", [number stringValue]]; +} + ++ (void)appendNullToString:(NSMutableString *)string { + [string appendString:@"null"]; +} + ++ (id)traverseObject:(id)object usingBlock:(id (^)(id object))block seenObjects:(NSMutableSet *)seen { + if ([object isKindOfClass:[PFObject class]]) { + if ([seen containsObject:object]) { + // We've already visited this object in this call. + return object; + } + [seen addObject:object]; + + for (NSString *key in [(PFObject *)object allKeys]) { + [self traverseObject:object[key] usingBlock:block seenObjects:seen]; + } + + return block(object); + } else if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *newArray = [object mutableCopy]; + [object enumerateObjectsUsingBlock:^(id child, NSUInteger idx, BOOL *stop) { + id newChild = [self traverseObject:child usingBlock:block seenObjects:seen]; + if (newChild) { + newArray[idx] = newChild; + } + }]; + return block(newArray); + } else if ([object isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *newDictionary = [object mutableCopy]; + [object enumerateKeysAndObjectsUsingBlock:^(id key, id child, BOOL *stop) { + id newChild = [self traverseObject:child usingBlock:block seenObjects:seen]; + if (newChild) { + newDictionary[key] = newChild; + } + }]; + return block(newDictionary); + } + + return block(object); +} + ++ (id)traverseObject:(id)object usingBlock:(id (^)(id object))block { + NSMutableSet *seen = [[NSMutableSet alloc] init]; + id result = [self traverseObject:object usingBlock:block seenObjects:seen]; + return result; +} + ++ (NSArray *)arrayBySplittingArray:(NSArray *)array withMaximumComponentsPerSegment:(NSUInteger)components { + if ([array count] <= components) { + return @[ array ]; + } + + NSMutableArray *splitArray = [NSMutableArray array]; + NSInteger index = 0; + + while (index < [array count]) { + NSInteger length = MIN([array count] - index, components); + + NSArray *subarray = [array subarrayWithRange:NSMakeRange(index, length)]; + [splitArray addObject:subarray]; + + index += length; + } + + return splitArray; +} + ++ (id)_stringWithFormat:(NSString *)format arguments:(NSArray *)arguments { + // We cannot reliably construct a va_list for 64-bit, so hard code up to N args. + const int maxNumArgs = 10; + PFRangeAssert(arguments.count <= maxNumArgs, @"Maximum of %d format args allowed", maxNumArgs); + NSMutableArray *args = [arguments mutableCopy]; + for (NSUInteger i = arguments.count; i < maxNumArgs; i++) { + [args addObject:@""]; + } + return [NSString stringWithFormat:format, + args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]]; +} + +@end + +// A PFJSONCacheItem is a pairing of a json string with its hash value. +// This is used by our mutable container checking. +@implementation PFJSONCacheItem + +- (instancetype)initWithObject:(id)object { + if (self = [super init]) { + NSObject *encoded = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:object]; + NSData *jsonData = [PFJSONSerialization dataFromJSONObject:encoded]; + _hashValue = PFMD5HashFromData(jsonData); + } + return self; +} + +- (BOOL)isEqual:(id)otherCache { + if (![otherCache isKindOfClass:[PFJSONCacheItem class]]) { + return NO; + } + + return [self.hashValue isEqualToString:[otherCache hashValue]]; +} + ++ (PFJSONCacheItem *)cacheFromObject:(id)object { + return [[PFJSONCacheItem alloc] initWithObject:object]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFJSONSerialization.h b/Pods/Parse/Parse/Internal/PFJSONSerialization.h new file mode 100644 index 0000000..e6fb16c --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFJSONSerialization.h @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFJSONSerialization : NSObject + +/*! + The object passed in must be one of: + * NSString + * NSNumber + * NSDictionary + * NSArray + * NSNull + + @returns NSData of JSON representing the passed in object. + */ ++ (nullable NSData *)dataFromJSONObject:(id)object; + +/*! + The object passed in must be one of: + * NSString + * NSNumber + * NSDictionary + * NSArray + * NSNull + + @returns NSString of JSON representing the passed in object. + */ ++ (nullable NSString *)stringFromJSONObject:(id)object; + +/*! + Takes a JSON string and returns the NSDictionaries and NSArrays in it. + You should still call decodeObject if you want Parse types. + */ ++ (nullable id)JSONObjectFromData:(NSData *)data; + +/*! + Takes a JSON string and returns the NSDictionaries and NSArrays in it. + You should still call decodeObject if you want Parse types. + */ ++ (nullable id)JSONObjectFromString:(NSString *)string; + +/*! + @abstract Takes a file path to json file and returns the NSDictionaries and NSArrays in it. + + @description You should still call decodeObject if you want Parse types. + + @param filePath File path to a file. + + @return Decoded object. + */ ++ (nullable id)JSONObjectFromFileAtPath:(NSString *)filePath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFJSONSerialization.m b/Pods/Parse/Parse/Internal/PFJSONSerialization.m new file mode 100644 index 0000000..c2c72fe --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFJSONSerialization.m @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFJSONSerialization.h" + +#import "PFAssert.h" +#import "PFLogging.h" + +@implementation PFJSONSerialization + ++ (NSData *)dataFromJSONObject:(id)object { + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:object options:0 error:&error]; + PFParameterAssert(data && !error, @"PFObject values must be serializable to JSON"); + + return data; +} + ++ (NSString *)stringFromJSONObject:(id)object { + NSData *data = [self dataFromJSONObject:object]; + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +} + ++ (id)JSONObjectFromData:(NSData *)data { + NSError *error = nil; + id object = [NSJSONSerialization JSONObjectWithData:data + options:0 + error:&error]; + if (!object || error != nil) { + PFLogError(PFLoggingTagCommon, @"JSON deserialization failed with error: %@", [error description]); + } + + return object; +} + ++ (id)JSONObjectFromString:(NSString *)string { + return [self JSONObjectFromData:[string dataUsingEncoding:NSUTF8StringEncoding]]; +} + ++ (id)JSONObjectFromFileAtPath:(NSString *)filePath { + NSInputStream *stream = [NSInputStream inputStreamWithFileAtPath:filePath]; + if (!stream) { + return nil; + } + + [stream open]; + + NSError *streamError = stream.streamError; + // Check if stream failed to open, because there is no such file. + if (streamError && [streamError.domain isEqualToString:NSPOSIXErrorDomain] && streamError.code == ENOENT) { + [stream close]; // Still close the stream. + return nil; + } + + NSError *error = nil; + id object = [NSJSONSerialization JSONObjectWithStream:stream options:0 error:&error]; + if (!object || error) { + PFLogError(PFLoggingTagCommon, @"JSON deserialization failed with error: %@", error.description); + } + + [stream close]; + + return object; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFKeychainStore.h b/Pods/Parse/Parse/Internal/PFKeychainStore.h new file mode 100644 index 0000000..3d7c06e --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFKeychainStore.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const PFKeychainStoreDefaultService; + +/*! + PFKeychainStore is NSUserDefaults-like wrapper on top of Keychain. + It supports any object, with NSCoding support. Every object is serialized using NSKeyedArchiver. + + All objects are available after the first device unlock and are not backed up. + */ +@interface PFKeychainStore : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithService:(NSString *)service NS_DESIGNATED_INITIALIZER; + +- (nullable id)objectForKey:(NSString *)key; +- (nullable id)objectForKeyedSubscript:(NSString *)key; + +- (BOOL)setObject:(nullable id)object forKey:(NSString *)key; +- (BOOL)setObject:(nullable id)object forKeyedSubscript:(NSString *)key; +- (BOOL)removeObjectForKey:(NSString *)key; +- (BOOL)removeAllObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFKeychainStore.m b/Pods/Parse/Parse/Internal/PFKeychainStore.m new file mode 100644 index 0000000..747ff10 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFKeychainStore.m @@ -0,0 +1,200 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFKeychainStore.h" + +#import "PFAssert.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "Parse.h" + +NSString *const PFKeychainStoreDefaultService = @"com.parse.sdk"; + +@interface PFKeychainStore () { + dispatch_queue_t _synchronizationQueue; +} + +@property (nonatomic, copy, readonly) NSString *service; +@property (nonatomic, copy, readonly) NSDictionary *keychainQueryTemplate; + +@end + +@implementation PFKeychainStore + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (NSDictionary *)_keychainQueryTemplateForService:(NSString *)service { + NSMutableDictionary *query = [NSMutableDictionary dictionary]; + if ([service length]) { + query[(__bridge NSString *)kSecAttrService] = service; + } + query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-pointer-compare" + if (&kSecAttrAccessible != nil) { + query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + } +#pragma clang diagnostic pop + + return [query copy]; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithService:(NSString *)service { + self = [super init]; + if (!self) return nil; + + _service = service; + _keychainQueryTemplate = [[self class] _keychainQueryTemplateForService:service]; + + NSString *queueLabel = [NSString stringWithFormat:@"com.parse.keychain.%@", service]; + _synchronizationQueue = dispatch_queue_create([queueLabel UTF8String], DISPATCH_QUEUE_CONCURRENT); + PFMarkDispatchQueue(_synchronizationQueue); + + return self; +} + +///-------------------------------------- +#pragma mark - Read +///-------------------------------------- + +- (id)objectForKey:(NSString *)key { + __block NSData *data = nil; + dispatch_sync(_synchronizationQueue, ^{ + data = [self _dataForKey:key]; + }); + + if (data) { + id object = nil; + @try { + object = [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + @catch (NSException *exception) {} + + return object; + } + return nil; +} + +- (id)objectForKeyedSubscript:(NSString *)key { + return [self objectForKey:key]; +} + +- (NSData *)_dataForKey:(NSString *)key { + NSMutableDictionary *query = [self.keychainQueryTemplate mutableCopy]; + + query[(__bridge NSString *)kSecAttrAccount] = key; + query[(__bridge NSString *)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; + query[(__bridge NSString *)kSecReturnData] = (__bridge id)kCFBooleanTrue; + + //recover data + CFDataRef data = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&data); + if (status != errSecSuccess && status != errSecItemNotFound) { + PFLogError(PFLoggingTagCommon, + @"PFKeychainStore failed to get object for key '%@', with error: %ld", key, (long)status); + } + return CFBridgingRelease(data); +} + +///-------------------------------------- +#pragma mark - Write +///-------------------------------------- + +- (BOOL)setObject:(id)object forKey:(NSString *)key { + NSParameterAssert(key != nil); + + if (!object) { + return [self removeObjectForKey:key]; + } + + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object]; + if (!data) { + return NO; + } + + NSMutableDictionary *query = [self.keychainQueryTemplate mutableCopy]; + query[(__bridge NSString *)kSecAttrAccount] = key; + + NSDictionary *update = @{ (__bridge NSString *)kSecValueData : data }; + + __block OSStatus status = errSecSuccess; + dispatch_barrier_sync(_synchronizationQueue,^{ + if ([self _dataForKey:key]) { + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update); + } else { + [query addEntriesFromDictionary:update]; + status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); + } + }); + + if (status != errSecSuccess) { + PFLogError(PFLoggingTagCommon, + @"PFKeychainStore failed to set object for key '%@', with error: %ld", key, (long)status); + } + + return (status == errSecSuccess); +} + +- (BOOL)setObject:(id)object forKeyedSubscript:(NSString *)key { + return [self setObject:object forKey:key]; +} + +- (BOOL)removeObjectForKey:(NSString *)key { + __block BOOL value = NO; + dispatch_barrier_sync(_synchronizationQueue, ^{ + value = [self _removeObjectForKey:key]; + }); + return value; +} + +- (BOOL)_removeObjectForKey:(NSString *)key { + PFAssertIsOnDispatchQueue(_synchronizationQueue); + NSMutableDictionary *query = [self.keychainQueryTemplate mutableCopy]; + query[(__bridge NSString *)kSecAttrAccount] = key; + + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + return (status == errSecSuccess); +} + +- (BOOL)removeAllObjects { + NSMutableDictionary *query = [self.keychainQueryTemplate mutableCopy]; + query[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue; + query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll; + + __block BOOL value = YES; + dispatch_barrier_sync(_synchronizationQueue, ^{ + CFArrayRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + if (status != errSecSuccess) { + return; + } + + for (NSDictionary *item in CFBridgingRelease(result)) { + NSString *key = item[(__bridge id)kSecAttrAccount]; + value = [self _removeObjectForKey:key]; + if (!value) { + return; + } + } + }); + return value; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFLocationManager.h b/Pods/Parse/Parse/Internal/PFLocationManager.h new file mode 100644 index 0000000..b1f115c --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFLocationManager.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class CLLocation; +@class CLLocationManager; + +#if TARGET_OS_IPHONE + +@class UIApplication; + +#endif + +typedef void(^PFLocationManagerLocationUpdateBlock)(CLLocation *location, NSError *error); + +/*! + PFLocationManager is an internal class which wraps a CLLocationManager and + returns an updated CLLocation via the provided block. + + When -addBlockForCurrentLocation is called, the CLLocationManager's + -startUpdatingLocations is called, and upon CLLocationManagerDelegate callback + (either success or failure), any handlers that were passed to this class will + be called _once_ with the updated location, then removed. The CLLocationManager + stopsUpdatingLocation upon a single failure or success case, so that the next + location request is guaranteed a speedily returned CLLocation. + */ +@interface PFLocationManager : NSObject + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithSystemLocationManager:(CLLocationManager *)manager; + +#if TARGET_OS_IPHONE + +- (instancetype)initWithSystemLocationManager:(CLLocationManager *)manager + application:(UIApplication *)application + bundle:(NSBundle *)bundle NS_DESIGNATED_INITIALIZER; + +#endif + +///-------------------------------------- +#pragma mark - Current Location +///-------------------------------------- + +- (void)addBlockForCurrentLocation:(PFLocationManagerLocationUpdateBlock)handler; + +@end diff --git a/Pods/Parse/Parse/Internal/PFLocationManager.m b/Pods/Parse/Parse/Internal/PFLocationManager.m new file mode 100644 index 0000000..82286bc --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFLocationManager.m @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFLocationManager.h" + +#import + +#import "PFConstants.h" +#import "PFGeoPoint.h" +#import "PFApplication.h" + +@interface PFLocationManager () + +@property (nonatomic, strong) CLLocationManager *locationManager; +@property (nonatomic, strong) NSBundle *bundle; +@property (nonatomic, strong) UIApplication *application; + +// We use blocks and not BFTasks because Tasks don't gain us much - we still +// have to manually hold onto them so that they can be resolved in the +// CLLocationManager callback. +@property (nonatomic, strong) NSMutableSet *blockSet; + +@end + +@implementation PFLocationManager + +///-------------------------------------- +#pragma mark - CLLocationManager +///-------------------------------------- + ++ (CLLocationManager *)_newSystemLocationManager { + __block CLLocationManager *manager = nil; + + // CLLocationManager should be created only on main thread, as it needs a run loop to serve delegate callbacks + dispatch_block_t block = ^{ + manager = [[CLLocationManager alloc] init]; + }; + if ([[NSThread currentThread] isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } + return manager; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + CLLocationManager *manager = [[self class] _newSystemLocationManager]; + return [self initWithSystemLocationManager:manager]; +} + +- (instancetype)initWithSystemLocationManager:(CLLocationManager *)manager { + return [self initWithSystemLocationManager:manager + application:[PFApplication currentApplication].systemApplication + bundle:[NSBundle mainBundle]]; +} + +- (instancetype)initWithSystemLocationManager:(CLLocationManager *)manager + application:(UIApplication *)application + bundle:(NSBundle *)bundle { + self = [super init]; + if (!self) return nil; + + _blockSet = [NSMutableSet setWithCapacity:1]; + _locationManager = manager; + _locationManager.delegate = self; + _bundle = bundle; + _application = application; + + return self; +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + _locationManager.delegate = nil; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (void)addBlockForCurrentLocation:(PFLocationManagerLocationUpdateBlock)handler { + @synchronized (self.blockSet) { + [self.blockSet addObject:[handler copy]]; + } + +#if TARGET_OS_WATCH + if ([self.bundle objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) { + [self.locationManager requestWhenInUseAuthorization]; + } else { + [self.locationManager requestAlwaysAuthorization]; + } + [self.locationManager requestLocation]; +#elif TARGET_OS_IOS + if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { + if (self.application.applicationState != UIApplicationStateBackground && + [self.bundle objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) { + [self.locationManager requestWhenInUseAuthorization]; + } else { + [self.locationManager requestAlwaysAuthorization]; + } + } + [self.locationManager startUpdatingLocation]; +#elif TARGET_OS_MAC + [self.locationManager startUpdatingLocation]; +#endif +} + +///-------------------------------------- +#pragma mark - CLLocationManagerDelegate +///-------------------------------------- + +- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { + CLLocation *location = [locations lastObject]; + + [manager stopUpdatingLocation]; + + NSMutableSet *callbacks = [NSMutableSet setWithCapacity:1]; + @synchronized (self.blockSet) { + [callbacks setSet:self.blockSet]; + [self.blockSet removeAllObjects]; + } + for (PFLocationManagerLocationUpdateBlock block in callbacks) { + block(location, nil); + } +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { + [manager stopUpdatingLocation]; + + NSMutableSet *callbacks = nil; + @synchronized (self.blockSet) { + callbacks = [self.blockSet copy]; + [self.blockSet removeAllObjects]; + } + for (PFLocationManagerLocationUpdateBlock block in callbacks) { + block(nil, error); + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFLogger.h b/Pods/Parse/Parse/Internal/PFLogger.h new file mode 100644 index 0000000..2a8858e --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFLogger.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +# import + +typedef uint8_t PFLoggingTag; + +@interface PFLogger : NSObject + +@property (atomic, assign) PFLogLevel logLevel; + +///-------------------------------------- +/// @name Shared Logger +///-------------------------------------- + +/*! +A shared instance of `PFLogger` that should be used for all logging. + +@returns An shared singleton instance of `PFLogger`. +*/ ++ (instancetype)sharedLogger; //TODO: (nlutsenko) Convert to use an instance everywhere instead of a shared singleton. + +///-------------------------------------- +/// @name Logging Messages +///-------------------------------------- + +/*! + Logs a message at a specific level for a tag. + If current logging level doesn't include this level - this method does nothing. + + @param level Logging Level + @param tag Logging Tag + @param format Format to use for the log message. + */ +- (void)logMessageWithLevel:(PFLogLevel)level + tag:(PFLoggingTag)tag + format:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4); + +@end diff --git a/Pods/Parse/Parse/Internal/PFLogger.m b/Pods/Parse/Parse/Internal/PFLogger.m new file mode 100644 index 0000000..d309096 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFLogger.m @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFLogger.h" + +#import "PFApplication.h" +#import "PFLogging.h" + +@implementation PFLogger + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (NSString *)_descriptionForLoggingTag:(PFLoggingTag)tag { + NSString *description = nil; + switch (tag) { + case PFLoggingTagCommon: + break; + case PFLoggingTagCrashReporting: + description = @"Crash Reporting"; + break; + default: + break; + } + return description; +} + ++ (NSString *)_descriptionForLogLevel:(PFLogLevel)logLevel { + NSString *description = nil; + switch (logLevel) { + case PFLogLevelNone: + break; + case PFLogLevelError: + description = @"Error"; + break; + case PFLogLevelWarning: + description = @"Warning"; + break; + case PFLogLevelInfo: + description = @"Info"; + break; + default: + break; + } + return description; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)sharedLogger { + static PFLogger *logger; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + logger = [[PFLogger alloc] init]; + }); + return logger; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _logLevel = ([PFApplication currentApplication].appStoreEnvironment ? PFLogLevelNone : PFLogLevelWarning); + + return self; +} + +///-------------------------------------- +#pragma mark - Logging Messages +///-------------------------------------- + +- (void)logMessageWithLevel:(PFLogLevel)level + tag:(PFLoggingTag)tag + format:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4) { + if (level > self.logLevel || level == PFLogLevelNone || !format) { + return; + } + + va_list args; + va_start(args, format); + + NSMutableString *message = [NSMutableString stringWithFormat:@"[%@]", [[self class] _descriptionForLogLevel:level]]; + + NSString *tagDescription = [[self class] _descriptionForLoggingTag:tag]; + if (tagDescription) { + [message appendFormat:@"[%@]", tagDescription]; + } + + [message appendFormat:@": %@", format]; + + NSLogv(message, args); + + va_end(args); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFLogging.h b/Pods/Parse/Parse/Internal/PFLogging.h new file mode 100644 index 0000000..90a4410 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFLogging.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef Parse_PFLogging_h +#define Parse_PFLogging_h + +# import + +#import "PFLogger.h" + +static const PFLoggingTag PFLoggingTagCommon = 0; +static const PFLoggingTag PFLoggingTagCrashReporting = 100; + +#define PFLog(level, loggingTag, frmt, ...) \ +[[PFLogger sharedLogger] logMessageWithLevel:level tag:loggingTag format:(frmt), ##__VA_ARGS__] + +#define PFLogError(tag, frmt, ...) \ +PFLog(PFLogLevelError, (tag), (frmt), ##__VA_ARGS__) + +#define PFLogWarning(tag, frmt, ...) \ +PFLog(PFLogLevelWarning, (tag), (frmt), ##__VA_ARGS__) + +#define PFLogInfo(tag, frmt, ...) \ +PFLog(PFLogLevelInfo, (tag), (frmt), ##__VA_ARGS__) + +#define PFLogDebug(tag, frmt, ...) \ +PFLog(PFLogLevelDebug, (tag), (frmt), ##__VA_ARGS__) + +#define PFLogException(exception) \ +PFLogError(PFLoggingTagCommon, @"Caught \"%@\" with reason \"%@\"%@", \ +exception.name, exception, \ +[exception callStackSymbols] ? [NSString stringWithFormat:@":\n%@.", [exception callStackSymbols]] : @"") + +#endif diff --git a/Pods/Parse/Parse/Internal/PFMacros.h b/Pods/Parse/Parse/Internal/PFMacros.h new file mode 100644 index 0000000..4f53214 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFMacros.h @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#ifndef Parse_PFMacros_h +#define Parse_PFMacros_h + +/*! + This macro allows to create NSSet via subscript. + */ +#define PF_SET(...) [NSSet setWithObjects:__VA_ARGS__, nil] + +/*! + This macro is a handy thing for converting libSystem objects to (void *) pointers. + If you are targeting OSX 10.8+ and iOS 6.0+ - this is no longer required. + */ +#if OS_OBJECT_USE_OBJC + #define PFOSObjectPointer(object) \ + (__bridge void *)(object) +#else + #define PFOSObjectPointer(object) \ + (void *)(object) +#endif + +/*! + Mark a queue in order to be able to check PFAssertIsOnMarkedQueue. + */ +#define PFMarkDispatchQueue(queue) \ +dispatch_queue_set_specific((queue), \ + PFOSObjectPointer(queue), \ + PFOSObjectPointer(queue), \ + NULL) + +///-------------------------------------- +/// @name Memory Management +/// +/// The following macros are influenced and include portions of libextobjc. +///-------------------------------------- + +/*! + Creates a __weak version of the variable provided, + which can later be safely used or converted into strong variable via @strongify. + */ +#define weakify(var) \ +try {} @catch (...) {} \ +__weak __typeof__(var) var ## _weak = var; + +/*! + Creates a strong shadow reference of the variable provided. + Variable must have previously been passed to @weakify. + */ +#define strongify(var) \ +try {} @catch (...) {} \ +__strong __typeof__(var) var = var ## _weak; + +///-------------------------------------- +/// @name KVC +///-------------------------------------- + +/*! + This macro ensures that object.key exists at compile time. + It can accept a chained key path. + */ +#define keypath(TYPE, PATH) \ +(((void)(NO && ((void)((TYPE *)(nil)).PATH, NO)), # PATH)) + +///-------------------------------------- +/// @name Runtime +///-------------------------------------- + +/*! + Using objc_msgSend directly is bad, very bad. Doing so without casting could result in stack-smashing on architectures + (*cough* x86 *cough*) that use strange methods of returning values of different types. + + The objc_msgSend_safe macro ensures that we properly cast the function call to use the right conventions when passing + parameters and getting return values. This also fixes some issues with ARC and objc_msgSend directly, though strange + things can happen when receiving values from NS_RETURNS_RETAINED methods. + */ +#define objc_msgSend(...) _Pragma("GCC error \"Use objc_msgSend_safe() instead!\"") +#define objc_msgSend_safe(returnType, argTypes...) ((returnType (*)(id, SEL, ##argTypes))(objc_msgSend)) + +/*! + This exists because if we throw an exception from dispatch_sync, it doesn't 'bubble up' to the calling thread. + This simply wraps dispatch_sync and properly throws the exception back to the calling thread, not the thread that + the exception was originally raised on. + + @param queue The queue to execute on + @param block The block to execute + + @see dispatch_sync + */ +#define pf_sync_with_throw(queue, block) \ + do { \ + __block NSException *caught = nil; \ + dispatch_sync(queue, ^{ \ + @try { block(); } \ + @catch (NSException *ex) { \ + caught = ex; \ + } \ + }); \ + if (caught) @throw caught; \ + } while (0) + +/*! + To prevent retain cycles by OCMock, this macro allows us to capture a weak reference to return from a stubbed method. + */ +#define andReturnWeak(variable) _andDo( \ + ({ \ + __weak typeof(variable) variable ## _weak = (variable); \ + ^(NSInvocation *invocation) { \ + __autoreleasing typeof(variable) variable ## _block = variable ## _weak; \ + [invocation setReturnValue:&(variable ## _block)]; \ + }; \ + }) \ +) + +/*! + This exists to use along with bolts generic tasks. Instead of returning a BFTask with no generic type, or a generic + type of 'NSNull' when there is no usable result from a task, we use the type 'PFVoid', which will always have a value + of 'nil'. + + This allows us to more easily descern between methods that have not yet updated the return type of their tasks, as well + as provide a more enforced API contract to the caller (as sending any message to PFVoid will result in a compile time + error). + */ +@class _PFVoid_Nonexistant; +typedef _PFVoid_Nonexistant *PFVoid; + +#endif diff --git a/Pods/Parse/Parse/Internal/PFMulticastDelegate.h b/Pods/Parse/Parse/Internal/PFMulticastDelegate.h new file mode 100644 index 0000000..7a54dc3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFMulticastDelegate.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +/*! + Represents an event that can be subscribed to by multiple observers. + */ +@interface PFMulticastDelegate : NSObject { +@private + NSMutableArray *callbacks; +} + +/*! + Subscribes a block for callback. + + Important: if you ever plan to be able to unsubscribe the block, you must copy the block + before passing it to subscribe, and use the same instance for unsubscribe. + */ +- (void)subscribe:(void(^)(id result, NSError *error))block; +- (void)unsubscribe:(void(^)(id result, NSError *error))block; +- (void)invoke:(id)result error:(NSError *)error; +- (void)clear; + +@end diff --git a/Pods/Parse/Parse/Internal/PFMulticastDelegate.m b/Pods/Parse/Parse/Internal/PFMulticastDelegate.m new file mode 100644 index 0000000..9d49fa8 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFMulticastDelegate.m @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMulticastDelegate.h" + +@implementation PFMulticastDelegate + +- (instancetype)init { + if (self = [super init]) { + callbacks = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)subscribe:(void(^)(id result, NSError *error))block { + [callbacks addObject:block]; +} + +- (void)unsubscribe:(void(^)(id result, NSError *error))block { + [callbacks removeObject:block]; +} + +- (void)invoke:(id)result error:(NSError *)error { + NSArray *callbackCopy = [callbacks copy]; + for (void (^block)(id result, NSError *error) in callbackCopy) { + block(result, error); + } +} +- (void)clear { + [callbacks removeAllObjects]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFNetworkCommand.h b/Pods/Parse/Parse/Internal/PFNetworkCommand.h new file mode 100644 index 0000000..6502923 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFNetworkCommand.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@protocol PFNetworkCommand + +///-------------------------------------- +/// @name Properties +///-------------------------------------- + +@property (nonatomic, copy, readonly) NSString *sessionToken; +@property (nonatomic, copy, readonly) NSString *operationSetUUID; + +// If this command creates an object that is referenced by some other command, +// then this localId will be updated with the new objectId that is returned. +@property (nonatomic, copy) NSString *localId; + +///-------------------------------------- +/// @name Encoding/Decoding +///-------------------------------------- + ++ (instancetype)commandFromDictionaryRepresentation:(NSDictionary *)dictionary; +- (NSDictionary *)dictionaryRepresentation; + ++ (BOOL)isValidDictionaryRepresentation:(NSDictionary *)dictionary; + +///-------------------------------------- +/// @name Local Identifiers +///-------------------------------------- + +/*! + Replaces all local ids in this command with the correct objectId for that object. + This should be called before sending the command over the network, so that there + are no local ids sent to the Parse Cloud. If any local id refers to an object that + has not yet been saved, and thus has no objectId, then this method raises an + exception. + */ +- (void)resolveLocalIds; + +@end diff --git a/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.h b/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.h new file mode 100644 index 0000000..17a0499 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFEventuallyQueue.h" + +@interface PFPinningEventuallyQueue : PFEventuallyQueue + +///-------------------------------------- +/// @name Init +///-------------------------------------- + ++ (instancetype)newDefaultPinningEventuallyQueueWithCommandRunner:(id)commandRunner; + +@end diff --git a/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.m b/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.m new file mode 100644 index 0000000..0d039d2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFPinningEventuallyQueue.m @@ -0,0 +1,327 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPinningEventuallyQueue.h" + +#import +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFErrorUtilities.h" +#import "PFEventuallyPin.h" +#import "PFEventuallyQueue_Private.h" +#import "PFMacros.h" +#import "PFObjectPrivate.h" +#import "PFOperationSet.h" +#import "PFRESTCommand.h" +#import "PFTaskQueue.h" + +@interface PFPinningEventuallyQueue () { + /*! + Queue for reading/writing eventually operations from LDS. Makes all reads/writes atomic + operations. + */ + PFTaskQueue *_taskQueue; + + /*! + List of `PFEventuallyPin.uuid` that are currently queued in `_processingQueue`. This contains + uuid of PFEventuallyPin that's enqueued. + */ + NSMutableArray *_eventuallyPinUUIDQueue; + + /*! + Map of eventually operation UUID to matching PFEventuallyPin. This contains PFEventuallyPin + that's enqueued. + */ + NSMutableDictionary *_uuidToEventuallyPin; + + /*! + Map OperationSetUUID to PFOperationSet + */ + NSMutableDictionary *_operationSetUUIDToOperationSet; + + /*! + Map OperationSetUUID to PFEventuallyPin + */ + NSMutableDictionary *_operationSetUUIDToEventuallyPin; +} + +@end + +@implementation PFPinningEventuallyQueue + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)newDefaultPinningEventuallyQueueWithCommandRunner:(id)commandRunner { + PFPinningEventuallyQueue *queue = [[self alloc] initWithCommandRunner:commandRunner + maxAttemptsCount:PFEventuallyQueueDefaultMaxAttemptsCount + retryInterval:PFEventuallyQueueDefaultTimeoutRetryInterval]; + [queue start]; + return queue; +} + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner + maxAttemptsCount:(NSUInteger)attemptsCount + retryInterval:(NSTimeInterval)retryInterval { + self = [super initWithCommandRunner:commandRunner maxAttemptsCount:attemptsCount retryInterval:retryInterval]; + if (!self) return nil; + + _taskQueue = [[PFTaskQueue alloc] init]; + + dispatch_sync(_synchronizationQueue, ^{ + _eventuallyPinUUIDQueue = [NSMutableArray array]; + _uuidToEventuallyPin = [NSMutableDictionary dictionary]; + _operationSetUUIDToOperationSet = [NSMutableDictionary dictionary]; + _operationSetUUIDToEventuallyPin = [NSMutableDictionary dictionary]; + }); + + // Populate Eventually Pin to make sure we pre-loaded any existing data. + [self _populateEventuallyPinAsync]; + + return self; +} + +///-------------------------------------- +#pragma mark - Controlling Queue +///-------------------------------------- + +- (void)removeAllCommands { + [super removeAllCommands]; + + BFTask *removeTask = [_taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueWithBlock:^id(BFTask *task) { + return [[PFEventuallyPin findAllEventuallyPin] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *eventuallyPins = task.result; + NSMutableArray *unpinTasks = [NSMutableArray array]; + + for (PFEventuallyPin *eventuallyPin in eventuallyPins) { + [unpinTasks addObject:[eventuallyPin unpinInBackgroundWithName:PFEventuallyPinPinName]]; + } + + return [BFTask taskForCompletionOfAllTasks:unpinTasks]; + }]; + }]; + }]; + + [removeTask waitForResult:nil]; + // Clear in-memory data + dispatch_sync(_synchronizationQueue, ^{ + [_eventuallyPinUUIDQueue removeAllObjects]; + [_uuidToEventuallyPin removeAllObjects]; + [_operationSetUUIDToEventuallyPin removeAllObjects]; + [_operationSetUUIDToOperationSet removeAllObjects]; + }); +} + +- (void)_simulateReboot { + [super _simulateReboot]; + + [_eventuallyPinUUIDQueue removeAllObjects]; + [_uuidToEventuallyPin removeAllObjects]; + [_operationSetUUIDToEventuallyPin removeAllObjects]; + [_operationSetUUIDToOperationSet removeAllObjects]; + + [self _populateEventuallyPinAsync]; +} + +///-------------------------------------- +#pragma mark - PFEventuallyQueueSubclass +///-------------------------------------- + +- (NSString *)_newIdentifierForCommand:(id)command { + return [[NSUUID UUID] UUIDString]; +} + +- (NSArray *)_pendingCommandIdentifiers { + [[self _populateEventuallyPinAsync] waitForResult:nil]; + + NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + PFEventuallyPin *pin = _uuidToEventuallyPin[evaluatedObject]; + // Filter out all pins that don't have `operationSet` data ready yet + // to make sure we send the command with all the changes. + if (pin.operationSetUUID) { + return (_operationSetUUIDToEventuallyPin[pin.operationSetUUID] != nil); + } + return YES; + }]; + return [_eventuallyPinUUIDQueue filteredArrayUsingPredicate:predicate]; +} + +- (id)_commandWithIdentifier:(NSString *)identifier error:(NSError **)error { + // Should be populated by `_pendingCommandIdentifiers` + PFEventuallyPin *eventuallyPin = _uuidToEventuallyPin[identifier]; + + // TODO (hallucinogen): this is a temporary hack. We need to change this to match the Android one. + // We need to construct the command just right when we want to execute it. Or else it will ask for localId + // when there's unsaved child. + switch (eventuallyPin.type) { + case PFEventuallyPinTypeSave: { + PFOperationSet *operationSet = _operationSetUUIDToOperationSet[eventuallyPin.operationSetUUID]; + return [eventuallyPin.object _constructSaveCommandForChanges:operationSet + sessionToken:eventuallyPin.sessionToken + objectEncoder:[PFPointerObjectEncoder objectEncoder]]; + } + case PFEventuallyPinTypeDelete: + return [eventuallyPin.object _currentDeleteCommandWithSessionToken:eventuallyPin.sessionToken]; + default: + break; + } + + id command = eventuallyPin.command; + if (!command && error) { + *error = [PFErrorUtilities errorWithCode:kPFErrorInternalServer + message:@"Failed to construct eventually command from cache." + shouldLog:NO]; + } + return command; +} + +- (BFTask *)_enqueueCommandInBackground:(id)command + object:(PFObject *)object + identifier:(NSString *)identifier { + return [_taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task){ + return [PFEventuallyPin pinEventually:object forCommand:command withUUID:identifier]; + }]; + }]; +} + +- (BFTask *)_didFinishRunningCommand:(id)command + withIdentifier:(NSString *)identifier + resultTask:(BFTask *)resultTask { + // Delete the commands regardless, even if it failed. Otherwise we'll just keep trying it forever. + // We don't need to wait for taskQueue since it will not be queued again since this + // PFEventuallyPin is still in `_eventuallyPinUUIDQueue` + PFEventuallyPin *eventuallyPin = _uuidToEventuallyPin[identifier]; + BFTask *unpinTask = [eventuallyPin unpinInBackgroundWithName:PFEventuallyPinPinName]; + unpinTask = [unpinTask continueWithBlock:^id(BFTask *task) { + // Remove data from memory. + dispatch_sync(_synchronizationQueue, ^{ + [_uuidToEventuallyPin removeObjectForKey:identifier]; + [_eventuallyPinUUIDQueue removeObject:identifier]; + }); + + if (resultTask.cancelled || resultTask.exception || resultTask.error) { + return resultTask; + } + + if (eventuallyPin.operationSetUUID) { + // Remove only if the operation succeeded + dispatch_sync(_synchronizationQueue, ^{ + [_operationSetUUIDToOperationSet removeObjectForKey:eventuallyPin.operationSetUUID]; + [_operationSetUUIDToEventuallyPin removeObjectForKey:eventuallyPin.operationSetUUID]; + }); + } + + PFCommandResult *commandResult = resultTask.result; + switch (eventuallyPin.type) { + case PFEventuallyPinTypeSave: { + + task = [task continueWithBlock:^id(BFTask *task) { + return [eventuallyPin.object handleSaveResultAsync:commandResult.result]; + }]; + break; + } + + case PFEventuallyPinTypeDelete: { + task = [task continueWithBlock:^id(BFTask *task) { + PFObject *object = eventuallyPin.object; + id controller = [[object class] objectController]; + return [controller processDeleteResultAsync:commandResult.result forObject:object]; + }]; + break; + } + + default:break; + } + + return task; + }]; + + // Notify event listener that we finished running. + return [[super _didFinishRunningCommand:command + withIdentifier:identifier + resultTask:resultTask] continueWithBlock:^id(BFTask *task) { + return unpinTask; + }]; +} + +/*! + Synchronizes PFObject taskQueue (Many) and PFPinningEventuallyQueue taskQueue (None). Each queue will be held + until both are ready, matched on operationSetUUID. Once both are ready, the eventually task will run. + */ +- (BFTask *)_waitForOperationSet:(PFOperationSet *)operationSet eventuallyPin:(PFEventuallyPin *)eventuallyPin { + if (eventuallyPin != nil && eventuallyPin.type != PFEventuallyPinTypeSave) { + // If not save, then we don't have to do anything special. + return [BFTask taskWithResult:nil]; + } + + // TODO (hallucinogen): actually wait for PFObject taskQueue and PFPinningEventually taskQueue + + __block NSString *uuid = nil; + dispatch_sync(_synchronizationQueue, ^{ + if (operationSet != nil) { + uuid = operationSet.uuid; + _operationSetUUIDToOperationSet[uuid] = operationSet; + } + if (eventuallyPin != nil) { + uuid = eventuallyPin.operationSetUUID; + _operationSetUUIDToEventuallyPin[uuid] = eventuallyPin; + } + }); + if (uuid == nil) { + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"Either operationSet or eventuallyPin must be set" + userInfo:nil]; + return [BFTask taskWithException:exception]; + } + return [BFTask taskWithResult:nil]; +} + +///-------------------------------------- +#pragma mark - Eventually Pin +///-------------------------------------- + +- (BFTask *)_populateEventuallyPinAsync { + return [_taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [[toAwait continueWithBlock:^id(BFTask *task) { + return [PFEventuallyPin findAllEventuallyPinWithExcludeUUIDs:_eventuallyPinUUIDQueue]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *eventuallyPins = task.result; + + for (PFEventuallyPin *eventuallyPin in eventuallyPins) { + // If it's enqueued already, we don't need to run it again. + if ([_eventuallyPinUUIDQueue containsObject:eventuallyPin.operationSetUUID]) { + continue; + } + + // Make sure the data is in memory. + dispatch_sync(_synchronizationQueue, ^{ + [_eventuallyPinUUIDQueue addObject:eventuallyPin.uuid]; + _uuidToEventuallyPin[eventuallyPin.uuid] = eventuallyPin; + }); + + // For now we don't care whether this will fail or not. + [[self _waitForOperationSet:nil eventuallyPin:eventuallyPin] waitForResult:nil]; + } + + return task; + }]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFReachability.h b/Pods/Parse/Parse/Internal/PFReachability.h new file mode 100644 index 0000000..8e6242a --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFReachability.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class PFReachability; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(uint8_t, PFReachabilityState) { + PFReachabilityStateNotReachable, + PFReachabilityStateReachableViaWiFi, + PFReachabilityStateReachableViaCell, +}; + +@protocol PFReachabilityListener + +- (void)reachability:(PFReachability *)reachability didChangeReachabilityState:(PFReachabilityState)state; + +@end + +PF_WATCH_UNAVAILABLE @interface PFReachability : NSObject + +@property (nonatomic, assign, readonly) PFReachabilityState currentState; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithURL:(NSURL *)url NS_DESIGNATED_INITIALIZER; + +/* + Returns a shared singleton instance, + that could be used to check if Parse is reachable + */ ++ (instancetype)sharedParseReachability; + +/* + Adds a weak reference to the listener, + callbacks are executed on the main thread when status or flags change. + */ +- (void)addListener:(id)listener; + +/* + Removes weak reference to the listener. + */ +- (void)removeListener:(id)listener; + +/* + Removes all references to all listener objects. + */ +- (void)removeAllListeners; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PFReachability.m b/Pods/Parse/Parse/Internal/PFReachability.m new file mode 100644 index 0000000..b14c7e0 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFReachability.m @@ -0,0 +1,213 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFReachability.h" + +#import + +#import "PFAssert.h" +#import "PFConstants.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFWeakValue.h" + +@interface PFReachability () { + dispatch_queue_t _synchronizationQueue; + NSMutableArray *_listenersArray; + + SCNetworkReachabilityRef _networkReachability; +} + +@property (nonatomic, assign, readwrite) SCNetworkReachabilityFlags flags; + +@end + +static void _reachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) { + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + PFReachability *reachability = (__bridge PFReachability *)info; + reachability.flags = flags; +} + +@implementation PFReachability + +@synthesize flags = _flags; + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (BOOL)_reachabilityStateForFlags:(SCNetworkConnectionFlags)flags { + PFReachabilityState reachabilityState = PFReachabilityStateNotReachable; + + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) { + // if target host is not reachable + return reachabilityState; + } + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) { + // if target host is reachable and no connection is required + // then we'll assume (for now) that your on Wi-Fi + reachabilityState = PFReachabilityStateReachableViaWiFi; + } + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) { + // ... and the connection is on-demand (or on-traffic) if the + // calling application is using the CFSocketStream or higher APIs + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) { + // ... and no [user] intervention is needed + reachabilityState = PFReachabilityStateReachableViaWiFi; + } + } + +#if TARGET_OS_IPHONE + if (((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) && + ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)) { + // ... but WWAN connections are OK if the calling application + // is using the CFNetwork (CFSocketStream?) APIs. + // ... and a network connection is not required (kSCNetworkReachabilityFlagsConnectionRequired) + // which could be et w/connection flag (e.g. IsWWAN) indicating type of connection required. + reachabilityState = PFReachabilityStateReachableViaCell; + } +#endif + + return reachabilityState; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithURL:(NSURL *)url { + self = [super init]; + if (!self) return nil; + + _synchronizationQueue = dispatch_queue_create("com.parse.reachability", DISPATCH_QUEUE_CONCURRENT); + _listenersArray = [NSMutableArray array]; + [self _startMonitoringReachabilityWithURL:url]; + + return self; +} + ++ (instancetype)sharedParseReachability { + static PFReachability *reachability; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *serverUrlAsString = [NSString stringWithFormat:@"%@/%ld", kPFParseServer, (long)PARSE_API_VERSION]; + NSURL *serverUrl = [NSURL URLWithString:serverUrlAsString]; + reachability = [[self alloc] initWithURL:serverUrl]; + }); + return reachability; +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + if (_networkReachability != NULL) { + SCNetworkReachabilitySetCallback(_networkReachability, NULL, NULL); + SCNetworkReachabilitySetDispatchQueue(_networkReachability, NULL); + CFRelease(_networkReachability); + _networkReachability = NULL; + } +} + +///-------------------------------------- +#pragma mark - Listeners +///-------------------------------------- + +- (void)addListener:(id)listener { + PFWeakValue *value = [PFWeakValue valueWithWeakObject:listener]; + dispatch_barrier_sync(_synchronizationQueue, ^{ + [_listenersArray addObject:value]; + }); +} + +- (void)removeListener:(id)listener { + dispatch_barrier_sync(_synchronizationQueue, ^{ + [_listenersArray filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + id weakObject = [evaluatedObject weakObject]; + return (weakObject == nil || weakObject == listener); + }]]; + }); +} + +- (void)removeAllListeners { + dispatch_barrier_sync(_synchronizationQueue, ^{ + [_listenersArray removeAllObjects]; + }); +} + +- (void)_notifyAllListeners { + @weakify(self); + dispatch_async(_synchronizationQueue, ^{ + @strongify(self); + PFReachabilityState state = [[self class] _reachabilityStateForFlags:_flags]; + for (PFWeakValue *value in _listenersArray) { + [value.weakObject reachability:self didChangeReachabilityState:state]; + } + + dispatch_barrier_async(_synchronizationQueue, ^{ + [_listenersArray filterUsingPredicate:[NSPredicate predicateWithFormat:@"SELf.weakObject != nil"]]; + }); + }); +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setFlags:(SCNetworkReachabilityFlags)flags { + dispatch_barrier_async(_synchronizationQueue, ^{ + _flags = flags; + [self _notifyAllListeners]; + }); +} + +- (SCNetworkReachabilityFlags)flags { + __block SCNetworkReachabilityFlags flags; + dispatch_sync(_synchronizationQueue, ^{ + flags = _flags; + }); + return flags; +} + +- (PFReachabilityState)currentState { + return [[self class] _reachabilityStateForFlags:self.flags]; +} + +///-------------------------------------- +#pragma mark - Reachability +///-------------------------------------- + +- (void)_startMonitoringReachabilityWithURL:(NSURL *)url { + dispatch_barrier_async(_synchronizationQueue, ^{ + _networkReachability = SCNetworkReachabilityCreateWithName(NULL, [[url host] UTF8String]); + if (_networkReachability != NULL) { + // Set the initial flags + SCNetworkReachabilityFlags flags; + SCNetworkReachabilityGetFlags(_networkReachability, &flags); + self.flags = flags; + + // Set up notification for changes in reachability. + SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + if (SCNetworkReachabilitySetCallback(_networkReachability, _reachabilityCallback, &context)) { + if (!SCNetworkReachabilitySetDispatchQueue(_networkReachability, _synchronizationQueue)) { + PFLogError(PFLoggingTagCommon, @"Unable to start listening for network connectivity status."); + } + } + } + }); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFTaskQueue.h b/Pods/Parse/Parse/Internal/PFTaskQueue.h new file mode 100644 index 0000000..c2a9a3e --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFTaskQueue.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +@interface PFTaskQueue : NSObject + +// The lock for this task queue. +@property (nonatomic, strong, readonly) NSObject *mutex; + +/*! + Enqueues a task created by the given block. Then block is given a task to + await once state is snapshotted (e.g. after capturing session tokens at the + time of the save call. Awaiting this task will wait for the created task's + turn in the queue. + */ +- (BFTask *)enqueue:(BFTask *(^)(BFTask *toAwait))taskStart; + +@end diff --git a/Pods/Parse/Parse/Internal/PFTaskQueue.m b/Pods/Parse/Parse/Internal/PFTaskQueue.m new file mode 100644 index 0000000..fb6d111 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFTaskQueue.m @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFTaskQueue.h" + +#import + +@interface PFTaskQueue () + +@property (nonatomic, strong, readwrite) BFTask *tail; +@property (nonatomic, strong, readwrite) NSObject *mutex; + +@end + +@implementation PFTaskQueue + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + self.mutex = [[NSObject alloc] init]; + + return self; +} + +- (BFTask *)enqueue:(BFTask *(^)(BFTask *toAwait))taskStart { + @synchronized (self.mutex) { + BFTask *oldTail = self.tail ?: [BFTask taskWithResult:nil]; + + // The task created by taskStart is responsible for waiting on the + // task passed to it before doing its work. This gives it an opportunity + // to do startup work or save state before waiting for its turn in the queue. + BFTask *task = taskStart(oldTail); + + // The tail task should be dependent on the old tail as well as the newly-created + // task. This prevents cancellation of the new task from causing the queue to run + // out of order. + self.tail = [BFTask taskForCompletionOfAllTasks:@[oldTail, task]]; + + return task; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/PFWeakValue.h b/Pods/Parse/Parse/Internal/PFWeakValue.h new file mode 100644 index 0000000..2ff1e47 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFWeakValue.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFWeakValue : NSObject + +@property (nonatomic, weak, readonly) id weakObject; + ++ (instancetype)valueWithWeakObject:(id)object; + +@end diff --git a/Pods/Parse/Parse/Internal/PFWeakValue.m b/Pods/Parse/Parse/Internal/PFWeakValue.m new file mode 100644 index 0000000..a129202 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PFWeakValue.m @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFWeakValue.h" + +@interface PFWeakValue () + +@property (nonatomic, weak, readwrite) id weakObject; + +@end + +@implementation PFWeakValue + ++ (instancetype)valueWithWeakObject:(id)object { + PFWeakValue *value = [[self alloc] init]; + value.weakObject = object; + return value; +} + +@end diff --git a/Pods/Parse/Parse/Internal/ParseInternal.h b/Pods/Parse/Parse/Internal/ParseInternal.h new file mode 100644 index 0000000..58e17fc --- /dev/null +++ b/Pods/Parse/Parse/Internal/ParseInternal.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +# import + +#import "PFAssert.h" +#import "PFCommandCache.h" +#import "PFEventuallyQueue.h" +#import "PFFieldOperation.h" +#import "PFGeoPointPrivate.h" +#import "PFInternalUtils.h" +#import "PFKeyValueCache.h" +#import "PFObjectPrivate.h" +#import "PFUserPrivate.h" +#import "ParseModule.h" + +@interface Parse (ParseModules) + ++ (void)enableParseModule:(id)module; ++ (void)disableParseModule:(id)module; ++ (BOOL)isModuleEnabled:(id)module; + +@end diff --git a/Pods/Parse/Parse/Internal/ParseManager.h b/Pods/Parse/Parse/Internal/ParseManager.h new file mode 100644 index 0000000..67328ab --- /dev/null +++ b/Pods/Parse/Parse/Internal/ParseManager.h @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" +#import "PFOfflineStore.h" +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFAnalyticsController; +@class PFCoreManager; +@class PFInstallationIdentifierStore; +@class PFKeychainStore; +@class PFPurchaseController; +@class PFPushManager; + +@interface ParseManager : NSObject + +@property (nonatomic, copy, readonly) NSString *applicationId; +@property (nonatomic, copy, readonly) NSString *clientKey; + +@property (nonatomic, copy, readonly) NSString *applicationGroupIdentifier; +@property (nonatomic, copy, readonly) NSString *containingApplicationIdentifier; + +@property (nonatomic, strong, readonly) PFCoreManager *coreManager; +@property (nonatomic, strong) PFPushManager *pushManager; + +@property (nonatomic, strong) PFAnalyticsController *analyticsController; + +#if TARGET_OS_IOS +@property (nonatomic, strong) PFPurchaseController *purchaseController; +#endif + +///-------------------------------------- +/// @name Initialization +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; + +/*! + Initializes an instance of ParseManager class. + + @param applicationId ApplicationId of Parse app. + @param clientKey ClientKey of Parse app. + + @returns `ParseManager` instance. + */ +- (instancetype)initWithApplicationId:(NSString *)applicationId + clientKey:(NSString *)clientKey NS_DESIGNATED_INITIALIZER; + +/*! + Configures ParseManager with specified properties. + + @param applicationGroupIdentifier Shared AppGroup container identifier. + @param containingApplicationIdentifier Containg application bundle identifier (for extensions). + @param localDataStoreEnabled `BOOL` flag to enable local datastore or not. + */ +- (void)configureWithApplicationGroupIdentifier:(NSString *)applicationGroupIdentifier + containingApplicationIdentifier:(NSString *)containingApplicationIdentifier + enabledLocalDataStore:(BOOL)localDataStoreEnabled; + +///-------------------------------------- +/// @name Offline Store +///-------------------------------------- + +- (void)loadOfflineStoreWithOptions:(PFOfflineStoreOptions)options; + +///-------------------------------------- +/// @name Eventually Queue +///-------------------------------------- + +- (void)clearEventuallyQueue; + +///-------------------------------------- +/// @name Core Manager +///-------------------------------------- + +- (void)unloadCoreManager; + +///-------------------------------------- +/// @name Preloading +///-------------------------------------- + +- (BFTask *)preloadDiskObjectsToMemoryAsync; + +@end diff --git a/Pods/Parse/Parse/Internal/ParseManager.m b/Pods/Parse/Parse/Internal/ParseManager.m new file mode 100644 index 0000000..74756c3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ParseManager.m @@ -0,0 +1,466 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ParseManager.h" + +#import + +#import "BFTask+Private.h" +#import "PFAnalyticsController.h" +#import "PFAssert.h" +#import "PFCommandCache.h" +#import "PFConfig.h" +#import "PFCoreManager.h" +#import "PFFileManager.h" +#import "PFInstallationIdentifierStore.h" +#import "PFKeyValueCache.h" +#import "PFKeychainStore.h" +#import "PFLogging.h" +#import "PFMultiProcessFileLockController.h" +#import "PFPinningEventuallyQueue.h" +#import "PFPushManager.h" +#import "PFUser.h" +#import "PFURLSessionCommandRunner.h" + +#if !TARGET_OS_WATCH +#import "PFInstallation.h" +#endif + +#if TARGET_OS_IOS +#import "PFPurchaseController.h" +#import "PFProduct.h" +#endif + +static NSString *const _ParseApplicationIdFileName = @"applicationId"; + +@interface ParseManager () +{ + dispatch_queue_t _offlineStoreAccessQueue; + dispatch_queue_t _eventuallyQueueAccessQueue; + dispatch_queue_t _keychainStoreAccessQueue; + dispatch_queue_t _fileManagerAccessQueue; + dispatch_queue_t _installationIdentifierStoreAccessQueue; + dispatch_queue_t _commandRunnerAccessQueue; + dispatch_queue_t _keyValueCacheAccessQueue; + dispatch_queue_t _coreManagerAccessQueue; + dispatch_queue_t _pushManagerAccessQueue; + dispatch_queue_t _controllerAccessQueue; + + dispatch_queue_t _preloadQueue; +} + +@end + +@implementation ParseManager + +@synthesize keychainStore = _keychainStore; +@synthesize fileManager = _fileManager; +@synthesize offlineStore = _offlineStore; +@synthesize eventuallyQueue = _eventuallyQueue; +@synthesize installationIdentifierStore = _installationIdentifierStore; +@synthesize commandRunner = _commandRunner; +@synthesize keyValueCache = _keyValueCache; +@synthesize coreManager = _coreManager; +@synthesize analyticsController = _analyticsController; +@synthesize pushManager = _pushManager; +#if TARGET_OS_IOS +@synthesize purchaseController = _purchaseController; +#endif + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithApplicationId:(NSString *)applicationId clientKey:(NSString *)clientKey { + self = [super init]; + if (!self) return nil; + + _offlineStoreAccessQueue = dispatch_queue_create("com.parse.offlinestore.access", DISPATCH_QUEUE_CONCURRENT); + _eventuallyQueueAccessQueue = dispatch_queue_create("com.parse.eventuallyqueue.access", DISPATCH_QUEUE_SERIAL); + _keychainStoreAccessQueue = dispatch_queue_create("com.parse.keychainstore.access", DISPATCH_QUEUE_SERIAL); + _fileManagerAccessQueue = dispatch_queue_create("com.parse.filemanager.access", DISPATCH_QUEUE_SERIAL); + _installationIdentifierStoreAccessQueue = dispatch_queue_create("com.parse.installationidentifierstore.access", + DISPATCH_QUEUE_SERIAL); + _commandRunnerAccessQueue = dispatch_queue_create("com.parse.commandrunner.access", DISPATCH_QUEUE_SERIAL); + _keyValueCacheAccessQueue = dispatch_queue_create("com.parse.keyvaluecache.access", DISPATCH_QUEUE_SERIAL); + _coreManagerAccessQueue = dispatch_queue_create("com.parse.coreManager.access", DISPATCH_QUEUE_SERIAL); + _pushManagerAccessQueue = dispatch_queue_create("com.parse.pushManager.access", DISPATCH_QUEUE_SERIAL); + _controllerAccessQueue = dispatch_queue_create("com.parse.controller.access", DISPATCH_QUEUE_SERIAL); + _preloadQueue = dispatch_queue_create("com.parse.preload", DISPATCH_QUEUE_SERIAL); + + _applicationId = [applicationId copy]; + _clientKey = [clientKey copy]; + + return self; +} + +- (void)configureWithApplicationGroupIdentifier:(NSString *)applicationGroupIdentifier + containingApplicationIdentifier:(NSString *)containingApplicationIdentifier + enabledLocalDataStore:(BOOL)localDataStoreEnabled { + _applicationGroupIdentifier = [applicationGroupIdentifier copy]; + _containingApplicationIdentifier = [containingApplicationIdentifier copy]; + + // Migrate any data if it's required. + [self _migrateSandboxDataToApplicationGroupContainerIfNeeded]; + + // Make sure the data on disk for Parse is for the current application. + [self _checkApplicationId]; + + if (localDataStoreEnabled) { + PFOfflineStoreOptions options = (_applicationGroupIdentifier ? + PFOfflineStoreOptionAlwaysFetchFromSQLite : 0); + [self loadOfflineStoreWithOptions:options]; + } +} + +///-------------------------------------- +#pragma mark - Offline Store +///-------------------------------------- + +- (void)loadOfflineStoreWithOptions:(PFOfflineStoreOptions)options { + PFConsistencyAssert(!_offlineStore, @"Can't load offline store more than once."); + dispatch_barrier_sync(_offlineStoreAccessQueue, ^{ + _offlineStore = [[PFOfflineStore alloc] initWithFileManager:self.fileManager options:options]; + }); +} + +- (void)setOfflineStore:(PFOfflineStore *)offlineStore { + dispatch_barrier_sync(_offlineStoreAccessQueue, ^{ + _offlineStore = offlineStore; + }); +} + +- (PFOfflineStore *)offlineStore { + __block PFOfflineStore *offlineStore = nil; + dispatch_sync(_offlineStoreAccessQueue, ^{ + offlineStore = _offlineStore; + }); + return offlineStore; +} + +- (BOOL)isOfflineStoreLoaded { + return (self.offlineStore != nil); +} + +///-------------------------------------- +#pragma mark - Eventually Queue +///-------------------------------------- + +- (PFEventuallyQueue *)eventuallyQueue { + __block PFEventuallyQueue *queue = nil; + dispatch_sync(_eventuallyQueueAccessQueue, ^{ + if (!_eventuallyQueue || + (self.offlineStoreLoaded && [_eventuallyQueue isKindOfClass:[PFCommandCache class]]) || + (!self.offlineStoreLoaded && [_eventuallyQueue isKindOfClass:[PFPinningEventuallyQueue class]])) { + + id commandRunner = self.commandRunner; + + PFCommandCache *commandCache = [self _newCommandCache]; + _eventuallyQueue = (self.offlineStoreLoaded ? + [PFPinningEventuallyQueue newDefaultPinningEventuallyQueueWithCommandRunner:commandRunner] + : + commandCache); + + // We still need to clear out the old command cache even if we're using Pinning in case + // anything is left over when the user upgraded. Checking number of pending and then + // clearing should be enough. + if (self.offlineStoreLoaded && commandCache.commandCount > 0) { + [commandCache removeAllCommands]; + } + } + queue = _eventuallyQueue; + }); + return queue; +} + +- (PFCommandCache *)_newCommandCache { + // Construct the path to the cache directory in /Library/Private Documents/Parse/Command Cache + // This isn't in the "Library/Caches" directory because we don't want the OS clearing it for us. + // It falls under the category of "offline data". + // See https://developer.apple.com/library/ios/#qa/qa1699/_index.html + NSString *folderPath = [self.fileManager parseDefaultDataDirectoryPath]; + return [PFCommandCache newDefaultCommandCacheWithCommandRunner:self.commandRunner cacheFolderPath:folderPath]; +} + +- (void)clearEventuallyQueue { + dispatch_sync(_preloadQueue, ^{ + dispatch_sync(_eventuallyQueueAccessQueue, ^{ + [_eventuallyQueue removeAllCommands]; + [_eventuallyQueue pause]; + _eventuallyQueue = nil; + }); + }); +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +#pragma mark KeychainStore + +- (PFKeychainStore *)keychainStore { + __block PFKeychainStore *store = nil; + dispatch_sync(_keychainStoreAccessQueue, ^{ + if (!_keychainStore) { + NSString *bundleIdentifier = (_containingApplicationIdentifier ?: [[NSBundle mainBundle] bundleIdentifier]); + NSString *service = [NSString stringWithFormat:@"%@.%@", bundleIdentifier, PFKeychainStoreDefaultService]; + _keychainStore = [[PFKeychainStore alloc] initWithService:service]; + } + store = _keychainStore; + }); + return store; +} + +#pragma mark FileManager + +- (PFFileManager *)fileManager { + __block PFFileManager *fileManager = nil; + dispatch_sync(_fileManagerAccessQueue, ^{ + if (!_fileManager) { + _fileManager = [[PFFileManager alloc] initWithApplicationIdentifier:self.applicationId + applicationGroupIdentifier:self.applicationGroupIdentifier]; + } + fileManager = _fileManager; + }); + return fileManager; +} + +#pragma mark InstallationIdentifierStore + +- (PFInstallationIdentifierStore *)installationIdentifierStore { + __block PFInstallationIdentifierStore *store = nil; + dispatch_sync(_installationIdentifierStoreAccessQueue, ^{ + if (!_installationIdentifierStore) { + _installationIdentifierStore = [[PFInstallationIdentifierStore alloc] initWithFileManager:self.fileManager]; + } + store = _installationIdentifierStore; + }); + return store; +} + +#pragma mark CommandRunner + +- (id)commandRunner { + __block id runner = nil; + dispatch_sync(_commandRunnerAccessQueue, ^{ + if (!_commandRunner) { + _commandRunner = [PFURLSessionCommandRunner commandRunnerWithDataSource:self + applicationId:self.applicationId + clientKey:self.clientKey]; + } + runner = _commandRunner; + }); + return runner; +} + +#pragma mark KeyValueCache + +- (PFKeyValueCache *)keyValueCache { + __block PFKeyValueCache *cache = nil; + dispatch_sync(_keyValueCacheAccessQueue, ^{ + if (!_keyValueCache) { + NSString *path = [self.fileManager parseCacheItemPathForPathComponent:@"../ParseKeyValueCache/"]; + _keyValueCache = [[PFKeyValueCache alloc] initWithCacheDirectoryPath:path]; + } + cache = _keyValueCache; + }); + return cache; +} + +#pragma mark CoreManager + +- (PFCoreManager *)coreManager { + __block PFCoreManager *manager = nil; + dispatch_sync(_coreManagerAccessQueue, ^{ + if (!_coreManager) { + _coreManager = [PFCoreManager managerWithDataSource:self]; + } + manager = _coreManager; + }); + return manager; +} + +- (void)unloadCoreManager { + dispatch_sync(_coreManagerAccessQueue, ^{ + _coreManager = nil; + }); +} + +#if !TARGET_OS_WATCH + +#pragma mark PushManager + +- (PFPushManager *)pushManager { + __block PFPushManager *manager = nil; + dispatch_sync(_pushManagerAccessQueue, ^{ + if (!_pushManager) { + _pushManager = [PFPushManager managerWithCommonDataSource:self coreDataSource:self.coreManager]; + } + manager = _pushManager; + }); + return manager; +} + +- (void)setPushManager:(PFPushManager *)pushManager { + dispatch_sync(_pushManagerAccessQueue, ^{ + _pushManager = pushManager; + }); +} + +#endif + +#pragma mark AnalyticsController + +- (PFAnalyticsController *)analyticsController { + __block PFAnalyticsController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_analyticsController) { + _analyticsController = [[PFAnalyticsController alloc] initWithDataSource:self]; + } + controller = _analyticsController; + }); + return controller; +} + +- (void)setAnalyticsController:(PFAnalyticsController *)analyticsController { + dispatch_sync(_controllerAccessQueue, ^{ + if (_analyticsController != analyticsController) { + _analyticsController = analyticsController; + } + }); +} + +#if TARGET_OS_IOS + +#pragma mark PurchaseController + +- (PFPurchaseController *)purchaseController { + __block PFPurchaseController *controller = nil; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_purchaseController) { + _purchaseController = [PFPurchaseController controllerWithCommandRunner:self.commandRunner + fileManager:self.fileManager + bundle:[NSBundle mainBundle]]; + } + controller = _purchaseController; + }); + return controller; +} + +- (void)setPurchaseController:(PFPurchaseController *)purchaseController { + dispatch_sync(_controllerAccessQueue, ^{ + _purchaseController = purchaseController; + }); +} + +#endif + +///-------------------------------------- +#pragma mark - Preloading +///-------------------------------------- + +- (BFTask *)preloadDiskObjectsToMemoryAsync { + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor executorWithDispatchQueue:_preloadQueue] withBlock:^id{ + @strongify(self); + [PFUser currentUser]; + [PFConfig currentConfig]; +#if !TARGET_OS_WATCH + [PFInstallation currentInstallation]; +#endif + + [self eventuallyQueue]; + + return nil; + }]; +} + +///-------------------------------------- +#pragma mark - ApplicationId +///-------------------------------------- + +/*! + Verifies that the data stored on disk for Parse was generated using the same application that is running now. + */ +- (void)_checkApplicationId { + NSFileManager *systemFileManager = [NSFileManager defaultManager]; + + // Make sure the current version of the cache is for this application id. + NSString *applicationIdFile = [self.fileManager parseDataItemPathForPathComponent:_ParseApplicationIdFileName]; + [[PFMultiProcessFileLockController sharedController] beginLockedContentAccessForFileAtPath:applicationIdFile]; + + if ([systemFileManager fileExistsAtPath:applicationIdFile]) { + NSError *error = nil; + NSString *applicationId = [NSString stringWithContentsOfFile:applicationIdFile + encoding:NSUTF8StringEncoding + error:&error]; + if (!error && ![applicationId isEqualToString:self.applicationId]) { + // The application id has changed, so everything on disk is invalid. + [self.keychainStore removeAllObjects]; + [self.keyValueCache removeAllObjects]; + + NSArray *tasks = @[ + // Remove the contents only, but don't delete the folder. + [PFFileManager removeDirectoryContentsAsyncAtPath:[self.fileManager parseDefaultDataDirectoryPath]], + // Completely remove everything in deprecated folder. + [PFFileManager removeItemAtPathAsync:[self.fileManager parseDataDirectoryPath_DEPRECATED]] + ]; + [[BFTask taskForCompletionOfAllTasks:tasks] waitForResult:nil withMainThreadWarning:NO]; + } + } + + if (![systemFileManager fileExistsAtPath:applicationIdFile]) { + NSError *error = nil; + BFTask *writeTask = [PFFileManager writeStringAsync:self.applicationId toFile:applicationIdFile]; + [writeTask waitForResult:&error withMainThreadWarning:NO]; + if (error) { + PFLogError(PFLoggingTagCommon, @"Unable to create applicationId file with error: %@", error); + } + } + + [[PFMultiProcessFileLockController sharedController] endLockedContentAccessForFileAtPath:applicationIdFile]; +} + +///-------------------------------------- +#pragma mark - Data Sharing +///-------------------------------------- + +- (void)_migrateSandboxDataToApplicationGroupContainerIfNeeded { + // There is no need to migrate anything on OSX, since we are using globally available folder. +#if TARGET_OS_IOS + // Do nothing if there is no application group container or containing application is specified. + if (!self.applicationGroupIdentifier || self.containingApplicationIdentifier) { + return; + } + + NSString *localSandboxDataPath = [self.fileManager parseLocalSandboxDataDirectoryPath]; + NSString *dataPath = [self.fileManager parseDefaultDataDirectoryPath]; + NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:localSandboxDataPath error:nil]; + if ([contents count] != 0) { + // If moving files fails - just log the error, but don't fail. + NSError *error = nil; + [[PFFileManager moveContentsOfDirectoryAsyncAtPath:localSandboxDataPath + toDirectoryAtPath:dataPath + executor:[BFExecutor immediateExecutor]] waitForResult:&error]; + if (error) { + PFLogError(PFLoggingTagCommon, + @"Failed to migrate local sandbox data to shared container with error %@", + [error localizedDescription]); + } else { + [[PFFileManager removeItemAtPathAsync:localSandboxDataPath withFileLock:NO] waitForResult:nil]; + } + } +#endif +} + +@end diff --git a/Pods/Parse/Parse/Internal/ParseModule.h b/Pods/Parse/Parse/Internal/ParseModule.h new file mode 100644 index 0000000..a9a1a49 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ParseModule.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@protocol ParseModule + +- (void)parseDidInitializeWithApplicationId:(NSString *)applicationId clientKey:(NSString *)clientKey; + +@end + +@interface ParseModuleCollection : NSObject + +- (void)addParseModule:(id)module; +- (void)removeParseModule:(id)module; + +- (BOOL)containsModule:(id)module; +- (NSUInteger)modulesCount; + +@end diff --git a/Pods/Parse/Parse/Internal/ParseModule.m b/Pods/Parse/Parse/Internal/ParseModule.m new file mode 100644 index 0000000..5167762 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ParseModule.m @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ParseModule.h" + +///-------------------------------------- +#pragma mark - ParseModuleCollection +///-------------------------------------- + +typedef void (^ParseModuleEnumerationBlock)(id module, BOOL *stop, BOOL *remove); + +@interface ParseModuleCollection () + +@property (atomic, strong) dispatch_queue_t collectionQueue; +@property (atomic, strong) NSPointerArray *modules; + +@end + +@implementation ParseModuleCollection + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (self) { + _collectionQueue = dispatch_queue_create("com.parse.ParseModuleCollection", DISPATCH_QUEUE_SERIAL); + _modules = [NSPointerArray weakObjectsPointerArray]; + } + return self; +} + +///-------------------------------------- +#pragma mark - Collection +///-------------------------------------- + +- (void)addParseModule:(id)module { + if (module == nil) { + return; + } + + [self performCollectionAccessBlock:^{ + [self.modules addPointer:(__bridge void *)module]; + }]; +} + +- (void)removeParseModule:(id)module { + if (module == nil) { + return; + } + + [self enumerateModulesWithBlock:^(id enumeratedModule, BOOL *stop, BOOL *remove) { + *remove = (module == enumeratedModule); + }]; +} + +- (BOOL)containsModule:(id)module { + __block BOOL retValue = NO; + [self enumerateModulesWithBlock:^(id enumeratedModule, BOOL *stop, BOOL *remove) { + if (module == enumeratedModule) { + retValue = YES; + *stop = YES; + } + }]; + return retValue; +} + +- (NSUInteger)modulesCount { + return [self.modules count]; +} + +///-------------------------------------- +#pragma mark - ParseModule +///-------------------------------------- + +- (void)parseDidInitializeWithApplicationId:(NSString *)applicationId clientKey:(NSString *)clientKey { + [self enumerateModulesWithBlock:^(id module, BOOL *stop, BOOL *remove) { + dispatch_async(dispatch_get_main_queue(), ^{ + [module parseDidInitializeWithApplicationId:applicationId clientKey:clientKey]; + }); + }]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (void)performCollectionAccessBlock:(dispatch_block_t)block { + dispatch_sync(self.collectionQueue, block); +} + +/*! + Enumerates all existing modules in this collection. + + NOTE: This **will modify the contents of the collection** if any of the modules were deallocated since last loop. + + @param block the block to enumerate with. + */ +- (void)enumerateModulesWithBlock:(ParseModuleEnumerationBlock)block { + [self performCollectionAccessBlock:^{ + NSMutableIndexSet *toRemove = [[NSMutableIndexSet alloc] init]; + + NSUInteger index = 0; + BOOL stop = NO; + + for (__strong id module in self.modules) { + BOOL remove = module == nil; + if (!remove) { + block(module, &stop, &remove); + } + + if (remove) { + [toRemove addIndex:index]; + } + + if (stop) break; + index++; + } + + // NSPointerArray doesn't have a -removeObjectsAtIndexes:... WHY!?!? + for (index = toRemove.firstIndex; index != NSNotFound; index = [toRemove indexGreaterThanIndex:index]) { + [self.modules removePointerAtIndex:index]; + } + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Parse_Private.h b/Pods/Parse/Parse/Internal/Parse_Private.h new file mode 100644 index 0000000..176dd1f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Parse_Private.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "ParseManager.h" + +@class PFEventuallyQueue; + +@interface Parse () + ++ (void)_resetDataSharingIdentifiers; + ++ (ParseManager *)_currentManager; ++ (void)_clearCurrentManager; + +@end diff --git a/Pods/Parse/Parse/Internal/Product/PFProduct+Private.h b/Pods/Parse/Parse/Internal/Product/PFProduct+Private.h new file mode 100644 index 0000000..af69e41 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Product/PFProduct+Private.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFProduct.h" + +typedef enum { + PFProductDownloadStateStart, + PFProductDownloadStateDownloading, + PFProductDownloadStateDownloaded +} PFProductDownloadState; + +@interface PFProduct () { + NSDecimalNumber *price; + NSLocale *priceLocale; + NSInteger progress; + NSString *contentPath; +} + +/// The properties below are transient properties, not stored on Parse's server. +/*! + The price of the product, discovered via iTunes Connect. + */ +@property (nonatomic, strong) NSDecimalNumber *price; + +/*! + The price locale of the product. + */ +@property (nonatomic, strong) NSLocale *priceLocale; + +/*! + The progress of the download, if one is in progress. It's an integer between 0 and 100. + */ +@property (nonatomic, assign) NSInteger progress; + +/*! + The content path of the download. + */ +@property (nonatomic, strong) NSString *contentPath; + +@end diff --git a/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.h b/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.h new file mode 100644 index 0000000..2c9c3da --- /dev/null +++ b/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +PF_WATCH_UNAVAILABLE @interface PFProductsRequestResult : NSObject + +@property (nonatomic, copy, readonly) NSSet *validProducts; +@property (nonatomic, copy, readonly) NSSet *invalidProductIdentifiers; + +- (instancetype)initWithProductsResponse:(SKProductsResponse *)response; + +@end + +/*! + * This class is responsible for handling the first part of an IAP handshake. + * It sends a request to iTunes Connect with a set of product identifiers, and iTunes returns + * with a list of valid and invalid products. The class then proceeds to call the completion block passed in. + */ +@interface PFProductsRequestHandler : NSObject + +- (instancetype)initWithProductsRequest:(SKProductsRequest *)request; + +- (BFTask *)findProductsAsync; + +@end diff --git a/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.m b/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.m new file mode 100644 index 0000000..4dbce94 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.m @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFProductsRequestHandler.h" + +#import +#import + +@implementation PFProductsRequestResult + +- (instancetype)initWithProductsResponse:(SKProductsResponse *)response { + self = [super init]; + if (!self) return nil; + + _validProducts = [NSSet setWithArray:response.products]; + _invalidProductIdentifiers = [NSSet setWithArray:response.invalidProductIdentifiers]; + + return self; +} + +@end + +@interface PFProductsRequestHandler () + +@property (nonatomic, strong) BFTaskCompletionSource *taskCompletionSource; +@property (nonatomic, strong) SKProductsRequest *productsRequest; + +@end + +@implementation PFProductsRequestHandler + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithProductsRequest:(SKProductsRequest *)request { + self = [super init]; + if (!self) return nil; + + _productsRequest = request; + _productsRequest.delegate = self; + + return self; +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + // Clear the delegate, as it's still an `assign`, instead of `weak` + _productsRequest.delegate = nil; +} + +///-------------------------------------- +#pragma mark - Find +///-------------------------------------- + +- (BFTask *)findProductsAsync { + _taskCompletionSource = [BFTaskCompletionSource taskCompletionSource]; + [_productsRequest start]; + return _taskCompletionSource.task; +} + +///-------------------------------------- +#pragma mark - SKProductsRequestDelegate +///-------------------------------------- + +- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { + PFProductsRequestResult *result = [[PFProductsRequestResult alloc] initWithProductsResponse:response]; + [self.taskCompletionSource trySetResult:result]; +} + +- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { + [self.taskCompletionSource trySetError:error]; + + // according to documentation, this method does not call requestDidFinish + request.delegate = nil; +} + +- (void)requestDidFinish:(SKRequest *)request { + // the documentation assures that this is the point safe to get rid of the request + request.delegate = nil; +} + +@end diff --git a/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.h b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.h new file mode 100644 index 0000000..efd4c17 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFBaseState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFPropertyInfo : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithClass:(Class)kls + name:(NSString *)propertyName; + +- (instancetype)initWithClass:(Class)kls + name:(NSString *)propertyName + associationType:(PFPropertyInfoAssociationType)associationType NS_DESIGNATED_INITIALIZER; + ++ (instancetype)propertyInfoWithClass:(Class)kls + name:(NSString *)propertyName; + ++ (instancetype)propertyInfoWithClass:(Class)kls + name:(NSString *)propertyName + associationType:(PFPropertyInfoAssociationType)associationType; + +@property (nonatomic, copy, readonly) NSString *name; +@property (nonatomic, readonly) PFPropertyInfoAssociationType associationType; + +/*! + Returns the value of this property, + properly wrapped from the target object. + When possible, just invokes the property. + When not, uses -valueForKey:. + */ +- (nullable id)getWrappedValueFrom:(id)object; +- (void)setWrappedValue:(nullable id)value forObject:(id)object; + +// Moves the value from one object to the other, based on the association type given. +- (void)takeValueFrom:(id)one toObject:(id)two; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.m b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.m new file mode 100644 index 0000000..9d1273c --- /dev/null +++ b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo.m @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPropertyInfo_Private.h" + +#import + +#import "PFAssert.h" +#import "PFMacros.h" +#import "PFPropertyInfo_Runtime.h" + +static inline NSString *safeStringWithPropertyAttributeValue(objc_property_t property, const char *attribute) { + char *value = property_copyAttributeValue(property, attribute); + if (!value) + return nil; + + // NSString initWithBytesNoCopy doesn't seem to work, so fall back to the CF counterpart. + return (__bridge_transfer NSString *)CFStringCreateWithCStringNoCopy(NULL, + value, + kCFStringEncodingUTF8, + kCFAllocatorMalloc); +} + +static inline NSString *stringByCapitalizingFirstCharacter(NSString *string) { + return [NSString stringWithFormat:@"%C%@", + (unichar)toupper([string characterAtIndex:0]), + [string substringFromIndex:1]]; +} + +@implementation PFPropertyInfo + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithClass:(Class)kls name:(NSString *)propertyName { + return [self initWithClass:kls name:propertyName associationType:_associationType]; +} + +- (instancetype)initWithClass:(Class)kls name:(NSString *)propertyName + associationType:(PFPropertyInfoAssociationType)associationType { + self = [super init]; + if (!self) return nil; + + _sourceClass = kls; + _name = [propertyName copy]; + _associationType = associationType; + + objc_property_t objcProperty = class_getProperty(kls, [_name UTF8String]); + + do { + _ivar = class_getInstanceVariable(kls, [safeStringWithPropertyAttributeValue(objcProperty, "V") UTF8String]); + if (_ivar) break; + + // Walk the superclass heirarchy for the property definition. Because property attributes are not inherited + // (but property definitions *are*), we must be careful to ensure that the variable was never actually + // implemented and synthesized in a superclass. Note if the same property is synthesized in multiple classes + // with different iVars, we take the class furthest from the root class as the 'source of truth'. + Class superClass = class_getSuperclass(kls); + while (superClass) { + objc_property_t superProperty = class_getProperty(superClass, [_name UTF8String]); + if (!superProperty) break; + + _ivar = class_getInstanceVariable(superClass, [safeStringWithPropertyAttributeValue(superProperty, "V") UTF8String]); + if (_ivar) break; + + superClass = class_getSuperclass(superClass); + } + + if (_ivar) break; + + // Attempt to infer the variable name. + _ivar = class_getInstanceVariable(kls, [[@"_" stringByAppendingString:_name] UTF8String]); + if (_ivar) break; + + _ivar = class_getInstanceVariable(kls, [_name UTF8String]); + } while (0); + + _typeEncoding = safeStringWithPropertyAttributeValue(objcProperty, "T"); + _object = [_typeEncoding hasPrefix:@"@"]; + + NSString *propertyGetter = safeStringWithPropertyAttributeValue(objcProperty, "G") ?: _name; + _getterSelector = NSSelectorFromString(propertyGetter); + + BOOL readonly = safeStringWithPropertyAttributeValue(objcProperty, "R") != nil; + NSString *propertySetter = safeStringWithPropertyAttributeValue(objcProperty, "S"); + if (propertySetter == nil && !readonly) { + propertySetter = [NSString stringWithFormat:@"set%@:", stringByCapitalizingFirstCharacter(_name)]; + } + + _setterSelector = NSSelectorFromString(propertySetter); + + if (_associationType == PFPropertyInfoAssociationTypeDefault) { + BOOL isCopy = safeStringWithPropertyAttributeValue(objcProperty, "C") != nil; + BOOL isWeak = safeStringWithPropertyAttributeValue(objcProperty, "W") != nil; + BOOL isRetain = safeStringWithPropertyAttributeValue(objcProperty, "&") != nil; + + if (isWeak) { + _associationType = PFPropertyInfoAssociationTypeWeak; + } else if (isCopy) { + _associationType = PFPropertyInfoAssociationTypeCopy; + } else if (isRetain) { + _associationType = PFPropertyInfoAssociationTypeStrong; + } else { + _associationType = PFPropertyInfoAssociationTypeAssign; + } + } + + return self; +} + ++ (instancetype)propertyInfoWithClass:(Class)kls name:(NSString *)propertyName { + return [[self alloc] initWithClass:kls name:propertyName]; +} + ++ (instancetype)propertyInfoWithClass:(Class)kls name:(NSString *)propertyName + associationType:(PFPropertyInfoAssociationType)associationType { + return [[self alloc] initWithClass:kls name:propertyName associationType:associationType]; +} + +///-------------------------------------- +#pragma mark - Wrapping +///-------------------------------------- + +- (id)getWrappedValueFrom:(id)object { + if (self.object) { + return objc_msgSend_safe(id)(object, self.getterSelector); + } + + return [object valueForKey:self.name]; +} + +- (void)setWrappedValue:(id)value forObject:(id)object { + if (self.object && self.setterSelector) { + objc_msgSend_safe(void, id)(object, self.setterSelector, value); + return; + } + + [object setValue:value forKey:self.name]; +} + +///-------------------------------------- +#pragma mark - Taking +///-------------------------------------- + +- (void)takeValueFrom:(id)one toObject:(id)two { + if (!self.ivar) { + id wrappedValue = [self getWrappedValueFrom:one]; + [self setWrappedValue:wrappedValue forObject:two]; + + return; + } + + NSUInteger size = 0; + NSGetSizeAndAlignment(ivar_getTypeEncoding(self.ivar), &size, NULL); + + char valuePtr[size]; + bzero(valuePtr, size); + + NSInvocation *invocation = nil; + + // TODO: (richardross) Cache the method signatures, as those are fairly slow to calculate. + if (one && [one respondsToSelector:self.getterSelector]) { + NSMethodSignature *methodSignature = [one methodSignatureForSelector:self.getterSelector]; + invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + + [invocation setTarget:one]; + [invocation setSelector:self.getterSelector]; + } + + [invocation invoke]; + [invocation getReturnValue:valuePtr]; + + object_setIvarValue_safe(two, self.ivar, valuePtr, self.associationType); +} + +///-------------------------------------- +#pragma mark - Equality +///-------------------------------------- + +- (NSUInteger)hash { + return 0; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[self class]]) { + return NO; + } + + PFPropertyInfo *other = object; + + // If they're the same property and one of them subclasses the other, do no further checking. + return [self.name isEqual:other.name] && + ([self.sourceClass isSubclassOfClass:other.sourceClass] || + [other.sourceClass isSubclassOfClass:self.sourceClass]); +} + +@end diff --git a/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Private.h b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Private.h new file mode 100644 index 0000000..db9ebea --- /dev/null +++ b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Private.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFPropertyInfo.h" + +@interface PFPropertyInfo () + +@property (atomic, assign, readonly) Class sourceClass; +@property (atomic, assign, readonly, getter=isObject) BOOL object; + +@property (atomic, copy, readonly) NSString *typeEncoding; +@property (atomic, assign, readonly) Ivar ivar; + +@property (atomic, assign, readonly) SEL getterSelector; +@property (atomic, assign, readonly) SEL setterSelector; + +@end diff --git a/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.h b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.h new file mode 100644 index 0000000..bc1b59e --- /dev/null +++ b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPropertyInfo.h" + +#import + +/*! + @abstract Safely sets an object's instance variable to the variable in the specified address. + @discussion The Objective-C runtime's built-in methods for setting instance variables (`object_setIvar`) and + (`object_setInstanceVariable`), are both terrible. They never read any more than a single pointer, so they + fail for structs, as well as 64 bit numbers on 32 bit platforms. Because of this, we need a solution to allow us to + safely set instance variable values whose sizes may be significantly more than a pointer. + + @note Like most Objective-C runtime methods, this WILL fail if you try and set a bitfield, so please don't do that. + + @param obj The object to operate on. + @param ivar The ivar to set the new value for. + @param fromMemory The **address** of the new value to set. + @param associationType The association type of the new value. One of PFPropertyInfoAssociationType. + */ +extern void object_setIvarValue_safe(__unsafe_unretained id obj, Ivar ivar, void *fromMemory, uint8_t associationType); + +/*! + @abstract Safely gets an object's instance variable and puts it into the specified address. + @discussion The Objective-C runtime's built-in methods for getting instance variables (`object_getIvar`) and + (`object_getInstanceVariable`), are both terrible. They never read any more than a single pointer, so they + fail for structs, as well as 64 bit numbers on 32 bit platforms. Because of this, we need a solution to allow us to + safely get instance variable values whose sizes may be significantly more than a pointer. + + @note Like most Objective-C runtime methods, this WILL fail if you try and set a bitfield, so please don't do that. + + @param obj The object to operate on. + @param ivar The ivar to get the value from. + @param toMemory The address to copy the value into. + @param associationType The assocation type of the new value. One of PFPrropertyInfoAssocationType. + */ +extern void object_getIvarValue_safe(__unsafe_unretained id obj, Ivar ivar, void *toMemory, uint8_t associationType); diff --git a/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.m b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.m new file mode 100644 index 0000000..a2071f1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.m @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPropertyInfo_Runtime.h" + +#import +#import + +/*! + This macro is really interesting. Because ARC will insert implicit retains, releases and other memory managment code + that we don't want here, we have to basically trick ARC into treating the functions we want as functions with type + `void *`. The way we do that is actually via the linker - instead of coming up with some crazy macro to forward all + arguments along to the correct function, especially when some of these functions aren't in any public headers. + + They are, however, well defined, according to the clang official ARC runtime support document: + http://clang.llvm.org/docs/AutomaticReferenceCounting.html#id55 + + That means this is unlikely to ever break. + + The weakref attribute is documented here: + https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes + + And we use this to make sure our type-invariant functions actually call the proper underlying ones. + */ +#define NO_TYPECHECK_SYMBOL(ret, fn, args...) static ret fn ## _noTypeCheck (args) __attribute__((weakref(#fn))); +#define OBJECT_GETOFFSET_PTR(obj, offset) (void *) ((uintptr_t)obj + offset) + +NO_TYPECHECK_SYMBOL(void *, objc_loadWeak, void **); + +NO_TYPECHECK_SYMBOL(void *, objc_storeWeak, void **, void *); +NO_TYPECHECK_SYMBOL(void *, objc_storeStrong, void **, void *); + +NO_TYPECHECK_SYMBOL(void *, objc_autorelease, void *); +NO_TYPECHECK_SYMBOL(void *, objc_retainAutorelease, void *); + +void object_getIvarValue_safe(__unsafe_unretained id obj, Ivar ivar, void *toMemory, uint8_t associationType) { + ptrdiff_t offset = ivar_getOffset(ivar); + void *location = OBJECT_GETOFFSET_PTR(obj, offset); + + switch (associationType) { + case PFPropertyInfoAssociationTypeDefault: + [NSException raise:NSInvalidArgumentException format:@"Invalid association type Default!"]; + break; + + case PFPropertyInfoAssociationTypeAssign: { + NSUInteger size = 0; + NSGetSizeAndAlignment(ivar_getTypeEncoding(ivar), &size, NULL); + + memcpy(toMemory, location, size); + break; + } + + case PFPropertyInfoAssociationTypeWeak: { + void *results = objc_loadWeak_noTypeCheck(location); + + memcpy(toMemory, &results, sizeof(id)); + break; + } + + case PFPropertyInfoAssociationTypeStrong: + case PFPropertyInfoAssociationTypeCopy: + case PFPropertyInfoAssociationTypeMutableCopy: { + void *objectValue = *(void **)location; + objectValue = objc_retainAutorelease_noTypeCheck(objectValue); + + memcpy(toMemory, &objectValue, sizeof(id)); + break; + } + } +} + +void object_setIvarValue_safe(__unsafe_unretained id obj, Ivar ivar, void *fromMemory, uint8_t associationType) { + ptrdiff_t offset = ivar_getOffset(ivar); + void *location = OBJECT_GETOFFSET_PTR(obj, offset); + + NSUInteger size = 0; + NSGetSizeAndAlignment(ivar_getTypeEncoding(ivar), &size, NULL); + + void *newValue = NULL; + + switch (associationType) { + case PFPropertyInfoAssociationTypeDefault: + [NSException raise:NSInvalidArgumentException format:@"Invalid association type Default!"]; + return; + + case PFPropertyInfoAssociationTypeAssign: { + memcpy(location, fromMemory, size); + return; + } + + case PFPropertyInfoAssociationTypeWeak: { + objc_storeWeak_noTypeCheck(location, *(void **)fromMemory); + return; + } + + case PFPropertyInfoAssociationTypeStrong: + newValue = *(void **)fromMemory; + break; + + case PFPropertyInfoAssociationTypeCopy: + case PFPropertyInfoAssociationTypeMutableCopy: { + SEL command = (associationType == PFPropertyInfoAssociationTypeCopy) ? @selector(copy) + : @selector(mutableCopy); + + + void *(*objc_msgSend_casted)(void *, SEL) = (void *)objc_msgSend; + void *oldValue = *(void **)fromMemory; + + newValue = objc_msgSend_casted(oldValue, command); + newValue = objc_autorelease_noTypeCheck(newValue); + break; + } + } + + objc_storeStrong_noTypeCheck(location, newValue); +} diff --git a/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.h b/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.h new file mode 100644 index 0000000..dcd1e04 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.h @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFFileManager; +@class PFPaymentTransactionObserver; +@class PFProductsRequestResult; + +@protocol PFCommandRunning; +@class SKPaymentQueue; +@class SKPaymentTransaction; + +PF_WATCH_UNAVAILABLE @interface PFPurchaseController : NSObject + +@property (nonatomic, strong, readonly) id commandRunner; +@property (nonatomic, strong, readonly) PFFileManager *fileManager; +@property (nonatomic, strong, readonly) NSBundle *bundle; + +@property (nonatomic, strong) SKPaymentQueue *paymentQueue; +@property (nonatomic, strong, readonly) PFPaymentTransactionObserver *transactionObserver; + +@property (nonatomic, assign) Class productsRequestClass; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommandRunner:(id)commandRunner + fileManager:(PFFileManager *)fileManager + bundle:(NSBundle *)bundle NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner + fileManager:(PFFileManager *)fileManager + bundle:(NSBundle *)bundle; + +///-------------------------------------- +/// @name Products +///-------------------------------------- + +- (BFTask *)findProductsAsyncWithIdentifiers:(NSSet *)productIdentifiers; +- (BFTask *)buyProductAsyncWithIdentifier:(NSString *)productIdentifier; +- (BFTask *)downloadAssetAsyncForTransaction:(SKPaymentTransaction *)transaction + withProgressBlock:(PFProgressBlock)progressBlock + sessionToken:(NSString *)sessionToken; + +- (NSString *)assetContentPathForProductWithIdentifier:(NSString *)identifier fileName:(NSString *)fileName; +- (BOOL)canPurchase; + +@end diff --git a/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.m b/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.m new file mode 100644 index 0000000..58f2110 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Purchase/Controller/PFPurchaseController.m @@ -0,0 +1,241 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPurchaseController.h" + +#import + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFConstants.h" +#import "PFDecoder.h" +#import "PFFileManager.h" +#import "PFFile_Private.h" +#import "PFHTTPRequest.h" +#import "PFMacros.h" +#import "PFPaymentTransactionObserver.h" +#import "PFProductsRequestHandler.h" +#import "PFRESTCommand.h" + +@interface PFPurchaseController () { + PFProductsRequestHandler *_currentProductsRequestHandler; +} + +@end + +@implementation PFPurchaseController + +@synthesize paymentQueue = _paymentQueue; +@synthesize transactionObserver = _transactionObserver; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner + fileManager:(PFFileManager *)fileManager + bundle:(NSBundle *)bundle { + self = [super init]; + if (!self) return nil; + + _commandRunner = commandRunner; + _fileManager = fileManager; + _bundle = bundle; + + return self; +} + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner + fileManager:(PFFileManager *)fileManager + bundle:(NSBundle *)bundle { + return [[self alloc] initWithCommandRunner:commandRunner fileManager:fileManager bundle:bundle]; +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + if (_paymentQueue && _transactionObserver) { + [_paymentQueue removeTransactionObserver:_transactionObserver]; + } +} + +///-------------------------------------- +#pragma mark - Products +///-------------------------------------- + +- (BFTask *)findProductsAsyncWithIdentifiers:(NSSet *)productIdentifiers { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id { + @strongify(self); + Class requestClass = self.productsRequestClass ?: [SKProductsRequest class]; + SKProductsRequest *request = [[requestClass alloc] initWithProductIdentifiers:productIdentifiers]; + _currentProductsRequestHandler = [[PFProductsRequestHandler alloc] initWithProductsRequest:request]; + return [_currentProductsRequestHandler findProductsAsync]; + }] continueWithSuccessBlock:^id(BFTask *task) { + _currentProductsRequestHandler = nil; + return task; + }]; +} + +- (BFTask *)buyProductAsyncWithIdentifier:(NSString *)productIdentifier { + PFParameterAssert(productIdentifier, @"You must pass in a valid product identifier."); + + if (![self canPurchase]) { + NSError *error = [NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorPaymentDisabled + userInfo:nil]; + return [BFTask taskWithError:error]; + } + NSSet *identifiers = PF_SET(productIdentifier); + @weakify(self); + return [[self findProductsAsyncWithIdentifiers:identifiers] continueWithSuccessBlock:^id(BFTask *task) { + PFProductsRequestResult *result = task.result; + @strongify(self); + + for (NSString *invalidIdentifier in result.invalidProductIdentifiers) { + if ([invalidIdentifier isEqualToString:productIdentifier]) { + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorInvalidProductIdentifier + userInfo:nil]]; + } + } + + for (SKProduct *product in result.validProducts) { + if ([product.productIdentifier isEqualToString:productIdentifier]) { + BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource]; + [self.transactionObserver handle:productIdentifier runOnceBlock:^(NSError *error) { + if (error) { + [source trySetError:error]; + } else { + [source trySetResult:nil]; + } + }]; + SKPayment *payment = [SKPayment paymentWithProduct:product]; + [self.paymentQueue addPayment:payment]; + return source.task; + } + } + + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorProductNotFoundInAppStore + userInfo:nil]]; + }]; +} + +- (BFTask *)downloadAssetAsyncForTransaction:(SKPaymentTransaction *)transaction + withProgressBlock:(PFProgressBlock)progressBlock + sessionToken:(NSString *)sessionToken { + NSString *productIdentifier = transaction.payment.productIdentifier; + NSURL *appStoreReceiptURL = [self.bundle appStoreReceiptURL]; + if (!productIdentifier || !appStoreReceiptURL) { + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorReceiptMissing + userInfo:nil]]; + } + + NSError *error = nil; + NSData *appStoreReceipt = [NSData dataWithContentsOfURL:appStoreReceiptURL + options:NSDataReadingMappedIfSafe + error:&error]; + if (!appStoreReceipt || error) { + NSDictionary *userInfo = nil; + if (error) { + userInfo = @{ NSUnderlyingErrorKey : error }; + } + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorReceiptMissing + userInfo:userInfo]]; + } + + NSDictionary *params = [[PFEncoder objectEncoder] encodeObject:@{ @"receipt" : appStoreReceipt, + @"productIdentifier" : productIdentifier }]; + PFRESTCommand *command = [PFRESTCommand commandWithHTTPPath:@"validate_purchase" + httpMethod:PFHTTPRequestMethodPOST + parameters:params + sessionToken:sessionToken]; + BFTask *task = [self.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed]; + @weakify(self); + return [task continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + + PFCommandResult *result = task.result; + PFFile *file = [[PFDecoder objectDecoder] decodeObject:result.result]; + if (![file isKindOfClass:[PFFile class]]) { + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorInvalidPurchaseReceipt + userInfo:result.result]]; + } + + NSString *finalFilePath = [self assetContentPathForProductWithIdentifier:transaction.payment.productIdentifier + fileName:file.name]; + NSString *directoryPath = [finalFilePath stringByDeletingLastPathComponent]; + return [[[[[PFFileManager createDirectoryIfNeededAsyncAtPath:directoryPath] continueWithBlock:^id(BFTask *task) { + if (task.faulted) { + return [BFTask taskWithError:[NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorProductDownloadFileSystemFailure + userInfo:nil]]; + } + return file; + }] continueWithSuccessBlock:^id(BFTask *task) { + return [file getDataStreamInBackgroundWithProgressBlock:progressBlock]; + }] continueWithSuccessBlock:^id(BFTask *task) { + NSString *cachedFilePath = [file _cachedFilePath]; + return [[PFFileManager copyItemAsyncAtPath:cachedFilePath + toPath:finalFilePath] continueWithBlock:^id(BFTask *task) { + // No-op file exists error. + if (task.error.code == NSFileWriteFileExistsError) { + return nil; + } + return task; + }]; + }] continueWithSuccessResult:finalFilePath]; + }]; +} + +- (NSString *)assetContentPathForProductWithIdentifier:(NSString *)identifier fileName:(NSString *)fileName { + // We store files locally at (ParsePrivateDir)/(ProductIdentifier)/filename + NSString *filePath = [self.fileManager parseDataItemPathForPathComponent:identifier]; + filePath = [filePath stringByAppendingPathComponent:fileName]; + return filePath; +} + +- (BOOL)canPurchase { + return [[self.paymentQueue class] canMakePayments]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (SKPaymentQueue *)paymentQueue { + if (!_paymentQueue) { + _paymentQueue = [SKPaymentQueue defaultQueue]; + } + return _paymentQueue; +} + +- (PFPaymentTransactionObserver *)transactionObserver { + if (!_transactionObserver) { + _transactionObserver = [[PFPaymentTransactionObserver alloc] init]; + [self.paymentQueue addTransactionObserver:_transactionObserver]; + } + return _transactionObserver; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.h b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.h new file mode 100644 index 0000000..db0e43c --- /dev/null +++ b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import + +/*! + * The PFPaymentTransactionObserver listens to the payment queue, processes a payment by running business logic, + * and completes the transaction. It's a complex interaction and best explained as follows: + * 1) an observer object is created and added to the payment queue, typically before IAP happens (but not necessarily), + * 2) PFPurchase creates a payment and adds it to the payment queue, + * 3) when the observer sees this payment, it runs the business logic associated with this payment, + * 4) when the business logic finishes, the observer completes the transaction. If the business logic does not finish, the transaction is not completed, which means the user does not get charged, + * 5) after the transaction finishes, custom UI logic is run. + */ +PF_WATCH_UNAVAILABLE @interface PFPaymentTransactionObserver : NSObject + +- (void)handle:(NSString *)productIdentifier block:(void (^)(SKPaymentTransaction *))block; +- (void)handle:(NSString *)productIdentifier runOnceBlock:(void (^)(NSError *))block; + +@end diff --git a/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.m b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.m new file mode 100644 index 0000000..100750d --- /dev/null +++ b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.m @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPaymentTransactionObserver_Private.h" + +#import "PFAssert.h" + +@implementation PFPaymentTransactionObserver + +@synthesize blocks; +@synthesize runOnceBlocks; +@synthesize lockObj; +@synthesize runOnceLockObj; + +///-------------------------------------- +#pragma mark - NSObject +///-------------------------------------- + +- (instancetype)init { + if (self = [super init]) { + blocks = [[NSMutableDictionary alloc] init]; + runOnceBlocks = [[NSMutableDictionary alloc] init]; + lockObj = [[NSObject alloc] init]; + runOnceLockObj = [[NSObject alloc] init]; + } + return self; +} + +///-------------------------------------- +#pragma mark - SKPaymentTransactionObserver +///-------------------------------------- + +- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { + for (SKPaymentTransaction *transaction in transactions) { + switch (transaction.transactionState) { + case SKPaymentTransactionStatePurchased: + case SKPaymentTransactionStateFailed: + case SKPaymentTransactionStateRestored: + [self completeTransaction:transaction fromPaymentQueue:queue]; + break; + default: + break; + } + } +} + +///-------------------------------------- +#pragma mark - PFPaymentTransactionObserver +///-------------------------------------- + +- (void)completeTransaction:(SKPaymentTransaction *)transaction fromPaymentQueue:(SKPaymentQueue *)queue { + NSString *productIdentifier = transaction.payment.productIdentifier; + + @synchronized(lockObj) { + void(^completion)(SKPaymentTransaction *) = self.blocks[productIdentifier]; + if (!transaction.error && completion) { + completion(transaction); + } + } + + @synchronized(runOnceLockObj) { + void(^runOnceBlock)(NSError *) = (void(^)(NSError *))[self.runOnceBlocks objectForKey:productIdentifier]; + if (runOnceBlock) { + runOnceBlock(transaction.error); + [self.runOnceBlocks removeObjectForKey:productIdentifier]; + } + } + + // Calling finish:transaction here prevents the user from registering another observer to handle this transaction. + [queue finishTransaction:transaction]; +} + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +- (void)handle:(NSString *)productIdentifier block:(void(^)(SKPaymentTransaction *))block { + @synchronized(lockObj) { + self.blocks[productIdentifier] = block; + } +} + +- (void)handle:(NSString *)productIdentifier runOnceBlock:(void(^)(NSError *))block { + @synchronized(runOnceLockObj) { + PFConsistencyAssert(self.runOnceBlocks[productIdentifier] == nil, + @"You cannot purchase a product that is in the process of being purchased."); + + if (!block) { + // Create a no-op action so that we can store it in the dictionary, + // this is useful because we use the existence of this block to test if + // the same product is being purchased at the time. + block = ^(NSError *error) { + }; + } + self.runOnceBlocks[productIdentifier] = block; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver_Private.h b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver_Private.h new file mode 100644 index 0000000..268af07 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver_Private.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPaymentTransactionObserver.h" + +@interface PFPaymentTransactionObserver () + +@property (nonatomic, strong) NSMutableDictionary *blocks; +@property (nonatomic, strong) NSMutableDictionary *runOnceBlocks; +@property (nonatomic, strong) NSObject *lockObj; +@property (nonatomic, strong) NSObject *runOnceLockObj; + +@end diff --git a/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.h b/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.h new file mode 100644 index 0000000..95bea76 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFCoreDataProvider.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFPushChannelsController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Get +///-------------------------------------- + +- (BFTask *)getSubscribedChannelsAsync; + +///-------------------------------------- +/// @name Subscribe +///-------------------------------------- + +- (BFTask *)subscribeToChannelAsyncWithName:(NSString *)name; +- (BFTask *)unsubscribeFromChannelAsyncWithName:(NSString *)name; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.m b/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.m new file mode 100644 index 0000000..e8d3d8e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/ChannelsController/PFPushChannelsController.m @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushChannelsController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCurrentInstallationController.h" +#import "PFErrorUtilities.h" +#import "PFInstallation.h" + +@interface PFPushChannelsController () + +@property (nonatomic, strong, readonly) PFCurrentInstallationController *currentInstallationController; + +@end + +@implementation PFPushChannelsController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithDataSource:(nonnull id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(nonnull id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Get +///-------------------------------------- + +- (BFTask *)getSubscribedChannelsAsync { + return [[self _getCurrentObjectAsync] continueWithSuccessBlock:^id(BFTask *task) { + PFInstallation *installation = task.result; + + BFTask *installationTask = (installation.objectId + ? (BFTask *)[installation fetchInBackground] + : (BFTask *)[installation saveInBackground]); + + return [installationTask continueWithSuccessBlock:^id(BFTask *task) { + return [NSSet setWithArray:installation.channels]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Subscribe +///-------------------------------------- + +- (BFTask *)subscribeToChannelAsyncWithName:(nonnull NSString *)name { + return [[self _getCurrentObjectAsync] continueWithSuccessBlock:^id(BFTask *task) { + PFInstallation *installation = task.result; + if ([installation.channels containsObject:name] && + ![installation isDirtyForKey:@"channels"]) { + return @YES; + } + + [installation addUniqueObject:name forKey:@"channels"]; + return [installation saveInBackground]; + }]; +} + +- (BFTask *)unsubscribeFromChannelAsyncWithName:(nonnull NSString *)name { + return [[self _getCurrentObjectAsync] continueWithSuccessBlock:^id(BFTask *task) { + PFInstallation *installation = task.result; + if (name.length != 0 && + ![installation.channels containsObject:name] && + ![installation isDirtyForKey:@"channels"]) { + return @YES; + } + [installation removeObject:name forKey:@"channels"]; + return [installation saveInBackground]; + }]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (BFTask *)_getCurrentObjectAsync { + return [[self.currentInstallationController getCurrentObjectAsync] continueWithSuccessBlock:^id(BFTask *task) { + PFInstallation *installation = task.result; + if (!installation.deviceToken) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorPushMisconfigured + message:@"There is no device token stored yet."]; + return [BFTask taskWithError:error]; + } + + return task; + }]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (PFCurrentInstallationController *)currentInstallationController { + return self.dataSource.currentInstallationController; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.h b/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.h new file mode 100644 index 0000000..0f66b42 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFPushState; +@protocol PFCommandRunning; + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFPushController : NSObject + +@property (nonatomic, strong, readonly) id commandRunner; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommandRunner:(id)commandRunner NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner; + +///-------------------------------------- +/// @name Sending Push +///-------------------------------------- + +/*! + Requests push notification to be sent for a given state. + + @param state State to use to send notifications. + @param sessionToken Current user session token. + + @returns `BFTask` with result set to `NSNumber` with `BOOL` identifying whether the request succeeded. + */ +- (BFTask *)sendPushNotificationAsyncWithState:(PFPushState *)state sessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.m b/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.m new file mode 100644 index 0000000..36472de --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Controller/PFPushController.m @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandRunning.h" +#import "PFMacros.h" +#import "PFRESTPushCommand.h" + +@implementation PFPushController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommandRunner:(id)commandRunner { + self = [super init]; + if (!self) return nil; + + _commandRunner = commandRunner; + + return self; +} + ++ (instancetype)controllerWithCommandRunner:(id)commandRunner { + return [[self alloc] initWithCommandRunner:commandRunner]; +} + +///-------------------------------------- +#pragma mark - Sending Push +///-------------------------------------- + +- (BFTask *)sendPushNotificationAsyncWithState:(PFPushState *)state + sessionToken:(NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTPushCommand sendPushCommandWithPushState:state sessionToken:sessionToken]; + return [self.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + return @(task.result != nil); + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.h b/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.h new file mode 100644 index 0000000..9d2878a --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFCoreDataProvider.h" +#import "PFDataProvider.h" + +@class PFPushChannelsController; +@class PFPushController; + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFPushManager : NSObject + +@property (nonatomic, weak, readonly) id commonDataSource; +@property (nonatomic, weak, readonly) id coreDataSource; + +@property (nonatomic, strong) PFPushController *pushController; +@property (nonatomic, strong) PFPushChannelsController *channelsController; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)managerWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.m b/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.m new file mode 100644 index 0000000..d02f681 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Manager/PFPushManager.m @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushManager.h" + +#import "PFAssert.h" +#import "PFMacros.h" +#import "PFPushChannelsController.h" +#import "PFPushController.h" + +@interface PFPushManager () { + dispatch_queue_t _controllerAccessQueue; +} + +@end + +@implementation PFPushManager + +@synthesize pushController = _pushController; +@synthesize channelsController = _channelsController; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + self = [super init]; + if (!self) return nil; + + _commonDataSource = commonDataSource; + _coreDataSource = coreDataSource; + _controllerAccessQueue = dispatch_queue_create("com.parse.push.controller.accessQueue", DISPATCH_QUEUE_SERIAL); + + return self; +} + ++ (instancetype)managerWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + return [[self alloc] initWithCommonDataSource:commonDataSource coreDataSource:coreDataSource]; +} + +///-------------------------------------- +#pragma mark - PushController +///-------------------------------------- + +- (PFPushController *)pushController { + __block PFPushController *controller; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_pushController) { + _pushController = [PFPushController controllerWithCommandRunner:self.commonDataSource.commandRunner]; + } + controller = _pushController; + }); + return controller; +} + +- (void)setPushController:(PFPushController *)pushController { + dispatch_sync(_controllerAccessQueue, ^{ + _pushController = pushController; + }); +} + +///-------------------------------------- +#pragma mark - Channels Controller +///-------------------------------------- + +- (PFPushChannelsController *)channelsController { + __block PFPushChannelsController *controller; + dispatch_sync(_controllerAccessQueue, ^{ + if (!_channelsController) { + _channelsController = [PFPushChannelsController controllerWithDataSource:self.coreDataSource]; + } + controller = _channelsController; + }); + return controller; +} + +- (void)setChannelsController:(PFPushChannelsController *)channelsController { + dispatch_sync(_controllerAccessQueue, ^{ + _channelsController = channelsController; + }); +} + +@end diff --git a/Pods/Parse/Parse/Internal/Push/PFPushPrivate.h b/Pods/Parse/Parse/Internal/Push/PFPushPrivate.h new file mode 100644 index 0000000..d45d114 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/PFPushPrivate.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol PFPushInternalUtils + +@optional ++ (NSString *)convertDeviceTokenToString:(id)deviceToken; ++ (nullable NSString *)getDeviceTokenFromKeychain; ++ (void)clearDeviceToken; + +#if TARGET_OS_IPHONE + ++ (void)showAlertViewWithTitle:(nullable NSString *)title message:(nullable NSString *)message NS_EXTENSION_UNAVAILABLE_IOS(""); ++ (void)playVibrate; ++ (void)playAudioWithName:(nullable NSString *)audioName; + +#endif + +@end + +@interface PFPush (Private) + +// For unit testability ++ (Class)pushInternalUtilClass; ++ (void)setPushInternalUtilClass:(nullable Class)utilClass; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.h b/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.h new file mode 100644 index 0000000..3adab09 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushState.h" + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFMutablePushState : PFPushState + +@property (nullable, nonatomic, copy, readwrite) NSSet *channels; +@property (nullable, nonatomic, copy, readwrite) PFQueryState *queryState; + +@property (nullable, nonatomic, strong, readwrite) NSDate *expirationDate; +@property (nullable, nonatomic, copy, readwrite) NSNumber *expirationTimeInterval; + +@property (nullable, nonatomic, copy, readwrite) NSDictionary *payload; + +///-------------------------------------- +/// @name Payload +///-------------------------------------- + +- (void)setPayloadWithMessage:(nullable NSString *)message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.m b/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.m new file mode 100644 index 0000000..998f75b --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/State/PFMutablePushState.m @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutablePushState.h" + +#import "PFPushState_Private.h" + +@implementation PFMutablePushState + +@dynamic channels; +@dynamic queryState; +@dynamic expirationDate; +@dynamic expirationTimeInterval; +@dynamic payload; + +///-------------------------------------- +#pragma mark - Payload +///-------------------------------------- + +- (void)setPayloadWithMessage:(NSString *)message { + if (!message) { + self.payload = nil; + } else { + self.payload = @{ @"alert" : [message copy] }; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/Push/State/PFPushState.h b/Pods/Parse/Parse/Internal/Push/State/PFPushState.h new file mode 100644 index 0000000..147756f --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/State/PFPushState.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFBaseState.h" + +@class PFQueryState; + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFPushState : PFBaseState + +@property (nullable, nonatomic, copy, readonly) NSSet *channels; +@property (nullable, nonatomic, copy, readonly) PFQueryState *queryState; + +@property (nullable, nonatomic, strong, readonly) NSDate *expirationDate; +@property (nullable, nonatomic, copy, readonly) NSNumber *expirationTimeInterval; + +@property (nullable, nonatomic, copy, readonly) NSDictionary *payload; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithState:(nullable PFPushState *)state; ++ (instancetype)stateWithState:(nullable PFPushState *)state; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/State/PFPushState.m b/Pods/Parse/Parse/Internal/Push/State/PFPushState.m new file mode 100644 index 0000000..6eaa245 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/State/PFPushState.m @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushState.h" +#import "PFPushState_Private.h" + +#import "PFMutablePushState.h" +#import "PFQueryState.h" + +@implementation PFPushState + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + return @{ + @"channels": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"queryState": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"expirationDate": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong], + @"expirationTimeInterval": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong], + @"payload": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy] + }; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithState:(PFPushState *)state { + return [super initWithState:state]; +} + ++ (instancetype)stateWithState:(PFPushState *)state { + return [super stateWithState:state]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (id)copyWithZone:(NSZone *)zone { + return [[PFPushState allocWithZone:zone] initWithState:self]; +} + +///-------------------------------------- +#pragma mark - NSMutableCopying +///-------------------------------------- + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutablePushState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Push/State/PFPushState_Private.h b/Pods/Parse/Parse/Internal/Push/State/PFPushState_Private.h new file mode 100644 index 0000000..33e1570 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/State/PFPushState_Private.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushState.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFPushState () + +@property (nullable, nonatomic, copy, readwrite) NSSet *channels; +@property (nullable, nonatomic, copy, readwrite) PFQueryState *queryState; + +@property (nullable, nonatomic, strong, readwrite) NSDate *expirationDate; +@property (nullable, nonatomic, copy, readwrite) NSNumber *expirationTimeInterval; + +@property (nullable, nonatomic, copy, readwrite) NSDictionary *payload; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.h b/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.h new file mode 100644 index 0000000..e47c90e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFPushPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +PF_WATCH_UNAVAILABLE @interface PFPushUtilities : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.m b/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.m new file mode 100644 index 0000000..39b3ba7 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Push/Utilites/PFPushUtilities.m @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPushUtilities.h" + +#import + +#if TARGET_OS_IOS +#import + +#import "PFAlertView.h" +#endif + +#import "PFInstallationPrivate.h" +#import "PFKeychainStore.h" +#import "PFLogging.h" +#import "PFMacros.h" + +@implementation PFPushUtilities + +///-------------------------------------- +#pragma mark - PFPushInternalUtils +///-------------------------------------- + ++ (NSString *)convertDeviceTokenToString:(id)deviceToken { + if ([deviceToken isKindOfClass:[NSString class]]) { + return deviceToken; + } else { + NSMutableString *hexString = [NSMutableString string]; + const unsigned char *bytes = [deviceToken bytes]; + for (int i = 0; i < [deviceToken length]; i++) { + [hexString appendFormat:@"%02x", bytes[i]]; + } + return [NSString stringWithString:hexString]; + } +} + ++ (NSString *)getDeviceTokenFromKeychain { + // Used the first time we construct the currentInstallation, + // for backward compability with older SDKs. + PFKeychainStore *store = [[PFKeychainStore alloc] initWithService:@"ParsePush"]; + return store[@"ParsePush"]; +} + ++ (void)clearDeviceToken { + // Used in test case setup. + [[PFInstallation currentInstallation] _clearDeviceToken]; + [[[PFKeychainStore alloc] initWithService:@"ParsePush"] removeObjectForKey:@"ParsePush"]; +} + +#if TARGET_OS_IPHONE + ++ (void)showAlertViewWithTitle:(NSString *)title message:(NSString *)message { + NSString *cancelButtonTitle = NSLocalizedStringFromTableInBundle(@"OK", @"Parse", + [NSBundle bundleForClass:[self class]], + @"Default alert view cancel button title."); + [PFAlertView showAlertWithTitle:title + message:message + cancelButtonTitle:cancelButtonTitle + otherButtonTitles:nil + completion:nil]; +} + ++ (void)playAudioWithName:(NSString *)audioFileName { + SystemSoundID soundId = -1; + + if (audioFileName) { + NSURL *bundlePath = [[NSBundle mainBundle] URLForResource:[audioFileName stringByDeletingPathExtension] + withExtension:[audioFileName pathExtension]]; + + AudioServicesCreateSystemSoundID((__bridge CFURLRef)bundlePath, &soundId); + } + + if (soundId != -1) { + AudioServicesPlaySystemSound(soundId); + } +} + ++ (void)playVibrate { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +} + +#endif + +@end diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.h b/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.h new file mode 100644 index 0000000..db0c057 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFQueryController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFCachedQueryController : PFQueryController + +@property (nonatomic, weak, readonly) id commonDataSource; + +- (instancetype)initWithCommonDataSource:(id)dataSource; ++ (instancetype)controllerWithCommonDataSource:(id)dataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.m b/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.m new file mode 100644 index 0000000..d89a928 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFCachedQueryController.m @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCachedQueryController.h" + +#import + +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFDecoder.h" +#import "PFErrorUtilities.h" +#import "PFJSONSerialization.h" +#import "PFKeyValueCache.h" +#import "PFMacros.h" +#import "PFQueryState.h" +#import "PFRESTCommand.h" +#import "PFRESTQueryCommand.h" +#import "PFUser.h" + +@implementation PFCachedQueryController + +@dynamic commonDataSource; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithCommonDataSource:(id)dataSource { + return [super initWithCommonDataSource:dataSource]; +} + ++ (instancetype)controllerWithCommonDataSource:(id)dataSource { + return [super controllerWithCommonDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - PFQueryControllerSubclass +///-------------------------------------- + +- (BFTask *)runNetworkCommandAsync:(PFRESTCommand *)command + withCancellationToken:(BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + switch (queryState.cachePolicy) { + case kPFCachePolicyIgnoreCache: + { + return [self _runNetworkCommandAsync:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + } + break; + case kPFCachePolicyNetworkOnly: + { + return [[self _runNetworkCommandAsync:command + withCancellationToken:cancellationToken + forQueryState:queryState] continueWithSuccessBlock:^id(BFTask *task) { + return [self _saveCommandResultAsync:task.result forCommandCacheKey:command.cacheKey]; + } cancellationToken:cancellationToken]; + } + break; + case kPFCachePolicyCacheOnly: + { + return [self _runNetworkCommandAsyncFromCache:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + } + break; + case kPFCachePolicyNetworkElseCache: { + // Don't retry for network-else-cache, because it just slows things down. + BFTask *networkTask = [self _runNetworkCommandAsync:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + @weakify(self); + return [networkTask continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (task.cancelled || task.exception) { + return task; + } else if (task.error) { + return [self _runNetworkCommandAsyncFromCache:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + } + return [self _saveCommandResultAsync:task.result forCommandCacheKey:command.cacheKey]; + } cancellationToken:cancellationToken]; + } + break; + case kPFCachePolicyCacheElseNetwork: + { + BFTask *cacheTask = [self _runNetworkCommandAsyncFromCache:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + @weakify(self); + return [cacheTask continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (task.error) { + return [self _runNetworkCommandAsync:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + } + return task; + } cancellationToken:cancellationToken]; + } + break; + case kPFCachePolicyCacheThenNetwork: + PFConsistencyAssert(NO, @"kPFCachePolicyCacheThenNetwork is not implemented as a runner."); + break; + default: + PFConsistencyAssert(NO, @"Unrecognized cache policy: %d", queryState.cachePolicy); + break; + } + return nil; +} + +- (BFTask *)_runNetworkCommandAsync:(PFRESTCommand *)command + withCancellationToken:(BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState { + PFCommandRunningOptions options = 0; + // We don't want retries on NetworkElseCache, but rather instantly back-off to cache. + if (queryState.cachePolicy != kPFCachePolicyNetworkElseCache) { + options = PFCommandRunningOptionRetryIfFailed; + } + BFTask *networkTask = [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:options + cancellationToken:cancellationToken]; + return [networkTask continueWithSuccessBlock:^id(BFTask *task) { + if (queryState.cachePolicy == kPFCachePolicyNetworkOnly || + queryState.cachePolicy == kPFCachePolicyNetworkElseCache || + queryState.cachePolicy == kPFCachePolicyCacheElseNetwork) { + return [self _saveCommandResultAsync:task.result forCommandCacheKey:command.cacheKey]; + } + // Roll-forward the original result. + return task; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Cache +///-------------------------------------- + +- (NSString *)cacheKeyForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { + return [PFRESTQueryCommand findCommandForQueryState:queryState withSessionToken:sessionToken].cacheKey; +} + +- (BOOL)hasCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { + // TODO: (nlutsenko) Once there is caching for `count`, the results for that command should also be checked. + // TODO: (nlutsenko) We should cache this result. + + NSString *cacheKey = [self cacheKeyForQueryState:queryState sessionToken:sessionToken]; + return ([self.commonDataSource.keyValueCache objectForKey:cacheKey maxAge:queryState.maxCacheAge] != nil); +} + +- (void)clearCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { + // TODO: (nlutsenko) Once there is caching for `count`, the results for that command should also be cleared. + NSString *cacheKey = [self cacheKeyForQueryState:queryState sessionToken:sessionToken]; + [self.commonDataSource.keyValueCache removeObjectForKey:cacheKey]; +} + +- (void)clearAllCachedResults { + [self.commonDataSource.keyValueCache removeAllObjects]; +} + +- (BFTask *)_runNetworkCommandAsyncFromCache:(PFRESTCommand *)command + withCancellationToken:(BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState { + NSString *jsonString = [self.commonDataSource.keyValueCache objectForKey:command.cacheKey + maxAge:queryState.maxCacheAge]; + if (!jsonString) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCacheMiss + message:@"Cache miss." + shouldLog:NO]; + return [BFTask taskWithError:error]; + } + + NSDictionary *object = [PFJSONSerialization JSONObjectFromString:jsonString]; + if (!object) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCacheMiss + message:@"Cache contains corrupted JSON."]; + return [BFTask taskWithError:error]; + } + + NSDictionary *decodedObject = [[PFDecoder objectDecoder] decodeObject:object]; + + PFCommandResult *result = [PFCommandResult commandResultWithResult:decodedObject + resultString:jsonString + httpResponse:nil]; + return [BFTask taskWithResult:result]; +} + +- (BFTask *)_saveCommandResultAsync:(PFCommandResult *)result forCommandCacheKey:(NSString *)cacheKey { + NSString *resultString = result.resultString; + if (resultString) { + [self.commonDataSource.keyValueCache setObject:resultString forKey:cacheKey]; + } + // Roll-forward the original result. + return [BFTask taskWithResult:result]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.h b/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.h new file mode 100644 index 0000000..a7f0241 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFQueryController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFOfflineQueryController : PFQueryController + +@property (nonatomic, weak, readonly) id commonDataSource; +@property (nonatomic, weak, readonly) id coreDataSource; + +- (instancetype)initWithCommonDataSource:(id)dataSource NS_UNAVAILABLE; ++ (instancetype)controllerWithCommonDataSource:(id)dataSource NS_UNAVAILABLE; + +- (instancetype)initWithCommonDataSource:(id)dataSource + coreDataSource:(id)coreDataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithCommonDataSource:(id)dataSource + coreDataSource:(id)coreDataSource; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.m b/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.m new file mode 100644 index 0000000..edca5d5 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFOfflineQueryController.m @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFOfflineQueryController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandRunning.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFPin.h" +#import "PFPinningObjectStore.h" +#import "PFQueryState.h" +#import "PFRESTCommand.h" +#import "PFRelationPrivate.h" + +@interface PFOfflineQueryController () { + PFOfflineStore *_offlineStore; // TODO: (nlutsenko) Lazy-load this via self.dataSource. +} + +@end + +@implementation PFOfflineQueryController + +@dynamic commonDataSource; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithCommonDataSource:(id)dataSource { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommonDataSource:(id)dataSource + coreDataSource:(id)coreDataSource { + self = [super initWithCommonDataSource:dataSource]; + if (!self) return nil; + + _offlineStore = dataSource.offlineStore; + _coreDataSource = coreDataSource; + + return self; +} + ++ (instancetype)controllerWithCommonDataSource:(id)dataSource + coreDataSource:(id)coreDataSource { + return [[self alloc] initWithCommonDataSource:dataSource coreDataSource:coreDataSource]; +} + +///-------------------------------------- +#pragma mark - Find +///-------------------------------------- + +- (BFTask *)findObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + if (queryState.queriesLocalDatastore) { + return [self _findObjectsFromLocalDatastoreAsyncForQueryState:queryState + withCancellationToken:cancellationToken + user:user]; + } + + NSDictionary *relationCondition = queryState.conditions[@"$relatedTo"]; + if (relationCondition) { + PFObject *object = relationCondition[@"object"]; + NSString *key = relationCondition[@"key"]; + if ([object isDataAvailableForKey:key]) { + PFRelation *relation = object[key]; + return [self _findObjectsAsyncInRelation:relation + ofObject:object + forQueryState:queryState + withCancellationToken:cancellationToken + user:user]; + } + } + + return [super findObjectsAsyncForQueryState:queryState withCancellationToken:cancellationToken user:user]; +} + +- (BFTask *)_findObjectsAsyncInRelation:(PFRelation *)relation + ofObject:(PFObject *)parentObject + forQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + return [[super findObjectsAsyncForQueryState:queryState + withCancellationToken:cancellationToken + user:user] continueWithSuccessBlock:^id(BFTask *fetchTask) { + + NSArray *objects = fetchTask.result; + for (PFObject *object in objects) { + [relation _addKnownObject:object]; + } + + return [[_offlineStore updateDataForObjectAsync:parentObject] continueWithBlock:^id(BFTask *task) { + // Roll-forward the result of find task instead of a result of update task. + return fetchTask; + } cancellationToken:cancellationToken]; + } cancellationToken:cancellationToken]; +} + + +- (BFTask *)_findObjectsFromLocalDatastoreAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + NSString *pinName = queryState.localDatastorePinName; + if (pinName) { + PFPinningObjectStore *objectStore = self.coreDataSource.pinningObjectStore; + return [objectStore fetchPinAsyncWithName:pinName]; + } + return nil; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFPin *pin = task.result; + return [_offlineStore findAsyncForQueryState:queryState user:user pin:pin]; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Count +///-------------------------------------- + +- (BFTask *)countObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + if (queryState.queriesLocalDatastore) { + return [self _countObjectsFromLocalDatastoreAsyncForQueryState:queryState + withCancellationToken:cancellationToken + user:user]; + } + return [super countObjectsAsyncForQueryState:queryState withCancellationToken:cancellationToken user:user]; +} + +- (BFTask *)_countObjectsFromLocalDatastoreAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + NSString *pinName = queryState.localDatastorePinName; + if (pinName) { + PFPinningObjectStore *controller = self.coreDataSource.pinningObjectStore; + return [controller fetchPinAsyncWithName:pinName]; + } + return nil; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFPin *pin = task.result; + return [_offlineStore countAsyncForQueryState:queryState user:user pin:pin]; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - PFQueryControllerSubclass +///-------------------------------------- + +- (BFTask *)runNetworkCommandAsync:(PFRESTCommand *)command + withCancellationToken:(BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState { + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed + cancellationToken:cancellationToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.h b/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.h new file mode 100644 index 0000000..8c95ecb --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" + +@class BFCancellationToken; + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFQueryState; +@class PFRESTCommand; +@class PFCommandResult; +@class PFUser; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFQueryController : NSObject + +@property (nonatomic, weak, readonly) id commonDataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommonDataSource:(id)dataSource NS_DESIGNATED_INITIALIZER; + ++ (instancetype)controllerWithCommonDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Find +///-------------------------------------- + +/*! + Finds objects from network or LDS for any given query state. + Supports cancellation and ACLed changes for a specific user. + + @param queryState Query state to use. + @param cancellationToken Cancellation token or `nil`. + @param user `user` to use for ACLs or `nil`. + + @returns Task that resolves to `NSArray` of `PFObject`s. + */ +- (BFTask *)findObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + user:(nullable PFUser *)user; // TODO: (nlutsenko) Pass `PFUserState` instead of user. + +///-------------------------------------- +/// @name Count +///-------------------------------------- + +/*! + Counts objects from network or LDS for any given query state. + Supports cancellation and ACLed changes for a specific user. + + @param queryState Query state to use. + @param cancellationToken Cancellation token or `nil`. + @param user `user` to use for ACLs or `nil`. + + @returns Task that resolves to `NSNumber` with a count of results. + */ +- (BFTask *)countObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + user:(nullable PFUser *)user; // TODO: (nlutsenko) Pass `PFUserState` instead of user. + +///-------------------------------------- +/// @name Caching +///-------------------------------------- + +- (NSString *)cacheKeyForQueryState:(PFQueryState *)queryState sessionToken:(nullable NSString *)sessionToken; +- (BOOL)hasCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(nullable NSString *)sessionToken; + +- (void)clearCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(nullable NSString *)sessionToken; +- (void)clearAllCachedResults; + +@end + +@protocol PFQueryControllerSubclass + +/*! + Implementation should run a command on a network runner. + + @param command Command to run. + @param cancellationToken Cancellation token. + @param queryState Query state to run command for. + + @returns `BFTask` instance with result of `PFCommandResult`. + */ +- (BFTask *)runNetworkCommandAsync:(PFRESTCommand *)command + withCancellationToken:(nullable BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.m b/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.m new file mode 100644 index 0000000..9b6c494 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Controller/PFQueryController.m @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQueryController.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFPin.h" +#import "PFQueryState.h" +#import "PFRESTQueryCommand.h" +#import "PFUser.h" +#import "Parse_Private.h" + +@interface PFQueryController () + +@end + +@implementation PFQueryController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithCommonDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _commonDataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithCommonDataSource:(id)dataSource { + return [[self alloc] initWithCommonDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Find +///-------------------------------------- + +- (BFTask *)findObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + NSDate *queryStart = (queryState.trace ? [NSDate date] : nil); + __block NSDate *querySent = nil; + + NSString *sessionToken = user.sessionToken; + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + PFRESTCommand *command = [PFRESTQueryCommand findCommandForQueryState:queryState withSessionToken:sessionToken]; + querySent = (queryState.trace ? [NSDate date] : nil); + return [self runNetworkCommandAsync:command + withCancellationToken:cancellationToken + forQueryState:queryState]; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + NSDate *queryReceived = (queryState.trace ? [NSDate date] : nil); + + NSArray *resultObjects = result.result[@"results"]; + NSMutableArray *foundObjects = [NSMutableArray arrayWithCapacity:resultObjects.count]; + if (resultObjects != nil) { + NSString *resultClassName = result.result[@"className"]; + if (!resultClassName) { + resultClassName = queryState.parseClassName; + } + NSArray *selectedKeys = queryState.selectedKeys.allObjects; + for (NSDictionary *resultObject in resultObjects) { + PFObject *object = [PFObject _objectFromDictionary:resultObject + defaultClassName:resultClassName + selectedKeys:selectedKeys]; + [foundObjects addObject:object]; + } + } + + NSString *traceLog = [result.result objectForKey:@"trace"]; + if (traceLog != nil) { + NSLog(@"Pre-processing took %f seconds\n%@Client side parsing took %f seconds", + [querySent timeIntervalSinceDate:queryStart], traceLog, + [queryReceived timeIntervalSinceNow]); + } + + return foundObjects; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Count +///-------------------------------------- + +- (BFTask *)countObjectsAsyncForQueryState:(PFQueryState *)queryState + withCancellationToken:(BFCancellationToken *)cancellationToken + user:(PFUser *)user { + NSString *sessionToken = user.sessionToken; + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + PFRESTQueryCommand *findCommand = [PFRESTQueryCommand findCommandForQueryState:queryState + withSessionToken:sessionToken]; + PFRESTCommand *countCommand = [PFRESTQueryCommand countCommandFromFindCommand:findCommand]; + return [self runNetworkCommandAsync:countCommand + withCancellationToken:cancellationToken + forQueryState:queryState]; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + return result.result[@"count"]; + } cancellationToken:cancellationToken]; +} + +///-------------------------------------- +#pragma mark - Caching +///-------------------------------------- + +- (NSString *)cacheKeyForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { + return nil; +} + +- (BOOL)hasCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { + return NO; +} + +- (void)clearCachedResultForQueryState:(PFQueryState *)queryState sessionToken:(NSString *)sessionToken { +} + +- (void)clearAllCachedResults { +} + +///-------------------------------------- +#pragma mark - PFQueryControllerSubclass +///-------------------------------------- + +- (BFTask *)runNetworkCommandAsync:(PFRESTCommand *)command + withCancellationToken:(BFCancellationToken *)cancellationToken + forQueryState:(PFQueryState *)queryState { + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed + cancellationToken:cancellationToken]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Query/PFQueryPrivate.h b/Pods/Parse/Parse/Internal/Query/PFQueryPrivate.h new file mode 100644 index 0000000..149386e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/PFQueryPrivate.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFQueryState.h" + +extern NSString *const PFQueryKeyNotEqualTo; +extern NSString *const PFQueryKeyLessThan; +extern NSString *const PFQueryKeyLessThanEqualTo; +extern NSString *const PFQueryKeyGreaterThan; +extern NSString *const PFQueryKeyGreaterThanOrEqualTo; +extern NSString *const PFQueryKeyContainedIn; +extern NSString *const PFQueryKeyNotContainedIn; +extern NSString *const PFQueryKeyContainsAll; +extern NSString *const PFQueryKeyNearSphere; +extern NSString *const PFQueryKeyWithin; +extern NSString *const PFQueryKeyRegex; +extern NSString *const PFQueryKeyExists; +extern NSString *const PFQueryKeyInQuery; +extern NSString *const PFQueryKeyNotInQuery; +extern NSString *const PFQueryKeySelect; +extern NSString *const PFQueryKeyDontSelect; +extern NSString *const PFQueryKeyRelatedTo; +extern NSString *const PFQueryKeyOr; +extern NSString *const PFQueryKeyQuery; +extern NSString *const PFQueryKeyKey; +extern NSString *const PFQueryKeyObject; + +extern NSString *const PFQueryOptionKeyMaxDistance; +extern NSString *const PFQueryOptionKeyBox; +extern NSString *const PFQueryOptionKeyRegexOptions; + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFObject; + +@interface PFQuery () + +@property (nonatomic, strong, readonly) PFQueryState *state; + +@end + +@interface PFQuery (Private) + +- (instancetype)whereRelatedToObject:(PFObject *)parent fromKey:(NSString *)key; +- (void)redirectClassNameForKey:(NSString *)key; + +@end diff --git a/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.h b/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.h new file mode 100644 index 0000000..e54a13c --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQueryState.h" + +@interface PFMutableQueryState : PFQueryState + +@property (nonatomic, copy, readwrite) NSString *parseClassName; + +@property (nonatomic, assign, readwrite) NSInteger limit; +@property (nonatomic, assign, readwrite) NSInteger skip; + +///-------------------------------------- +/// @name Remote + Caching Options +///-------------------------------------- + +@property (nonatomic, assign, readwrite) PFCachePolicy cachePolicy; +@property (nonatomic, assign, readwrite) NSTimeInterval maxCacheAge; + +@property (nonatomic, assign, readwrite) BOOL trace; + +///-------------------------------------- +/// @name Local Datastore Options +///-------------------------------------- + +@property (nonatomic, assign, readwrite) BOOL shouldIgnoreACLs; +@property (nonatomic, assign, readwrite) BOOL shouldIncludeDeletingEventually; +@property (nonatomic, assign, readwrite) BOOL queriesLocalDatastore; +@property (nonatomic, copy, readwrite) NSString *localDatastorePinName; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithParseClassName:(NSString *)className; ++ (instancetype)stateWithParseClassName:(NSString *)className; + +///-------------------------------------- +/// @name Conditions +///-------------------------------------- + +- (void)setConditionType:(NSString *)type withObject:(id)object forKey:(NSString *)key; + +- (void)setEqualityConditionWithObject:(id)object forKey:(NSString *)key; +- (void)setRelationConditionWithObject:(id)object forKey:(NSString *)key; + +- (void)removeAllConditions; + +///-------------------------------------- +/// @name Sort +///-------------------------------------- + +- (void)sortByKey:(NSString *)key ascending:(BOOL)ascending; +- (void)addSortKey:(NSString *)key ascending:(BOOL)ascending; +- (void)addSortKeysFromSortDescriptors:(NSArray *)sortDescriptors; + +///-------------------------------------- +/// @name Includes +///-------------------------------------- + +- (void)includeKey:(NSString *)key; + +///-------------------------------------- +/// @name Selected Keys +///-------------------------------------- + +- (void)selectKeys:(NSArray *)keys; + +///-------------------------------------- +/// @name Redirect +///-------------------------------------- + +- (void)redirectClassNameForKey:(NSString *)key; + +@end diff --git a/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.m b/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.m new file mode 100644 index 0000000..3ac9be9 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/State/PFMutableQueryState.m @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableQueryState.h" + +#import "PFQueryState_Private.h" + +@interface PFMutableQueryState () { + NSMutableDictionary *_conditions; + NSMutableArray *_sortKeys; + NSMutableSet *_includedKeys; + NSMutableDictionary *_extraOptions; +} + +@end + +@implementation PFMutableQueryState + +@synthesize conditions = _conditions; +@synthesize sortKeys = _sortKeys; +@synthesize includedKeys = _includedKeys; +@synthesize extraOptions = _extraOptions; + +@dynamic parseClassName; +@dynamic selectedKeys; +@dynamic limit; +@dynamic skip; +@dynamic cachePolicy; +@dynamic maxCacheAge; +@dynamic trace; +@dynamic shouldIgnoreACLs; +@dynamic shouldIncludeDeletingEventually; +@dynamic queriesLocalDatastore; +@dynamic localDatastorePinName; + +///-------------------------------------- +#pragma mark - Property Attributes +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + NSMutableDictionary *attributes = [[super propertyAttributes] mutableCopy]; + + attributes[@"conditions"] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + attributes[@"sortKeys"] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + attributes[@"includedKeys"] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + attributes[@"extraOptions"] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + + return attributes; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithParseClassName:(NSString *)className { + self = [self init]; + if (!self) return nil; + + _parseClassName = [className copy]; + + return self; +} + ++ (instancetype)stateWithParseClassName:(NSString *)className { + return [[self alloc] initWithParseClassName:className]; +} + +///-------------------------------------- +#pragma mark - Conditions +///-------------------------------------- + +- (void)setConditionType:(NSString *)type withObject:(id)object forKey:(NSString *)key { + NSMutableDictionary *conditionObject = nil; + + // Check if we already have some sort of condition + id existingCondition = _conditions[key]; + if ([existingCondition isKindOfClass:[NSMutableDictionary class]]) { + conditionObject = existingCondition; + } + if (!conditionObject) { + conditionObject = [NSMutableDictionary dictionary]; + } + conditionObject[type] = object; + + [self setEqualityConditionWithObject:conditionObject forKey:key]; +} + +- (void)setEqualityConditionWithObject:(id)object forKey:(NSString *)key { + if (!_conditions) { + _conditions = [NSMutableDictionary dictionary]; + } + _conditions[key] = object; +} + +- (void)setRelationConditionWithObject:(id)object forKey:(NSString *)key { + // We need to force saved PFObject here. + NSMutableDictionary *condition = [NSMutableDictionary dictionaryWithCapacity:2]; + condition[@"object"] = object; + condition[@"key"] = key; + [self setEqualityConditionWithObject:condition forKey:@"$relatedTo"]; +} + +- (void)removeAllConditions { + [_conditions removeAllObjects]; +} + +///-------------------------------------- +#pragma mark - Sort +///-------------------------------------- + +- (void)sortByKey:(NSString *)key ascending:(BOOL)ascending { + [_sortKeys removeAllObjects]; + [self addSortKey:key ascending:ascending]; +} + +- (void)addSortKey:(NSString *)key ascending:(BOOL)ascending { + if (!key) { + return; + } + + NSString *sortKey = (ascending ? key : [NSString stringWithFormat:@"-%@", key]); + if (!_sortKeys) { + _sortKeys = [NSMutableArray arrayWithObject:sortKey]; + } else { + [_sortKeys addObject:sortKey]; + } +} + +- (void)addSortKeysFromSortDescriptors:(NSArray *)sortDescriptors { + [_sortKeys removeAllObjects]; + for (NSSortDescriptor *sortDescriptor in sortDescriptors) { + [self addSortKey:sortDescriptor.key ascending:sortDescriptor.ascending]; + } +} + +///-------------------------------------- +#pragma mark - Includes +///-------------------------------------- + +- (void)includeKey:(NSString *)key { + if (!_includedKeys) { + _includedKeys = [NSMutableSet setWithObject:key]; + } else { + [_includedKeys addObject:key]; + } +} + +///-------------------------------------- +#pragma mark - Selected Keys +///-------------------------------------- + +- (void)selectKeys:(NSArray *)keys { + if (keys) { + _selectedKeys = (_selectedKeys ? [_selectedKeys setByAddingObjectsFromArray:keys] : [NSSet setWithArray:keys]); + } else { + _selectedKeys = nil; + } +} + +///-------------------------------------- +#pragma mark - Redirect +///-------------------------------------- + +- (void)redirectClassNameForKey:(NSString *)key { + if (!_extraOptions) { + _extraOptions = [NSMutableDictionary dictionary]; + } + _extraOptions[@"redirectClassNameForKey"] = key; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Query/State/PFQueryState.h b/Pods/Parse/Parse/Internal/Query/State/PFQueryState.h new file mode 100644 index 0000000..c97ff58 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/State/PFQueryState.h @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFBaseState.h" + +@interface PFQueryState : PFBaseState + +@property (nonatomic, copy, readonly) NSString *parseClassName; + +@property (nonatomic, copy, readonly) NSDictionary *conditions; + +@property (nonatomic, copy, readonly) NSArray *sortKeys; +@property (nonatomic, copy, readonly) NSString *sortOrderString; + +@property (nonatomic, copy, readonly) NSSet *includedKeys; +@property (nonatomic, copy, readonly) NSSet *selectedKeys; +@property (nonatomic, copy, readonly) NSDictionary *extraOptions; + +@property (nonatomic, assign, readonly) NSInteger limit; +@property (nonatomic, assign, readonly) NSInteger skip; + +///-------------------------------------- +/// @name Remote + Caching Options +///-------------------------------------- + +@property (nonatomic, assign, readonly) PFCachePolicy cachePolicy; +@property (nonatomic, assign, readonly) NSTimeInterval maxCacheAge; + +@property (nonatomic, assign, readonly) BOOL trace; + +///-------------------------------------- +/// @name Local Datastore Options +///-------------------------------------- + +/*! + If ignoreACLs is enabled, we don't check ACLs when querying from LDS. We also don't grab + `PFUser currentUser` since it's unnecessary when ignoring ACLs. + */ +@property (nonatomic, assign, readonly) BOOL shouldIgnoreACLs; +/*! + This is currently unused, but is here to allow future querying across objects that are in the + process of being deleted eventually. + */ +@property (nonatomic, assign, readonly) BOOL shouldIncludeDeletingEventually; +@property (nonatomic, assign, readonly) BOOL queriesLocalDatastore; +@property (nonatomic, copy, readonly) NSString *localDatastorePinName; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithState:(PFQueryState *)state; ++ (instancetype)stateWithState:(PFQueryState *)state; + +@end diff --git a/Pods/Parse/Parse/Internal/Query/State/PFQueryState.m b/Pods/Parse/Parse/Internal/Query/State/PFQueryState.m new file mode 100644 index 0000000..d5da258 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/State/PFQueryState.m @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQueryState.h" +#import "PFQueryState_Private.h" + +#import "PFMutableQueryState.h" +#import "PFPropertyInfo.h" + +@implementation PFQueryState + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + return @{ + @"parseClassName": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"conditions": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"sortKeys": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"includedKeys": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"selectedKeys": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"extraOptions": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + + @"limit": [PFPropertyAttributes attributes], + @"skip": [PFPropertyAttributes attributes], + @"cachePolicy": [PFPropertyAttributes attributes], + @"maxCacheAge": [PFPropertyAttributes attributes], + + @"trace": [PFPropertyAttributes attributes], + @"shouldIgnoreACLs": [PFPropertyAttributes attributes], + @"shouldIncludeDeletingEventually": [PFPropertyAttributes attributes], + @"queriesLocalDatastore": [PFPropertyAttributes attributes], + + @"localDatastorePinName": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy] + }; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _cachePolicy = kPFCachePolicyIgnoreCache; + _maxCacheAge = INFINITY; + _limit = -1; + + return self; +} + +- (instancetype)initWithState:(PFQueryState *)state { + return [super initWithState:state]; +} + ++ (instancetype)stateWithState:(PFQueryState *)state { + return [super stateWithState:state]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSString *)sortOrderString { + return [self.sortKeys componentsJoinedByString:@","]; +} + +///-------------------------------------- +#pragma mark - Mutable Copying +///-------------------------------------- + +- (id)copyWithZone:(NSZone *)zone { + return [[PFQueryState allocWithZone:zone] initWithState:self]; +} + +- (instancetype)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableQueryState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Query/State/PFQueryState_Private.h b/Pods/Parse/Parse/Internal/Query/State/PFQueryState_Private.h new file mode 100644 index 0000000..006d3c2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/State/PFQueryState_Private.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQueryState.h" + +@interface PFQueryState () { +@protected + NSString *_parseClassName; + + NSDictionary *_conditions; + + NSArray *_sortKeys; + + NSSet *_includedKeys; + NSSet *_selectedKeys; + NSDictionary *_extraOptions; + + NSInteger _limit; + NSInteger _skip; + + PFCachePolicy _cachePolicy; + NSTimeInterval _maxCacheAge; + + BOOL _trace; + + BOOL _shouldIgnoreACLs; + BOOL _shouldIncludeDeletingEventually; + BOOL _queriesLocalDatastore; + NSString *_localDatastorePinName; +} + +@property (nonatomic, copy, readwrite) NSString *parseClassName; + +@property (nonatomic, assign, readwrite) NSInteger limit; +@property (nonatomic, assign, readwrite) NSInteger skip; + +///-------------------------------------- +/// @name Remote + Caching Options +///-------------------------------------- + +@property (nonatomic, assign, readwrite) PFCachePolicy cachePolicy; +@property (nonatomic, assign, readwrite) NSTimeInterval maxCacheAge; + +@property (nonatomic, assign, readwrite) BOOL trace; + +///-------------------------------------- +/// @name Local Datastore Options +///-------------------------------------- + +@property (nonatomic, assign, readwrite) BOOL shouldIgnoreACLs; +@property (nonatomic, assign, readwrite) BOOL shouldIncludeDeletingEventually; +@property (nonatomic, assign, readwrite) BOOL queriesLocalDatastore; +@property (nonatomic, copy, readwrite) NSString *localDatastorePinName; + +@end diff --git a/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.h b/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.h new file mode 100644 index 0000000..59276df --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface PFQueryUtilities : NSObject + +///-------------------------------------- +/// @name Predicate +///-------------------------------------- + +/*! + Takes an arbitrary predicate and normalizes it to a form that can easily be converted to a `PFQuery`. + */ ++ (NSPredicate *)predicateByNormalizingPredicate:(NSPredicate *)predicate; + +///-------------------------------------- +/// @name Regex +///-------------------------------------- + +/*! + Converts a string into a regex that matches it. + + @param string String to convert from. + + @returns Query regex string from a string. + */ ++ (NSString *)regexStringForString:(NSString *)string; + +///-------------------------------------- +/// @name Errors +///-------------------------------------- + ++ (NSError *)objectNotFoundError; + +@end diff --git a/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.m b/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.m new file mode 100644 index 0000000..5e9b717 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Query/Utilities/PFQueryUtilities.m @@ -0,0 +1,530 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQueryUtilities.h" + +#import "PFAssert.h" +#import "PFConstants.h" +#import "PFErrorUtilities.h" + +@implementation PFQueryUtilities + +///-------------------------------------- +#pragma mark - Predicate +///-------------------------------------- + ++ (NSPredicate *)predicateByNormalizingPredicate:(NSPredicate *)predicate { + return [self _hoistCommonPredicates:[self _normalizeToDNF:predicate]]; +} + +/*! + Traverses over all of the subpredicates in the given predicate, calling the given blocks to + transform any instances of NSPredicate. + */ ++ (NSPredicate *)_mapPredicate:(NSPredicate *)predicate + compoundBlock:(NSPredicate *(^)(NSCompoundPredicate *))compoundBlock + comparisonBlock:(NSPredicate *(^)(NSComparisonPredicate *predicate))comparisonBlock { + if ([predicate isKindOfClass:[NSCompoundPredicate class]]) { + if (compoundBlock) { + return compoundBlock((NSCompoundPredicate *)predicate); + } else { + NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; + + NSMutableArray *newSubpredicates = [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + for (NSPredicate *subPredicate in compound.subpredicates) { + [newSubpredicates addObject:[self _mapPredicate:subPredicate + compoundBlock:compoundBlock + comparisonBlock:comparisonBlock]]; + } + + NSCompoundPredicateType type = compound.compoundPredicateType; + return [[NSCompoundPredicate alloc] initWithType:type subpredicates:newSubpredicates]; + } + } + + if ([predicate isKindOfClass:[NSComparisonPredicate class]]) { + if (comparisonBlock) { + return comparisonBlock((NSComparisonPredicate *)predicate); + } else { + return predicate; + } + } + + [NSException raise:NSInternalInconsistencyException format:@"NSExpression predicates are not supported."]; + return nil; +} + +/*! + Returns a predicate that is the negation of the input predicate, or throws on error. + */ ++ (NSPredicate *)_negatePredicate:(NSPredicate *)predicate { + return [self _mapPredicate:predicate + compoundBlock:^NSPredicate *(NSCompoundPredicate *compound) { + switch (compound.compoundPredicateType) { + case NSNotPredicateType: { + return [compound.subpredicates objectAtIndex:0]; + } + case NSAndPredicateType: { + NSMutableArray *newSubpredicates = + [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + for (NSPredicate *subpredicate in compound.subpredicates) { + [newSubpredicates addObject:[self _negatePredicate:subpredicate]]; + } + return [NSCompoundPredicate orPredicateWithSubpredicates:newSubpredicates]; + } + case NSOrPredicateType: { + NSMutableArray *newSubpredicates = + [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + for (NSPredicate *subpredicate in compound.subpredicates) { + [newSubpredicates addObject:[self _negatePredicate:subpredicate]]; + } + return [NSCompoundPredicate andPredicateWithSubpredicates:newSubpredicates]; + } + default: { + [NSException raise:NSInternalInconsistencyException + format:@"This compound predicate cannot be negated. (%zd)", + compound.compoundPredicateType]; + return nil; + } + } + } comparisonBlock:^NSPredicate *(NSComparisonPredicate *comparison) { + NSPredicateOperatorType newType; + NSComparisonPredicateModifier newModifier = comparison.comparisonPredicateModifier; + SEL customSelector; + + switch (comparison.predicateOperatorType) { + case NSEqualToPredicateOperatorType: { + newType = NSNotEqualToPredicateOperatorType; + break; + } + case NSNotEqualToPredicateOperatorType: { + newType = NSEqualToPredicateOperatorType; + break; + } + case NSInPredicateOperatorType: { + newType = NSCustomSelectorPredicateOperatorType; + customSelector = NSSelectorFromString(@"notContainedIn:"); + break; + } + case NSLessThanPredicateOperatorType: { + newType = NSGreaterThanOrEqualToPredicateOperatorType; + break; + } + case NSLessThanOrEqualToPredicateOperatorType: { + newType = NSGreaterThanPredicateOperatorType; + break; + } + case NSGreaterThanPredicateOperatorType: { + newType = NSLessThanOrEqualToPredicateOperatorType; + break; + } + case NSGreaterThanOrEqualToPredicateOperatorType: { + newType = NSLessThanPredicateOperatorType; + break; + } + case NSBetweenPredicateOperatorType: { + [NSException raise:NSInternalInconsistencyException + format:@"A BETWEEN predicate was found after they should have been removed."]; + } + case NSMatchesPredicateOperatorType: + case NSLikePredicateOperatorType: + case NSBeginsWithPredicateOperatorType: + case NSEndsWithPredicateOperatorType: + case NSContainsPredicateOperatorType: + case NSCustomSelectorPredicateOperatorType: + default: { + [NSException raise:NSInternalInconsistencyException + format:@"This comparison predicate cannot be negated. (%@)", comparison]; + return nil; + } + } + + if (newType == NSCustomSelectorPredicateOperatorType) { + return [NSComparisonPredicate predicateWithLeftExpression:comparison.leftExpression + rightExpression:comparison.rightExpression + customSelector:customSelector]; + } else { + return [NSComparisonPredicate predicateWithLeftExpression:comparison.leftExpression + rightExpression:comparison.rightExpression + modifier:newModifier + type:newType + options:comparison.options]; + } + }]; +} + +/*! + Returns a version of the given predicate that contains no NSNotPredicateType compound predicates. + This greatly simplifies the diversity of predicates we have to handle later in the pipeline. + */ ++ (NSPredicate *)removeNegation:(NSPredicate *)predicate { + return [self _mapPredicate:predicate + compoundBlock:^NSPredicate *(NSCompoundPredicate *compound) { + // Remove negation from any subpredicates. + NSMutableArray *newSubpredicates = + [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + for (NSPredicate *subPredicate in [compound subpredicates]) { + [newSubpredicates addObject:[self removeNegation:subPredicate]]; + } + + // If this is a NOT predicate, return the negation of the subpredicate. + // Otherwise, just pass it on. + if (compound.compoundPredicateType == NSNotPredicateType) { + return [self _negatePredicate:[newSubpredicates objectAtIndex:0]]; + } else { + return [[NSCompoundPredicate alloc] initWithType:compound.compoundPredicateType + subpredicates:newSubpredicates]; + } + } comparisonBlock:nil]; +} + +/*! + Returns a version of the given predicate that contains no NSBetweenPredicateOperatorType predicates. + (A BETWEEN {C, D}) gets converted to (A >= C AND A <= D). + */ ++ (NSPredicate *)removeBetween:(NSPredicate *)predicate { + return [self _mapPredicate:predicate + compoundBlock:nil + comparisonBlock:^NSPredicate *(NSComparisonPredicate *predicate) { + if ([predicate predicateOperatorType] == NSBetweenPredicateOperatorType) { + NSComparisonPredicate *between = (NSComparisonPredicate *)predicate; + NSExpression *rhs = between.rightExpression; + + PFConsistencyAssert(rhs.expressionType == NSConstantValueExpressionType || + rhs.expressionType == NSAggregateExpressionType, + @"The right-hand side of a BETWEEN operation must be a value or literal."); + + PFConsistencyAssert([rhs.constantValue isKindOfClass:[NSArray class]], + @"The right-hand side of a BETWEEN operation must be an array."); + + NSArray *array = rhs.constantValue; + PFConsistencyAssert(array.count == 2, @"The right-hand side of a BETWEEN operation must have 2 items."); + + id minValue = array[0]; + id maxValue = array[1]; + + NSExpression *minExpression = ([minValue isKindOfClass:[NSExpression class]] + ? minValue + : [NSExpression expressionForConstantValue:minValue]); + NSExpression *maxExpression = ([maxValue isKindOfClass:[NSExpression class]] + ? maxValue + : [NSExpression expressionForConstantValue:maxValue]); + + return [NSCompoundPredicate andPredicateWithSubpredicates: + @[ [NSComparisonPredicate predicateWithLeftExpression:between.leftExpression + rightExpression:minExpression + modifier:between.comparisonPredicateModifier + type:NSGreaterThanOrEqualToPredicateOperatorType + options:between.options], + [NSComparisonPredicate predicateWithLeftExpression:between.leftExpression + rightExpression:maxExpression + modifier:between.comparisonPredicateModifier + type:NSLessThanOrEqualToPredicateOperatorType + options:between.options] + ]]; + } + return predicate; + }]; +} + +/*! + Returns a version of the given predicate that contains no Yoda conditions. + A Yoda condition is one where there's a constant on the LHS, such as (3 <= X). + The predicate returned by this method will instead have (X >= 3). + */ ++ (NSPredicate *)reverseYodaConditions:(NSPredicate *)predicate { + return [self _mapPredicate:predicate + compoundBlock:nil + comparisonBlock:^NSPredicate *(NSComparisonPredicate *comparison) { + if (comparison.leftExpression.expressionType == NSConstantValueExpressionType && + comparison.rightExpression.expressionType == NSKeyPathExpressionType) { + // This is a Yoda condition. + NSPredicateOperatorType newType; + switch ([comparison predicateOperatorType]) { + case NSEqualToPredicateOperatorType: { + newType = NSEqualToPredicateOperatorType; + break; + } + case NSNotEqualToPredicateOperatorType: { + newType = NSNotEqualToPredicateOperatorType; + break; + } + case NSLessThanPredicateOperatorType: { + newType = NSGreaterThanPredicateOperatorType; + break; + } + case NSLessThanOrEqualToPredicateOperatorType: { + newType = NSGreaterThanOrEqualToPredicateOperatorType; + break; + } + case NSGreaterThanPredicateOperatorType: { + newType = NSLessThanPredicateOperatorType; + break; + } + case NSGreaterThanOrEqualToPredicateOperatorType: { + newType = NSLessThanOrEqualToPredicateOperatorType; + break; + } + case NSInPredicateOperatorType: { + // This is like "5 IN X" where X is an array. + // Mongo handles this with syntax like "X = 5". + newType = NSEqualToPredicateOperatorType; + break; + } + case NSContainsPredicateOperatorType: + case NSMatchesPredicateOperatorType: + case NSLikePredicateOperatorType: + case NSBeginsWithPredicateOperatorType: + case NSEndsWithPredicateOperatorType: + case NSCustomSelectorPredicateOperatorType: + case NSBetweenPredicateOperatorType: + default: { + // We don't know how to reverse this Yoda condition, but maybe that's okay. + return predicate; + } + } + return [NSComparisonPredicate predicateWithLeftExpression:comparison.rightExpression + rightExpression:comparison.leftExpression + modifier:comparison.comparisonPredicateModifier + type:newType + options:comparison.options]; + } + return comparison; + }]; +} + +/*! + Returns a version of the given predicate converted to disjunctive normal form (DNF). + Unlike normalizeToDNF:error:, this method only accepts compound predicates, and assumes that + removeNegation:error: has already been applied to the given predicate. + */ ++ (NSPredicate *)asOrOfAnds:(NSCompoundPredicate *)compound { + // Convert the sub-predicates to DNF. + NSMutableArray *dnfSubpredicates = [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + for (NSPredicate *subpredicate in compound.subpredicates) { + if ([subpredicate isKindOfClass:[NSCompoundPredicate class]]) { + [dnfSubpredicates addObject:[self asOrOfAnds:(NSCompoundPredicate *)subpredicate]]; + } else { + [dnfSubpredicates addObject:subpredicate]; + } + } + + if (compound.compoundPredicateType == NSOrPredicateType) { + // We just need to flatten any child ORs into this OR. + NSMutableArray *newSubpredicates = [NSMutableArray arrayWithCapacity:dnfSubpredicates.count]; + for (NSPredicate *subpredicate in dnfSubpredicates) { + if ([subpredicate isKindOfClass:[NSCompoundPredicate class]] && + ((NSCompoundPredicate *)subpredicate).compoundPredicateType == NSOrPredicateType) { + for (NSPredicate *grandchild in ((NSCompoundPredicate *)subpredicate).subpredicates) { + [newSubpredicates addObject:grandchild]; + } + } else { + [newSubpredicates addObject:subpredicate]; + } + } + // There's no reason to wrap a single predicate in an OR. + if (newSubpredicates.count == 1) { + return newSubpredicates.lastObject; + } + return [NSCompoundPredicate orPredicateWithSubpredicates:newSubpredicates]; + } + + if (compound.compoundPredicateType == NSAndPredicateType) { + // This is tough. We need to take the cross product of all the subpredicates. + NSMutableArray *disjunction = [NSMutableArray arrayWithObject:@[]]; + for (NSPredicate *subpredicate in dnfSubpredicates) { + NSMutableArray *newDisjunction = [NSMutableArray array]; + if ([subpredicate isKindOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *subcompound = (NSCompoundPredicate *)subpredicate; + if (subcompound.compoundPredicateType == NSOrPredicateType) { + // We have to add every item in the OR to every AND list we have. + for (NSArray *conjunction in disjunction) { + for (NSPredicate *grandchild in subcompound.subpredicates) { + [newDisjunction addObject:[conjunction arrayByAddingObject:grandchild]]; + } + } + + } else if (subcompound.compoundPredicateType == NSAndPredicateType) { + // Just add all these conditions to all the conjunctions in progress. + for (NSArray *conjunction in disjunction) { + NSArray *grandchildren = subcompound.subpredicates; + [newDisjunction addObject:[conjunction arrayByAddingObjectsFromArray:grandchildren]]; + } + + } else { + [NSException raise:NSInternalInconsistencyException + format:@"[PFQuery asOrOfAnds:] found a compound query that wasn't OR or AND."]; + } + } else { + // Just add this condition to all the conjunctions in progress. + for (NSArray *conjunction in disjunction) { + [newDisjunction addObject:[conjunction arrayByAddingObject:subpredicate]]; + } + } + disjunction = newDisjunction; + } + + // Now disjunction contains an OR of ANDs. We just need to convert it to NSPredicates. + NSMutableArray *andPredicates = [NSMutableArray arrayWithCapacity:disjunction.count]; + for (NSArray *conjunction in disjunction) { + if (conjunction.count > 0) { + if (conjunction.count == 1) { + [andPredicates addObject:conjunction.lastObject]; + } else { + [andPredicates addObject:[NSCompoundPredicate + andPredicateWithSubpredicates:conjunction]]; + } + } + } + if (andPredicates.count == 1) { + return andPredicates.lastObject; + } else { + return [NSCompoundPredicate orPredicateWithSubpredicates:andPredicates]; + } + } + + [NSException raise:NSInternalInconsistencyException + format:@"[PFQuery asOrOfAnds:] was passed a compound query that wasn't OR or AND."]; + + return nil; +} + +/*! + Throws an exception if any comparison predicate inside this predicate has any modifiers, such as ANY, EVERY, etc. + */ ++ (void)assertNoPredicateModifiers:(NSPredicate *)predicate { + [self _mapPredicate:predicate + compoundBlock:nil + comparisonBlock:^NSPredicate *(NSComparisonPredicate *comparison) { + PFConsistencyAssert(comparison.comparisonPredicateModifier == NSDirectPredicateModifier, + @"Unsupported comparison predicate modifier %zd.", + comparison.comparisonPredicateModifier); + return comparison; + }]; +} + +/*! + Returns a version of the given predicate converted to disjunctive normal form (DNF), + known colloqially as an "or of ands", the only form of query that PFQuery accepts. + */ ++ (NSPredicate *)_normalizeToDNF:(NSPredicate *)predicate { + // Make sure they didn't use ANY, EVERY, etc. + [self assertNoPredicateModifiers:predicate]; + + // Change any BETWEEN operators to a conjunction. + predicate = [self removeBetween:predicate]; + + // Change any backwards (3 <= X) to the standardized (X >= 3). + predicate = [self reverseYodaConditions:predicate]; + + // Push any negation into the leaves. + predicate = [self removeNegation:predicate]; + + // Any comparison predicate is trivially DNF. + if (![predicate isKindOfClass:[NSCompoundPredicate class]]) { + return predicate; + } + + // It must be a compound predicate. Convert it to an OR of ANDs. + return [self asOrOfAnds:(NSCompoundPredicate *)predicate]; +} + +/*! + Takes a predicate like ((A AND B) OR (A AND C)) and rewrites it as the more efficient (A AND (B OR C)). + Assumes the input predicate is already in DNF. + // TODO: (nlutsenko): Move this logic into the server and remove it from here. + */ ++ (NSPredicate *)_hoistCommonPredicates:(NSPredicate *)predicate { + // This only makes sense for queries with a top-level OR. + if (!([predicate isKindOfClass:[NSCompoundPredicate class]] && + ((NSCompoundPredicate *)predicate).compoundPredicateType == NSOrPredicateType)) { + return predicate; + } + + // Find the set of predicates that are included in every branch of this OR. + NSArray *andPredicates = ((NSCompoundPredicate *)predicate).subpredicates; + NSMutableSet *common = nil; + for (NSPredicate *andPredicate in andPredicates) { + NSMutableSet *comparisonPredicates = nil; + if ([andPredicate isKindOfClass:[NSComparisonPredicate class]]) { + comparisonPredicates = [NSMutableSet setWithObject:andPredicate]; + } else { + comparisonPredicates = + [NSMutableSet setWithArray:((NSCompoundPredicate *)andPredicate).subpredicates]; + } + + if (!common) { + common = comparisonPredicates; + } else { + [common intersectSet:comparisonPredicates]; + } + } + + if (!common.count) { + return predicate; + } + + NSMutableArray *newAndPredicates = [NSMutableArray array]; + + // Okay, there were common sub-predicates. Hoist them up to this one. + for (NSPredicate *andPredicate in andPredicates) { + NSMutableSet *comparisonPredicates = nil; + if ([andPredicate isKindOfClass:[NSComparisonPredicate class]]) { + comparisonPredicates = [NSMutableSet setWithObject:andPredicate]; + } else { + comparisonPredicates = + [NSMutableSet setWithArray:((NSCompoundPredicate *)andPredicate).subpredicates]; + } + + for (NSPredicate *comparisonPredicate in common) { + [comparisonPredicates removeObject:comparisonPredicate]; + } + + if (comparisonPredicates.count == 0) { + // One of the OR predicates reduces to TRUE, so just return the hoisted part. + return [NSCompoundPredicate andPredicateWithSubpredicates:common.allObjects]; + } else if (comparisonPredicates.count == 1) { + [newAndPredicates addObject:comparisonPredicates.allObjects.lastObject]; + } else { + NSPredicate *newAndPredicate = + [NSCompoundPredicate andPredicateWithSubpredicates:comparisonPredicates.allObjects]; + [newAndPredicates addObject:newAndPredicate]; + } + } + + // Make an AND of the hoisted predicates and the OR of the modified subpredicates. + NSPredicate *newOrPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:newAndPredicates]; + NSArray *newPredicates = [@[ newOrPredicate ] arrayByAddingObjectsFromArray:common.allObjects]; + return [NSCompoundPredicate andPredicateWithSubpredicates:newPredicates]; +} + +///-------------------------------------- +#pragma mark - Regex +///-------------------------------------- + +/*! + This is used to create a regex string to match the input string. By using Q and E flags to match, we can do this + without requiring super expensive rewrites, but me must be careful to escape existing \E flags in the input string. + By replacing it with `\E\\E\Q`, the regex engine will end the old literal block, put in the user's `\E` string, and + Begin another literal block. + */ ++ (NSString *)regexStringForString:(NSString *)string { + return [NSString stringWithFormat:@"\\Q%@\\E", [string stringByReplacingOccurrencesOfString:@"\\E" + withString:@"\\E\\\\E\\Q"]]; +} + +///-------------------------------------- +#pragma mark - Errors +///-------------------------------------- + ++ (NSError *)objectNotFoundError { + return [PFErrorUtilities errorWithCode:kPFErrorObjectNotFound message:@"No results matched the query."]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/PFRelationPrivate.h b/Pods/Parse/Parse/Internal/Relation/PFRelationPrivate.h new file mode 100644 index 0000000..26d26fd --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/PFRelationPrivate.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class PFDecoder; + +@interface PFRelation (Private) + ++ (PFRelation *)relationForObject:(PFObject *)parent forKey:(NSString *)key; ++ (PFRelation *)relationWithTargetClass:(NSString *)targetClass; ++ (PFRelation *)relationFromDictionary:(NSDictionary *)dictionary withDecoder:(PFDecoder *)decoder; +- (void)ensureParentIs:(PFObject *)someParent andKeyIs:(NSString *)someKey; +- (NSDictionary *)encodeIntoDictionary; +- (BOOL)_hasKnownObject:(PFObject *)object; +- (void)_addKnownObject:(PFObject *)object; +- (void)_removeKnownObject:(PFObject *)object; + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.h b/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.h new file mode 100644 index 0000000..3fd99ed --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRelationState.h" + +@interface PFMutableRelationState : PFRelationState + +@property (nonatomic, weak, readwrite) PFObject *parent; +@property (nonatomic, copy, readwrite) NSString *targetClass; +@property (nonatomic, copy, readwrite) NSMutableSet *knownObjects; +@property (nonatomic, copy, readwrite) NSString *key; + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.m b/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.m new file mode 100644 index 0000000..6a92279 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/State/PFMutableRelationState.m @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableRelationState.h" + +#import "PFObject.h" +#import "PFRelationState_Private.h" + +@implementation PFMutableRelationState + +@dynamic parent; +@dynamic parentObjectId; +@dynamic parentClassName; +@dynamic targetClass; +@dynamic knownObjects; +@dynamic key; + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + NSMutableDictionary *parentAttributes = [[super propertyAttributes] mutableCopy]; + + parentAttributes[@"knownObjects"] = [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeMutableCopy]; + + return parentAttributes; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _knownObjects = [[NSMutableSet alloc] init]; + + return self; +} + +///-------------------------------------- +#pragma mark - Properties +///-------------------------------------- + +- (void)setParent:(PFObject *)parent { + if (_parent != parent || ![self.parentClassName isEqualToString:parent.parseClassName] || + ![self.parentObjectId isEqualToString:parent.objectId]) { + _parent = parent; + _parentClassName = [[parent parseClassName] copy]; + _parentObjectId = [[parent objectId] copy]; + } +} + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.h b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.h new file mode 100644 index 0000000..3ce8e80 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFBaseState.h" + +@class PFObject; + +@interface PFRelationState : PFBaseState + +@property (nonatomic, weak, readonly) PFObject *parent; +@property (nonatomic, copy, readonly) NSString *parentClassName; +@property (nonatomic, copy, readonly) NSString *parentObjectId; +@property (nonatomic, copy, readonly) NSString *targetClass; +@property (nonatomic, copy, readonly) NSSet *knownObjects; +@property (nonatomic, copy, readonly) NSString *key; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithState:(PFRelationState *)otherState; ++ (instancetype)stateWithState:(PFRelationState *)otherState; + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.m b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.m new file mode 100644 index 0000000..bdf36c6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState.m @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRelationState.h" +#import "PFRelationState_Private.h" + +#import "PFMutableRelationState.h" + +@implementation PFRelationState + +///-------------------------------------- +#pragma mark - PFBaseStateSubclass +///-------------------------------------- + ++ (NSDictionary *)propertyAttributes { + return @{ + @"parent": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeWeak], + @"parentClassName": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"parentObjectId": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"targetClass": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"knownObjects": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + @"key": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], + }; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _knownObjects = [[NSSet alloc] init]; + + return self; +} + +- (instancetype)initWithState:(PFRelationState *)otherState { + return [super initWithState:otherState]; +} + ++ (instancetype)stateWithState:(PFRelationState *)otherState { + return [super stateWithState:otherState]; +} + +///-------------------------------------- +#pragma mark - Copying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[PFRelationState allocWithZone:zone] initWithState:self]; +} + +- (instancetype)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableRelationState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Relation/State/PFRelationState_Private.h b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState_Private.h new file mode 100644 index 0000000..bf13b9e --- /dev/null +++ b/Pods/Parse/Parse/Internal/Relation/State/PFRelationState_Private.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRelationState.h" + +@interface PFRelationState() { +@protected + __weak PFObject *_parent; + NSString *_parentClassName; + NSString *_parentObjectId; + NSSet *_knownObjects; + NSString *_key; +} + +@property (nonatomic, weak, readwrite) PFObject *parent; +@property (nonatomic, copy, readwrite) NSString *parentClassName; +@property (nonatomic, copy, readwrite) NSString *parentObjectId; +@property (nonatomic, copy, readwrite) NSString *targetClass; +@property (nonatomic, copy, readwrite) NSSet *knownObjects; +@property (nonatomic, copy, readwrite) NSString *key; + +@end diff --git a/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.h b/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.h new file mode 100644 index 0000000..83d06e4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFDataProvider.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFSession; + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSessionController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Current Session +///-------------------------------------- + +- (BFTask *)getCurrentSessionAsyncWithSessionToken:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.m b/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.m new file mode 100644 index 0000000..7abe535 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Session/Controller/PFSessionController.m @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSessionController.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFObjectPrivate.h" +#import "PFRESTSessionCommand.h" +#import "PFSession.h" + +@implementation PFSessionController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Current Session +///-------------------------------------- + +- (BFTask *)getCurrentSessionAsyncWithSessionToken:(NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTSessionCommand getCurrentSessionCommandWithSessionToken:sessionToken]; + return [self.dataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + NSDictionary *dictionary = result.result; + PFSession *session = [PFSession _objectFromDictionary:dictionary + defaultClassName:[PFSession parseClassName] + completeData:YES]; + return session; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/Session/PFSession_Private.h b/Pods/Parse/Parse/Internal/Session/PFSession_Private.h new file mode 100644 index 0000000..b647585 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Session/PFSession_Private.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class PFSessionController; + +@interface PFSession () + +///-------------------------------------- +/// @name Session Controller +///-------------------------------------- + ++ (PFSessionController *)sessionController; + +@end diff --git a/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.h b/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.h new file mode 100644 index 0000000..806d7da --- /dev/null +++ b/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PFSessionUtilities : NSObject + +///-------------------------------------- +/// @name Session Token +///-------------------------------------- + ++ (BOOL)isSessionTokenRevocable:(nullable NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.m b/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.m new file mode 100644 index 0000000..4dff476 --- /dev/null +++ b/Pods/Parse/Parse/Internal/Session/Utilities/PFSessionUtilities.m @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSessionUtilities.h" + +@implementation PFSessionUtilities + +///-------------------------------------- +#pragma mark - Session Token +///-------------------------------------- + ++ (BOOL)isSessionTokenRevocable:(NSString *)sessionToken { + return (sessionToken && [sessionToken rangeOfString:@"r:"].location != NSNotFound); +} + +@end diff --git a/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.h b/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.h new file mode 100644 index 0000000..7ca2a64 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +extern dispatch_queue_t PFThreadsafetyCreateQueueForObject(id object); +extern void PFThreadsafetySafeDispatchSync(dispatch_queue_t queue, dispatch_block_t block); diff --git a/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.m b/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.m new file mode 100644 index 0000000..e78f547 --- /dev/null +++ b/Pods/Parse/Parse/Internal/ThreadSafety/PFThreadsafety.m @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFThreadsafety.h" + +static void *const PFThreadsafetyQueueIDKey = (void *)&PFThreadsafetyQueueIDKey; + +dispatch_queue_t PFThreadsafetyCreateQueueForObject(id object) { + NSString *label = [NSStringFromClass([object class]) stringByAppendingString:@".synchronizationQueue"]; + dispatch_queue_t queue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL); + + void *uuid = calloc(1, sizeof(uuid)); + dispatch_queue_set_specific(queue, PFThreadsafetyQueueIDKey, uuid, free); + + return queue; +} + +void PFThreadsafetySafeDispatchSync(dispatch_queue_t queue, dispatch_block_t block) { + void *uuidMine = dispatch_get_specific(PFThreadsafetyQueueIDKey); + void *uuidOther = dispatch_queue_get_specific(queue, PFThreadsafetyQueueIDKey); + + if (uuidMine == uuidOther) { + block(); + } else { + dispatch_sync(queue, block); + } +} diff --git a/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.h b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.h new file mode 100644 index 0000000..746c247 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +#import "PFCoreDataProvider.h" + +NS_ASSUME_NONNULL_BEGIN + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFUser; + +@interface PFUserAuthenticationController : NSObject + +@property (nonatomic, weak, readonly) id dataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)initWithDataSource:(id)dataSource; ++ (instancetype)controllerWithDataSource:(id)dataSource; + +///-------------------------------------- +/// @name Authentication Providers +///-------------------------------------- + +- (void)registerAuthenticationDelegate:(id)delegate forAuthType:(NSString *)authType; +- (void)unregisterAuthenticationDelegateForAuthType:(NSString *)authType; + +- (id)authenticationDelegateForAuthType:(NSString *)authType; + +///-------------------------------------- +/// @name Authentication +///-------------------------------------- + +- (BFTask PF_GENERIC(NSNumber *) *)restoreAuthenticationAsyncWithAuthData:(nullable NSDictionary *)authData + forAuthType:(NSString *)authType; +- (BFTask PF_GENERIC(NSNumber *) *)deauthenticateAsyncWithAuthType:(NSString *)authType; + +///-------------------------------------- +/// @name Log In +///-------------------------------------- + +- (BFTask *)logInUserAsyncWithAuthType:(NSString *)authType authData:(NSDictionary *)authData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.m b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.m new file mode 100644 index 0000000..2f50838 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.m @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserAuthenticationController.h" + +#import "BFTask+Private.h" +#import "PFMacros.h" +#import "PFUserPrivate.h" +#import "PFObjectPrivate.h" +#import "PFAnonymousUtils.h" +#import "PFAnonymousAuthenticationProvider.h" +#import "PFUserController.h" +#import "PFCurrentUserController.h" +#import "PFAssert.h" + +@interface PFUserAuthenticationController () { + dispatch_queue_t _dataAccessQueue; + NSMutableDictionary PF_GENERIC(NSString *, id) *_authenticationDelegates; +} + +@end + +@implementation PFUserAuthenticationController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithDataSource:(id)dataSource { + self = [super init]; + if (!self) return nil; + + _dataSource = dataSource; + _dataAccessQueue = dispatch_queue_create("com.parse.user.authenticationManager", DISPATCH_QUEUE_SERIAL); + _authenticationDelegates = [NSMutableDictionary dictionary]; + + return self; +} + ++ (instancetype)controllerWithDataSource:(id)dataSource { + return [[self alloc] initWithDataSource:dataSource]; +} + +///-------------------------------------- +#pragma mark - Authentication Providers +///-------------------------------------- + +- (void)registerAuthenticationDelegate:(id)delegate forAuthType:(NSString *)authType { + PFParameterAssert(delegate, @"Authentication delegate can't be `nil`."); + PFParameterAssert(authType, @"`authType` can't be `nil`."); + PFConsistencyAssert(![self authenticationDelegateForAuthType:authType], + @"Authentication delegate already registered for authType `%@`.", authType); + + dispatch_sync(_dataAccessQueue, ^{ + _authenticationDelegates[authType] = delegate; + }); + + // TODO: (nlutsenko) Decouple this further. + [[self.dataSource.currentUserController getCurrentUserAsyncWithOptions:0] continueWithSuccessBlock:^id(BFTask *task) { + PFUser *user = task.result; + [user synchronizeAuthDataWithAuthType:authType]; + return nil; + }]; +} + +- (void)unregisterAuthenticationDelegateForAuthType:(NSString *)authType { + if (!authType) { + return; + } + dispatch_sync(_dataAccessQueue, ^{ + [_authenticationDelegates removeObjectForKey:authType]; + }); +} + +- (id)authenticationDelegateForAuthType:(NSString *)authType { + if (!authType) { + return nil; + } + + __block id delegate = nil; + dispatch_sync(_dataAccessQueue, ^{ + delegate = _authenticationDelegates[authType]; + }); + return delegate; +} + +///-------------------------------------- +#pragma mark - Authentication +///-------------------------------------- + +- (BFTask PF_GENERIC(NSNumber *)*)restoreAuthenticationAsyncWithAuthData:(nullable NSDictionary *)authData + forAuthType:(NSString *)authType { + id provider = [self authenticationDelegateForAuthType:authType]; + if (!provider) { + return [BFTask taskWithResult:@YES]; + } + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id { + return [BFTask taskWithResult:@([provider restoreAuthenticationWithAuthData:authData])]; + }]; +} + +- (BFTask PF_GENERIC(NSNumber *)*)deauthenticateAsyncWithAuthType:(NSString *)authType { + return [self restoreAuthenticationAsyncWithAuthData:nil forAuthType:authType]; +} + +///-------------------------------------- +#pragma mark - Log In +///-------------------------------------- + +- (BFTask *)logInUserAsyncWithAuthType:(NSString *)authType authData:(NSDictionary *)authData { + //TODO: (nlutsenko) Make it fully async. + //TODO: (nlutsenko) Inject `PFUserController` here. + PFUser *currentUser = [PFUser currentUser]; + if (currentUser && [PFAnonymousUtils isLinkedWithUser:currentUser]) { + if ([currentUser isLazy]) { + PFUser *user = currentUser; + BFTask *resolveLaziness = nil; + NSDictionary *oldAnonymousData = nil; + @synchronized(user.lock) { + oldAnonymousData = user.authData[PFAnonymousUserAuthenticationType]; + + // Replace any anonymity with the new linked authData + [user stripAnonymity]; + + [user.authData setObject:authData forKey:authType]; + [user.linkedServiceNames addObject:authType]; + + resolveLaziness = [user resolveLazinessAsync:[BFTask taskWithResult:nil]]; + } + + return [resolveLaziness continueAsyncWithBlock:^id(BFTask *task) { + if (task.isCancelled || task.exception || task.error) { + [user.authData removeObjectForKey:authType]; + [user.linkedServiceNames removeObject:authType]; + [user restoreAnonymity:oldAnonymousData]; + return task; + } + return task.result; + }]; + } else { + return [[currentUser linkWithAuthTypeInBackground:authType + authData:authData] continueAsyncWithBlock:^id(BFTask *task) { + NSError *error = task.error; + if (error) { + if (error.code == kPFErrorAccountAlreadyLinked) { + // An account that's linked to the given authData already exists, + // so log in instead of trying to claim. + return [[PFUser userController] logInCurrentUserAsyncWithAuthType:authType + authData:authData + revocableSession:[PFUser _isRevocableSessionEnabled]]; + } else { + return task; + } + } + + return [BFTask taskWithResult:currentUser]; + }]; + } + } + return [[PFUser userController] logInCurrentUserAsyncWithAuthType:authType + authData:authData + revocableSession:[PFUser _isRevocableSessionEnabled]]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.h b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.h new file mode 100644 index 0000000..d906dd1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const PFAnonymousUserAuthenticationType; + +@interface PFAnonymousAuthenticationProvider : NSObject + +/*! + Gets auth data with a fresh UUID. + */ +@property (nonatomic, copy, readonly) NSDictionary *authData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.m b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.m new file mode 100644 index 0000000..e338151 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.m @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAnonymousAuthenticationProvider.h" + +#import + +NSString *const PFAnonymousUserAuthenticationType = @"anonymous"; + +@implementation PFAnonymousAuthenticationProvider + +///-------------------------------------- +#pragma mark - PFAnonymousAuthenticationProvider +///-------------------------------------- + +- (BOOL)restoreAuthenticationWithAuthData:(NSDictionary *)authData { + return YES; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (NSDictionary *)authData { + NSString *uuidString = [NSUUID UUID].UUIDString; + uuidString = [uuidString lowercaseString]; + return @{ @"id" : uuidString }; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousUtils_Private.h b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousUtils_Private.h new file mode 100644 index 0000000..6d966f3 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousUtils_Private.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class PFAnonymousAuthenticationProvider; +@class PFUser; + +@interface PFAnonymousUtils (Private) + ++ (PFAnonymousAuthenticationProvider *)_authenticationProvider; ++ (void)_clearAuthenticationProvider; + ++ (PFUser *)_lazyLogIn; + +@end diff --git a/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.h b/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.h new file mode 100644 index 0000000..4ccbf77 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectFileCodingLogic.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFUserFileCodingLogic : PFObjectFileCodingLogic + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.m b/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.m new file mode 100644 index 0000000..d459bc2 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Coder/File/PFUserFileCodingLogic.m @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserFileCodingLogic.h" + +#import "PFDecoder.h" +#import "PFMutableUserState.h" +#import "PFObjectPrivate.h" +#import "PFUserConstants.h" +#import "PFUserPrivate.h" + +@interface PFUserFileCodingLogic () + +@end + +@implementation PFUserFileCodingLogic + +///-------------------------------------- +#pragma mark - Coding +///-------------------------------------- + +- (void)updateObject:(PFObject *)object fromDictionary:(NSDictionary *)dictionary usingDecoder:(PFDecoder *)decoder { + PFUser *user = (PFUser *)object; + + NSString *newSessionToken = dictionary[@"session_token"] ?: dictionary[PFUserSessionTokenRESTKey]; + if (newSessionToken) { + PFMutableUserState *state = [user._state mutableCopy]; + state.sessionToken = newSessionToken; + user._state = state; + } + + // Merge the linked service metadata + NSDictionary *newAuthData = dictionary[@"auth_data"] ?: dictionary[PFUserAuthDataRESTKey]; + newAuthData = [decoder decodeObject:newAuthData]; + if (newAuthData) { + [user.authData removeAllObjects]; + [user.linkedServiceNames removeAllObjects]; + [newAuthData enumerateKeysAndObjectsUsingBlock:^(id key, id linkData, BOOL *stop) { + if (linkData != [NSNull null]) { + user.authData[key] = linkData; + [user.linkedServiceNames addObject:key]; + [user synchronizeAuthDataWithAuthType:key]; + } else { + [user.authData removeObjectForKey:key]; + [user.linkedServiceNames removeObject:key]; + [user synchronizeAuthDataWithAuthType:key]; + } + }]; + } + + [super updateObject:user fromDictionary:dictionary usingDecoder:decoder]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.h b/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.h new file mode 100644 index 0000000..f8da41e --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.h @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +extern NSString *const PFUserUsernameRESTKey; +extern NSString *const PFUserPasswordRESTKey; +extern NSString *const PFUserSessionTokenRESTKey; +extern NSString *const PFUserAuthDataRESTKey; diff --git a/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.m b/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.m new file mode 100644 index 0000000..ecccaf1 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Constants/PFUserConstants.m @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserConstants.h" + +NSString *const PFUserUsernameRESTKey = @"username"; +NSString *const PFUserPasswordRESTKey = @"password"; +NSString *const PFUserSessionTokenRESTKey = @"sessionToken"; +NSString *const PFUserAuthDataRESTKey = @"authData"; diff --git a/Pods/Parse/Parse/Internal/User/Controller/PFUserController.h b/Pods/Parse/Parse/Internal/User/Controller/PFUserController.h new file mode 100644 index 0000000..4ff2208 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Controller/PFUserController.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "PFCoreDataProvider.h" +#import "PFDataProvider.h" +#import "PFObjectControlling.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PFUserController : NSObject + +@property (nonatomic, weak, readonly) id commonDataSource; +@property (nonatomic, weak, readonly) id coreDataSource; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; ++ (instancetype)controllerWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; + +///-------------------------------------- +/// @name Log In +///-------------------------------------- + +- (BFTask *)logInCurrentUserAsyncWithSessionToken:(NSString *)sessionToken; +- (BFTask *)logInCurrentUserAsyncWithUsername:(NSString *)username + password:(NSString *)password + revocableSession:(BOOL)revocableSession; + +//TODO: (nlutsenko) Move this method into PFUserAuthenticationController after PFUser is decoupled further. +- (BFTask *)logInCurrentUserAsyncWithAuthType:(NSString *)authType + authData:(NSDictionary *)authData + revocableSession:(BOOL)revocableSession; + +///-------------------------------------- +/// @name Reset Password +///-------------------------------------- + +- (BFTask *)requestPasswordResetAsyncForEmail:(NSString *)email; + +///-------------------------------------- +/// @name Log Out +///-------------------------------------- + +- (BFTask *)logOutUserAsyncWithSessionToken:(NSString *)sessionToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Internal/User/Controller/PFUserController.m b/Pods/Parse/Parse/Internal/User/Controller/PFUserController.m new file mode 100644 index 0000000..cebeb52 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/Controller/PFUserController.m @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserController.h" + +#import "BFTask+Private.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFCurrentUserController.h" +#import "PFErrorUtilities.h" +#import "PFMacros.h" +#import "PFObjectPrivate.h" +#import "PFRESTUserCommand.h" +#import "PFUserPrivate.h" + +@implementation PFUserController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + self = [super init]; + if (!self) return nil; + + _commonDataSource = commonDataSource; + _coreDataSource = coreDataSource; + + return self; +} + ++ (instancetype)controllerWithCommonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + return [[self alloc] initWithCommonDataSource:commonDataSource + coreDataSource:coreDataSource]; +} + +///-------------------------------------- +#pragma mark - Log In +///-------------------------------------- + +- (BFTask *)logInCurrentUserAsyncWithSessionToken:(NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTUserCommand getCurrentUserCommandWithSessionToken:sessionToken]; + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFCommandResult *result = task.result; + NSDictionary *dictionary = result.result; + + // We test for a null object, if it isn't, we can use the response to create a PFUser. + if ([dictionary isKindOfClass:[NSNull class]] || !dictionary) { + return [BFTask taskWithError:[PFErrorUtilities errorWithCode:kPFErrorObjectNotFound + message:@"Invalid Session Token."]]; + } + + PFUser *user = [PFUser _objectFromDictionary:dictionary + defaultClassName:[PFUser parseClassName] + completeData:YES]; + // Serialize the object to disk so we can later access it via currentUser + PFCurrentUserController *controller = self.coreDataSource.currentUserController; + return [[controller saveCurrentObjectAsync:user] continueWithBlock:^id(BFTask *task) { + return user; + }]; + }]; +} + +- (BFTask *)logInCurrentUserAsyncWithUsername:(NSString *)username + password:(NSString *)password + revocableSession:(BOOL)revocableSession { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + PFRESTCommand *command = [PFRESTUserCommand logInUserCommandWithUsername:username + password:password + revocableSession:revocableSession]; + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + PFCommandResult *result = task.result; + NSDictionary *dictionary = result.result; + + // We test for a null object, if it isn't, we can use the response to create a PFUser. + if ([dictionary isKindOfClass:[NSNull class]] || !dictionary) { + return [BFTask taskWithError:[PFErrorUtilities errorWithCode:kPFErrorObjectNotFound + message:@"Invalid login credentials."]]; + } + + PFUser *user = [PFUser _objectFromDictionary:dictionary + defaultClassName:[PFUser parseClassName] + completeData:YES]; + + // Serialize the object to disk so we can later access it via currentUser + PFCurrentUserController *controller = self.coreDataSource.currentUserController; + return [[controller saveCurrentObjectAsync:user] continueWithBlock:^id(BFTask *task) { + return user; + }]; + }]; +} + +- (BFTask *)logInCurrentUserAsyncWithAuthType:(NSString *)authType + authData:(NSDictionary *)authData + revocableSession:(BOOL)revocableSession { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTUserCommand serviceLoginUserCommandWithAuthenticationType:authType + authenticationData:authData + revocableSession:revocableSession]; + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + PFUser *user = [PFUser _objectFromDictionary:result.result + defaultClassName:[PFUser parseClassName] + completeData:YES]; + @synchronized ([user lock]) { + user.authData[authType] = authData; + [user.linkedServiceNames addObject:authType]; + [user startSave]; + return [user _handleServiceLoginCommandResult:result]; + } + }]; +} + +///-------------------------------------- +#pragma mark - Reset Password +///-------------------------------------- + +- (BFTask *)requestPasswordResetAsyncForEmail:(NSString *)email { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTUserCommand resetPasswordCommandForUserWithEmail:email]; + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessResult:nil]; +} + +///-------------------------------------- +#pragma mark - Log Out +///-------------------------------------- + +- (BFTask *)logOutUserAsyncWithSessionToken:(NSString *)sessionToken { + @weakify(self); + return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @strongify(self); + PFRESTCommand *command = [PFRESTUserCommand logOutUserCommandWithSessionToken:sessionToken]; + return [self.commonDataSource.commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithSuccessResult:nil]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.h b/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.h new file mode 100644 index 0000000..15b8e99 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFCoreDataProvider.h" +#import "PFCurrentObjectControlling.h" +#import "PFDataProvider.h" +#import "PFMacros.h" + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFUser; + +typedef NS_OPTIONS(NSUInteger, PFCurrentUserLoadingOptions) { + PFCurrentUserLoadingOptionCreateLazyIfNotAvailable = 1 << 0, +}; + +@interface PFCurrentUserController : NSObject + +@property (nonatomic, weak, readonly) id commonDataSource; +@property (nonatomic, weak, readonly) id coreDataSource; + +@property (atomic, assign) BOOL automaticUsersEnabled; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithStorageType:(PFCurrentObjectStorageType)storageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource NS_DESIGNATED_INITIALIZER; ++ (instancetype)controllerWithStorageType:(PFCurrentObjectStorageType)storageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource; + +///-------------------------------------- +/// @name User +///-------------------------------------- + +- (BFTask *)getCurrentUserAsyncWithOptions:(PFCurrentUserLoadingOptions)options; + +- (BFTask *)logOutCurrentUserAsync; + +///-------------------------------------- +/// @name Session Token +///-------------------------------------- + +- (BFTask *)getCurrentUserSessionTokenAsync; + +@end diff --git a/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.m b/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.m new file mode 100644 index 0000000..098b4e6 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/CurrentUserController/PFCurrentUserController.m @@ -0,0 +1,364 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCurrentUserController.h" + +#import + +#import "BFTask+Private.h" +#import "PFAnonymousUtils_Private.h" +#import "PFAssert.h" +#import "PFAsyncTaskQueue.h" +#import "PFFileManager.h" +#import "PFKeychainStore.h" +#import "PFMutableUserState.h" +#import "PFObjectFilePersistenceController.h" +#import "PFObjectPrivate.h" +#import "PFQuery.h" +#import "PFUserConstants.h" +#import "PFUserPrivate.h" + +@interface PFCurrentUserController () { + dispatch_queue_t _dataQueue; + PFAsyncTaskQueue *_dataTaskQueue; + + PFUser *_currentUser; + BOOL _currentUserMatchesDisk; +} + +@end + +@implementation PFCurrentUserController + +@synthesize storageType = _storageType; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + PFNotDesignatedInitializer(); +} + +- (instancetype)initWithStorageType:(PFCurrentObjectStorageType)storageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + self = [super init]; + if (!self) return nil; + + _dataQueue = dispatch_queue_create("com.parse.currentUser.controller", DISPATCH_QUEUE_CONCURRENT); + _dataTaskQueue = [PFAsyncTaskQueue taskQueue]; + + _storageType = storageType; + _commonDataSource = commonDataSource; + _coreDataSource = coreDataSource; + + return self; +} + ++ (instancetype)controllerWithStorageType:(PFCurrentObjectStorageType)dataStorageType + commonDataSource:(id)commonDataSource + coreDataSource:(id)coreDataSource { + return [[self alloc] initWithStorageType:dataStorageType + commonDataSource:commonDataSource + coreDataSource:coreDataSource]; +} + +///-------------------------------------- +#pragma mark - PFCurrentObjectControlling +///-------------------------------------- + +- (BFTask *)getCurrentObjectAsync { + PFCurrentUserLoadingOptions options = 0; + if (self.automaticUsersEnabled) { + options |= PFCurrentUserLoadingOptionCreateLazyIfNotAvailable; + } + return [self getCurrentUserAsyncWithOptions:options]; +} + +- (BFTask *)saveCurrentObjectAsync:(PFUser *)object { + return [_dataTaskQueue enqueue:^id(BFTask *task) { + return [self _saveCurrentUserAsync:object]; + }]; +} + +///-------------------------------------- +#pragma mark - User +///-------------------------------------- + +- (BFTask *)getCurrentUserAsyncWithOptions:(PFCurrentUserLoadingOptions)options { + return [_dataTaskQueue enqueue:^id(BFTask *task) { + return [self _getCurrentUserAsyncWithOptions:options]; + }]; +} + +- (BFTask *)_getCurrentUserAsyncWithOptions:(PFCurrentUserLoadingOptions)options { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + __block BOOL matchesDisk = NO; + __block PFUser *currentUser = nil; + dispatch_sync(_dataQueue, ^{ + matchesDisk = _currentUserMatchesDisk; + currentUser = _currentUser; + }); + if (currentUser) { + return currentUser; + } + + if (matchesDisk) { + if (options & PFCurrentUserLoadingOptionCreateLazyIfNotAvailable) { + return [self _lazyLogInUser]; + } + return nil; + } + + return [[[[self _loadCurrentUserFromDiskAsync] continueWithSuccessBlock:^id(BFTask *task) { + PFUser *user = task.result; + // If the object was not yet saved, but is already linked with AnonymousUtils - it means it is lazy. + // So mark it's state as `isLazy` and make it `dirty` + if (!user.objectId && [PFAnonymousUtils isLinkedWithUser:user]) { + user.isLazy = YES; + [user _setDirty:YES]; + } + return user; + }] continueWithBlock:^id(BFTask *task) { + dispatch_barrier_sync(_dataQueue, ^{ + _currentUser = task.result; + _currentUserMatchesDisk = !task.faulted; + }); + return task; + }] continueWithBlock:^id(BFTask *task) { + // If there's no user and automatic user is enabled, do lazy login. + if (!task.result && (options & PFCurrentUserLoadingOptionCreateLazyIfNotAvailable)) { + return [self _lazyLogInUser]; + } + return task; + }]; + }]; +} + +- (BFTask *)_saveCurrentUserAsync:(PFUser *)user { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + __block PFUser *currentUser = nil; + dispatch_sync(_dataQueue, ^{ + currentUser = _currentUser; + }); + + BFTask *task = [BFTask taskWithResult:nil]; + // Check for objectId equality to not logout in case we are saving another instance of the same user. + if (currentUser != nil && currentUser != user && ![user.objectId isEqualToString:currentUser.objectId]) { + task = [task continueWithBlock:^id(BFTask *task) { + return [currentUser _logOutAsync]; + }]; + } + return [[task continueWithBlock:^id(BFTask *task) { + @synchronized (user.lock) { + [user setIsCurrentUser:YES]; + [user synchronizeAllAuthData]; + } + return [self _saveCurrentUserToDiskAsync:user]; + }] continueWithBlock:^id(BFTask *task) { + dispatch_barrier_sync(_dataQueue, ^{ + _currentUser = user; + _currentUserMatchesDisk = !task.faulted && !task.cancelled; + }); + return user; + }]; + }]; +} + +- (BFTask *)logOutCurrentUserAsync { + return [_dataTaskQueue enqueue:^id(BFTask *task) { + return [[self _getCurrentUserAsyncWithOptions:0] continueWithBlock:^id(BFTask *task) { + BFTask *userLogoutTask = nil; + + PFUser *user = task.result; + if (user) { + userLogoutTask = [user _logOutAsync]; + } else { + userLogoutTask = [BFTask taskWithResult:nil]; + } + + NSString *filePath = [self.commonDataSource.fileManager parseDataItemPathForPathComponent:PFUserCurrentUserFileName]; + BFTask *fileTask = [PFFileManager removeItemAtPathAsync:filePath]; + BFTask *unpinTask = nil; + + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + unpinTask = [PFObject unpinAllObjectsInBackgroundWithName:PFUserCurrentUserPinName]; + } else { + unpinTask = [BFTask taskWithResult:nil]; + } + + [self _deleteSensitiveUserDataFromKeychainWithItemName:PFUserCurrentUserFileName]; + + BFTask *logoutTask = [[BFTask taskForCompletionOfAllTasks:@[ fileTask, unpinTask ]] continueWithBlock:^id(BFTask *task) { + dispatch_barrier_sync(_dataQueue, ^{ + _currentUser = nil; + _currentUserMatchesDisk = YES; + }); + return nil; + }]; + return [BFTask taskForCompletionOfAllTasks:@[ userLogoutTask, logoutTask ]]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Data Storage +///-------------------------------------- + +- (BFTask *)_loadCurrentUserFromDiskAsync { + BFTask *task = nil; + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + // Try loading from OfflineStore + PFQuery *query = [[[PFQuery queryWithClassName:[PFUser parseClassName]] + fromPinWithName:PFUserCurrentUserPinName] + // We need to ignoreACLs here because right now we don't have currentUser. + ignoreACLs]; + + // Silence the warning if we are loading from LDS + task = [[query findObjectsInBackground] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *results = task.result; + if ([results count] == 1) { + return [BFTask taskWithResult:results.firstObject]; + } else if ([results count] != 0) { + return [[PFObject unpinAllObjectsInBackgroundWithName:PFUserCurrentUserPinName] + continueWithSuccessResult:nil]; + } + + // Backward compatibility if we previously have non-LDS currentUser. + return [PFObject _migrateObjectInBackgroundFromFile:PFUserCurrentUserFileName toPin:PFUserCurrentUserPinName usingMigrationBlock:^id(BFTask *task) { + PFUser *user = task.result; + // Only migrate session token to Keychain if it was loaded from Data File. + if (user.sessionToken) { + return [self _saveSensitiveUserDataAsync:user + toKeychainItemWithName:PFUserCurrentUserKeychainItemName]; + } + return nil; + }]; + }]; + } else { + PFObjectFilePersistenceController *controller = self.coreDataSource.objectFilePersistenceController; + task = [controller loadPersistentObjectAsyncForKey:PFUserCurrentUserFileName]; + } + return [task continueWithSuccessBlock:^id(BFTask *task) { + PFUser *user = task.result; + [user setIsCurrentUser:YES]; + return [[self _loadSensitiveUserDataAsync:user + fromKeychainItemWithName:PFUserCurrentUserKeychainItemName] continueWithSuccessResult:user]; + }]; +} + +- (BFTask *)_saveCurrentUserToDiskAsync:(PFUser *)user { + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + return [[[PFObject unpinAllObjectsInBackgroundWithName:PFUserCurrentUserPinName] continueWithSuccessBlock:^id(BFTask *task) { + return [self _saveSensitiveUserDataAsync:user toKeychainItemWithName:PFUserCurrentUserKeychainItemName]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // We don't want to include children of `currentUser` automatically. + return [user _pinInBackgroundWithName:PFUserCurrentUserPinName includeChildren:NO]; + }]; + } + + return [[self _saveSensitiveUserDataAsync:user + toKeychainItemWithName:PFUserCurrentUserKeychainItemName] continueWithBlock:^id(BFTask *task) { + PFObjectFilePersistenceController *controller = self.coreDataSource.objectFilePersistenceController; + return [controller persistObjectAsync:user forKey:PFUserCurrentUserFileName]; + }]; +} + +///-------------------------------------- +#pragma mark - Sensitive Data +///-------------------------------------- + +- (BFTask *)_loadSensitiveUserDataAsync:(PFUser *)user fromKeychainItemWithName:(NSString *)itemName { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSDictionary *userData = self.commonDataSource.keychainStore[itemName]; + @synchronized (user.lock) { + if (userData) { + PFMutableUserState *state = [user._state mutableCopy]; + + NSString *sessionToken = userData[PFUserSessionTokenRESTKey] ?: userData[@"session_token"]; + if (sessionToken) { + state.sessionToken = sessionToken; + } + + user._state = state; + + NSDictionary *newAuthData = userData[PFUserAuthDataRESTKey] ?: userData[@"auth_data"]; + [newAuthData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + user.authData[key] = obj; + if (obj != nil) { + [user.linkedServiceNames addObject:key]; + } + [user synchronizeAuthDataWithAuthType:key]; + }]; + } + } + return nil; + }]; +} + +- (BFTask *)_saveSensitiveUserDataAsync:(PFUser *)user toKeychainItemWithName:(NSString *)itemName { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + NSMutableDictionary *userData = [NSMutableDictionary dictionaryWithCapacity:2]; + @synchronized (user.lock) { + if (user.sessionToken) { + userData[PFUserSessionTokenRESTKey] = [user.sessionToken copy]; + } + if ([user.authData count]) { + userData[PFUserAuthDataRESTKey] = [user.authData copy]; + } + } + self.commonDataSource.keychainStore[itemName] = userData; + + return nil; + }]; +} + +- (void)_deleteSensitiveUserDataFromKeychainWithItemName:(NSString *)itemName { + [self.commonDataSource.keychainStore removeObjectForKey:itemName]; +} + +///-------------------------------------- +#pragma mark - Session Token +///-------------------------------------- + +- (BFTask *)getCurrentUserSessionTokenAsync { + return [[self getCurrentUserAsyncWithOptions:0] continueWithSuccessBlock:^id(BFTask *task) { + PFUser *user = task.result; + return user.sessionToken; + }]; +} + +///-------------------------------------- +#pragma mark - Lazy Login +///-------------------------------------- + +- (BFTask *)_lazyLogInUser { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + PFUser *user = [PFAnonymousUtils _lazyLogIn]; + + // When LDS is enabled, we will immediately save the anon user to LDS. When LDS is disabled, we + // will create the anon user, but will lazily save it to Parse on an object save that has this + // user in its ACL. + // The main differences here would be that non-LDS may have different anon users in different + // sessions until an object is saved and LDS will persist the same anon user. This shouldn't be a + // big deal... + if (self.storageType == PFCurrentObjectStorageTypeOfflineStore) { + return [[self _saveCurrentUserAsync:user] continueWithSuccessResult:user]; + } + + dispatch_barrier_sync(_dataQueue, ^{ + _currentUser = user; + _currentUserMatchesDisk = YES; + }); + return user; + }]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/PFUserPrivate.h b/Pods/Parse/Parse/Internal/User/PFUserPrivate.h new file mode 100644 index 0000000..4dfebd4 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/PFUserPrivate.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import "PFMacros.h" + +extern NSString *const PFUserCurrentUserFileName; +extern NSString *const PFUserCurrentUserPinName; +extern NSString *const PFUserCurrentUserKeychainItemName; + +@class BFTask PF_GENERIC(__covariant BFGenericType); +@class PFCommandResult; +@class PFUserController; + +@interface PFUser (Private) + +///-------------------------------------- +/// @name Current User +///-------------------------------------- ++ (BFTask *)_getCurrentUserSessionTokenAsync; ++ (NSString *)currentSessionToken; + +- (void)synchronizeAllAuthData; + +- (BFTask *)_handleServiceLoginCommandResult:(PFCommandResult *)result; + +- (void)synchronizeAuthDataWithAuthType:(NSString *)authType; + ++ (PFUser *)logInLazyUserWithAuthType:(NSString *)authType authData:(NSDictionary *)authData; +- (BFTask *)resolveLazinessAsync:(BFTask *)toAwait; +- (void)stripAnonymity; +- (void)restoreAnonymity:(id)data; + +///-------------------------------------- +/// @name Revocable Session +///-------------------------------------- ++ (BOOL)_isRevocableSessionEnabled; ++ (void)_setRevocableSessionEnabled:(BOOL)enabled; + ++ (PFUserController *)userController; + +@end + +// Private Properties +@interface PFUser () { + BOOL isCurrentUser; + NSMutableDictionary *authData; + NSMutableSet *linkedServiceNames; + BOOL isLazy; +} + +// This earmarks the user as being an "identity" user. This will make saves write through +// to the currentUser singleton and disk object +@property (nonatomic, assign) BOOL isCurrentUser; + +@property (nonatomic, strong, readonly) NSMutableDictionary *authData; +@property (nonatomic, strong, readonly) NSMutableSet *linkedServiceNames; +@property (nonatomic, assign) BOOL isLazy; + +- (BOOL)_isAuthenticatedWithCurrentUser:(PFUser *)currentUser; + +- (BFTask *)_logOutAsync; + +///-------------------------------------- +/// @name Third-party Authentication (Private) +///-------------------------------------- + ++ (void)_unregisterAuthenticationDelegateForAuthType:(NSString *)authType; + +@end diff --git a/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.h b/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.h new file mode 100644 index 0000000..43e8f2d --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserState.h" + +@interface PFMutableUserState : PFUserState + +@property (nonatomic, copy, readwrite) NSString *sessionToken; +@property (nonatomic, copy, readwrite) NSDictionary *authData; + +@property (nonatomic, assign, readwrite) BOOL isNew; + +@end diff --git a/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.m b/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.m new file mode 100644 index 0000000..255265b --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/State/PFMutableUserState.m @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFMutableUserState.h" + +#import "PFUserState_Private.h" + +@implementation PFMutableUserState + +@dynamic sessionToken; +@dynamic authData; +@dynamic isNew; + +@end diff --git a/Pods/Parse/Parse/Internal/User/State/PFUserState.h b/Pods/Parse/Parse/Internal/User/State/PFUserState.h new file mode 100644 index 0000000..72fa098 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/State/PFUserState.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObjectState.h" + +@interface PFUserState : PFObjectState + +@property (nonatomic, copy, readonly) NSString *sessionToken; +@property (nonatomic, copy, readonly) NSDictionary *authData; + +@property (nonatomic, assign, readonly) BOOL isNew; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithState:(PFUserState *)state; ++ (instancetype)stateWithState:(PFUserState *)state; + +@end diff --git a/Pods/Parse/Parse/Internal/User/State/PFUserState.m b/Pods/Parse/Parse/Internal/User/State/PFUserState.m new file mode 100644 index 0000000..2c50485 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/State/PFUserState.m @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserState.h" +#import "PFUserState_Private.h" + +#import "PFMutableUserState.h" +#import "PFObjectState_Private.h" +#import "PFUserConstants.h" + +@implementation PFUserState + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithState:(PFUserState *)state { + self = [super initWithState:state]; + if (!self) return nil; + + _sessionToken = [state.sessionToken copy]; + _authData = [state.authData copy]; + _isNew = state.isNew; + + return self; +} + ++ (instancetype)stateWithState:(PFUserState *)state { + return [super stateWithState:state]; +} + +///-------------------------------------- +#pragma mark - Serialization +///-------------------------------------- + +- (NSDictionary *)dictionaryRepresentationWithObjectEncoder:(PFEncoder *)objectEncoder { + NSMutableDictionary *dictionary = [[super dictionaryRepresentationWithObjectEncoder:objectEncoder] mutableCopy]; + [dictionary removeObjectForKey:PFUserPasswordRESTKey]; + return dictionary; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (id)copyWithZone:(NSZone *)zone { + return [[PFUserState allocWithZone:zone] initWithState:self]; +} + +///-------------------------------------- +#pragma mark - NSMutableCopying +///-------------------------------------- + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [[PFMutableUserState allocWithZone:zone] initWithState:self]; +} + +@end diff --git a/Pods/Parse/Parse/Internal/User/State/PFUserState_Private.h b/Pods/Parse/Parse/Internal/User/State/PFUserState_Private.h new file mode 100644 index 0000000..c305f76 --- /dev/null +++ b/Pods/Parse/Parse/Internal/User/State/PFUserState_Private.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUserState.h" + +@interface PFUserState () { +@protected + NSString *_sessionToken; + NSDictionary *_authData; + + BOOL _isNew; +} + +@property (nonatomic, copy, readwrite) NSString *sessionToken; +@property (nonatomic, copy, readwrite) NSDictionary *authData; + +@property (nonatomic, assign, readwrite) BOOL isNew; + +@end diff --git a/Pods/Parse/Parse/PFACL.h b/Pods/Parse/Parse/PFACL.h new file mode 100644 index 0000000..a952585 --- /dev/null +++ b/Pods/Parse/Parse/PFACL.h @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +@class PFRole; +@class PFUser; + +/*! + The `PFACL` class is used to control which users can access or modify a particular object. + Each can have its own `PFACL`. You can grant read and write permissions separately to specific users, + to groups of users that belong to roles, or you can grant permissions to "the public" so that, + for example, any user could read a particular object but only a particular set of users could write to that object. + */ +@interface PFACL : NSObject + +///-------------------------------------- +/// @name Creating an ACL +///-------------------------------------- + +/*! + @abstract Creates an ACL with no permissions granted. + + @returns Returns a new `PFACL`. + */ ++ (instancetype)ACL; + +/*! + @abstract Creates an ACL where only the provided user has access. + + @param user The user to assign access. + */ ++ (instancetype)ACLWithUser:(PFUser *)user; + +///-------------------------------------- +/// @name Controlling Public Access +///-------------------------------------- + +/*! + @abstract Set whether the public is allowed to read this object. + + @param allowed Whether the public can read this object. + */ +- (void)setPublicReadAccess:(BOOL)allowed; + +/*! + @abstract Gets whether the public is allowed to read this object. + + @returns `YES` if the public read access is enabled, otherwise `NO`. + */ +- (BOOL)getPublicReadAccess; + +/*! + @abstract Set whether the public is allowed to write this object. + + @param allowed Whether the public can write this object. + */ +- (void)setPublicWriteAccess:(BOOL)allowed; + +/*! + @abstract Gets whether the public is allowed to write this object. + + @returns `YES` if the public write access is enabled, otherwise `NO`. + */ +- (BOOL)getPublicWriteAccess; + +///-------------------------------------- +/// @name Controlling Access Per-User +///-------------------------------------- + +/*! + @abstract Set whether the given user id is allowed to read this object. + + @param allowed Whether the given user can write this object. + @param userId The <[PFObject objectId]> of the user to assign access. + */ +- (void)setReadAccess:(BOOL)allowed forUserId:(NSString *)userId; + +/*! + @abstract Gets whether the given user id is *explicitly* allowed to read this object. + Even if this returns `NO`, the user may still be able to access it if returns `YES` + or if the user belongs to a role that has access. + + @param userId The <[PFObject objectId]> of the user for which to retrive access. + + @returns `YES` if the user with this `objectId` has *explicit* read access, otherwise `NO`. + */ +- (BOOL)getReadAccessForUserId:(NSString *)userId; + +/*! + @abstract Set whether the given user id is allowed to write this object. + + @param allowed Whether the given user can read this object. + @param userId The `objectId` of the user to assign access. + */ +- (void)setWriteAccess:(BOOL)allowed forUserId:(NSString *)userId; + +/*! + @abstract Gets whether the given user id is *explicitly* allowed to write this object. + Even if this returns NO, the user may still be able to write it if returns `YES` + or if the user belongs to a role that has access. + + @param userId The <[PFObject objectId]> of the user for which to retrive access. + + @returns `YES` if the user with this `objectId` has *explicit* write access, otherwise `NO`. + */ +- (BOOL)getWriteAccessForUserId:(NSString *)userId; + +/*! + @abstract Set whether the given user is allowed to read this object. + + @param allowed Whether the given user can read this object. + @param user The user to assign access. + */ +- (void)setReadAccess:(BOOL)allowed forUser:(PFUser *)user; + +/*! + @abstract Gets whether the given user is *explicitly* allowed to read this object. + Even if this returns `NO`, the user may still be able to access it if returns `YES` + or if the user belongs to a role that has access. + + @param user The user for which to retrive access. + + @returns `YES` if the user has *explicit* read access, otherwise `NO`. + */ +- (BOOL)getReadAccessForUser:(PFUser *)user; + +/*! + @abstract Set whether the given user is allowed to write this object. + + @param allowed Whether the given user can write this object. + @param user The user to assign access. + */ +- (void)setWriteAccess:(BOOL)allowed forUser:(PFUser *)user; + +/*! + @abstract Gets whether the given user is *explicitly* allowed to write this object. + Even if this returns `NO`, the user may still be able to write it if returns `YES` + or if the user belongs to a role that has access. + + @param user The user for which to retrive access. + + @returns `YES` if the user has *explicit* write access, otherwise `NO`. + */ +- (BOOL)getWriteAccessForUser:(PFUser *)user; + +///-------------------------------------- +/// @name Controlling Access Per-Role +///-------------------------------------- + +/*! + @abstract Get whether users belonging to the role with the given name are allowed to read this object. + Even if this returns `NO`, the role may still be able to read it if a parent role has read access. + + @param name The name of the role. + + @returns `YES` if the role has read access, otherwise `NO`. + */ +- (BOOL)getReadAccessForRoleWithName:(NSString *)name; + +/*! + @abstract Set whether users belonging to the role with the given name are allowed to read this object. + + @param allowed Whether the given role can read this object. + @param name The name of the role. + */ +- (void)setReadAccess:(BOOL)allowed forRoleWithName:(NSString *)name; + +/*! + @abstract Get whether users belonging to the role with the given name are allowed to write this object. + Even if this returns `NO`, the role may still be able to write it if a parent role has write access. + + @param name The name of the role. + + @returns `YES` if the role has read access, otherwise `NO`. + */ +- (BOOL)getWriteAccessForRoleWithName:(NSString *)name; + +/*! + @abstract Set whether users belonging to the role with the given name are allowed to write this object. + + @param allowed Whether the given role can write this object. + @param name The name of the role. + */ +- (void)setWriteAccess:(BOOL)allowed forRoleWithName:(NSString *)name; + +/*! + @abstract Get whether users belonging to the given role are allowed to read this object. + Even if this returns `NO`, the role may still be able to read it if a parent role has read access. + + @discussion The role must already be saved on the server and + it's data must have been fetched in order to use this method. + + @param role The name of the role. + + @returns `YES` if the role has read access, otherwise `NO`. + */ +- (BOOL)getReadAccessForRole:(PFRole *)role; + +/*! + @abstract Set whether users belonging to the given role are allowed to read this object. + + @discussion The role must already be saved on the server and + it's data must have been fetched in order to use this method. + + @param allowed Whether the given role can read this object. + @param role The role to assign access. + */ +- (void)setReadAccess:(BOOL)allowed forRole:(PFRole *)role; + +/*! + @abstract Get whether users belonging to the given role are allowed to write this object. + Even if this returns `NO`, the role may still be able to write it if a parent role has write access. + + @discussion The role must already be saved on the server and + it's data must have been fetched in order to use this method. + + @param role The name of the role. + + @returns `YES` if the role has write access, otherwise `NO`. + */ +- (BOOL)getWriteAccessForRole:(PFRole *)role; + +/*! + @abstract Set whether users belonging to the given role are allowed to write this object. + + @discussion The role must already be saved on the server and + it's data must have been fetched in order to use this method. + + @param allowed Whether the given role can write this object. + @param role The role to assign access. + */ +- (void)setWriteAccess:(BOOL)allowed forRole:(PFRole *)role; + +///-------------------------------------- +/// @name Setting Access Defaults +///-------------------------------------- + +/*! + @abstract Sets a default ACL that will be applied to all instances of when they are created. + + @param acl The ACL to use as a template for all instance of created after this method has been called. + This value will be copied and used as a template for the creation of new ACLs, so changes to the + instance after this method has been called will not be reflected in new instance of . + @param currentUserAccess - If `YES`, the `PFACL` that is applied to newly-created instance of will + provide read and write access to the <[PFUser currentUser]> at the time of creation. + - If `NO`, the provided `acl` will be used without modification. + - If `acl` is `nil`, this value is ignored. + */ ++ (void)setDefaultACL:(PF_NULLABLE PFACL *)acl withAccessForCurrentUser:(BOOL)currentUserAccess; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFACL.m b/Pods/Parse/Parse/PFACL.m new file mode 100644 index 0000000..59190f9 --- /dev/null +++ b/Pods/Parse/Parse/PFACL.m @@ -0,0 +1,363 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFACL.h" +#import "PFACLPrivate.h" + +#import "BFTask+Private.h" +#import "PFACLState.h" +#import "PFAssert.h" +#import "PFDefaultACLController.h" +#import "PFMacros.h" +#import "PFMutableACLState.h" +#import "PFObjectPrivate.h" +#import "PFObjectUtilities.h" +#import "PFRole.h" +#import "PFUser.h" +#import "PFUserPrivate.h" + +static NSString *const PFACLPublicKey_ = @"*"; +static NSString *const PFACLUnresolvedKey_ = @"*unresolved"; +static NSString *const PFACLCodingDataKey_ = @"ACL"; + +@interface PFACL () + +@property (atomic, strong, readwrite) PFACLState *state; + +@end + +@implementation PFACL { + PFUser *unresolvedUser; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _state = [[PFACLState alloc] init]; + + return self; +} + +///-------------------------------------- +#pragma mark - Default ACL +///-------------------------------------- + ++ (instancetype)ACL { + return [[self alloc] init]; +} + ++ (instancetype)ACLWithUser:(PFUser *)user { + PFACL *acl = [self ACL]; + [acl setReadAccess:YES forUser:user]; + [acl setWriteAccess:YES forUser:user]; + return acl; +} + ++ (instancetype)ACLWithDictionary:(NSDictionary *)dictionary { + return [[self alloc] initWithDictionary:dictionary]; +} + ++ (PFACL *)defaultACL { + return [[[PFDefaultACLController defaultController] getDefaultACLAsync] waitForResult:NULL + withMainThreadWarning:NO]; +} + ++ (void)setDefaultACL:(PFACL *)acl withAccessForCurrentUser:(BOOL)currentUserAccess { + [[PFDefaultACLController defaultController] setDefaultACLAsync:acl withCurrentUserAccess:currentUserAccess]; +} + +- (void)setShared:(BOOL)newShared { + self.state = [self.state copyByMutatingWithBlock:^(PFMutableACLState *newState) { + newState.shared = newShared; + }]; +} + +- (BOOL)isShared { + return self.state.shared; +} + +- (instancetype)createUnsharedCopy { + PFACL *newACL = [[self class] ACLWithDictionary:self.state.permissions]; + if (unresolvedUser) { + [newACL setReadAccess:[self getReadAccessForUser:unresolvedUser] forUser:unresolvedUser]; + [newACL setWriteAccess:[self getWriteAccessForUser:unresolvedUser] forUser:unresolvedUser]; + } + return newACL; +} + +- (void)resolveUser:(PFUser *)user { + if (user != unresolvedUser) { + return; + } + NSMutableDictionary *unresolvedPermissions = self.state.permissions[PFACLUnresolvedKey_]; + if (unresolvedPermissions) { + self.state = [self.state copyByMutatingWithBlock:^(PFMutableACLState *newState) { + newState.permissions[user.objectId] = unresolvedPermissions; + [newState.permissions removeObjectForKey:PFACLUnresolvedKey_]; + }]; + } + unresolvedUser = nil; +} + +- (BOOL)hasUnresolvedUser { + return unresolvedUser != nil; +} + +- (void)setAccess:(NSString *)accessType to:(BOOL)allowed forUserId:(NSString *)userId { + NSDictionary *permissions = self.state.permissions[userId]; + + // No change needed. + if ([permissions[accessType] boolValue] == allowed) { + return; + } + + NSMutableDictionary *newPermissions = [NSMutableDictionary dictionaryWithDictionary:permissions]; + if (allowed) { + newPermissions[accessType] = @YES; + } else { + [newPermissions removeObjectForKey:accessType]; + } + + self.state = [self.state copyByMutatingWithBlock:^(PFMutableACLState *newState) { + if (newPermissions.count) { + newState.permissions[userId] = [newPermissions copy]; + } else { + [newState.permissions removeObjectForKey:userId]; + } + }]; +} + +- (BOOL)getAccess:(NSString *)accessType forUserId:(NSString *)userId { + return [self.state.permissions[userId][accessType] boolValue]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + self = [self init]; + if (!self) return nil; + + // We iterate over the input ACL rather than just copying to + // permissionsById so that we can ensure it is the right format. + [dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *userId, NSDictionary *permissions, BOOL *stop) { + [permissions enumerateKeysAndObjectsUsingBlock:^(NSString *accessType, id obj, BOOL *stop) { + [self setAccess:accessType to:[obj boolValue] forUserId:userId]; + }]; + }]; + + return self; +} + +- (void)setReadAccess:(BOOL)allowed forUserId:(NSString *)userId { + PFParameterAssert(userId, @"Can't setReadAccess for nil userId."); + [self setAccess:@"read" to:allowed forUserId:userId]; +} + +- (BOOL)getReadAccessForUserId:(NSString *)userId { + PFParameterAssert(userId, @"Can't getReadAccessForUserId for nil userId."); + return [self getAccess:@"read" forUserId:userId]; +} + +- (void)setWriteAccess:(BOOL)allowed forUserId:(NSString *)userId { + PFParameterAssert(userId, @"Can't setWriteAccess for nil userId."); + [self setAccess:@"write" to:allowed forUserId:userId]; +} + +- (BOOL)getWriteAccessForUserId:(NSString *)userId { + PFParameterAssert(userId, @"Can't getWriteAccessForUserId for nil userId."); + return [self getAccess:@"write" forUserId:userId]; +} + +- (void)setPublicReadAccess:(BOOL)allowed { + [self setReadAccess:allowed forUserId:PFACLPublicKey_]; +} + +- (BOOL)getPublicReadAccess { + return [self getReadAccessForUserId:PFACLPublicKey_]; +} + +- (void)setPublicWriteAccess:(BOOL)allowed { + [self setWriteAccess:allowed forUserId:PFACLPublicKey_]; +} + +- (BOOL)getPublicWriteAccess { + return [self getWriteAccessForUserId:PFACLPublicKey_]; +} + +- (BOOL)getReadAccessForRoleWithName:(NSString *)name { + PFParameterAssert(name, @"Can't get read access for nil role name."); + return [self getReadAccessForUserId:[@"role:" stringByAppendingString:name]]; +} + +- (void)setReadAccess:(BOOL)allowed forRoleWithName:(NSString *)name { + PFParameterAssert(name, @"Can't set read access for nil role name."); + [self setReadAccess:allowed forUserId:[@"role:" stringByAppendingString:name]]; +} + +- (BOOL)getWriteAccessForRoleWithName:(NSString *)name { + PFParameterAssert(name, @"Can't get write access for nil role name."); + return [self getWriteAccessForUserId:[@"role:" stringByAppendingString:name]]; +} + +- (void)setWriteAccess:(BOOL)allowed forRoleWithName:(NSString *)name { + PFParameterAssert(name, @"Can't set write access for nil role name."); + [self setWriteAccess:allowed forUserId:[@"role:" stringByAppendingString:name]]; +} + +- (void)validateRoleState:(PFRole *)role { + // Validates that a role has already been saved to the server, and thus can be used in an ACL. + PFParameterAssert(role.objectId, @"Roles must be saved to the server before they can be used in an ACL."); +} + +- (BOOL)getReadAccessForRole:(PFRole *)role { + [self validateRoleState:role]; + return [self getReadAccessForRoleWithName:role.name]; +} + +- (void)setReadAccess:(BOOL)allowed forRole:(PFRole *)role { + [self validateRoleState:role]; + [self setReadAccess:allowed forRoleWithName:role.name]; +} + +- (BOOL)getWriteAccessForRole:(PFRole *)role { + [self validateRoleState:role]; + return [self getWriteAccessForRoleWithName:role.name]; +} + +- (void)setWriteAccess:(BOOL)allowed forRole:(PFRole *)role { + [self validateRoleState:role]; + [self setWriteAccess:allowed forRoleWithName:role.name]; +} + +- (void)prepareUnresolvedUser:(PFUser *)user { + // TODO: (nlutsenko) Consider making @synchronized. + if (unresolvedUser != user) { + // If the unresolved user changed, register the save listener on the new user. This listener + // will call resolveUser with the user. + self.state = [self.state copyByMutatingWithBlock:^(PFMutableACLState *newState) { + [newState.permissions removeObjectForKey:PFACLUnresolvedKey_]; + }]; + + unresolvedUser = user; + + // Note: callback is a reference back to the same block so that it can unregister itself. + @weakify(self); + __weak __block void (^weakCallback)(id result, NSError *error) = nil; + __block void (^callback)(id result, NSError *error) = [^(id result, NSError *error) { + @strongify(self); + [self resolveUser:result]; + [result unregisterSaveListener:weakCallback]; + } copy]; + weakCallback = callback; + [user registerSaveListener:callback]; + } +} + +- (void)setUnresolvedReadAccess:(BOOL)allowed forUser:(PFUser *)user { + [self prepareUnresolvedUser:user]; + [self setReadAccess:allowed forUserId:PFACLUnresolvedKey_]; +} + +- (void)setReadAccess:(BOOL)allowed forUser:(PFUser *)user { + NSString *objectId = user.objectId; + if (!objectId) { + if ([user isLazy]) { + [self setUnresolvedReadAccess:allowed forUser:user]; + return; + } + PFParameterAssert(objectId, @"Can't setReadAcccess for unsaved user."); + } + [self setReadAccess:allowed forUserId:objectId]; +} + +- (BOOL)getReadAccessForUser:(PFUser *)user { + if (user == unresolvedUser) { + return [self getReadAccessForUserId:PFACLUnresolvedKey_]; + } + NSString *objectId = user.objectId; + PFParameterAssert(objectId, @"Can't getReadAccessForUser who isn't saved."); + return [self getReadAccessForUserId:objectId]; +} + +- (void)setUnresolvedWriteAccess:(BOOL)allowed forUser:(PFUser *)user { + [self prepareUnresolvedUser:user]; + [self setWriteAccess:allowed forUserId:PFACLUnresolvedKey_]; +} + +- (void)setWriteAccess:(BOOL)allowed forUser:(PFUser *)user { + NSString *objectId = user.objectId; + if (!objectId) { + if ([user isLazy]) { + [self setUnresolvedWriteAccess:allowed forUser:user]; + return; + } + PFParameterAssert(objectId, @"Can't setWriteAccess for unsaved user."); + } + [self setWriteAccess:allowed forUserId:objectId]; +} + +- (BOOL)getWriteAccessForUser:(PFUser *)user { + if (user == unresolvedUser) { + return [self getWriteAccessForUserId:PFACLUnresolvedKey_]; + } + NSString *objectId = user.objectId; + PFParameterAssert(objectId, @"Can't getWriteAccessForUser who isn't saved."); + return [self getWriteAccessForUserId:objectId]; +} + +- (NSDictionary *)encodeIntoDictionary { + return self.state.permissions; +} + +///-------------------------------------- +#pragma mark - NSObject +///-------------------------------------- + +- (BOOL)isEqual:(id)object { + if (object == self) { + return YES; + } + if (![object isKindOfClass:[PFACL class]]) { + return NO; + } + + PFACL *acl = (PFACL *)object; + return [self.state isEqual:acl.state] && [PFObjectUtilities isObject:self->unresolvedUser + equalToObject:acl->unresolvedUser]; +} + +- (NSUInteger)hash { + return [self.state hash] ^ [unresolvedUser hash]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[PFACL allocWithZone:zone] initWithDictionary:self.state.permissions]; +} + +///-------------------------------------- +#pragma mark - NSCoding +///-------------------------------------- + +- (instancetype)initWithCoder:(NSCoder *)coder { + NSDictionary *dictionary = [coder decodeObjectForKey:PFACLCodingDataKey_]; + return [self initWithDictionary:dictionary]; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:[self encodeIntoDictionary] forKey:PFACLCodingDataKey_]; +} + +@end diff --git a/Pods/Parse/Parse/PFAnalytics.h b/Pods/Parse/Parse/PFAnalytics.h new file mode 100644 index 0000000..7655e8f --- /dev/null +++ b/Pods/Parse/Parse/PFAnalytics.h @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + `PFAnalytics` provides an interface to Parse's logging and analytics backend. + + Methods will return immediately and cache the request (+ timestamp) to be + handled "eventually." That is, the request will be sent immediately if possible + or the next time a network connection is available. + */ +@interface PFAnalytics : NSObject + +///-------------------------------------- +/// @name App-Open / Push Analytics +///-------------------------------------- + +/*! + @abstract Tracks this application being launched. If this happened as the result of the + user opening a push notification, this method sends along information to + correlate this open with that push. + + @discussion Pass in `nil` to track a standard "application opened" event. + + @param launchOptions The `NSDictionary` indicating the reason the application was + launched, if any. This value can be found as a parameter to various + `UIApplicationDelegate` methods, and can be empty or `nil`. + + @returns Returns the task encapsulating the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)trackAppOpenedWithLaunchOptions:(PF_NULLABLE NSDictionary *)launchOptions; + +/*! + @abstract Tracks this application being launched. + If this happened as the result of the user opening a push notification, + this method sends along information to correlate this open with that push. + + @discussion Pass in `nil` to track a standard "application opened" event. + + @param launchOptions The dictionary indicating the reason the application was + launched, if any. This value can be found as a parameter to various + `UIApplicationDelegate` methods, and can be empty or `nil`. + @param block The block to execute on server response. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)trackAppOpenedWithLaunchOptionsInBackground:(PF_NULLABLE NSDictionary *)launchOptions + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract Tracks this application being launched. If this happened as the result of the + user opening a push notification, this method sends along information to + correlate this open with that push. + + @param userInfo The Remote Notification payload, if any. This value can be + found either under `UIApplicationLaunchOptionsRemoteNotificationKey` on `launchOptions`, + or as a parameter to `application:didReceiveRemoteNotification:`. + This can be empty or `nil`. + + @returns Returns the task encapsulating the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)trackAppOpenedWithRemoteNotificationPayload:(PF_NULLABLE NSDictionary *)userInfo; + +/*! + @abstract Tracks this application being launched. If this happened as the result of the + user opening a push notification, this method sends along information to + correlate this open with that push. + + @param userInfo The Remote Notification payload, if any. This value can be + found either under `UIApplicationLaunchOptionsRemoteNotificationKey` on `launchOptions`, + or as a parameter to `application:didReceiveRemoteNotification:`. This can be empty or `nil`. + @param block The block to execute on server response. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)trackAppOpenedWithRemoteNotificationPayloadInBackground:(PF_NULLABLE NSDictionary *)userInfo + block:(PF_NULLABLE PFBooleanResultBlock)block; + +///-------------------------------------- +/// @name Custom Analytics +///-------------------------------------- + +/*! + @abstract Tracks the occurrence of a custom event. + + @discussion Parse will store a data point at the time of invocation with the given event name. + + @param name The name of the custom event to report to Parse as having happened. + + @returns Returns the task encapsulating the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)trackEvent:(NSString *)name; + +/*! + @abstract Tracks the occurrence of a custom event. Parse will store a data point at the + time of invocation with the given event name. The event will be sent at some + unspecified time in the future, even if Parse is currently inaccessible. + + @param name The name of the custom event to report to Parse as having happened. + @param block The block to execute on server response. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)trackEventInBackground:(NSString *)name block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract Tracks the occurrence of a custom event with additional dimensions. Parse will + store a data point at the time of invocation with the given event name. + + @discussion Dimensions will allow segmentation of the occurrences of this custom event. + Keys and values should be NSStrings, and will throw otherwise. + + To track a user signup along with additional metadata, consider the following: + + NSDictionary *dimensions = @{ @"gender": @"m", + @"source": @"web", + @"dayType": @"weekend" }; + [PFAnalytics trackEvent:@"signup" dimensions:dimensions]; + + @warning There is a default limit of 8 dimensions per event tracked. + + @param name The name of the custom event to report to Parse as having happened. + @param dimensions The `NSDictionary` of information by which to segment this event. + + @returns Returns the task encapsulating the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)trackEvent:(NSString *)name + dimensions:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, NSString *)*)dimensions; + +/*! + @abstract Tracks the occurrence of a custom event with additional dimensions. Parse will + store a data point at the time of invocation with the given event name. The + event will be sent at some unspecified time in the future, even if Parse is currently inaccessible. + + @discussionDimensions will allow segmentation of the occurrences of this custom event. + Keys and values should be NSStrings, and will throw otherwise. + + To track a user signup along with additional metadata, consider the following: + NSDictionary *dimensions = @{ @"gender": @"m", + @"source": @"web", + @"dayType": @"weekend" }; + [PFAnalytics trackEvent:@"signup" dimensions:dimensions]; + + There is a default limit of 8 dimensions per event tracked. + + @param name The name of the custom event to report to Parse as having happened. + @param dimensions The `NSDictionary` of information by which to segment this event. + @param block The block to execute on server response. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)trackEventInBackground:(NSString *)name + dimensions:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, NSString *)*)dimensions + block:(PF_NULLABLE PFBooleanResultBlock)block; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFAnalytics.m b/Pods/Parse/Parse/PFAnalytics.m new file mode 100644 index 0000000..514095a --- /dev/null +++ b/Pods/Parse/Parse/PFAnalytics.m @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAnalytics.h" +#import "PFAnalytics_Private.h" + +#import "BFTask+Private.h" +#import "PFAnalyticsController.h" +#import "PFAssert.h" +#import "PFEncoder.h" +#import "PFEventuallyQueue.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +@implementation PFAnalytics + +///-------------------------------------- +#pragma mark - App-Open / Push Analytics +///-------------------------------------- + ++ (BFTask PF_GENERIC(NSNumber *)*)trackAppOpenedWithLaunchOptions:(PF_NULLABLE NSDictionary *)launchOptions { +#if TARGET_OS_WATCH + NSDictionary *userInfo = nil; +#elif TARGET_OS_IOS + NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; +#elif PF_TARGET_OS_OSX + NSDictionary *userInfo = launchOptions[NSApplicationLaunchUserNotificationKey]; +#endif + + return [self trackAppOpenedWithRemoteNotificationPayload:userInfo]; +} + ++ (void)trackAppOpenedWithLaunchOptionsInBackground:(PF_NULLABLE NSDictionary *)launchOptions + block:(PF_NULLABLE PFBooleanResultBlock)block { + [[self trackAppOpenedWithLaunchOptions:launchOptions] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BFTask PF_GENERIC(NSNumber *)*)trackAppOpenedWithRemoteNotificationPayload:(PF_NULLABLE NSDictionary *)userInfo { + return [[[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + PFAnalyticsController *controller = [Parse _currentManager].analyticsController; + return [controller trackAppOpenedEventAsyncWithRemoteNotificationPayload:userInfo sessionToken:sessionToken]; + }] continueWithSuccessResult:@YES]; +} + ++ (void)trackAppOpenedWithRemoteNotificationPayloadInBackground:(PF_NULLABLE NSDictionary *)userInfo + block:(PF_NULLABLE PFBooleanResultBlock)block { + [[self trackAppOpenedWithRemoteNotificationPayload:userInfo] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Custom Analytics +///-------------------------------------- + ++ (BFTask PF_GENERIC(NSNumber *)*)trackEvent:(NSString *)name { + return [self trackEvent:name dimensions:nil]; +} + ++ (void)trackEventInBackground:(NSString *)name block:(PF_NULLABLE PFBooleanResultBlock)block { + [self trackEventInBackground:name dimensions:nil block:block]; +} + ++ (BFTask PF_GENERIC(NSNumber *)*)trackEvent:(NSString *)name + dimensions:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, NSString *) *)dimensions { + PFParameterAssert([[name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length], + @"A name for the custom event must be provided."); + [dimensions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + PFParameterAssert([key isKindOfClass:[NSString class]] && [obj isKindOfClass:[NSString class]], + @"trackEvent dimensions expect keys and values of type NSString."); + }]; + + return [[[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + PFAnalyticsController *controller = [Parse _currentManager].analyticsController; + return [controller trackEventAsyncWithName:name dimensions:dimensions sessionToken:sessionToken]; + }] continueWithSuccessResult:@YES]; +} + ++ (void)trackEventInBackground:(NSString *)name + dimensions:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, NSString *) *)dimensions + block:(PF_NULLABLE PFBooleanResultBlock)block { + [[self trackEvent:name dimensions:dimensions] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +@end diff --git a/Pods/Parse/Parse/PFAnonymousUtils.h b/Pods/Parse/Parse/PFAnonymousUtils.h new file mode 100644 index 0000000..ff9ed8b --- /dev/null +++ b/Pods/Parse/Parse/PFAnonymousUtils.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + Provides utility functions for working with Anonymously logged-in users. + Anonymous users have some unique characteristics: + + - Anonymous users don't need a user name or password. + - Once logged out, an anonymous user cannot be recovered. + - When the current user is anonymous, the following methods can be used to switch + to a different user or convert the anonymous user into a regular one: + - signUp converts an anonymous user to a standard user with the given username and password. + Data associated with the anonymous user is retained. + - logIn switches users without converting the anonymous user. + Data associated with the anonymous user will be lost. + - Service logIn (e.g. Facebook, Twitter) will attempt to convert + the anonymous user into a standard user by linking it to the service. + If a user already exists that is linked to the service, it will instead switch to the existing user. + - Service linking (e.g. Facebook, Twitter) will convert the anonymous user + into a standard user by linking it to the service. + */ +@interface PFAnonymousUtils : NSObject + +///-------------------------------------- +/// @name Creating an Anonymous User +///-------------------------------------- + +/*! + @abstract Creates an anonymous user asynchronously and sets as a result to `BFTask`. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(PFUser *)*)logInInBackground; + +/*! + @abstract Creates an anonymous user. + + @param block The block to execute when anonymous user creation is complete. + It should have the following argument signature: `^(PFUser *user, NSError *error)`. + */ ++ (void)logInWithBlock:(PF_NULLABLE PFUserResultBlock)block; + +/* + @abstract Creates an anonymous user. + + @param target Target object for the selector. + @param selector The selector that will be called when the asynchronous request is complete. + It should have the following signature: `(void)callbackWithUser:(PFUser *)user error:(NSError *)error`. + */ ++ (void)logInWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Determining Whether a User is Anonymous +///-------------------------------------- + +/*! + @abstract Whether the object is logged in anonymously. + + @param user object to check for anonymity. The user must be logged in on this device. + + @returns `YES` if the user is anonymous. `NO` if the user is not the current user or is not anonymous. + */ ++ (BOOL)isLinkedWithUser:(PF_NULLABLE PFUser *)user; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFAnonymousUtils.m b/Pods/Parse/Parse/PFAnonymousUtils.m new file mode 100644 index 0000000..073e525 --- /dev/null +++ b/Pods/Parse/Parse/PFAnonymousUtils.m @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFAnonymousUtils.h" +#import "PFAnonymousUtils_Private.h" + +#import "BFTask+Private.h" +#import "PFAnonymousAuthenticationProvider.h" +#import "PFInternalUtils.h" +#import "PFUserPrivate.h" + +@implementation PFAnonymousUtils + +///-------------------------------------- +#pragma mark - Log In +///-------------------------------------- + ++ (BFTask *)logInInBackground { + PFAnonymousAuthenticationProvider *provider = [self _authenticationProvider]; + return [PFUser logInWithAuthTypeInBackground:PFAnonymousUserAuthenticationType authData:provider.authData]; +} + ++ (void)logInWithBlock:(PFUserResultBlock)block { + [[self logInInBackground] thenCallBackOnMainThreadAsync:block]; +} + ++ (void)logInWithTarget:(id)target selector:(SEL)selector { + [self logInWithBlock:^(PFUser *user, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:user object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Link +///-------------------------------------- + ++ (BOOL)isLinkedWithUser:(PFUser *)user { + return [user isLinkedWithAuthType:PFAnonymousUserAuthenticationType]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +static PFAnonymousAuthenticationProvider *authenticationProvider_; + ++ (dispatch_queue_t)_providerAccessQueue { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("com.parse.anonymousUtils.provider.access", DISPATCH_QUEUE_SERIAL); + }); + return queue; +} + ++ (PFAnonymousAuthenticationProvider *)_authenticationProvider { + __block PFAnonymousAuthenticationProvider *provider = nil; + dispatch_sync([self _providerAccessQueue], ^{ + provider = authenticationProvider_; + if (!provider) { + provider = [[PFAnonymousAuthenticationProvider alloc] init]; + [PFUser registerAuthenticationDelegate:provider forAuthType:PFAnonymousUserAuthenticationType]; + authenticationProvider_ = provider; + } + }); + return provider; +} + ++ (void)_clearAuthenticationProvider { + [PFUser _unregisterAuthenticationDelegateForAuthType:PFAnonymousUserAuthenticationType]; + dispatch_sync([self _providerAccessQueue], ^{ + authenticationProvider_ = nil; + }); +} + +///-------------------------------------- +#pragma mark - Lazy Login +///-------------------------------------- + ++ (PFUser *)_lazyLogIn { + PFAnonymousAuthenticationProvider *provider = [self _authenticationProvider]; + return [PFUser logInLazyUserWithAuthType:PFAnonymousUserAuthenticationType authData:provider.authData]; +} + +@end diff --git a/Pods/Parse/Parse/PFCloud.h b/Pods/Parse/Parse/PFCloud.h new file mode 100644 index 0000000..b807b75 --- /dev/null +++ b/Pods/Parse/Parse/PFCloud.h @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFCloud` class provides methods for interacting with Parse Cloud Functions. + */ +@interface PFCloud : NSObject + +/*! + @abstract Calls the given cloud function *synchronously* with the parameters provided. + + @param function The function name to call. + @param parameters The parameters to send to the function. + + @returns The response from the cloud function. + */ ++ (PF_NULLABLE_S id)callFunction:(NSString *)function + withParameters:(PF_NULLABLE NSDictionary *)parameters PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Calls the given cloud function *synchronously* with the parameters provided and + sets the error if there is one. + + @param function The function name to call. + @param parameters The parameters to send to the function. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns The response from the cloud function. + This result could be a `NSDictionary`, an `NSArray`, `NSNumber` or `NSString`. + */ ++ (PF_NULLABLE_S id)callFunction:(NSString *)function + withParameters:(PF_NULLABLE NSDictionary *)parameters + error:(NSError **)error; + +/*! + @abstract Calls the given cloud function *asynchronously* with the parameters provided. + + @param function The function name to call. + @param parameters The parameters to send to the function. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(id) *)callFunctionInBackground:(NSString *)function + withParameters:(PF_NULLABLE NSDictionary *)parameters; + +/*! + @abstract Calls the given cloud function *asynchronously* with the parameters provided + and executes the given block when it is done. + + @param function The function name to call. + @param parameters The parameters to send to the function. + @param block The block to execute when the function call finished. + It should have the following argument signature: `^(id result, NSError *error)`. + */ ++ (void)callFunctionInBackground:(NSString *)function + withParameters:(PF_NULLABLE NSDictionary *)parameters + block:(PF_NULLABLE PFIdResultBlock)block; + +/* + @abstract Calls the given cloud function *asynchronously* with the parameters provided + and then executes the given selector when it is done. + + @param function The function name to call. + @param parameters The parameters to send to the function. + @param target The object to call the selector on. + @param selector The selector to call when the function call finished. + It should have the following signature: `(void)callbackWithResult:(id)result error:(NSError *)error`. + Result will be `nil` if error is set and vice versa. + */ ++ (void)callFunctionInBackground:(NSString *)function + withParameters:(PF_NULLABLE NSDictionary *)parameters + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFCloud.m b/Pods/Parse/Parse/PFCloud.m new file mode 100644 index 0000000..bb019ae --- /dev/null +++ b/Pods/Parse/Parse/PFCloud.m @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFCloud.h" + +#import "BFTask+Private.h" +#import "PFCloudCodeController.h" +#import "PFCommandResult.h" +#import "PFCoreManager.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +@implementation PFCloud + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + ++ (id)callFunction:(NSString *)function withParameters:(NSDictionary *)parameters { + return [self callFunction:function withParameters:parameters error:nil]; +} + ++ (id)callFunction:(NSString *)function withParameters:(NSDictionary *)parameters error:(NSError **)error { + return [[self callFunctionInBackground:function withParameters:parameters] waitForResult:error]; +} + ++ (BFTask *)callFunctionInBackground:(NSString *)functionName withParameters:(NSDictionary *)parameters { + return [[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + PFCloudCodeController *controller = [Parse _currentManager].coreManager.cloudCodeController; + return [controller callCloudCodeFunctionAsync:functionName + withParameters:parameters + sessionToken:sessionToken]; + }]; +} + ++ (void)callFunctionInBackground:(NSString *)function + withParameters:(NSDictionary *)parameters + target:(id)target + selector:(SEL)selector { + [self callFunctionInBackground:function withParameters:parameters block:^(id results, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:results object:error]; + }]; +} + ++ (void)callFunctionInBackground:(NSString *)function + withParameters:(NSDictionary *)parameters + block:(PFIdResultBlock)block { + [[self callFunctionInBackground:function withParameters:parameters] thenCallBackOnMainThreadAsync:block]; +} + +@end diff --git a/Pods/Parse/Parse/PFConfig.h b/Pods/Parse/Parse/PFConfig.h new file mode 100644 index 0000000..7420692 --- /dev/null +++ b/Pods/Parse/Parse/PFConfig.h @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +@class PFConfig; + +typedef void(^PFConfigResultBlock)(PFConfig *PF_NULLABLE_S config, NSError *PF_NULLABLE_S error); + +/*! + `PFConfig` is a representation of the remote configuration object. + It enables you to add things like feature gating, a/b testing or simple "Message of the day". + */ +@interface PFConfig : NSObject + +///-------------------------------------- +/// @name Current Config +///-------------------------------------- + +/*! + @abstract Returns the most recently fetched config. + + @discussion If there was no config fetched - this method will return an empty instance of `PFConfig`. + + @returns Current, last fetched instance of PFConfig. + */ ++ (PFConfig *)currentConfig; + +///-------------------------------------- +/// @name Retrieving Config +///-------------------------------------- + +/*! + @abstract Gets the `PFConfig` object *synchronously* from the server. + + @returns Instance of `PFConfig` if the operation succeeded, otherwise `nil`. + */ ++ (PF_NULLABLE PFConfig *)getConfig PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Gets the `PFConfig` object *synchronously* from the server and sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Instance of PFConfig if the operation succeeded, otherwise `nil`. + */ ++ (PF_NULLABLE PFConfig *)getConfig:(NSError **)error; + +/*! + @abstract Gets the `PFConfig` *asynchronously* and sets it as a result of a task. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(PFConfig *)*)getConfigInBackground; + +/*! + @abstract Gets the `PFConfig` *asynchronously* and executes the given callback block. + + @param block The block to execute. + It should have the following argument signature: `^(PFConfig *config, NSError *error)`. + */ ++ (void)getConfigInBackgroundWithBlock:(PF_NULLABLE PFConfigResultBlock)block; + +///-------------------------------------- +/// @name Parameters +///-------------------------------------- + +/*! + @abstract Returns the object associated with a given key. + + @param key The key for which to return the corresponding configuration value. + + @returns The value associated with `key`, or `nil` if there is no such value. + */ +- (PF_NULLABLE_S id)objectForKey:(NSString *)key; + +/*! + @abstract Returns the object associated with a given key. + + @discussion This method enables usage of literal syntax on `PFConfig`. + E.g. `NSString *value = config[@"key"];` + + @see objectForKey: + + @param keyedSubscript The keyed subscript for which to return the corresponding configuration value. + + @returns The value associated with `key`, or `nil` if there is no such value. + */ +- (PF_NULLABLE_S id)objectForKeyedSubscript:(NSString *)keyedSubscript; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFConfig.m b/Pods/Parse/Parse/PFConfig.m new file mode 100644 index 0000000..b4d1655 --- /dev/null +++ b/Pods/Parse/Parse/PFConfig.m @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFConfig.h" + +#import "BFTask+Private.h" +#import "PFConfigController.h" +#import "PFCoreManager.h" +#import "PFCurrentConfigController.h" +#import "PFCurrentUserController.h" +#import "PFInternalUtils.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +NSString *const PFConfigParametersRESTKey = @"params"; + +@interface PFConfig () + +@property (atomic, copy, readwrite) NSDictionary *parametersDictionary; + +@end + +@implementation PFConfig + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (PFConfigController *)_configController { + return [Parse _currentManager].coreManager.configController; +} + +#pragma mark Public + ++ (PFConfig *)currentConfig { + return [[[self _configController].currentConfigController getCurrentConfigAsync] waitForResult:nil + withMainThreadWarning:NO]; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithFetchedConfig:(NSDictionary *)resultDictionary { + self = [self init]; + if (!self) return nil; + + _parametersDictionary = resultDictionary[PFConfigParametersRESTKey]; + + return self; +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + ++ (PFConfig *)getConfig { + return [self getConfig:nil]; +} + ++ (PFConfig *)getConfig:(NSError **)error { + return [[self getConfigInBackground] waitForResult:error]; +} + ++ (BFTask *)getConfigInBackground { + PFCurrentUserController *controller = [Parse _currentManager].coreManager.currentUserController; + return [[controller getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [[self _configController] fetchConfigAsyncWithSessionToken:sessionToken]; + }]; +} + ++ (void)getConfigInBackgroundWithBlock:(PFConfigResultBlock)block { + [[self getConfigInBackground] thenCallBackOnMainThreadAsync:block]; +} + +///-------------------------------------- +#pragma mark - Getting Values +///-------------------------------------- + +- (id)objectForKey:(NSString *)key { + return _parametersDictionary[key]; +} + +- (id)objectForKeyedSubscript:(NSString *)keyedSubscript { + return _parametersDictionary[keyedSubscript]; +} + +#pragma mark Equality Testing + +- (NSUInteger)hash { + return [_parametersDictionary hash]; +} + +- (BOOL)isEqual:(id)object { + if ([object isKindOfClass:[PFConfig class]]) { + PFConfig *other = object; + + // Compare pointers first, to account for nil dictionary + return self.parametersDictionary == other.parametersDictionary || + [self.parametersDictionary isEqual:other.parametersDictionary]; + } + + return NO; +} + +@end diff --git a/Pods/Parse/Parse/PFConstants.h b/Pods/Parse/Parse/PFConstants.h new file mode 100644 index 0000000..c46eab0 --- /dev/null +++ b/Pods/Parse/Parse/PFConstants.h @@ -0,0 +1,509 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class PFObject; +@class PFUser; + +///-------------------------------------- +/// @name Version +///-------------------------------------- + +#define PARSE_VERSION @"1.9.0" + +extern NSInteger const PARSE_API_VERSION; + +///-------------------------------------- +/// @name Platform +///-------------------------------------- + +#define PARSE_IOS_ONLY (TARGET_OS_IPHONE) +#define PARSE_OSX_ONLY (TARGET_OS_MAC && !(TARGET_OS_IPHONE)) + +extern NSString *const PF_NONNULL_S kPFDeviceType; + +#if PARSE_IOS_ONLY +#import +#else +#import +#endif + +///-------------------------------------- +/// @name Server +///-------------------------------------- + +extern NSString *const PF_NONNULL_S kPFParseServer; + +///-------------------------------------- +/// @name Cache Policies +///-------------------------------------- + +/*! + `PFCachePolicy` specifies different caching policies that could be used with . + + This lets you show data when the user's device is offline, + or when the app has just started and network requests have not yet had time to complete. + Parse takes care of automatically flushing the cache when it takes up too much space. + + @warning Cache policy could only be set when Local Datastore is not enabled. + + @see PFQuery + */ +typedef NS_ENUM(uint8_t, PFCachePolicy) { + /*! + @abstract The query does not load from the cache or save results to the cache. + This is the default cache policy. + */ + kPFCachePolicyIgnoreCache = 0, + /*! + @abstract The query only loads from the cache, ignoring the network. + If there are no cached results, this causes a `NSError` with `kPFErrorCacheMiss` code. + */ + kPFCachePolicyCacheOnly, + /*! + @abstract The query does not load from the cache, but it will save results to the cache. + */ + kPFCachePolicyNetworkOnly, + /*! + @abstract The query first tries to load from the cache, but if that fails, it loads results from the network. + If there are no cached results, this causes a `NSError` with `kPFErrorCacheMiss` code. + */ + kPFCachePolicyCacheElseNetwork, + /*! + @abstract The query first tries to load from the network, but if that fails, it loads results from the cache. + If there are no cached results, this causes a `NSError` with `kPFErrorCacheMiss` code. + */ + kPFCachePolicyNetworkElseCache, + /*! + @abstract The query first loads from the cache, then loads from the network. + The callback will be called twice - first with the cached results, then with the network results. + Since it returns two results at different times, this cache policy cannot be used with synchronous or task methods. + */ + kPFCachePolicyCacheThenNetwork +}; + +///-------------------------------------- +/// @name Logging Levels +///-------------------------------------- + +/*! + `PFLogLevel` enum specifies different levels of logging that could be used to limit or display more messages in logs. + + @see [Parse setLogLevel:] + @see [Parse logLevel] + */ +typedef NS_ENUM(uint8_t, PFLogLevel) { + /*! + Log level that disables all logging. + */ + PFLogLevelNone = 0, + /*! + Log level that if set is going to output error messages to the log. + */ + PFLogLevelError = 1, + /*! + Log level that if set is going to output the following messages to log: + - Errors + - Warnings + */ + PFLogLevelWarning = 2, + /*! + Log level that if set is going to output the following messages to log: + - Errors + - Warnings + - Informational messages + */ + PFLogLevelInfo = 3, + /*! + Log level that if set is going to output the following messages to log: + - Errors + - Warnings + - Informational messages + - Debug messages + */ + PFLogLevelDebug = 4 +}; + +///-------------------------------------- +/// @name Errors +///-------------------------------------- + +extern NSString *const PF_NONNULL_S PFParseErrorDomain; + +/*! + `PFErrorCode` enum contains all custom error codes that are used as `code` for `NSError` for callbacks on all classes. + + These codes are used when `domain` of `NSError` that you receive is set to `PFParseErrorDomain`. + */ +typedef NS_ENUM(NSInteger, PFErrorCode) { + /*! + @abstract Internal server error. No information available. + */ + kPFErrorInternalServer = 1, + /*! + @abstract The connection to the Parse servers failed. + */ + kPFErrorConnectionFailed = 100, + /*! + @abstract Object doesn't exist, or has an incorrect password. + */ + kPFErrorObjectNotFound = 101, + /*! + @abstract You tried to find values matching a datatype that doesn't + support exact database matching, like an array or a dictionary. + */ + kPFErrorInvalidQuery = 102, + /*! + @abstract Missing or invalid classname. Classnames are case-sensitive. + They must start with a letter, and `a-zA-Z0-9_` are the only valid characters. + */ + kPFErrorInvalidClassName = 103, + /*! + @abstract Missing object id. + */ + kPFErrorMissingObjectId = 104, + /*! + @abstract Invalid key name. Keys are case-sensitive. + They must start with a letter, and `a-zA-Z0-9_` are the only valid characters. + */ + kPFErrorInvalidKeyName = 105, + /*! + @abstract Malformed pointer. Pointers must be arrays of a classname and an object id. + */ + kPFErrorInvalidPointer = 106, + /*! + @abstract Malformed json object. A json dictionary is expected. + */ + kPFErrorInvalidJSON = 107, + /*! + @abstract Tried to access a feature only available internally. + */ + kPFErrorCommandUnavailable = 108, + /*! + @abstract Field set to incorrect type. + */ + kPFErrorIncorrectType = 111, + /*! + @abstract Invalid channel name. A channel name is either an empty string (the broadcast channel) + or contains only `a-zA-Z0-9_` characters and starts with a letter. + */ + kPFErrorInvalidChannelName = 112, + /*! + @abstract Invalid device token. + */ + kPFErrorInvalidDeviceToken = 114, + /*! + @abstract Push is misconfigured. See details to find out how. + */ + kPFErrorPushMisconfigured = 115, + /*! + @abstract The object is too large. + */ + kPFErrorObjectTooLarge = 116, + /*! + @abstract That operation isn't allowed for clients. + */ + kPFErrorOperationForbidden = 119, + /*! + @abstract The results were not found in the cache. + */ + kPFErrorCacheMiss = 120, + /*! + @abstract Keys in `NSDictionary` values may not include `$` or `.`. + */ + kPFErrorInvalidNestedKey = 121, + /*! + @abstract Invalid file name. + A file name can contain only `a-zA-Z0-9_.` characters and should be between 1 and 36 characters. + */ + kPFErrorInvalidFileName = 122, + /*! + @abstract Invalid ACL. An ACL with an invalid format was saved. This should not happen if you use . + */ + kPFErrorInvalidACL = 123, + /*! + @abstract The request timed out on the server. Typically this indicates the request is too expensive. + */ + kPFErrorTimeout = 124, + /*! + @abstract The email address was invalid. + */ + kPFErrorInvalidEmailAddress = 125, + /*! + A unique field was given a value that is already taken. + */ + kPFErrorDuplicateValue = 137, + /*! + @abstract Role's name is invalid. + */ + kPFErrorInvalidRoleName = 139, + /*! + @abstract Exceeded an application quota. Upgrade to resolve. + */ + kPFErrorExceededQuota = 140, + /*! + @abstract Cloud Code script had an error. + */ + kPFScriptError = 141, + /*! + @abstract Cloud Code validation failed. + */ + kPFValidationError = 142, + /*! + @abstract Product purchase receipt is missing. + */ + kPFErrorReceiptMissing = 143, + /*! + @abstract Product purchase receipt is invalid. + */ + kPFErrorInvalidPurchaseReceipt = 144, + /*! + @abstract Payment is disabled on this device. + */ + kPFErrorPaymentDisabled = 145, + /*! + @abstract The product identifier is invalid. + */ + kPFErrorInvalidProductIdentifier = 146, + /*! + @abstract The product is not found in the App Store. + */ + kPFErrorProductNotFoundInAppStore = 147, + /*! + @abstract The Apple server response is not valid. + */ + kPFErrorInvalidServerResponse = 148, + /*! + @abstract Product fails to download due to file system error. + */ + kPFErrorProductDownloadFileSystemFailure = 149, + /*! + @abstract Fail to convert data to image. + */ + kPFErrorInvalidImageData = 150, + /*! + @abstract Unsaved file. + */ + kPFErrorUnsavedFile = 151, + /*! + @abstract Fail to delete file. + */ + kPFErrorFileDeleteFailure = 153, + /*! + @abstract Application has exceeded its request limit. + */ + kPFErrorRequestLimitExceeded = 155, + /*! + @abstract Invalid event name. + */ + kPFErrorInvalidEventName = 160, + /*! + @abstract Username is missing or empty. + */ + kPFErrorUsernameMissing = 200, + /*! + @abstract Password is missing or empty. + */ + kPFErrorUserPasswordMissing = 201, + /*! + @abstract Username has already been taken. + */ + kPFErrorUsernameTaken = 202, + /*! + @abstract Email has already been taken. + */ + kPFErrorUserEmailTaken = 203, + /*! + @abstract The email is missing, and must be specified. + */ + kPFErrorUserEmailMissing = 204, + /*! + @abstract A user with the specified email was not found. + */ + kPFErrorUserWithEmailNotFound = 205, + /*! + @abstract The user cannot be altered by a client without the session. + */ + kPFErrorUserCannotBeAlteredWithoutSession = 206, + /*! + @abstract Users can only be created through sign up. + */ + kPFErrorUserCanOnlyBeCreatedThroughSignUp = 207, + /*! + @abstract An existing Facebook account already linked to another user. + */ + kPFErrorFacebookAccountAlreadyLinked = 208, + /*! + @abstract An existing account already linked to another user. + */ + kPFErrorAccountAlreadyLinked = 208, + /*! + Error code indicating that the current session token is invalid. + */ + kPFErrorInvalidSessionToken = 209, + kPFErrorUserIdMismatch = 209, + /*! + @abstract Facebook id missing from request. + */ + kPFErrorFacebookIdMissing = 250, + /*! + @abstract Linked id missing from request. + */ + kPFErrorLinkedIdMissing = 250, + /*! + @abstract Invalid Facebook session. + */ + kPFErrorFacebookInvalidSession = 251, + /*! + @abstract Invalid linked session. + */ + kPFErrorInvalidLinkedSession = 251, +}; + +///-------------------------------------- +/// @name Blocks +///-------------------------------------- + +typedef void (^PFBooleanResultBlock)(BOOL succeeded, NSError *PF_NULLABLE_S error); +typedef void (^PFIntegerResultBlock)(int number, NSError *PF_NULLABLE_S error); +typedef void (^PFArrayResultBlock)(NSArray *PF_NULLABLE_S objects, NSError *PF_NULLABLE_S error); +typedef void (^PFObjectResultBlock)(PFObject *PF_NULLABLE_S object, NSError *PF_NULLABLE_S error); +typedef void (^PFSetResultBlock)(NSSet *PF_NULLABLE_S channels, NSError *PF_NULLABLE_S error); +typedef void (^PFUserResultBlock)(PFUser *PF_NULLABLE_S user, NSError *PF_NULLABLE_S error); +typedef void (^PFDataResultBlock)(NSData *PF_NULLABLE_S data, NSError *PF_NULLABLE_S error); +typedef void (^PFDataStreamResultBlock)(NSInputStream *PF_NULLABLE_S stream, NSError *PF_NULLABLE_S error); +typedef void (^PFFilePathResultBlock)(NSString *PF_NULLABLE_S filePath, NSError *PF_NULLABLE_S error); +typedef void (^PFStringResultBlock)(NSString *PF_NULLABLE_S string, NSError *PF_NULLABLE_S error); +typedef void (^PFIdResultBlock)(PF_NULLABLE_S id object, NSError *PF_NULLABLE_S error); +typedef void (^PFProgressBlock)(int percentDone); + +///-------------------------------------- +/// @name Network Notifications +///-------------------------------------- + +/*! + @abstract The name of the notification that is going to be sent before any URL request is sent. + */ +extern NSString *const PF_NONNULL_S PFNetworkWillSendURLRequestNotification; + +/*! + @abstract The name of the notification that is going to be sent after any URL response is received. + */ +extern NSString *const PF_NONNULL_S PFNetworkDidReceiveURLResponseNotification; + +/*! + @abstract The key of request(NSURLRequest) in the userInfo dictionary of a notification. + @note This key is populated in userInfo, only if `PFLogLevel` on `Parse` is set to `PFLogLevelDebug`. + */ +extern NSString *const PF_NONNULL_S PFNetworkNotificationURLRequestUserInfoKey; + +/*! + @abstract The key of response(NSHTTPURLResponse) in the userInfo dictionary of a notification. + @note This key is populated in userInfo, only if `PFLogLevel` on `Parse` is set to `PFLogLevelDebug`. + */ +extern NSString *const PF_NONNULL_S PFNetworkNotificationURLResponseUserInfoKey; + +/*! + @abstract The key of repsonse body (usually `NSString` with JSON) in the userInfo dictionary of a notification. + @note This key is populated in userInfo, only if `PFLogLevel` on `Parse` is set to `PFLogLevelDebug`. + */ +extern NSString *const PF_NONNULL_S PFNetworkNotificationURLResponseBodyUserInfoKey; + + +///-------------------------------------- +/// @name Deprecated Macros +///-------------------------------------- + +#ifndef PARSE_DEPRECATED +# ifdef __deprecated_msg +# define PARSE_DEPRECATED(_MSG) __deprecated_msg(_MSG) +# else +# ifdef __deprecated +# define PARSE_DEPRECATED(_MSG) __attribute__((deprecated)) +# else +# define PARSE_DEPRECATED(_MSG) +# endif +# endif +#endif + +///-------------------------------------- +/// @name Extensions Macros +///-------------------------------------- + +#ifndef PF_EXTENSION_UNAVAILABLE +# if PARSE_IOS_ONLY +# ifdef NS_EXTENSION_UNAVAILABLE_IOS +# define PF_EXTENSION_UNAVAILABLE(_msg) NS_EXTENSION_UNAVAILABLE_IOS(_msg) +# else +# define PF_EXTENSION_UNAVAILABLE(_msg) +# endif +# else +# ifdef NS_EXTENSION_UNAVAILABLE_MAC +# define PF_EXTENSION_UNAVAILABLE(_msg) NS_EXTENSION_UNAVAILABLE_MAC(_msg) +# else +# define PF_EXTENSION_UNAVAILABLE(_msg) +# endif +# endif +#endif + +///-------------------------------------- +/// @name Swift Macros +///-------------------------------------- + +#ifndef PF_SWIFT_UNAVAILABLE +# ifdef NS_SWIFT_UNAVAILABLE +# define PF_SWIFT_UNAVAILABLE NS_SWIFT_UNAVAILABLE("") +# else +# define PF_SWIFT_UNAVAILABLE +# endif +#endif + +///-------------------------------------- +/// @name Obj-C Generics Macros +///-------------------------------------- + +#if __has_feature(objc_generics) || __has_extension(objc_generics) +# define PF_GENERIC(...) <__VA_ARGS__> +#else +# define PF_GENERIC(...) +# define PFGenericObject PFObject * +#endif + +///-------------------------------------- +/// @name Platform Availability Defines +///-------------------------------------- + +#ifndef TARGET_OS_IOS +# define TARGET_OS_IOS TARGET_OS_IPHONE +#endif +#ifndef TARGET_OS_WATCH +# define TARGET_OS_WATCH 0 +#endif +#ifndef TARGET_OS_TV +# define TARGET_OS_TV 0 +#endif + +#ifndef PF_TARGET_OS_OSX +# define PF_TARGET_OS_OSX TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV +#endif + +///-------------------------------------- +/// @name Avaiability Macros +///-------------------------------------- + +#ifndef PF_WATCH_UNAVAILABLE +# ifdef __WATCHOS_UNAVAILABLE +# define PF_WATCH_UNAVAILABLE __WATCHOS_UNAVAILABLE +# else +# define PF_WATCH_UNAVAILABLE +# endif +#endif diff --git a/Pods/Parse/Parse/PFConstants.m b/Pods/Parse/Parse/PFConstants.m new file mode 100644 index 0000000..174b5a7 --- /dev/null +++ b/Pods/Parse/Parse/PFConstants.m @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFConstants.h" + +NSInteger const PARSE_API_VERSION = 2; + +#if PARSE_IOS_ONLY +NSString *const kPFDeviceType = @"ios"; +#else +NSString *const kPFDeviceType = @"osx"; +#endif + +NSString *const kPFParseServer = @"https://api.parse.com"; + +NSString *const PFParseErrorDomain = @"Parse"; + +///-------------------------------------- +#pragma mark - Network Notifications +///-------------------------------------- + +NSString *const PFNetworkWillSendURLRequestNotification = @"PFNetworkWillSendURLRequestNotification"; +NSString *const PFNetworkDidReceiveURLResponseNotification = @"PFNetworkDidReceiveURLResponseNotification"; +NSString *const PFNetworkNotificationURLRequestUserInfoKey = @"PFNetworkNotificationURLRequestUserInfoKey"; +NSString *const PFNetworkNotificationURLResponseUserInfoKey = @"PFNetworkNotificationURLResponseUserInfoKey"; +NSString *const PFNetworkNotificationURLResponseBodyUserInfoKey = @"PFNetworkNotificationURLResponseBodyUserInfoKey"; diff --git a/Pods/Parse/Parse/PFFile.h b/Pods/Parse/Parse/PFFile.h new file mode 100644 index 0000000..c2313cc --- /dev/null +++ b/Pods/Parse/Parse/PFFile.h @@ -0,0 +1,446 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + `PFFile` representes a file of binary data stored on the Parse servers. + This can be a image, video, or anything else that an application needs to reference in a non-relational way. + */ +@interface PFFile : NSObject + +///-------------------------------------- +/// @name Creating a PFFile +///-------------------------------------- + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/*! + @abstract Creates a file with given data. A name will be assigned to it by the server. + + @param data The contents of the new `PFFile`. + + @returns A new `PFFile`. + */ ++ (PF_NULLABLE instancetype)fileWithData:(NSData *)data; + +/*! + @abstract Creates a file with given data and name. + + @param name The name of the new PFFile. The file name must begin with and + alphanumeric character, and consist of alphanumeric characters, periods, + spaces, underscores, or dashes. + @param data The contents of the new `PFFile`. + + @returns A new `PFFile` object. + */ ++ (PF_NULLABLE instancetype)fileWithName:(PF_NULLABLE NSString *)name data:(NSData *)data; + +/*! + @abstract Creates a file with the contents of another file. + + @warning This method raises an exception if the file at path is not accessible + or if there is not enough disk space left. + + @param name The name of the new `PFFile`. The file name must begin with and alphanumeric character, + and consist of alphanumeric characters, periods, spaces, underscores, or dashes. + @param path The path to the file that will be uploaded to Parse. + + @returns A new `PFFile` instance. + */ ++ (PF_NULLABLE instancetype)fileWithName:(PF_NULLABLE NSString *)name + contentsAtPath:(NSString *)path PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Creates a file with the contents of another file. + + @param name The name of the new `PFFile`. The file name must begin with and alphanumeric character, + and consist of alphanumeric characters, periods, spaces, underscores, or dashes. + @param path The path to the file that will be uploaded to Parse. + @param error On input, a pointer to an error object. + If an error occurs, this pointer is set to an actual error object containing the error information. + You may specify `nil` for this parameter if you do not want the error information. + + @returns A new `PFFile` instance or `nil` if the error occured. + */ ++ (PF_NULLABLE instancetype)fileWithName:(PF_NULLABLE NSString *)name + contentsAtPath:(NSString *)path + error:(NSError **)error; + +/*! + @abstract Creates a file with given data, name and content type. + + @warning This method raises an exception if the data supplied is not accessible or could not be saved. + + @param name The name of the new `PFFile`. The file name must begin with and alphanumeric character, + and consist of alphanumeric characters, periods, spaces, underscores, or dashes. + @param data The contents of the new `PFFile`. + @param contentType Represents MIME type of the data. + + @returns A new `PFFile` instance. + */ ++ (PF_NULLABLE instancetype)fileWithName:(PF_NULLABLE NSString *)name + data:(NSData *)data + contentType:(PF_NULLABLE NSString *)contentType PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Creates a file with given data, name and content type. + + @param name The name of the new `PFFile`. The file name must begin with and alphanumeric character, + and consist of alphanumeric characters, periods, spaces, underscores, or dashes. + @param data The contents of the new `PFFile`. + @param contentType Represents MIME type of the data. + @param error On input, a pointer to an error object. + If an error occurs, this pointer is set to an actual error object containing the error information. + You may specify `nil` for this parameter if you do not want the error information. + + @returns A new `PFFile` instance or `nil` if the error occured. + */ ++ (PF_NULLABLE instancetype)fileWithName:(PF_NULLABLE NSString *)name + data:(NSData *)data + contentType:(PF_NULLABLE NSString *)contentType + error:(NSError **)error; + +/*! + @abstract Creates a file with given data and content type. + + @param data The contents of the new `PFFile`. + @param contentType Represents MIME type of the data. + + @returns A new `PFFile` object. + */ ++ (instancetype)fileWithData:(NSData *)data contentType:(PF_NULLABLE NSString *)contentType; + +///-------------------------------------- +/// @name File Properties +///-------------------------------------- + +/*! + @abstract The name of the file. + + @discussion Before the file is saved, this is the filename given by + the user. After the file is saved, that name gets prefixed with a unique + identifier. + */ +@property (nonatomic, copy, readonly) NSString *name; + +/*! + @abstract The url of the file. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy, readonly) NSString *url; + +/*! + @abstract Whether the file has been uploaded for the first time. + */ +@property (nonatomic, assign, readonly) BOOL isDirty; + +///-------------------------------------- +/// @name Storing Data with Parse +///-------------------------------------- + +/*! + @abstract Saves the file *synchronously*. + + @returns Returns whether the save succeeded. + */ +- (BOOL)save PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Saves the file *synchronously* and sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the save succeeded. + */ +- (BOOL)save:(NSError **)error; + +/*! + @abstract Saves the file *asynchronously*. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)saveInBackground; + +/*! + @abstract Saves the file *asynchronously* + + @param progressBlock The block should have the following argument signature: `^(int percentDone)` + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)saveInBackgroundWithProgressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract Saves the file *asynchronously* and executes the given block. + + @param block The block should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)saveInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract Saves the file *asynchronously* and executes the given block. + + @discussion This method will execute the progressBlock periodically with the percent progress. + `progressBlock` will get called with `100` before `resultBlock` is called. + + @param block The block should have the following argument signature: `^(BOOL succeeded, NSError *error)` + @param progressBlock The block should have the following argument signature: `^(int percentDone)` + */ +- (void)saveInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block + progressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/* + @abstract Saves the file *asynchronously* and calls the given callback. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ +- (void)saveInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Getting Data from Parse +///-------------------------------------- + +/*! + @abstract Whether the data is available in memory or needs to be downloaded. + */ +@property (assign, readonly) BOOL isDataAvailable; + +/*! + @abstract *Synchronously* gets the data from cache if available or fetches its contents from the network. + + @returns The `NSData` object containing file data. Returns `nil` if there was an error in fetching. + */ +- (PF_NULLABLE NSData *)getData PF_SWIFT_UNAVAILABLE; + +/*! + @abstract This method is like but avoids ever holding the entire `PFFile` contents in memory at once. + + @discussion This can help applications with many large files avoid memory warnings. + + @returns A stream containing the data. Returns `nil` if there was an error in fetching. + */ +- (PF_NULLABLE NSInputStream *)getDataStream PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* gets the data from cache if available or fetches its contents from the network. + Sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns The `NSData` object containing file data. Returns `nil` if there was an error in fetching. + */ +- (PF_NULLABLE NSData *)getData:(NSError **)error; + +/*! + @abstract This method is like but avoids ever holding the entire `PFFile` contents in memory at once. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns A stream containing the data. Returns nil if there was an error in + fetching. + */ +- (PF_NULLABLE NSInputStream *)getDataStream:(NSError **)error; + +/*! + @abstract This method is like but it fetches asynchronously to avoid blocking the current thread. + + @see getData + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSData *)*)getDataInBackground; + +/*! + @abstract This method is like but it fetches asynchronously to avoid blocking the current thread. + + @discussion This can help applications with many large files avoid memory warnings. + + @see getData + + @param progressBlock The block should have the following argument signature: ^(int percentDone) + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSData *)*)getDataInBackgroundWithProgressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract This method is like but avoids + ever holding the entire `PFFile` contents in memory at once. + + @discussion This can help applications with many large files avoid memory warnings. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSInputStream *)*)getDataStreamInBackground; + +/*! + @abstract This method is like , but yields a live-updating stream. + + @discussion Instead of , which yields a stream that can be read from only after the request has + completed, this method gives you a stream directly written to by the HTTP session. As this stream is not pre-buffered, + it is strongly advised to use the `NSStreamDelegate` methods, in combination with a run loop, to consume the data in + the stream, to do proper async file downloading. + + @note You MUST open this stream before reading from it. + @note Do NOT call on this task from the main thread. It may result in a deadlock. + + @returns A task that produces a *live* stream that is being written to with the data from the server. + */ +- (BFTask PF_GENERIC(NSInputStream *)*)getDataDownloadStreamInBackground; + +/*! + @abstract This method is like but avoids + ever holding the entire `PFFile` contents in memory at once. + + @discussion This can help applications with many large files avoid memory warnings. + @param progressBlock The block should have the following argument signature: ^(int percentDone) + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSInputStream *)*)getDataStreamInBackgroundWithProgressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract This method is like , but yields a live-updating stream. + + @discussion Instead of , which yields a stream that can be read from only after the request has + completed, this method gives you a stream directly written to by the HTTP session. As this stream is not pre-buffered, + it is strongly advised to use the `NSStreamDelegate` methods, in combination with a run loop, to consume the data in + the stream, to do proper async file downloading. + + @note You MUST open this stream before reading from it. + @note Do NOT call on this task from the main thread. It may result in a deadlock. + + @param progressBlock The block should have the following argument signature: `^(int percentDone)` + + @returns A task that produces a *live* stream that is being written to with the data from the server. + */ +- (BFTask PF_GENERIC(NSInputStream *)*)getDataDownloadStreamInBackgroundWithProgressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract *Asynchronously* gets the data from cache if available or fetches its contents from the network. + + @param block The block should have the following argument signature: `^(NSData *result, NSError *error)` + */ +- (void)getDataInBackgroundWithBlock:(PF_NULLABLE PFDataResultBlock)block; + +/*! + @abstract This method is like but avoids + ever holding the entire `PFFile` contents in memory at once. + + @discussion This can help applications with many large files avoid memory warnings. + + @param block The block should have the following argument signature: `(NSInputStream *result, NSError *error)` + */ +- (void)getDataStreamInBackgroundWithBlock:(PF_NULLABLE PFDataStreamResultBlock)block; + +/*! + @abstract *Asynchronously* gets the data from cache if available or fetches its contents from the network. + + @discussion This method will execute the progressBlock periodically with the percent progress. + `progressBlock` will get called with `100` before `resultBlock` is called. + + @param resultBlock The block should have the following argument signature: ^(NSData *result, NSError *error) + @param progressBlock The block should have the following argument signature: ^(int percentDone) + */ +- (void)getDataInBackgroundWithBlock:(PF_NULLABLE PFDataResultBlock)resultBlock + progressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract This method is like but avoids + ever holding the entire `PFFile` contents in memory at once. + + @discussion This can help applications with many large files avoid memory warnings. + + @param resultBlock The block should have the following argument signature: `^(NSInputStream *result, NSError *error)`. + @param progressBlock The block should have the following argument signature: `^(int percentDone)`. + */ +- (void)getDataStreamInBackgroundWithBlock:(PF_NULLABLE PFDataStreamResultBlock)resultBlock + progressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/* + @abstract *Asynchronously* gets the data from cache if available or fetches its contents from the network. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSData *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + */ +- (void)getDataInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract *Asynchronously* gets the file path for file from cache if available or fetches its contents from the network. + + @note The file path may change between versions of SDK. + @note If you overwrite the contents of the file at returned path it will persist those change + until the file cache is cleared. + + @returns The task, with the result set to `NSString` representation of a file path. + */ +- (BFTask PF_GENERIC(NSString *)*)getFilePathInBackground; + +/*! + @abstract *Asynchronously* gets the file path for file from cache if available or fetches its contents from the network. + + @note The file path may change between versions of SDK. + @note If you overwrite the contents of the file at returned path it will persist those change + until the file cache is cleared. + + @param progressBlock The block should have the following argument signature: `^(int percentDone)`. + + @returns The task, with the result set to `NSString` representation of a file path. + */ +- (BFTask PF_GENERIC(NSString *)*)getFilePathInBackgroundWithProgressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +/*! + @abstract *Asynchronously* gets the file path for file from cache if available or fetches its contents from the network. + + @note The file path may change between versions of SDK. + @note If you overwrite the contents of the file at returned path it will persist those change + until the file cache is cleared. + + @param block The block should have the following argument signature: `^(NSString *filePath, NSError *error)`. + */ +- (void)getFilePathInBackgroundWithBlock:(PF_NULLABLE PFFilePathResultBlock)block; + +/*! + @abstract *Asynchronously* gets the file path for file from cache if available or fetches its contents from the network. + + @note The file path may change between versions of SDK. + @note If you overwrite the contents of the file at returned path it will persist those change + until the file cache is cleared. + + @param block The block should have the following argument signature: `^(NSString *filePath, NSError *error)`. + @param progressBlock The block should have the following argument signature: `^(int percentDone)`. + */ +- (void)getFilePathInBackgroundWithBlock:(PF_NULLABLE PFFilePathResultBlock)block + progressBlock:(PF_NULLABLE PFProgressBlock)progressBlock; + +///-------------------------------------- +/// @name Interrupting a Transfer +///-------------------------------------- + +/*! + @abstract Cancels the current request (upload or download of file). + */ +- (void)cancel; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFFile.m b/Pods/Parse/Parse/PFFile.m new file mode 100644 index 0000000..26fcda3 --- /dev/null +++ b/Pods/Parse/Parse/PFFile.m @@ -0,0 +1,546 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFFile.h" +#import "PFFile_Private.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFAsyncTaskQueue.h" +#import "PFCommandResult.h" +#import "PFCoreManager.h" +#import "PFErrorUtilities.h" +#import "PFFileController.h" +#import "PFFileManager.h" +#import "PFFileStagingController.h" +#import "PFInternalUtils.h" +#import "PFMacros.h" +#import "PFMutableFileState.h" +#import "PFRESTFileCommand.h" +#import "PFThreadsafety.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +static const unsigned long long PFFileMaxFileSize = 10 * 1024 * 1024; // 10 MB + +@interface PFFile () { + dispatch_queue_t _synchronizationQueue; +} + +@property (nonatomic, strong, readwrite) PFFileState *state; +@property (nonatomic, copy, readonly) NSString *stagedFilePath; +@property (nonatomic, assign, readonly, getter=isDirty) BOOL dirty; + +// +// Private +@property (nonatomic, strong) PFAsyncTaskQueue *taskQueue; +@property (nonatomic, strong) BFCancellationTokenSource *cancellationTokenSource; + +@end + +@implementation PFFile + +@synthesize stagedFilePath = _stagedFilePath; + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + +#pragma mark Init + ++ (instancetype)fileWithData:(NSData *)data { + return [self fileWithName:nil data:data contentType:nil]; +} + ++ (instancetype)fileWithName:(NSString *)name data:(NSData *)data { + return [self fileWithName:name data:data contentType:nil]; +} + ++ (instancetype)fileWithName:(NSString *)name contentsAtPath:(NSString *)path { + NSError *error = nil; + PFFile *file = [self fileWithName:name contentsAtPath:path error:&error]; + PFParameterAssert(!error, @"Could not access file at %@: %@", path, error); + return file; +} + ++ (instancetype)fileWithName:(NSString *)name contentsAtPath:(NSString *)path error:(NSError **)error { + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL directory = NO; + + if (![fileManager fileExistsAtPath:path isDirectory:&directory] || directory) { + NSString *message = [NSString stringWithFormat:@"Failed to create PFFile at path '%@': " + "file does not exist.", path]; + if (error) { + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileNoSuchFileError + userInfo:@{ NSLocalizedDescriptionKey: message }]; + } + return nil; + } + + NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:nil]; + unsigned long long length = [attributes[NSFileSize] unsignedLongValue]; + if (length > PFFileMaxFileSize) { + NSString *message = [NSString stringWithFormat:@"Failed to create PFFile at path '%@': " + "file is larger than %lluMB.", path, (PFFileMaxFileSize >> 20)]; + if (error) { + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileReadTooLargeError + userInfo:@{ NSLocalizedDescriptionKey: message }]; + } + return nil; + } + + PFFile *file = [self fileWithName:name url:nil]; + if (![file _stageWithPath:path error:error]) { + return nil; + } + return file; +} + ++ (instancetype)fileWithName:(NSString *)name + data:(NSData *)data + contentType:(NSString *)contentType { + NSError *error = nil; + PFFile *file = [self fileWithName:name data:data contentType:contentType error:&error]; + PFConsistencyAssert(!error, @"Could not save file data for %@ : %@", name, error); + return file; +} + ++ (instancetype)fileWithName:(NSString *)name + data:(NSData *)data + contentType:(NSString *)contentType + error:(NSError **)error { + if (!data) { + NSString *message = @"Cannot create a PFFile with nil data."; + if (error) { + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileNoSuchFileError + userInfo:@{ NSLocalizedDescriptionKey: message }]; + } + return nil; + } + + if ([data length] > PFFileMaxFileSize) { + NSString *message = [NSString stringWithFormat:@"Failed to create PFFile with data: " + "data is larger than %lluMB.", (PFFileMaxFileSize >> 20)]; + if (error) { + *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileReadTooLargeError + userInfo:@{ NSLocalizedDescriptionKey: message }]; + } + return nil; + } + + PFFile *file = [[self alloc] initWithName:name urlString:nil mimeType:contentType]; + if (![file _stageWithData:data error:error]) { + return nil; + } + return file; +} + ++ (instancetype)fileWithData:(NSData *)data contentType:(NSString *)contentType { + return [self fileWithName:nil data:data contentType:contentType]; +} + +#pragma mark Uploading + +- (BOOL)save { + return [self save:nil]; +} + +- (BOOL)save:(NSError **)error { + return [[[self saveInBackground] waitForResult:error] boolValue]; +} + +- (BFTask *)saveInBackground { + return [self _uploadAsyncWithProgressBlock:nil]; +} + +- (BFTask *)saveInBackgroundWithProgressBlock:(PFProgressBlock)progressBlock { + return [self _uploadAsyncWithProgressBlock:progressBlock]; +} + +- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self saveInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block + progressBlock:(PFProgressBlock)progressBlock { + [[self _uploadAsyncWithProgressBlock:progressBlock] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (void)saveInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +#pragma mark Downloading + +- (NSData *)getData { + return [self getData:nil]; +} + +- (NSInputStream *)getDataStream { + return [self getDataStream:nil]; +} + +- (NSData *)getData:(NSError **)error { + return [[self getDataInBackground] waitForResult:error]; +} + +- (NSInputStream *)getDataStream:(NSError **)error { + return [[self getDataStreamInBackground] waitForResult:error]; +} + +- (BFTask *)getDataInBackground { + return [self _getDataAsyncWithProgressBlock:nil]; +} + +- (BFTask *)getDataInBackgroundWithProgressBlock:(PFProgressBlock)progressBlock { + return [self _getDataAsyncWithProgressBlock:progressBlock]; +} + +- (BFTask *)getDataStreamInBackground { + return [self _getDataStreamAsyncWithProgressBlock:nil]; +} + +- (BFTask *)getDataStreamInBackgroundWithProgressBlock:(PFProgressBlock)progressBlock { + return [self _getDataStreamAsyncWithProgressBlock:progressBlock]; +} + +- (BFTask *)getDataDownloadStreamInBackground { + return [self getDataDownloadStreamInBackgroundWithProgressBlock:nil]; +} + +- (BFTask *)getDataDownloadStreamInBackgroundWithProgressBlock:(PFProgressBlock)progressBlock { + return [self _downloadStreamAsyncWithProgressBlock:progressBlock]; +} + +- (void)getDataInBackgroundWithBlock:(PFDataResultBlock)block { + [self getDataInBackgroundWithBlock:block progressBlock:nil]; +} + +- (void)getDataStreamInBackgroundWithBlock:(PFDataStreamResultBlock)block { + [self getDataStreamInBackgroundWithBlock:block progressBlock:nil]; +} + +- (void)getDataInBackgroundWithBlock:(PFDataResultBlock)resultBlock + progressBlock:(PFProgressBlock)progressBlock { + [[self _getDataAsyncWithProgressBlock:progressBlock] thenCallBackOnMainThreadAsync:resultBlock]; +} + +- (void)getDataStreamInBackgroundWithBlock:(PFDataStreamResultBlock)resultBlock + progressBlock:(PFProgressBlock)progressBlock { + [[self _getDataStreamAsyncWithProgressBlock:progressBlock] thenCallBackOnMainThreadAsync:resultBlock]; +} + +- (void)getDataInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:data object:error]; + }]; +} + +- (BFTask PF_GENERIC(NSString *) *)getFilePathInBackground { + return [self getFilePathInBackgroundWithProgressBlock:nil]; +} + +- (BFTask PF_GENERIC(NSString *)*)getFilePathInBackgroundWithProgressBlock:(PFProgressBlock)progressBlock { + return [[self _downloadAsyncWithProgressBlock:progressBlock] continueWithSuccessBlock:^id(BFTask *task) { + if (self.dirty) { + return self.stagedFilePath; + } + return [[[self class] fileController] cachedFilePathForFileState:self.state]; + }]; +} + +- (void)getFilePathInBackgroundWithBlock:(PF_NULLABLE PFFilePathResultBlock)block { + [[self getFilePathInBackground] thenCallBackOnMainThreadAsync:block]; +} + +- (void)getFilePathInBackgroundWithBlock:(PF_NULLABLE PFFilePathResultBlock)block + progressBlock:(PF_NULLABLE PFProgressBlock)progressBlock { + [[self getFilePathInBackgroundWithProgressBlock:progressBlock] thenCallBackOnMainThreadAsync:block]; +} + +#pragma mark Interrupting + +- (void)cancel { + [self _performDataAccessBlock:^{ + [self.cancellationTokenSource cancel]; + self.cancellationTokenSource = nil; + }]; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +#pragma mark Init + +- (instancetype)initWithName:(NSString *)name urlString:(NSString *)url mimeType:(NSString *)mimeType { + self = [super init]; + if (!self) return nil; + + _taskQueue = [[PFAsyncTaskQueue alloc] init]; + _synchronizationQueue = PFThreadsafetyCreateQueueForObject(self); + + _state = [[PFFileState alloc] initWithName:name urlString:url mimeType:mimeType]; + + return self; +} + ++ (instancetype)fileWithName:(NSString *)name url:(NSString *)url { + return [[self alloc] initWithName:name urlString:url mimeType:nil]; +} + +#pragma mark Upload + +- (BFTask *)_uploadAsyncWithProgressBlock:(PFProgressBlock)progressBlock { + @weakify(self); + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id { + @strongify(self); + + __block BFCancellationToken *cancellationToken = nil; + [self _performDataAccessBlock:^{ + if (!self.cancellationTokenSource || self.cancellationTokenSource.cancellationRequested) { + self.cancellationTokenSource = [BFCancellationTokenSource cancellationTokenSource]; + } + cancellationToken = self.cancellationTokenSource.token; + }]; + + return [[[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [self.taskQueue enqueue:^id(BFTask *task) { + if (!self.dirty) { + [self _performProgressBlockAsync:progressBlock withProgress:100]; + return [BFTask taskWithResult:nil]; + } + + return [self _uploadFileAsyncWithSessionToken:sessionToken + cancellationToken:cancellationToken + progressBlock:progressBlock]; + }]; + }] continueWithSuccessResult:@YES]; + }]; +} + +- (BFTask *)_uploadFileAsyncWithSessionToken:(NSString *)sessionToken + cancellationToken:(BFCancellationToken *)cancellationToken + progressBlock:(PFProgressBlock)progressBlock { + if (cancellationToken.cancellationRequested) { + return [BFTask cancelledTask]; + } + + PFFileController *controller = [[self class] fileController]; + NSString *sourceFilePath = self.stagedFilePath; + @weakify(self); + return [[[controller uploadFileAsyncWithState:[self _fileState] + sourceFilePath:sourceFilePath + sessionToken:sessionToken + cancellationToken:cancellationToken + progressBlock:progressBlock] continueWithSuccessBlock:^id(BFTask *task) { + @strongify(self); + [self _performDataAccessBlock:^{ + self.state = [task.result copy]; + }]; + return nil; + } cancellationToken:cancellationToken] continueWithBlock:^id(BFTask *task) { + @strongify(self); + [self _performDataAccessBlock:^{ + self.cancellationTokenSource = nil; + }]; + return task; + }]; +} + +#pragma mark Download + +- (BFTask *)_getDataAsyncWithProgressBlock:(PFProgressBlock)progressBlock { + return [[self _downloadAsyncWithProgressBlock:progressBlock] continueWithSuccessBlock:^id(BFTask *task) { + return [self _cachedData]; + }]; +} + +- (BFTask *)_getDataStreamAsyncWithProgressBlock:(PFProgressBlock)progressBlock { + return [[self _downloadAsyncWithProgressBlock:progressBlock] continueWithSuccessBlock:^id(BFTask *task) { + return [self _cachedDataStream]; + }]; +} + +- (BFTask *)_downloadAsyncWithProgressBlock:(PFProgressBlock)progressBlock { + __block BFCancellationToken *cancellationToken = nil; + [self _performDataAccessBlock:^{ + if (!self.cancellationTokenSource || self.cancellationTokenSource.cancellationRequested) { + self.cancellationTokenSource = [BFCancellationTokenSource cancellationTokenSource]; + } + cancellationToken = self.cancellationTokenSource.token; + }]; + + @weakify(self); + return [self.taskQueue enqueue:^id(BFTask *task) { + @strongify(self); + if (self.isDataAvailable) { + [self _performProgressBlockAsync:progressBlock withProgress:100]; + return [BFTask taskWithResult:nil]; + } + + PFFileController *controller = [[self class] fileController]; + return [[controller downloadFileAsyncWithState:[self _fileState] + cancellationToken:cancellationToken + progressBlock:progressBlock] continueWithBlock:^id(BFTask *task) { + [self _performDataAccessBlock:^{ + self.cancellationTokenSource = nil; + }]; + return task; + }]; + }]; +} + +- (BFTask *)_downloadStreamAsyncWithProgressBlock:(PFProgressBlock)progressBlock { + __block BFCancellationToken *cancellationToken = nil; + [self _performDataAccessBlock:^{ + if (!self.cancellationTokenSource || self.cancellationTokenSource.cancellationRequested) { + self.cancellationTokenSource = [BFCancellationTokenSource cancellationTokenSource]; + } + cancellationToken = self.cancellationTokenSource.token; + }]; + + @weakify(self); + return [self.taskQueue enqueue:^id(BFTask *task) { + @strongify(self); + if (self.isDataAvailable) { + [self _performProgressBlockAsync:progressBlock withProgress:100]; + return [self _cachedDataStream]; + } + + PFFileController *controller = [[self class] fileController]; + return [[controller downloadFileStreamAsyncWithState:[self _fileState] + cancellationToken:cancellationToken + progressBlock:progressBlock] continueWithBlock:^id(BFTask *task) { + [self _performDataAccessBlock:^{ + self.cancellationTokenSource = nil; + }]; + return task; + }]; + }]; +} + +#pragma mark Caching + +- (NSString *)_cachedFilePath { + return [[[self class] fileController] cachedFilePathForFileState:self.state]; +} + +- (NSData *)_cachedData { + NSString *filePath = (self.dirty ? self.stagedFilePath : [self _cachedFilePath]); + return [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:NULL]; +} + +- (NSInputStream *)_cachedDataStream { + NSString *filePath = (self.dirty ? self.stagedFilePath : [[[self class] fileController] cachedFilePathForFileState:self.state]); + return [NSInputStream inputStreamWithFileAtPath:filePath]; +} + +///-------------------------------------- +#pragma mark - Staging +///-------------------------------------- + +- (BOOL)_stageWithData:(NSData *)data error:(NSError **)error { + __block BOOL result = NO; + [self _performDataAccessBlock:^{ + _stagedFilePath = [[[[self class] fileController].fileStagingController stageFileAsyncWithData:data + name:self.state.name + uniqueId:(uintptr_t)self] + waitForResult:error withMainThreadWarning:NO]; + + result = (_stagedFilePath != nil); + }]; + return result; +} + +- (BOOL)_stageWithPath:(NSString *)path error:(NSError **)error { + __block BOOL result = NO; + [self _performDataAccessBlock:^{ + _stagedFilePath = [[[[self class] fileController].fileStagingController stageFileAsyncAtPath:path + name:self.state.name + uniqueId:(uintptr_t)self] + waitForResult:error withMainThreadWarning:NO]; + + result = (_stagedFilePath != nil); + }]; + return result; +} + +#pragma mark Data Access + +- (NSString *)name { + __block NSString *name = nil; + [self _performDataAccessBlock:^{ + name = self.state.name; + }]; + return name; +} + +- (NSString *)url { + __block NSString *url = nil; + [self _performDataAccessBlock:^{ + url = self.state.secureURLString; + }]; + return url; +} + +- (BOOL)isDirty { + return !self.url; +} + +- (BOOL)isDataAvailable { + __block BOOL available = NO; + [self _performDataAccessBlock:^{ + available = self.dirty || [[NSFileManager defaultManager] fileExistsAtPath:[self _cachedFilePath]]; + }]; + return available; +} + +- (void)_performDataAccessBlock:(dispatch_block_t)block { + PFThreadsafetySafeDispatchSync(_synchronizationQueue, block); +} + +- (PFFileState *)_fileState { + __block PFFileState *state = nil; + [self _performDataAccessBlock:^{ + state = self.state; + }]; + return state; +} + +#pragma mark Progress + +- (void)_performProgressBlockAsync:(PFProgressBlock)block withProgress:(int)progress { + if (!block) { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + block(progress); + }); +} + +///-------------------------------------- +#pragma mark - FileController +///-------------------------------------- + ++ (PFFileController *)fileController { + return [Parse _currentManager].coreManager.fileController; +} + +@end diff --git a/Pods/Parse/Parse/PFGeoPoint.h b/Pods/Parse/Parse/PFGeoPoint.h new file mode 100644 index 0000000..37d3bb0 --- /dev/null +++ b/Pods/Parse/Parse/PFGeoPoint.h @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +@class PFGeoPoint; + +typedef void(^PFGeoPointResultBlock)(PFGeoPoint *PF_NULLABLE_S geoPoint, NSError *PF_NULLABLE_S error); + +/*! + `PFGeoPoint` may be used to embed a latitude / longitude point as the value for a key in a . + It could be used to perform queries in a geospatial manner using <[PFQuery whereKey:nearGeoPoint:]>. + + Currently, instances of may only have one key associated with a `PFGeoPoint` type. + */ +@interface PFGeoPoint : NSObject + +///-------------------------------------- +/// @name Creating a Geo Point +///-------------------------------------- + +/*! + @abstract Create a PFGeoPoint object. Latitude and longitude are set to `0.0`. + + @returns Returns a new `PFGeoPoint`. + */ ++ (instancetype)geoPoint; + +/*! + @abstract Creates a new `PFGeoPoint` object for the given `CLLocation`, set to the location's coordinates. + + @param location Instace of `CLLocation`, with set latitude and longitude. + + @returns Returns a new PFGeoPoint at specified location. + */ ++ (instancetype)geoPointWithLocation:(PF_NULLABLE CLLocation *)location; + +/*! + @abstract Create a new `PFGeoPoint` object with the specified latitude and longitude. + + @param latitude Latitude of point in degrees. + @param longitude Longitude of point in degrees. + + @returns New point object with specified latitude and longitude. + */ ++ (instancetype)geoPointWithLatitude:(double)latitude longitude:(double)longitude; + +/*! + @abstract Fetches the current device location and executes a block with a new `PFGeoPoint` object. + + @param resultBlock A block which takes the newly created `PFGeoPoint` as an argument. + It should have the following argument signature: `^(PFGeoPoint *geoPoint, NSError *error)` + */ ++ (void)geoPointForCurrentLocationInBackground:(PF_NULLABLE PFGeoPointResultBlock)resultBlock; + +///-------------------------------------- +/// @name Controlling Position +///-------------------------------------- + +/*! + @abstract Latitude of point in degrees. Valid range is from `-90.0` to `90.0`. + */ +@property (nonatomic, assign) double latitude; + +/*! + @abstract Longitude of point in degrees. Valid range is from `-180.0` to `180.0`. + */ +@property (nonatomic, assign) double longitude; + +///-------------------------------------- +/// @name Calculating Distance +///-------------------------------------- + +/*! + @abstract Get distance in radians from this point to specified point. + + @param point `PFGeoPoint` that represents the location of other point. + + @returns Distance in radians between the receiver and `point`. + */ +- (double)distanceInRadiansTo:(PF_NULLABLE PFGeoPoint *)point; + +/*! + @abstract Get distance in miles from this point to specified point. + + @param point `PFGeoPoint` that represents the location of other point. + + @returns Distance in miles between the receiver and `point`. + */ +- (double)distanceInMilesTo:(PF_NULLABLE PFGeoPoint *)point; + +/*! + @abstract Get distance in kilometers from this point to specified point. + + @param point `PFGeoPoint` that represents the location of other point. + + @returns Distance in kilometers between the receiver and `point`. + */ +- (double)distanceInKilometersTo:(PF_NULLABLE PFGeoPoint *)point; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFGeoPoint.m b/Pods/Parse/Parse/PFGeoPoint.m new file mode 100644 index 0000000..5912bff --- /dev/null +++ b/Pods/Parse/Parse/PFGeoPoint.m @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFGeoPoint.h" + +#import + +#import "PFAssert.h" +#import "PFCoreManager.h" +#import "PFHash.h" +#import "PFLocationManager.h" +#import "Parse_Private.h" + +const double EARTH_RADIUS_MILES = 3958.8; +const double EARTH_RADIUS_KILOMETERS = 6371.0; + +@implementation PFGeoPoint + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)geoPoint { + return [[self alloc] init]; +} + ++ (instancetype)geoPointWithLocation:(CLLocation *)location { + return [self geoPointWithLatitude:location.coordinate.latitude + longitude:location.coordinate.longitude]; +} + ++ (instancetype)geoPointWithLatitude:(double)latitude longitude:(double)longitude { + PFGeoPoint *gpt = [self geoPoint]; + gpt.latitude = latitude; + gpt.longitude = longitude; + return gpt; +} + ++ (void)geoPointForCurrentLocationInBackground:(PFGeoPointResultBlock)resultBlock { + if (!resultBlock) { + return; + } + + void(^locationHandler)(CLLocation *, NSError *) = ^(CLLocation *location, NSError *error) { + PFGeoPoint *newGeoPoint = [PFGeoPoint geoPointWithLocation:location]; + resultBlock(newGeoPoint, error); + }; + [[Parse _currentManager].coreManager.locationManager addBlockForCurrentLocation:locationHandler]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setLatitude:(double)latitude { + PFParameterAssert(latitude >= -90.0 && latitude <= 90.0, + @"`latitude` is out of range [-90.0, 90.0]: %f", latitude); + _latitude = latitude; +} + +- (void)setLongitude:(double)longitude { + PFParameterAssert(longitude >= -180.0 && longitude <= 180.0, + @"`longitude` is out of range [-180.0, 180.0]: %f", longitude); + _longitude = longitude; +} + +- (double)distanceInRadiansTo:(PFGeoPoint *)point { + double d2r = M_PI / 180.0; // radian conversion factor + double lat1rad = self.latitude * d2r; + double long1rad = self.longitude * d2r; + double lat2rad = [point latitude] * d2r; + double long2rad = [point longitude] * d2r; + double deltaLat = lat1rad - lat2rad; + double deltaLong = long1rad - long2rad; + double sinDeltaLatDiv2 = sin(deltaLat / 2.); + double sinDeltaLongDiv2 = sin(deltaLong / 2.); + // Square of half the straight line chord distance between both points. [0.0, 1.0] + double a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + + cos(lat1rad) * cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = fmin(1.0, a); + return 2. * asin(sqrt(a)); +} + +- (double)distanceInMilesTo:(PFGeoPoint *)point { + return [self distanceInRadiansTo:point] * EARTH_RADIUS_MILES; +} + +- (double)distanceInKilometersTo:(PFGeoPoint *)point { + return [self distanceInRadiansTo:point] * EARTH_RADIUS_KILOMETERS; +} + +///-------------------------------------- +#pragma mark - Encoding +///-------------------------------------- + +static NSString *const PFGeoPointCodingTypeKey = @"__type"; +static NSString *const PFGeoPointCodingLatitudeKey = @"latitude"; +static NSString *const PFGeoPointCodingLongitudeKey = @"longitude"; + +- (NSDictionary *)encodeIntoDictionary { + return @{ + PFGeoPointCodingTypeKey : @"GeoPoint", + PFGeoPointCodingLatitudeKey : @(self.latitude), + PFGeoPointCodingLongitudeKey : @(self.longitude) + }; +} + ++ (instancetype)geoPointWithDictionary:(NSDictionary *)dictionary { + return [[self alloc] initWithEncodedDictionary:dictionary]; +} + +- (instancetype)initWithEncodedDictionary:(NSDictionary *)dictionary { + self = [self init]; + if (!self) return nil; + + id latObj = dictionary[PFGeoPointCodingLatitudeKey]; + PFParameterAssert([latObj isKindOfClass:[NSNumber class]], @"Invalid latitude type passed: %@", latObj); + + id longObj = dictionary[PFGeoPointCodingLongitudeKey]; + PFParameterAssert([longObj isKindOfClass:[NSNumber class]], @"Invalid longitude type passed: %@", longObj); + + _latitude = [latObj doubleValue]; + _longitude = [longObj doubleValue]; + + return self; +} + +///-------------------------------------- +#pragma mark - NSObject +///-------------------------------------- + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[PFGeoPoint class]]) { + return NO; + } + + PFGeoPoint *geoPoint = object; + + return (self.latitude == geoPoint.latitude && + self.longitude == geoPoint.longitude); +} + +- (NSUInteger)hash { + return PFDoublePairHash(self.latitude, self.longitude); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p, latitude: %f, longitude: %f>", + [self class], + self, + self.latitude, + self.longitude]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + PFGeoPoint *geoPoint = [[self class] geoPointWithLatitude:self.latitude longitude:self.longitude]; + return geoPoint; +} + +///-------------------------------------- +#pragma mark - NSCoding +///-------------------------------------- + +- (instancetype)initWithCoder:(NSCoder *)coder { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + dictionary[PFGeoPointCodingTypeKey] = [coder decodeObjectForKey:PFGeoPointCodingTypeKey]; + dictionary[PFGeoPointCodingLatitudeKey] = [coder decodeObjectForKey:PFGeoPointCodingLatitudeKey]; + dictionary[PFGeoPointCodingLongitudeKey] = [coder decodeObjectForKey:PFGeoPointCodingLongitudeKey]; + return [self initWithEncodedDictionary:dictionary]; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + NSDictionary *dictionary = [self encodeIntoDictionary]; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [coder encodeObject:obj forKey:key]; + }]; +} + +@end diff --git a/Pods/Parse/Parse/PFInstallation.h b/Pods/Parse/Parse/PFInstallation.h new file mode 100644 index 0000000..3b1c042 --- /dev/null +++ b/Pods/Parse/Parse/PFInstallation.h @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + A Parse Framework Installation Object that is a local representation of an + installation persisted to the Parse cloud. This class is a subclass of a + , and retains the same functionality of a PFObject, but also extends + it with installation-specific fields and related immutability and validity + checks. + + A valid `PFInstallation` can only be instantiated via + <[PFInstallation currentInstallation]> because the required identifier fields + are readonly. The and fields are also readonly properties which + are automatically updated to match the device's time zone and application badge + when the `PFInstallation` is saved, thus these fields might not reflect the + latest device state if the installation has not recently been saved. + + `PFInstallation` objects which have a valid and are saved to + the Parse cloud can be used to target push notifications. + */ + +PF_WATCH_UNAVAILABLE @interface PFInstallation : PFObject + +///-------------------------------------- +/// @name Accessing the Current Installation +///-------------------------------------- + +/*! + @abstract Gets the currently-running installation from disk and returns an instance of it. + + @discussion If this installation is not stored on disk, returns a `PFInstallation` + with and fields set to those of the + current installation. + + @result Returns a `PFInstallation` that represents the currently-running installation. + */ ++ (instancetype)currentInstallation; + +///-------------------------------------- +/// @name Installation Properties +///-------------------------------------- + +/*! + @abstract The device type for the `PFInstallation`. + */ +@property (nonatomic, copy, readonly) NSString *deviceType; + +/*! + @abstract The installationId for the `PFInstallation`. + */ +@property (nonatomic, copy, readonly) NSString *installationId; + +/*! + @abstract The device token for the `PFInstallation`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy) NSString *deviceToken; + +/*! + @abstract The badge for the `PFInstallation`. + */ +@property (nonatomic, assign) NSInteger badge; + +/*! + @abstract The name of the time zone for the `PFInstallation`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy, readonly) NSString *timeZone; + +/*! + @abstract The channels for the `PFInstallation`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy) NSArray *channels; + +/*! + @abstract Sets the device token string property from an `NSData`-encoded token. + + @param deviceTokenData A token that identifies the device. + */ +- (void)setDeviceTokenFromData:(PF_NULLABLE NSData *)deviceTokenData; + +///-------------------------------------- +/// @name Querying for Installations +///-------------------------------------- + +/*! + @abstract Creates a for `PFInstallation` objects. + + @discussion Only the following types of queries are allowed for installations: + + - `[query getObjectWithId:]` + - `[query whereKey:@"installationId" equalTo:]` + - `[query whereKey:@"installationId" matchesKey: inQuery:]` + + You can add additional query conditions, but one of the above must appear as a top-level `AND` clause in the query. + */ ++ (PF_NULLABLE PFQuery *)query; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFInstallation.m b/Pods/Parse/Parse/PFInstallation.m new file mode 100644 index 0000000..2536d64 --- /dev/null +++ b/Pods/Parse/Parse/PFInstallation.m @@ -0,0 +1,342 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFInstallation.h" +#import "PFInstallationPrivate.h" + +#import "BFTask+Private.h" +#import "PFApplication.h" +#import "PFAssert.h" +#import "PFCoreManager.h" +#import "PFCurrentInstallationController.h" +#import "PFFileManager.h" +#import "PFInstallationConstants.h" +#import "PFInstallationController.h" +#import "PFInstallationIdentifierStore.h" +#import "PFInternalUtils.h" +#import "PFObject+Subclass.h" +#import "PFObjectEstimatedData.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFPushPrivate.h" +#import "PFQueryPrivate.h" +#import "Parse_Private.h" +#import "PFErrorUtilities.h" + +@implementation PFInstallation (Private) + +static NSSet *protectedKeys; + ++ (void)initialize { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + protectedKeys = PF_SET(PFInstallationKeyDeviceType, + PFInstallationKeyInstallationId, + PFInstallationKeyTimeZone, + PFInstallationKeyLocaleIdentifier, + PFInstallationKeyParseVersion, + PFInstallationKeyAppVersion, + PFInstallationKeyAppName, + PFInstallationKeyAppIdentifier); + }); +} + +// Clear device token. Used for testing. +- (void)_clearDeviceToken { + [super removeObjectForKey:PFInstallationKeyDeviceToken]; +} + +- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync { + return [[super _validateDeleteAsync] continueWithSuccessBlock:^id(BFTask PF_GENERIC(PFVoid) *task) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorCommandUnavailable + message:@"Installation cannot be deleted"]; + return [BFTask taskWithError:error]; + }]; +} + +// Validates a class name. We override this to only allow the installation class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[PFInstallation parseClassName]], + @"Cannot initialize a PFInstallation with a custom class name."); +} + +- (BOOL)_isCurrentInstallation { + return (self == [[self class] _currentInstallationController].memoryCachedCurrentInstallation); +} + +- (void)_markAllFieldsDirty { + @synchronized(self.lock) { + NSDictionary *estimatedData = self._estimatedData.dictionaryRepresentation; + [estimatedData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [super setObject:obj forKey:key]; + }]; + } +} + +- (NSString *)displayClassName { + return NSStringFromClass([PFInstallation class]); +} + +///-------------------------------------- +#pragma mark - Command Handlers +///-------------------------------------- + +- (BFTask *)handleSaveResultAsync:(NSDictionary *)result { + @weakify(self); + return [[super handleSaveResultAsync:result] continueWithBlock:^id(BFTask *task) { + @strongify(self); + BFTask *saveTask = [[[self class] _currentInstallationController] saveCurrentObjectAsync:self]; + return [saveTask continueWithResult:task]; + }]; +} + +///-------------------------------------- +#pragma mark - Current Installation Controller +///-------------------------------------- + ++ (PFCurrentInstallationController *)_currentInstallationController { + return [Parse _currentManager].coreManager.currentInstallationController; +} + +@end + +@implementation PFInstallation + +@dynamic deviceType; +@dynamic installationId; +@dynamic deviceToken; +@dynamic timeZone; +@dynamic channels; +@dynamic badge; + +///-------------------------------------- +#pragma mark - PFSubclassing +///-------------------------------------- + ++ (NSString *)parseClassName { + return @"_Installation"; +} + ++ (PFQuery *)query { + return [super query]; +} + +///-------------------------------------- +#pragma mark - Current Installation +///-------------------------------------- + ++ (instancetype)currentInstallation { + BFTask *task = [[self _currentInstallationController] getCurrentObjectAsync]; + return [task waitForResult:nil withMainThreadWarning:NO]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (id)objectForKey:(NSString *)key { + if ([key isEqualToString:PFInstallationKeyBadge] && [self _isCurrentInstallation]) { + // Update the data dictionary badge value from the device. + [self _updateBadgeFromDevice]; + } + + return [super objectForKey:key]; +} + +- (void)setObject:(id)object forKey:(NSString *)key { + PFParameterAssert(![protectedKeys containsObject:key], + @"Can't change the '%@' field of a PFInstallation.", key); + + if ([key isEqualToString:PFInstallationKeyBadge]) { + // Set the application badge and update the badge value in the data dictionary. + NSInteger badge = [object integerValue]; + PFParameterAssert(badge >= 0, @"Can't set the badge to less than zero."); + + [PFApplication currentApplication].iconBadgeNumber = badge; + [super setObject:@(badge) forKey:PFInstallationKeyBadge]; + } + + [super setObject:object forKey:key]; +} + +- (void)incrementKey:(NSString *)key byAmount:(NSNumber *)amount { + PFParameterAssert(![key isEqualToString:PFInstallationKeyBadge], + @"Can't atomically increment the 'badge' field of a PFInstallation."); + + [super incrementKey:key byAmount:amount]; +} + +- (void)removeObjectForKey:(NSString *)key { + PFParameterAssert(![protectedKeys containsObject:key], + @"Can't remove the '%@' field of a PFInstallation.", key); + PFParameterAssert(![key isEqualToString:PFInstallationKeyBadge], + @"Can't remove the 'badge' field of a PFInstallation."); + [super removeObjectForKey:key]; +} + +// Internal mutators override the dynamic accessor and use super to avoid +// read-only checks on automatic fields. +- (void)setDeviceType:(NSString *)deviceType { + [self _setObject:deviceType forKey:PFInstallationKeyDeviceType onlyIfDifferent:YES]; +} + +- (void)setInstallationId:(NSString *)installationId { + [self _setObject:installationId forKey:PFInstallationKeyInstallationId onlyIfDifferent:YES]; +} + +- (void)setDeviceToken:(NSString *)deviceToken { + [self _setObject:deviceToken forKey:PFInstallationKeyDeviceToken onlyIfDifferent:YES]; +} + +- (void)setDeviceTokenFromData:(NSData *)deviceTokenData { + [self _setObject:[[PFPush pushInternalUtilClass] convertDeviceTokenToString:deviceTokenData] + forKey:PFInstallationKeyDeviceToken + onlyIfDifferent:YES]; +} + +- (void)setTimeZone:(NSString *)timeZone { + [self _setObject:timeZone forKey:PFInstallationKeyTimeZone onlyIfDifferent:YES]; +} + +- (void)setLocaleIdentifier:(NSString *)localeIdentifier { + [self _setObject:localeIdentifier + forKey:PFInstallationKeyLocaleIdentifier + onlyIfDifferent:YES]; +} + +- (void)setChannels:(NSArray *)channels { + [self _setObject:channels forKey:PFInstallationKeyChannels onlyIfDifferent:YES]; +} + +///-------------------------------------- +#pragma mark - PFObject +///-------------------------------------- + +- (BFTask *)saveAsync:(BFTask *)toAwait { + return [[super saveAsync:toAwait] continueWithBlock:^id(BFTask *task) { + // Do not attempt to resave an object if LDS is enabled, since changing objectId is not allowed. + if ([Parse _currentManager].offlineStoreLoaded) { + return task; + } + + if (task.error.code == kPFErrorObjectNotFound) { + @synchronized (self.lock) { + // Retry the fetch as a save operation because this Installation was deleted on the server. + // We always want [currentInstallation save] to succeed. + self.objectId = nil; + [self _markAllFieldsDirty]; + return [super saveAsync:nil]; + } + } + return task; + }]; +} + +- (BOOL)needsDefaultACL { + return NO; +} + +///-------------------------------------- +#pragma mark - Automatic Info +///-------------------------------------- + +- (void)_objectWillSave { + if ([self _isCurrentInstallation]) { + @synchronized(self.lock) { + [self _updateTimeZoneFromDevice]; + [self _updateBadgeFromDevice]; + [self _updateVersionInfoFromDevice]; + [self _updateLocaleIdentifierFromDevice]; + } + } +} + +- (void)_updateTimeZoneFromDevice { + // Get the system time zone (after clearing the cached value) and update + // the installation if necessary. + NSString *systemTimeZoneName = [PFInternalUtils currentSystemTimeZoneName]; + if (![systemTimeZoneName isEqualToString:self.timeZone]) { + self.timeZone = systemTimeZoneName; + } +} + +- (void)_updateBadgeFromDevice { + // Get the application icon and update the installation if necessary. + NSNumber *applicationBadge = @([PFApplication currentApplication].iconBadgeNumber); + NSNumber *installationBadge = [super objectForKey:PFInstallationKeyBadge]; + if (installationBadge == nil || ![applicationBadge isEqualToNumber:installationBadge]) { + [super setObject:applicationBadge forKey:PFInstallationKeyBadge]; + } +} + +- (void)_updateVersionInfoFromDevice { + NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary]; + NSString *appName = appInfo[(__bridge NSString *)kCFBundleNameKey]; + NSString *appVersion = appInfo[(__bridge NSString *)kCFBundleVersionKey]; + NSString *appIdentifier = appInfo[(__bridge NSString *)kCFBundleIdentifierKey]; + // It's possible that the app was created without an info.plist and we just + // cannot get the data we need. + // Note: it's important to make the possibly nil string the message receptor for + // nil propegation instead of a BAD_ACCESS + if (appName && ![self[PFInstallationKeyAppName] isEqualToString:appName]) { + [super setObject:appName forKey:PFInstallationKeyAppName]; + } + if (appVersion && ![self[PFInstallationKeyAppVersion] isEqualToString:appVersion]) { + [super setObject:appVersion forKey:PFInstallationKeyAppVersion]; + } + if (appIdentifier && ![self[PFInstallationKeyAppIdentifier] isEqualToString:appIdentifier]) { + [super setObject:appIdentifier forKey:PFInstallationKeyAppIdentifier]; + } + if (![self[PFInstallationKeyParseVersion] isEqualToString:PARSE_VERSION]) { + [super setObject:PARSE_VERSION forKey:PFInstallationKeyParseVersion]; + } +} + +/*! + @abstract Save localeIdentifier in the following format: [language code]-[COUNTRY CODE]. + + @discussion The language codes are two-letter lowercase ISO language codes (such as "en") as defined by + ISO 639-1. + The country codes are two-letter uppercase ISO country codes (such as "US") as defined by + ISO 3166-1. + + Many iOS locale identifiers don't contain the country code -> inconsistencies with Android/Windows Phone. + */ +- (void)_updateLocaleIdentifierFromDevice { + NSLocale *currentLocale = [NSLocale currentLocale]; + NSString *language = [currentLocale objectForKey:NSLocaleLanguageCode]; + NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; + + if (language.length == 0) { + return; + } + + NSString *localeIdentifier = nil; + if (countryCode.length > 0) { + localeIdentifier = [NSString stringWithFormat:@"%@-%@", language, countryCode]; + } else { + localeIdentifier = language; + } + + NSString *currentLocaleIdentifier = self[PFInstallationKeyLocaleIdentifier]; + if (localeIdentifier.length > 0 && ![localeIdentifier isEqualToString:currentLocaleIdentifier]) { + // Call into super to avoid checking on protected keys. + [super setObject:localeIdentifier forKey:PFInstallationKeyLocaleIdentifier]; + } +} + +///-------------------------------------- +#pragma mark - Data Source +///-------------------------------------- + ++ (id)objectController { + return [Parse _currentManager].coreManager.installationController; +} + +@end diff --git a/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.h b/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.h new file mode 100644 index 0000000..d5b376a --- /dev/null +++ b/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + `PFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. + When enabled, it will start managing the network activity indicator in the status bar, + according to the network operations that are performed by Parse SDK. + + The number of active requests is incremented or decremented like a stack or a semaphore, + the activity indicator will animate, as long as the number is greater than zero. + */ +@interface PFNetworkActivityIndicatorManager : NSObject + +/*! + A Boolean value indicating whether the manager is enabled. + If `YES` - the manager will start managing the status bar network activity indicator, + according to the network operations that are performed by Parse SDK. + The default value is `YES`. + */ +@property (nonatomic, assign, getter = isEnabled) BOOL enabled; + +/*! + A Boolean value indicating whether the network activity indicator is currently displayed in the status bar. + */ +@property (nonatomic, assign, readonly, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +/*! + The value that indicates current network activities count. + */ +@property (nonatomic, assign, readonly) NSUInteger networkActivityCount; + +/*! + @abstract Returns the shared network activity indicator manager object for the system. + + @returns The systemwide network activity indicator manager. + */ ++ (instancetype)sharedManager; + +/*! + @abstract Increments the number of active network requests. + + @discussion If this number was zero before incrementing, + this will start animating network activity indicator in the status bar. + */ +- (void)incrementActivityCount; + +/*! + @abstract Decrements the number of active network requests. + + @discussion If this number becomes zero after decrementing, + this will stop animating network activity indicator in the status bar. + */ +- (void)decrementActivityCount; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.m b/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.m new file mode 100644 index 0000000..c8d9e6a --- /dev/null +++ b/Pods/Parse/Parse/PFNetworkActivityIndicatorManager.m @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFNetworkActivityIndicatorManager.h" + +#import + +#import "PFApplication.h" +#import "PFCommandRunningConstants.h" + +static NSTimeInterval const PFNetworkActivityIndicatorVisibilityDelay = 0.17; + +@interface PFNetworkActivityIndicatorManager () { + dispatch_queue_t _networkActivityAccessQueue; +} + +@property (nonatomic, assign, readwrite) NSUInteger networkActivityCount; + +@property (nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer; + +@end + +@implementation PFNetworkActivityIndicatorManager + +@synthesize enabled = _enabled; +@synthesize networkActivityCount = _networkActivityCount; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + ++ (instancetype)sharedManager { + static PFNetworkActivityIndicatorManager *manager; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + manager = [[self alloc] init]; + manager.enabled = YES; + }); + return manager; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _networkActivityAccessQueue = dispatch_queue_create("com.parse.networkActivityIndicatorManager", + DISPATCH_QUEUE_SERIAL); + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_handleWillSendURLRequestNotification:) + name:PFNetworkWillSendURLRequestNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_handleDidReceiveURLResponseNotification:) + name:PFNetworkDidReceiveURLResponseNotification + object:nil]; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [_activityIndicatorVisibilityTimer invalidate]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setNetworkActivityCount:(NSUInteger)networkActivityCount { + dispatch_sync(_networkActivityAccessQueue, ^{ + _networkActivityCount = networkActivityCount; + }); + dispatch_async(dispatch_get_main_queue(), ^{ + [self _updateNetworkActivityIndicatorVisibilityAfterDelay]; + }); +} + +- (NSUInteger)networkActivityCount { + __block NSUInteger count = 0; + dispatch_sync(_networkActivityAccessQueue, ^{ + count = _networkActivityCount; + }); + return count; +} + +- (BOOL)isNetworkActivityIndicatorVisible { + return self.networkActivityCount > 0; +} + +///-------------------------------------- +#pragma mark - Counts +///-------------------------------------- + +- (void)incrementActivityCount { + dispatch_sync(_networkActivityAccessQueue, ^{ + _networkActivityCount++; + }); + dispatch_async(dispatch_get_main_queue(), ^{ + [self _updateNetworkActivityIndicatorVisibilityAfterDelay]; + }); +} + +- (void)decrementActivityCount { + dispatch_sync(_networkActivityAccessQueue, ^{ + _networkActivityCount = MAX(_networkActivityCount - 1, 0); + }); + dispatch_async(dispatch_get_main_queue(), ^{ + [self _updateNetworkActivityIndicatorVisibilityAfterDelay]; + }); +} + +///-------------------------------------- +#pragma mark - Network Activity Indicator +///-------------------------------------- + +- (void)_updateNetworkActivityIndicatorVisibilityAfterDelay { + if (self.enabled) { + // Delay hiding of activity indicator for a short interval, to avoid flickering + if (![self isNetworkActivityIndicatorVisible]) { + [self.activityIndicatorVisibilityTimer invalidate]; + + NSTimeInterval timeInterval = PFNetworkActivityIndicatorVisibilityDelay; + SEL selector = @selector(_updateNetworkActivityIndicatorVisibility); + self.activityIndicatorVisibilityTimer = [NSTimer timerWithTimeInterval:timeInterval + target:self + selector:selector + userInfo:nil + repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.activityIndicatorVisibilityTimer + forMode:NSRunLoopCommonModes]; + } else { + [self performSelectorOnMainThread:@selector(_updateNetworkActivityIndicatorVisibility) + withObject:nil + waitUntilDone:NO + modes:@[ NSRunLoopCommonModes ]]; + } + } +} + +- (void)_updateNetworkActivityIndicatorVisibility NS_EXTENSION_UNAVAILABLE_IOS("") { + if (![PFApplication currentApplication].extensionEnvironment) { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:self.networkActivityIndicatorVisible]; + } +} + +///-------------------------------------- +#pragma mark - Command Running +///-------------------------------------- + +- (void)_handleWillSendURLRequestNotification:(NSNotification *)notification { + [self incrementActivityCount]; +} + +- (void)_handleDidReceiveURLResponseNotification:(NSNotification *)notification { + [self decrementActivityCount]; +} + +@end diff --git a/Pods/Parse/Parse/PFNullability.h b/Pods/Parse/Parse/PFNullability.h new file mode 100644 index 0000000..8c1b958 --- /dev/null +++ b/Pods/Parse/Parse/PFNullability.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef Parse_PFNullability_h +#define Parse_PFNullability_h + +///-------------------------------------- +/// @name Nullability Annotation Support +///-------------------------------------- + +#if __has_feature(nullability) +# define PF_NONNULL nonnull +# define PF_NONNULL_S __nonnull +# define PF_NULLABLE nullable +# define PF_NULLABLE_S __nullable +# define PF_NULLABLE_PROPERTY nullable, +#else +# define PF_NONNULL +# define PF_NONNULL_S +# define PF_NULLABLE +# define PF_NULLABLE_S +# define PF_NULLABLE_PROPERTY +#endif + +#if __has_feature(assume_nonnull) +# ifdef NS_ASSUME_NONNULL_BEGIN +# define PF_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN +# else +# define PF_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +# endif +# ifdef NS_ASSUME_NONNULL_END +# define PF_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END +# else +# define PF_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +# endif +#else +# define PF_ASSUME_NONNULL_BEGIN +# define PF_ASSUME_NONNULL_END +#endif + +#endif diff --git a/Pods/Parse/Parse/PFObject+Subclass.h b/Pods/Parse/Parse/PFObject+Subclass.h new file mode 100644 index 0000000..585d5c1 --- /dev/null +++ b/Pods/Parse/Parse/PFObject+Subclass.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import + +@class PFQuery PF_GENERIC(PFGenericObject : PFObject *); + +PF_ASSUME_NONNULL_BEGIN + +/*! + ### Subclassing Notes + + Developers can subclass `PFObject` for a more native object-oriented class structure. + Strongly-typed subclasses of `PFObject` must conform to the protocol + and must call before <[Parse setApplicationId:clientKey:]> is called. + After this it will be returned by and other `PFObject` factories. + + All methods in except for <[PFSubclassing parseClassName]> + are already implemented in the `PFObject+Subclass` category. + + Including `PFObject+Subclass.h` in your implementation file provides these implementations automatically. + + Subclasses support simpler initializers, query syntax, and dynamic synthesizers. + The following shows an example subclass: + + \@interface MYGame : PFObject + + // Accessing this property is the same as objectForKey:@"title" + @property (nonatomic, copy) NSString *title; + + + (NSString *)parseClassName; + + @end + + + @implementation MYGame + + @dynamic title; + + + (NSString *)parseClassName { + return @"Game"; + } + + @end + + + MYGame *game = [[MYGame alloc] init]; + game.title = @"Bughouse"; + [game saveInBackground]; + */ +@interface PFObject (Subclass) + +///-------------------------------------- +/// @name Methods for Subclasses +///-------------------------------------- + +/*! + @abstract Creates an instance of the registered subclass with this class's . + + @discussion This helps a subclass ensure that it can be subclassed itself. + For example, `[PFUser object]` will return a `MyUser` object if `MyUser` is a registered subclass of `PFUser`. + For this reason, `[MyClass object]` is preferred to `[[MyClass alloc] init]`. + This method can only be called on subclasses which conform to `PFSubclassing`. + A default implementation is provided by `PFObject` which should always be sufficient. + */ ++ (instancetype)object; + +/*! + @abstract Creates a reference to an existing `PFObject` for use in creating associations between `PFObjects`. + + @discussion Calling on this object will return `NO` until or has been called. + This method can only be called on subclasses which conform to . + A default implementation is provided by `PFObject` which should always be sufficient. + No network request will be made. + + @param objectId The object id for the referenced object. + + @returns An instance of `PFObject` without data. + */ ++ (instancetype)objectWithoutDataWithObjectId:(PF_NULLABLE NSString *)objectId; + +/*! + @abstract Registers an Objective-C class for Parse to use for representing a given Parse class. + + @discussion Once this is called on a `PFObject` subclass, any `PFObject` Parse creates with a class name + that matches `[self parseClassName]` will be an instance of subclass. + This method can only be called on subclasses which conform to . + A default implementation is provided by `PFObject` which should always be sufficient. + */ ++ (void)registerSubclass; + +/*! + @abstract Returns a query for objects of type . + + @discussion This method can only be called on subclasses which conform to . + A default implementation is provided by which should always be sufficient. + */ ++ (PF_NULLABLE PFQuery *)query; + +/*! + @abstract Returns a query for objects of type with a given predicate. + + @discussion A default implementation is provided by which should always be sufficient. + @warning This method can only be called on subclasses which conform to . + + @param predicate The predicate to create conditions from. + + @returns An instance of . + + @see [PFQuery queryWithClassName:predicate:] + */ ++ (PF_NULLABLE PFQuery *)queryWithPredicate:(PF_NULLABLE NSPredicate *)predicate; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFObject.h b/Pods/Parse/Parse/PFObject.h new file mode 100644 index 0000000..aeb51c7 --- /dev/null +++ b/Pods/Parse/Parse/PFObject.h @@ -0,0 +1,1429 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +@protocol PFSubclassing; +@class PFRelation; + +/*! + The name of the default pin that for PFObject local data store. + */ +extern NSString *const PFObjectDefaultPin; + +/*! + The `PFObject` class is a local representation of data persisted to the Parse cloud. + This is the main class that is used to interact with objects in your app. + */ +NS_REQUIRES_PROPERTY_DEFINITIONS +@interface PFObject : NSObject { + BOOL dirty; + + // An array of NSDictionary of NSString -> PFFieldOperation. + // Each dictionary has a subset of the object's keys as keys, and the + // changes to the value for that key as its value. + // There is always at least one dictionary of pending operations. + // Every time a save is started, a new dictionary is added to the end. + // Whenever a save completes, the new data is put into fetchedData, and + // a dictionary is removed from the start. + NSMutableArray *PF_NULLABLE_S operationSetQueue; +} + +///-------------------------------------- +/// @name Creating a PFObject +///-------------------------------------- + +/*! + @abstract Initializes a new empty `PFObject` instance with a class name. + + @param newClassName A class name can be any alphanumeric string that begins with a letter. + It represents an object in your app, like a 'User' or a 'Document'. + + @returns Returns the object that is instantiated with the given class name. + */ +- (instancetype)initWithClassName:(NSString *)newClassName; + +/*! + @abstract Creates a new PFObject with a class name. + + @param className A class name can be any alphanumeric string that begins with a letter. + It represents an object in your app, like a 'User' or a 'Document'. + + @returns Returns the object that is instantiated with the given class name. + */ ++ (instancetype)objectWithClassName:(NSString *)className; + +/*! + @abstract Creates a new `PFObject` with a class name, initialized with data + constructed from the specified set of objects and keys. + + @param className The object's class. + @param dictionary An `NSDictionary` of keys and objects to set on the new `PFObject`. + + @returns A PFObject with the given class name and set with the given data. + */ ++ (instancetype)objectWithClassName:(NSString *)className dictionary:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, id)*)dictionary; + +/*! + @abstract Creates a reference to an existing PFObject for use in creating associations between PFObjects. + + @discussion Calling on this object will return `NO` until has been called. + No network request will be made. + + @param className The object's class. + @param objectId The object id for the referenced object. + + @returns A `PFObject` instance without data. + */ ++ (instancetype)objectWithoutDataWithClassName:(NSString *)className objectId:(PF_NULLABLE NSString *)objectId; + +///-------------------------------------- +/// @name Managing Object Properties +///-------------------------------------- + +/*! + @abstract The class name of the object. + */ +@property (strong, readonly) NSString *parseClassName; + +/*! + @abstract The id of the object. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *objectId; + +/*! + @abstract When the object was last updated. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong, readonly) NSDate *updatedAt; + +/*! + @abstract When the object was created. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong, readonly) NSDate *createdAt; + +/*! + @abstract The ACL for this object. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) PFACL *ACL; + +/*! + @abstract Returns an array of the keys contained in this object. + + @discussion This does not include `createdAt`, `updatedAt`, `authData`, or `objectId`. + It does include things like username and ACL. + */ +- (NSArray PF_GENERIC(NSString *)*)allKeys; + +///-------------------------------------- +/// @name Accessors +///-------------------------------------- + +/*! + @abstract Returns the value associated with a given key. + + @param key The key for which to return the corresponding value. + */ +- (PF_NULLABLE_S id)objectForKey:(NSString *)key; + +/*! + @abstract Sets the object associated with a given key. + + @param object The object for `key`. A strong reference to the object is maintained by PFObject. + Raises an `NSInvalidArgumentException` if `object` is `nil`. + If you need to represent a `nil` value - use `NSNull`. + @param key The key for `object`. + Raises an `NSInvalidArgumentException` if `key` is `nil`. + + @see setObject:forKeyedSubscript: + */ +- (void)setObject:(id)object forKey:(NSString *)key; + +/*! + @abstract Unsets a key on the object. + + @param key The key. + */ +- (void)removeObjectForKey:(NSString *)key; + +/*! + @abstract Returns the value associated with a given key. + + @discussion This method enables usage of literal syntax on `PFObject`. + E.g. `NSString *value = object[@"key"];` + + @param key The key for which to return the corresponding value. + + @see objectForKey: + */ +- (PF_NULLABLE_S id)objectForKeyedSubscript:(NSString *)key; + +/*! + @abstract Returns the value associated with a given key. + + @discussion This method enables usage of literal syntax on `PFObject`. + E.g. `object[@"key"] = @"value";` + + @param object The object for `key`. A strong reference to the object is maintained by PFObject. + Raises an `NSInvalidArgumentException` if `object` is `nil`. + If you need to represent a `nil` value - use `NSNull`. + @param key The key for `object`. + Raises an `NSInvalidArgumentException` if `key` is `nil`. + + @see setObject:forKey: + */ +- (void)setObject:(id)object forKeyedSubscript:(NSString *)key; + +/*! + @abstract Returns the relation object associated with the given key. + + @param key The key that the relation is associated with. + */ +- (PFRelation *)relationForKey:(NSString *)key; + +/*! + @abstract Returns the relation object associated with the given key. + + @param key The key that the relation is associated with. + + @deprecated Please use `[PFObject relationForKey:]` instead. + */ +- (PFRelation *)relationforKey:(NSString *)key PARSE_DEPRECATED("Please use -relationForKey: instead."); + +/*! + @abstract Clears any changes to this object made since the last call to save and sets it back to the server state. + */ +- (void)revert; + +/*! + @abstract Clears any changes to this object's key that were done after last successful save and sets it back to the + server state. + + @param key The key to revert changes for. + */ +- (void)revertObjectForKey:(NSString *)key; + +///-------------------------------------- +/// @name Array Accessors +///-------------------------------------- + +/*! + @abstract Adds an object to the end of the array associated with a given key. + + @param object The object to add. + @param key The key. + */ +- (void)addObject:(id)object forKey:(NSString *)key; + +/*! + @abstract Adds the objects contained in another array to the end of the array associated with a given key. + + @param objects The array of objects to add. + @param key The key. + */ +- (void)addObjectsFromArray:(NSArray *)objects forKey:(NSString *)key; + +/*! + @abstract Adds an object to the array associated with a given key, only if it is not already present in the array. + + @discussion The position of the insert is not guaranteed. + + @param object The object to add. + @param key The key. + */ +- (void)addUniqueObject:(id)object forKey:(NSString *)key; + +/*! + @abstract Adds the objects contained in another array to the array associated with a given key, + only adding elements which are not already present in the array. + + @dicsussion The position of the insert is not guaranteed. + + @param objects The array of objects to add. + @param key The key. + */ +- (void)addUniqueObjectsFromArray:(NSArray *)objects forKey:(NSString *)key; + +/*! + @abstract Removes all occurrences of an object from the array associated with a given key. + + @param object The object to remove. + @param key The key. + */ +- (void)removeObject:(id)object forKey:(NSString *)key; + +/*! + @abstract Removes all occurrences of the objects contained in another array from the array associated with a given key. + + @param objects The array of objects to remove. + @param key The key. + */ +- (void)removeObjectsInArray:(NSArray *)objects forKey:(NSString *)key; + +///-------------------------------------- +/// @name Increment +///-------------------------------------- + +/*! + @abstract Increments the given key by `1`. + + @param key The key. + */ +- (void)incrementKey:(NSString *)key; + +/*! + @abstract Increments the given key by a number. + + @param key The key. + @param amount The amount to increment. + */ +- (void)incrementKey:(NSString *)key byAmount:(NSNumber *)amount; + +///-------------------------------------- +/// @name Saving Objects +///-------------------------------------- + +/*! + @abstract *Synchronously* saves the `PFObject`. + + @returns Returns whether the save succeeded. + */ +- (BOOL)save PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* saves the `PFObject` and sets an error if it occurs. + + @param error Pointer to an NSError that will be set if necessary. + + @returns Returns whether the save succeeded. + */ +- (BOOL)save:(NSError **)error; + +/*! + @abstract Saves the `PFObject` *asynchronously*. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)saveInBackground; + +/*! + @abstract Saves the `PFObject` *asynchronously* and executes the given callback block. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)saveInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract Saves the `PFObject` asynchronously and calls the given callback. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ +- (void)saveInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Saves this object to the server at some unspecified time in the future, + even if Parse is currently inaccessible. + + @discussion Use this when you may not have a solid network connection, and don't need to know when the save completes. + If there is some problem with the object such that it can't be saved, it will be silently discarded. If the save + completes successfully while the object is still in memory, then callback will be called. + + Objects saved with this method will be stored locally in an on-disk cache until they can be delivered to Parse. + They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection is + available. Objects saved this way will persist even after the app is closed, in which case they will be sent the + next time the app is opened. If more than 10MB of data is waiting to be sent, subsequent calls to + will cause old saves to be silently discarded until the connection can be re-established, and the queued objects + can be saved. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)saveEventually PF_WATCH_UNAVAILABLE; + +/*! + @abstract Saves this object to the server at some unspecified time in the future, + even if Parse is currently inaccessible. + + @discussion Use this when you may not have a solid network connection, and don't need to know when the save completes. + If there is some problem with the object such that it can't be saved, it will be silently discarded. If the save + completes successfully while the object is still in memory, then callback will be called. + + Objects saved with this method will be stored locally in an on-disk cache until they can be delivered to Parse. + They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection is + available. Objects saved this way will persist even after the app is closed, in which case they will be sent the + next time the app is opened. If more than 10MB of data is waiting to be sent, subsequent calls to + will cause old saves to be silently discarded until the connection can be re-established, and the queued objects + can be saved. + + @param callback The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)saveEventually:(PF_NULLABLE PFBooleanResultBlock)callback PF_WATCH_UNAVAILABLE; + +///-------------------------------------- +/// @name Saving Many Objects +///-------------------------------------- + +/*! + @abstract Saves a collection of objects *synchronously all at once. + + @param objects The array of objects to save. + + @returns Returns whether the save succeeded. + */ ++ (BOOL)saveAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Saves a collection of objects *synchronously* all at once and sets an error if necessary. + + @param objects The array of objects to save. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the save succeeded. + */ ++ (BOOL)saveAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects error:(NSError **)error; + +/*! + @abstract Saves a collection of objects all at once *asynchronously*. + + @param objects The array of objects to save. + + @returns The task that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)saveAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract Saves a collection of objects all at once `asynchronously` and executes the block when done. + + @param objects The array of objects to save. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)saveAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract Saves a collection of objects all at once *asynchronously* and calls a callback when done. + + @param objects The array of objects to save. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)number error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)saveAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Deleting Many Objects +///-------------------------------------- + +/*! + @abstract *Synchronously* deletes a collection of objects all at once. + + @param objects The array of objects to delete. + + @returns Returns whether the delete succeeded. + */ ++ (BOOL)deleteAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* deletes a collection of objects all at once and sets an error if necessary. + + @param objects The array of objects to delete. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the delete succeeded. + */ ++ (BOOL)deleteAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects error:(NSError **)error; + +/*! + @abstract Deletes a collection of objects all at once asynchronously. + @param objects The array of objects to delete. + @returns The task that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)deleteAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract Deletes a collection of objects all at once *asynchronously* and executes the block when done. + + @param objects The array of objects to delete. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)deleteAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract Deletes a collection of objects all at once *asynchronously* and calls a callback when done. + + @param objects The array of objects to delete. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)number error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)deleteAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Getting an Object +///-------------------------------------- + +/*! + @abstract Gets whether the `PFObject` has been fetched. + + @returns `YES` if the PFObject is new or has been fetched or refreshed, otherwise `NO`. + */ +- (BOOL)isDataAvailable; + +#if PARSE_IOS_ONLY + +/*! + @abstract Refreshes the PFObject with the current data from the server. + + @deprecated Please use `-fetch` instead. + */ +- (PF_NULLABLE instancetype)refresh PF_SWIFT_UNAVAILABLE PARSE_DEPRECATED("Please use `-fetch` instead."); + +/*! + @abstract *Synchronously* refreshes the `PFObject` with the current data from the server and sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + + @deprecated Please use `-fetch:` instead. + */ +- (PF_NULLABLE instancetype)refresh:(NSError **)error PARSE_DEPRECATED("Please use `-fetch:` instead."); + +/*! + @abstract *Asynchronously* refreshes the `PFObject` and executes the given callback block. + + @param block The block to execute. + The block should have the following argument signature: `^(PFObject *object, NSError *error)` + + @deprecated Please use `-fetchInBackgroundWithBlock:` instead. + */ +- (void)refreshInBackgroundWithBlock:(PF_NULLABLE PFObjectResultBlock)block PARSE_DEPRECATED("Please use `-fetchInBackgroundWithBlock:` instead."); + +/* + @abstract *Asynchronously* refreshes the `PFObject` and calls the given callback. + + @param target The target on which the selector will be called. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(PFObject *)refreshedObject error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `refreshedObject` will be the `PFObject` with the refreshed data. + + @deprecated Please use `fetchInBackgroundWithTarget:selector:` instead. + */ +- (void)refreshInBackgroundWithTarget:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector PARSE_DEPRECATED("Please use `fetchInBackgroundWithTarget:selector:` instead."); + +#endif + +/*! + @abstract *Synchronously* fetches the PFObject with the current data from the server. + */ +- (PF_NULLABLE instancetype)fetch PF_SWIFT_UNAVAILABLE; +/*! + @abstract *Synchronously* fetches the PFObject with the current data from the server and sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + */ +- (PF_NULLABLE instancetype)fetch:(NSError **)error; + +/*! + @abstract *Synchronously* fetches the `PFObject` data from the server if is `NO`. + */ +- (PF_NULLABLE instancetype)fetchIfNeeded PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* fetches the `PFObject` data from the server if is `NO`. + + @param error Pointer to an `NSError` that will be set if necessary. + */ +- (PF_NULLABLE instancetype)fetchIfNeeded:(NSError **)error; + +/*! + @abstract Fetches the `PFObject` *asynchronously* and sets it as a result for the task. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(__kindof PFObject *)*)fetchInBackground; + +/*! + @abstract Fetches the PFObject *asynchronously* and executes the given callback block. + + @param block The block to execute. + It should have the following argument signature: `^(PFObject *object, NSError *error)`. + */ +- (void)fetchInBackgroundWithBlock:(PF_NULLABLE PFObjectResultBlock)block; + +/* + @abstract Fetches the `PFObject *asynchronously* and calls the given callback. + + @param target The target on which the selector will be called. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(PFObject *)refreshedObject error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `refreshedObject` will be the `PFObject` with the refreshed data. + */ +- (void)fetchInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Fetches the `PFObject` data *asynchronously* if isDataAvailable is `NO`, + then sets it as a result for the task. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(__kindof PFObject *)*)fetchIfNeededInBackground; + +/*! + @abstract Fetches the `PFObject` data *asynchronously* if is `NO`, then calls the callback block. + + @param block The block to execute. + It should have the following argument signature: `^(PFObject *object, NSError *error)`. + */ +- (void)fetchIfNeededInBackgroundWithBlock:(PF_NULLABLE PFObjectResultBlock)block; + +/* + @abstract Fetches the PFObject's data asynchronously if isDataAvailable is false, then calls the callback. + + @param target The target on which the selector will be called. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(PFObject *)fetchedObject error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `refreshedObject` will be the `PFObject` with the refreshed data. + */ +- (void)fetchIfNeededInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Getting Many Objects +///-------------------------------------- + +/*! + @abstract *Synchronously* fetches all of the `PFObject` objects with the current data from the server. + + @param objects The list of objects to fetch. + */ ++ (PF_NULLABLE NSArray PF_GENERIC(__kindof PFObject *)*)fetchAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* fetches all of the `PFObject` objects with the current data from the server + and sets an error if it occurs. + + @param objects The list of objects to fetch. + @param error Pointer to an `NSError` that will be set if necessary. + */ ++ (PF_NULLABLE NSArray PF_GENERIC(__kindof PFObject *)*)fetchAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + error:(NSError **)error; + +/*! + @abstract *Synchronously* fetches all of the `PFObject` objects with the current data from the server. + @param objects The list of objects to fetch. + */ ++ (PF_NULLABLE NSArray PF_GENERIC(__kindof PFObject *)*)fetchAllIfNeeded:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* fetches all of the `PFObject` objects with the current data from the server + and sets an error if it occurs. + + @param objects The list of objects to fetch. + @param error Pointer to an `NSError` that will be set if necessary. + */ ++ (PF_NULLABLE NSArray PF_GENERIC(__kindof PFObject *)*)fetchAllIfNeeded:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + error:(NSError **)error; + +/*! + @abstract Fetches all of the `PFObject` objects with the current data from the server *asynchronously*. + + @param objects The list of objects to fetch. + + @returns The task that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSArray<__kindof PFObject *> *)*)fetchAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract Fetches all of the `PFObject` objects with the current data from the server *asynchronously* + and calls the given block. + + @param objects The list of objects to fetch. + @param block The block to execute. + It should have the following argument signature: `^(NSArray *objects, NSError *error)`. + */ ++ (void)fetchAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + block:(PF_NULLABLE PFArrayResultBlock)block; + +/* + @abstract Fetches all of the `PFObject` objects with the current data from the server *asynchronously* + and calls the given callback. + + @param objects The list of objects to fetch. + @param target The target on which the selector will be called. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSArray *)fetchedObjects error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `fetchedObjects` will the array of `PFObject` objects that were fetched. + */ ++ (void)fetchAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Fetches all of the `PFObject` objects with the current data from the server *asynchronously*. + + @param objects The list of objects to fetch. + + @returns The task that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSArray<__kindof PFObject *> *)*)fetchAllIfNeededInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract Fetches all of the PFObjects with the current data from the server *asynchronously* + and calls the given block. + + @param objects The list of objects to fetch. + @param block The block to execute. + It should have the following argument signature: `^(NSArray *objects, NSError *error)`. + */ ++ (void)fetchAllIfNeededInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + block:(PF_NULLABLE PFArrayResultBlock)block; + +/* + @abstract Fetches all of the PFObjects with the current data from the server *asynchronously* + and calls the given callback. + + @param objects The list of objects to fetch. + @param target The target on which the selector will be called. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSArray *)fetchedObjects error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `fetchedObjects` will the array of `PFObject` objects that were fetched. + */ ++ (void)fetchAllIfNeededInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Fetching From Local Datastore +///-------------------------------------- + +/*! + @abstract *Synchronously* loads data from the local datastore into this object, + if it has not been fetched from the server already. + */ +- (PF_NULLABLE instancetype)fetchFromLocalDatastore PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* loads data from the local datastore into this object, if it has not been fetched + from the server already. + + @discussion If the object is not stored in the local datastore, this `error` will be set to + return kPFErrorCacheMiss. + + @param error Pointer to an `NSError` that will be set if necessary. + */ +- (PF_NULLABLE instancetype)fetchFromLocalDatastore:(NSError **)error; + +/*! + @abstract *Asynchronously* loads data from the local datastore into this object, + if it has not been fetched from the server already. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(__kindof PFObject *)*)fetchFromLocalDatastoreInBackground; + +/*! + @abstract *Asynchronously* loads data from the local datastore into this object, + if it has not been fetched from the server already. + + @param block The block to execute. + It should have the following argument signature: `^(PFObject *object, NSError *error)`. + */ +- (void)fetchFromLocalDatastoreInBackgroundWithBlock:(PF_NULLABLE PFObjectResultBlock)block; + +///-------------------------------------- +/// @name Deleting an Object +///-------------------------------------- + +/*! + @abstract *Synchronously* deletes the `PFObject`. + + @returns Returns whether the delete succeeded. + */ +- (BOOL)delete PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* deletes the `PFObject` and sets an error if it occurs. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the delete succeeded. + */ +- (BOOL)delete:(NSError **)error; + +/*! + @abstract Deletes the `PFObject` *asynchronously*. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)deleteInBackground; + +/*! + @abstract Deletes the `PFObject` *asynchronously* and executes the given callback block. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)deleteInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract Deletes the `PFObject` *asynchronously* and calls the given callback. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ +- (void)deleteInBackgroundWithTarget:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Deletes this object from the server at some unspecified time in the future, + even if Parse is currently inaccessible. + + @discussion Use this when you may not have a solid network connection, + and don't need to know when the delete completes. If there is some problem with the object + such that it can't be deleted, the request will be silently discarded. + + Delete instructions made with this method will be stored locally in an on-disk cache until they can be transmitted + to Parse. They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection + is available. Delete requests will persist even after the app is closed, in which case they will be sent the + next time the app is opened. If more than 10MB of or commands are waiting + to be sent, subsequent calls to or will cause old requests to be silently discarded + until the connection can be re-established, and the queued requests can go through. + + @returns The task that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)deleteEventually PF_WATCH_UNAVAILABLE; + +///-------------------------------------- +/// @name Dirtiness +///-------------------------------------- + +/*! + @abstract Gets whether any key-value pair in this object (or its children) + has been added/updated/removed and not saved yet. + + @returns Returns whether this object has been altered and not saved yet. + */ +- (BOOL)isDirty; + +/*! + @abstract Get whether a value associated with a key has been added/updated/removed and not saved yet. + + @param key The key to check for + + @returns Returns whether this key has been altered and not saved yet. + */ +- (BOOL)isDirtyForKey:(NSString *)key; + +///-------------------------------------- +/// @name Pinning +///-------------------------------------- + +/*! + @abstract *Synchronously* stores the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @returns Returns whether the pin succeeded. + + @see unpin: + @see PFObjectDefaultPin + */ +- (BOOL)pin PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* stores the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the pin succeeded. + + @see unpin: + @see PFObjectDefaultPin + */ +- (BOOL)pin:(NSError **)error; + +/*! + @abstract *Synchronously* stores the object and every object it points to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param name The name of the pin. + + @returns Returns whether the pin succeeded. + + @see unpinWithName: + */ +- (BOOL)pinWithName:(NSString *)name PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* stores the object and every object it points to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param name The name of the pin. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the pin succeeded. + + @see unpinWithName: + */ +- (BOOL)pinWithName:(NSString *)name + error:(NSError **)error; + +/*! + @abstract *Asynchronously* stores the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @returns The task that encapsulates the work being done. + + @see unpinInBackground + @see PFObjectDefaultPin + */ +- (BFTask PF_GENERIC(NSNumber *)*)pinInBackground; + +/*! + @abstract *Asynchronously* stores the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see unpinInBackgroundWithBlock: + @see PFObjectDefaultPin + */ +- (void)pinInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Asynchronously* stores the object and every object it points to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param name The name of the pin. + + @returns The task that encapsulates the work being done. + + @see unpinInBackgroundWithName: + */ +- (BFTask PF_GENERIC(NSNumber *)*)pinInBackgroundWithName:(NSString *)name; + +/*! + @abstract *Asynchronously* stores the object and every object it points to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + <[PFObject objectWithoutDataWithClassName:objectId:]> and then call on it. + + @param name The name of the pin. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see unpinInBackgroundWithName:block: + */ +- (void)pinInBackgroundWithName:(NSString *)name block:(PF_NULLABLE PFBooleanResultBlock)block; + +///-------------------------------------- +/// @name Pinning Many Objects +///-------------------------------------- + +/*! + @abstract *Synchronously* stores the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + + @returns Returns whether the pin succeeded. + + @see unpinAll: + @see PFObjectDefaultPin + */ ++ (BOOL)pinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* stores the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the pin succeeded. + + @see unpinAll:error: + @see PFObjectDefaultPin + */ ++ (BOOL)pinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects error:(NSError **)error; + +/*! + @abstract *Synchronously* stores the objects and every object they point to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param name The name of the pin. + + @returns Returns whether the pin succeeded. + + @see unpinAll:withName: + */ ++ (BOOL)pinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects withName:(NSString *)name PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* stores the objects and every object they point to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param name The name of the pin. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the pin succeeded. + + @see unpinAll:withName:error: + */ ++ (BOOL)pinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + withName:(NSString *)name + error:(NSError **)error; + +/*! + @abstract *Asynchronously* stores the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + + @returns The task that encapsulates the work being done. + + @see unpinAllInBackground: + @see PFObjectDefaultPin + */ ++ (BFTask PF_GENERIC(NSNumber *)*)pinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract *Asynchronously* stores the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see unpinAllInBackground:block: + @see PFObjectDefaultPin + */ ++ (void)pinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Asynchronously* stores the objects and every object they point to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param name The name of the pin. + + @returns The task that encapsulates the work being done. + + @see unpinAllInBackground:withName: + */ ++ (BFTask PF_GENERIC(NSNumber *)*)pinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects withName:(NSString *)name; + +/*! + @abstract *Asynchronously* stores the objects and every object they point to in the local datastore, recursively. + + @discussion If those other objects have not been fetched from Parse, they will not be stored. However, + if they have changed data, all the changes will be retained. To get the objects back later, you can + use a that uses <[PFQuery fromLocalDatastore]>, or you can create an unfetched pointer with + `[PFObject objectWithoutDataWithClassName:objectId:]` and then call `fetchFromLocalDatastore:` on it. + + @param objects The objects to be pinned. + @param name The name of the pin. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see unpinAllInBackground:withName:block: + */ ++ (void)pinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + withName:(NSString *)name + block:(PF_NULLABLE PFBooleanResultBlock)block; + +///-------------------------------------- +/// @name Unpinning +///-------------------------------------- + +/*! + @abstract *Synchronously* removes the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @returns Returns whether the unpin succeeded. + + @see pin: + @see PFObjectDefaultPin + */ +- (BOOL)unpin PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + + @see pin: + @see PFObjectDefaultPin + */ +- (BOOL)unpin:(NSError **)error; + +/*! + @abstract *Synchronously* removes the object and every object it points to in the local datastore, recursively. + + @param name The name of the pin. + + @returns Returns whether the unpin succeeded. + + @see pinWithName: + */ +- (BOOL)unpinWithName:(NSString *)name PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes the object and every object it points to in the local datastore, recursively. + + @param name The name of the pin. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + + @see pinWithName:error: + */ +- (BOOL)unpinWithName:(NSString *)name + error:(NSError **)error; + +/*! + @abstract *Asynchronously* removes the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @returns The task that encapsulates the work being done. + + @see pinInBackground + @see PFObjectDefaultPin + */ +- (BFTask PF_GENERIC(NSNumber *)*)unpinInBackground; + +/*! + @abstract *Asynchronously* removes the object and every object it points to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see pinInBackgroundWithBlock: + @see PFObjectDefaultPin + */ +- (void)unpinInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Asynchronously* removes the object and every object it points to in the local datastore, recursively. + + @param name The name of the pin. + + @returns The task that encapsulates the work being done. + + @see pinInBackgroundWithName: + */ +- (BFTask PF_GENERIC(NSNumber *)*)unpinInBackgroundWithName:(NSString *)name; + +/*! + @abstract *Asynchronously* removes the object and every object it points to in the local datastore, recursively. + + @param name The name of the pin. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see pinInBackgroundWithName:block: + */ +- (void)unpinInBackgroundWithName:(NSString *)name block:(PF_NULLABLE PFBooleanResultBlock)block; + +///-------------------------------------- +/// @name Unpinning Many Objects +///-------------------------------------- + +/*! + @abstract *Synchronously* removes all objects in the local datastore + using a default pin name: `PFObjectDefaultPin`. + + @returns Returns whether the unpin succeeded. + + @see PFObjectDefaultPin + */ ++ (BOOL)unpinAllObjects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes all objects in the local datastore + using a default pin name: `PFObjectDefaultPin`. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + + @see PFObjectDefaultPin + */ ++ (BOOL)unpinAllObjects:(NSError **)error; + +/*! + @abstract *Synchronously* removes all objects with the specified pin name. + + @param name The name of the pin. + + @returns Returns whether the unpin succeeded. + */ ++ (BOOL)unpinAllObjectsWithName:(NSString *)name PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes all objects with the specified pin name. + + @param name The name of the pin. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + */ ++ (BOOL)unpinAllObjectsWithName:(NSString *)name + error:(NSError **)error; + +/*! + @abstract *Asynchronously* removes all objects in the local datastore + using a default pin name: `PFObjectDefaultPin`. + + @returns The task that encapsulates the work being done. + + @see PFObjectDefaultPin + */ ++ (BFTask PF_GENERIC(NSNumber *)*)unpinAllObjectsInBackground; + +/*! + @abstract *Asynchronously* removes all objects in the local datastore + using a default pin name: `PFObjectDefaultPin`. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see PFObjectDefaultPin + */ ++ (void)unpinAllObjectsInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Asynchronously* removes all objects with the specified pin name. + + @param name The name of the pin. + + @returns The task that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)unpinAllObjectsInBackgroundWithName:(NSString *)name; + +/*! + @abstract *Asynchronously* removes all objects with the specified pin name. + + @param name The name of the pin. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)unpinAllObjectsInBackgroundWithName:(NSString *)name block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Synchronously* removes the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param objects The objects. + + @returns Returns whether the unpin succeeded. + + @see pinAll: + @see PFObjectDefaultPin + */ ++ (BOOL)unpinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param objects The objects. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + + @see pinAll:error: + @see PFObjectDefaultPin + */ ++ (BOOL)unpinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects error:(NSError **)error; + +/*! + @abstract *Synchronously* removes the objects and every object they point to in the local datastore, recursively. + + @param objects The objects. + @param name The name of the pin. + + @returns Returns whether the unpin succeeded. + + @see pinAll:withName: + */ ++ (BOOL)unpinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects withName:(NSString *)name PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* removes the objects and every object they point to in the local datastore, recursively. + + @param objects The objects. + @param name The name of the pin. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unpin succeeded. + + @see pinAll:withName:error: + */ ++ (BOOL)unpinAll:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + withName:(NSString *)name + error:(NSError **)error; + +/*! + @abstract *Asynchronously* removes the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param objects The objects. + + @returns The task that encapsulates the work being done. + + @see pinAllInBackground: + @see PFObjectDefaultPin + */ ++ (BFTask PF_GENERIC(NSNumber *)*)unpinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects; + +/*! + @abstract *Asynchronously* removes the objects and every object they point to in the local datastore, recursively, + using a default pin name: `PFObjectDefaultPin`. + + @param objects The objects. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see pinAllInBackground:block: + @see PFObjectDefaultPin + */ ++ (void)unpinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Asynchronously* removes the objects and every object they point to in the local datastore, recursively. + + @param objects The objects. + @param name The name of the pin. + + @returns The task that encapsulates the work being done. + + @see pinAllInBackground:withName: + */ ++ (BFTask PF_GENERIC(NSNumber *)*)unpinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects withName:(NSString *)name; + +/*! + @abstract *Asynchronously* removes the objects and every object they point to in the local datastore, recursively. + + @param objects The objects. + @param name The name of the pin. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + + @see pinAllInBackground:withName:block: + */ ++ (void)unpinAllInBackground:(PF_NULLABLE NSArray PF_GENERIC(PFObject *)*)objects + withName:(NSString *)name + block:(PF_NULLABLE PFBooleanResultBlock)block; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFObject.m b/Pods/Parse/Parse/PFObject.m new file mode 100644 index 0000000..b403694 --- /dev/null +++ b/Pods/Parse/Parse/PFObject.m @@ -0,0 +1,2770 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFObject.h" +#import "PFObject+Subclass.h" +#import "PFObjectSubclassingController.h" + +#import +#import +#import + +#import + +#import "BFTask+Private.h" +#import "PFACLPrivate.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFConstants.h" +#import "PFCoreManager.h" +#import "PFCurrentUserController.h" +#import "PFDateFormatter.h" +#import "PFDecoder.h" +#import "PFEncoder.h" +#import "PFErrorUtilities.h" +#import "PFEventuallyQueue_Private.h" +#import "PFFileManager.h" +#import "PFFile_Private.h" +#import "PFJSONSerialization.h" +#import "PFLogging.h" +#import "PFMacros.h" +#import "PFMultiProcessFileLockController.h" +#import "PFMutableObjectState.h" +#import "PFObjectBatchController.h" +#import "PFObjectConstants.h" +#import "PFObjectController.h" +#import "PFObjectEstimatedData.h" +#import "PFObjectFileCodingLogic.h" +#import "PFObjectFilePersistenceController.h" +#import "PFObjectLocalIdStore.h" +#import "PFObjectUtilities.h" +#import "PFOfflineStore.h" +#import "PFOperationSet.h" +#import "PFPin.h" +#import "PFPinningObjectStore.h" +#import "PFQueryPrivate.h" +#import "PFRESTObjectBatchCommand.h" +#import "PFRESTObjectCommand.h" +#import "PFRelation.h" +#import "PFRelationPrivate.h" +#import "PFSubclassing.h" +#import "PFTaskQueue.h" +#import "ParseInternal.h" +#import "Parse_Private.h" + +/*! + Checks if an object can be used as a value for PFObject. + */ +static void PFObjectAssertValueIsKindOfValidClass(id object) { + static NSArray *classes; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + classes = @[ [NSDictionary class], [NSArray class], + [NSString class], [NSNumber class], [NSNull class], [NSDate class], [NSData class], + [PFObject class], [PFFile class], [PFACL class], [PFGeoPoint class] ]; + }); + + for (Class class in classes) { + if ([object isKindOfClass:class]) { + return; + } + } + + PFParameterAssert(NO, @"PFObject values may not have class: %@", [object class]); +} + +/*! + Checks if a class is a of container kind to be used as a value for PFObject. + */ +static BOOL PFObjectValueIsKindOfMutableContainerClass(id object) { + static NSArray *classes; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + classes = @[ [NSDictionary class], [NSArray class], [PFACL class], [PFGeoPoint class] ]; + }); + + for (Class class in classes) { + if ([object isKindOfClass:class]) { + return YES; + } + } + + return NO; +} + +@interface PFObject () { + // A lock for accessing any of the internal state of this object. + // Guards basically all of the variables below. + NSObject *lock; + + PFObjectState *_pfinternal_state; + + PFObjectEstimatedData *_estimatedData; + NSMutableSet *_availableKeys; // TODO: (nlutsenko) Maybe decouple this further. + + // TODO (grantland): Derive this off the EventuallyPins as opposed to +/- count. + int _deletingEventually; + + // A dictionary that maps id (objects) => PFJSONCache + NSMutableDictionary *hashedObjectsCache; + + NSString *localId; + + // This queue is used to guarantee the order of *Eventually commands + // and offload all the work to the background thread + PFTaskQueue *_eventuallyTaskQueue; +} + +@property (nonatomic, strong, readwrite) NSString *localId; + +@property (nonatomic, strong, readwrite) PFTaskQueue *taskQueue; + ++ (void)assertSubclassIsRegistered:(Class)subclass; + +@end + +@implementation PFObject (Private) + ++ (void)unregisterSubclass:(Class)subclass { + [[self subclassingController] unregisterSubclass:subclass]; +} + +/*! + Returns the object that should be used to synchronize all internal data access. + */ +- (NSObject *)lock { + return lock; +} + +/*! + Blocks until all outstanding operations have completed. + */ +- (void)waitUntilFinished { + [[self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return toAwait; + }] waitForResult:nil]; +} + +/*! + For operations that need to be put into multiple objects queues, like saveAll + and fetchAll, this method does the nasty work. + @param taskStart - A block that is called when all of the objects are ready. + It can return a promise that all of the queues will then wait on. + @param objects - The objects that this operation affects. + @returns - Returns a promise that is fulfilled once the promise returned by the + block is fulfilled. + */ ++ (BFTask *)_enqueue:(BFTask *(^)(BFTask *toAwait))taskStart forObjects:(NSArray *)objects { + // The task that will be complete when all of the child queues indicate they're ready to start. + BFTaskCompletionSource *readyToStart = [BFTaskCompletionSource taskCompletionSource]; + + // First, we need to lock the mutex for the queue for every object. We have to hold this + // from at least when taskStart() is called to when obj.taskQueue enqueue is called, so + // that saves actually get executed in the order they were setup by taskStart(). + // The locks have to be sorted so that we always acquire them in the same order. + // Otherwise, there's some risk of deadlock. + NSMutableArray *mutexes = [NSMutableArray array]; + for (PFObject *obj in objects) { + [mutexes addObject:obj.taskQueue.mutex]; + } + [mutexes sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { + void *lock1 = (__bridge void *)obj1; + void *lock2 = (__bridge void *)obj2; + return lock1 - lock2; + }]; + for (NSObject *lock in mutexes) { + objc_sync_enter(lock); + } + + @try { + // The task produced by taskStart. By running this immediately, we allow everything prior + // to toAwait to run before waiting for all of the queues on all of the objects. + BFTask *fullTask = taskStart(readyToStart.task); + + // Add fullTask to each of the objects' queues. + NSMutableArray *childTasks = [NSMutableArray array]; + for (PFObject *obj in objects) { + [obj.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + [childTasks addObject:toAwait]; + return fullTask; + }]; + } + + // When all of the objects' queues are ready, signal fullTask that it's ready to go on. + [[BFTask taskForCompletionOfAllTasks:childTasks] continueWithBlock:^id(BFTask *task) { + readyToStart.result = nil; + return nil; + }]; + + return fullTask; + + } @finally { + for (NSObject *lock in mutexes) { + objc_sync_exit(lock); + } + } +} + +///-------------------------------------- +#pragma mark - Children helpers +///-------------------------------------- + +/*! + Finds all of the objects that are reachable from child, including child itself, + and adds them to the given mutable array. It traverses arrays and json objects. + @param node An kind object to search for children. + @param dirtyChildren The array to collect the result into. + @param seen The set of all objects that have already been seen. + @param seenNew The set of new objects that have already been seen since the + last existing object. + */ ++ (void)collectDirtyChildren:(id)node + children:(NSMutableSet *)dirtyChildren + files:(NSMutableSet *)dirtyFiles + seen:(NSSet *)seen + seenNew:(NSSet *)seenNew + currentUser:(PFUser *)currentUser { + if ([node isKindOfClass:[NSArray class]]) { + for (id elem in node) { + @autoreleasepool { + [self collectDirtyChildren:elem + children:dirtyChildren + files:dirtyFiles + seen:seen + seenNew:seenNew + currentUser:currentUser]; + } + } + } else if ([node isKindOfClass:[NSDictionary class]]) { + [node enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [self collectDirtyChildren:obj + children:dirtyChildren + files:dirtyFiles + seen:seen + seenNew:seenNew + currentUser:currentUser]; + }]; + } else if ([node isKindOfClass:[PFACL class]]) { + PFACL *acl = (PFACL *)node; + if ([acl hasUnresolvedUser]) { + [self collectDirtyChildren:currentUser + children:dirtyChildren + files:dirtyFiles + seen:seen + seenNew:seenNew + currentUser:currentUser]; + } + + } else if ([node isKindOfClass:[PFObject class]]) { + PFObject *object = (PFObject *)node; + NSDictionary *toSearch = nil; + + @synchronized ([object lock]) { + // Check for cycles of new objects. Any such cycle means it will be + // impossible to save this collection of objects, so throw an exception. + if (object.objectId) { + seenNew = [NSSet set]; + } else { + if ([seenNew containsObject:object]) { + [NSException raise:NSInternalInconsistencyException + format:@"Found a circular dependency when saving."]; + } + seenNew = [seenNew setByAddingObject:object]; + } + + // Check for cycles of any object. If this occurs, then there's no + // problem, but we shouldn't recurse any deeper, because it would be + // an infinite recursion. + if ([seen containsObject:object]) { + return; + } + seen = [seen setByAddingObject:object]; + + // Recurse into this object's children looking for dirty children. + // We only need to look at the child object's current estimated data, + // because that's the only data that might need to be saved now. + toSearch = [object->_estimatedData.dictionaryRepresentation copy]; + } + + [self collectDirtyChildren:toSearch + children:dirtyChildren + files:dirtyFiles + seen:seen + seenNew:seenNew + currentUser:currentUser]; + + if ([object isDirty:NO]) { + [dirtyChildren addObject:object]; + } + } else if ([node isKindOfClass:[PFFile class]]) { + PFFile *file = (PFFile *)node; + if (!file.url) { + [dirtyFiles addObject:node]; + } + } +} + +// Helper version of collectDirtyChildren:children:seen:seenNew so that callers +// don't have to add the internally used parameters. ++ (void)collectDirtyChildren:(id)child + children:(NSMutableSet *)dirtyChildren + files:(NSMutableSet *)dirtyFiles + currentUser:(PFUser *)currentUser { + [self collectDirtyChildren:child + children:dirtyChildren + files:dirtyFiles + seen:[NSSet set] + seenNew:[NSSet set] + currentUser:currentUser]; +} + +// Returns YES if the given object can be serialized for saving as a value +// that is pointed to by a PFObject. +// @param value The object we want to serialize as a value. +// @param saved The set of all objects we can assume will be saved before this one. +// @param error The reason why it can't be serialized. ++ (BOOL)canBeSerializedAsValue:(id)value + afterSaving:(NSMutableArray *)saved + error:(NSError **)error { + if ([value isKindOfClass:[PFObject class]]) { + PFObject *object = (PFObject *)value; + if (!object.objectId && ![saved containsObject:object]) { + if (error) { + *error = [PFErrorUtilities errorWithCode:kPFErrorInvalidPointer + message:@"Pointer to an unsaved object."]; + } + return NO; + } + + } else if ([value isKindOfClass:[NSDictionary class]]) { + __block BOOL retValue = YES; + [value enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if (![[self class] canBeSerializedAsValue:obj + afterSaving:saved + error:error]) { + retValue = NO; + *stop = YES; + } + }]; + return retValue; + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *array = (NSArray *)value; + for (NSString *item in array) { + if (![[self class] canBeSerializedAsValue:item + afterSaving:saved + error:error]) { + return NO; + } + } + } + + return YES; +} + +// Returns YES if this object can be serialized for saving. +// @param saved A set of objects that we can assume will have been saved. +// @param error The reason why it can't be serialized. +- (BOOL)canBeSerializedAfterSaving:(NSMutableArray *)saved withCurrentUser:(PFUser *)user error:(NSError **)error { + @synchronized (lock) { + // This method is only used for batching sets of objects for saveAll + // and when saving children automatically. Since it's only used to + // determine whether or not save should be called on them, it only + // needs to examine their current values, so we use estimatedData. + if (![[self class] canBeSerializedAsValue:_estimatedData.dictionaryRepresentation + afterSaving:saved + error:error]) { + return NO; + } + + if ([self isDataAvailableForKey:@"ACL"] && + [[self ACLWithoutCopying] hasUnresolvedUser] && + ![saved containsObject:user]) { + if (error) { + *error = [PFErrorUtilities errorWithCode:kPFErrorInvalidACL + message:@"User associated with ACL must be signed up."]; + } + return NO; + } + + return YES; + } +} + +// This saves all of the objects and files reachable from the given object. +// It does its work in multiple waves, saving as many as possible in each wave. +// If there's ever an error, it just gives up, sets error, and returns NO; ++ (BFTask *)_deepSaveAsync:(id)object withCurrentUser:(PFUser *)currentUser sessionToken:(NSString *)sessionToken { + BFTask *task = [BFTask taskWithResult:@YES]; + + NSMutableSet *uniqueObjects = [NSMutableSet set]; + NSMutableSet *uniqueFiles = [NSMutableSet set]; + [self collectDirtyChildren:object children:uniqueObjects files:uniqueFiles currentUser:currentUser]; + for (PFFile *file in uniqueFiles) { + task = [task continueAsyncWithSuccessBlock:^id(BFTask *task) { + return [[file saveInBackground] continueAsyncWithBlock:^id(BFTask *task) { + // This is a stupid hack because our current behavior is to fail file + // saves with an error when a file save inside it is cancelled. + if (task.isCancelled) { + NSError *newError = [PFErrorUtilities errorWithCode:kPFErrorUnsavedFile + message:@"A file save was cancelled."]; + return [BFTask taskWithError:newError]; + } + return task; + }]; + }]; + } + + // TODO: (nlutsenko) Get rid of this once we allow localIds in batches. + NSArray *remaining = [uniqueObjects allObjects]; + NSMutableArray *finished = [NSMutableArray array]; + while ([remaining count] > 0) { + // Partition the objects into two sets: those that can be save immediately, + // and those that rely on other objects to be created first. + NSMutableArray *current = [NSMutableArray array]; + NSMutableArray *nextBatch = [NSMutableArray array]; + for (PFObject *object in remaining) { + if ([object canBeSerializedAfterSaving:finished withCurrentUser:currentUser error:nil]) { + [current addObject:object]; + } else { + [nextBatch addObject:object]; + } + } + remaining = nextBatch; + + if (current.count == 0) { + // We do cycle-detection when building the list of objects passed to this + // function, so this should never get called. But we should check for it + // anyway, so that we get an exception instead of an infinite loop. + [NSException raise:NSInternalInconsistencyException + format:@"Unable to save a PFObject with a relation to a cycle."]; + } + + // If a lazy user is one of the objects in the array, resolve its laziness now and + // remove it from the list of things to save. + // + // This has to happen separately from everything else because there [PFUser save] + // is special-cased to work for lazy users, but new users can't be created by + // PFMultiCommand's regular save. + if ([currentUser isLazy] && [current containsObject:currentUser]) { + task = [task continueAsyncWithSuccessBlock:^id(BFTask *task) { + return [currentUser saveInBackground]; + }]; + + [finished addObject:currentUser]; + [current removeObject:currentUser]; + if (current.count == 0) { + continue; + } + } + + task = [task continueAsyncWithSuccessBlock:^id(BFTask *task) { + // Batch requests have currently a limit of 50 packaged requests per single request + // This splitting will split the overall array into segments of upto 50 requests + // and execute them concurrently with a wrapper task for all of them. + NSArray *objectBatches = [PFInternalUtils arrayBySplittingArray:current + withMaximumComponentsPerSegment:PFRESTObjectBatchCommandSubcommandsLimit]; + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:[objectBatches count]]; + + for (NSArray *objectBatch in objectBatches) { + BFTask *batchTask = [self _enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + NSMutableArray *commands = [NSMutableArray arrayWithCapacity:[objectBatch count]]; + for (PFObject *object in objectBatch) { + PFRESTCommand *command = nil; + @synchronized ([object lock]) { + [object _objectWillSave]; + [object _checkSaveParametersWithCurrentUser:currentUser]; + command = [object _constructSaveCommandForChanges:[object unsavedChanges] + sessionToken:sessionToken + objectEncoder:[PFPointerObjectEncoder objectEncoder]]; + [object startSave]; + } + [commands addObject:command]; + } + + PFRESTCommand *batchCommand = [PFRESTObjectBatchCommand batchCommandWithCommands:commands + sessionToken:sessionToken]; + return [[[Parse _currentManager].commandRunner runCommandAsync:batchCommand withOptions:0] + continueAsyncWithBlock:^id(BFTask *commandRunnerTask) { + NSArray *results = [commandRunnerTask.result result]; + + NSMutableArray *handleSaveTasks = [NSMutableArray arrayWithCapacity:[objectBatch count]]; + + __block NSError *error = task.error; + [objectBatch enumerateObjectsUsingBlock:^(PFObject *object, NSUInteger idx, BOOL *stop) { + // If the task resulted in an error - don't even bother looking into + // the result of the command, just roll the error further + + BFTask *task = nil; + if (commandRunnerTask.error) { + task = [object handleSaveResultAsync:nil]; + } else { + NSDictionary *commandResult = results[idx]; + + NSDictionary *errorResult = commandResult[@"error"]; + if (errorResult) { + error = [PFErrorUtilities errorFromResult:errorResult]; + task = [[object handleSaveResultAsync:nil] continueWithBlock:^id(BFTask *task) { + return [BFTask taskWithError:error]; + }]; + } else { + NSDictionary *successfulResult = commandResult[@"success"]; + task = [object handleSaveResultAsync:successfulResult]; + } + } + [handleSaveTasks addObject:task]; + }]; + + return [[BFTask taskForCompletionOfAllTasks:handleSaveTasks] continueAsyncWithBlock:^id(BFTask *task) { + if (commandRunnerTask.error || commandRunnerTask.cancelled || commandRunnerTask.exception) { + return commandRunnerTask; + } + + // Reiterate saveAll tasks, return first error. + for (BFTask *handleSaveTask in handleSaveTasks) { + if (handleSaveTask.error || handleSaveTask.exception) { + return handleSaveTask; + } + } + + return @YES; + }]; + }]; + }]; + } forObjects:objectBatch]; + [tasks addObject:batchTask]; + } + + return [[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task) { + // Return the first exception, instead of the aggregated one + // for the sake of compatability with old versions + + if ([task.exception.name isEqualToString:BFTaskMultipleExceptionsException]) { + NSException *firstException = [task.exception.userInfo[@"exceptions"] firstObject]; + if (firstException) { + return [BFTask taskWithException:firstException]; + } + } + + if (task.error || task.cancelled || task.exception) { + return task; + } + + return @YES; + }]; + }]; + + [finished addObjectsFromArray:current]; + } + + return task; +} + +// Just like deepSaveAsync, but uses saveEventually instead of saveAsync. +// Because you shouldn't wait for saveEventually calls to complete, this +// does not return any operation. ++ (BFTask *)_enqueueSaveEventuallyChildrenOfObject:(PFObject *)object + currentUser:(PFUser *)currentUser { + return [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + NSMutableSet *uniqueObjects = [NSMutableSet set]; + NSMutableSet *uniqueFiles = [NSMutableSet set]; + [self collectDirtyChildren:object children:uniqueObjects files:uniqueFiles currentUser:currentUser]; + for (PFFile *file in uniqueFiles) { + if (!file.url) { + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:@"Unable to saveEventually a PFObject with a relation to a new, unsaved PFFile." + userInfo:nil]; + return [BFTask taskWithException:exception]; + } + } + + // Remove object from the queue of objects to save as this method should only save children. + [uniqueObjects removeObject:object]; + + NSArray *remaining = [uniqueObjects allObjects]; + NSMutableArray *finished = [NSMutableArray array]; + NSMutableArray *enqueueTasks = [NSMutableArray array]; + while ([remaining count] > 0) { + // Partition the objects into two sets: those that can be save immediately, + // and those that rely on other objects to be created first. + NSMutableArray *current = [NSMutableArray array]; + NSMutableArray *nextBatch = [NSMutableArray array]; + for (PFObject *object in remaining) { + if ([object canBeSerializedAfterSaving:finished withCurrentUser:currentUser error:nil]) { + [current addObject:object]; + } else { + [nextBatch addObject:object]; + } + } + remaining = nextBatch; + + if (current.count == 0) { + // We do cycle-detection when building the list of objects passed to this + // function, so this should never get called. But we should check for it + // anyway, so that we get an exception instead of an infinite loop. + [NSException raise:NSInternalInconsistencyException + format:@"Unable to save a PFObject with a relation to a cycle."]; + } + + // If a lazy user is one of the objects in the array, resolve its laziness now and + // remove it from the list of things to save. + // + // This has to happen separately from everything else because there [PFUser save] + // is special-cased to work for lazy users, but new users can't be created by + // PFMultiCommand's regular save. + // + // Unfortunately, ACLs with lazy users still cannot be saved, because the ACL does + // does not get updated after the user save completes. + // TODO: (nlutsenko) Make the ACL update after the user is saved. + if ([currentUser isLazy] && [current containsObject:currentUser]) { + [enqueueTasks addObject:[currentUser _enqueueSaveEventuallyWithChildren:NO]]; + [finished addObject:currentUser]; + [current removeObject:currentUser]; + if (current.count == 0) { + continue; + } + } + + // TODO: (nlutsenko) Allow batching with saveEventually. + for (PFObject *object in current) { + [enqueueTasks addObject:[object _enqueueSaveEventuallyWithChildren:NO]]; + } + + [finished addObjectsFromArray:current]; + } + return [BFTask taskForCompletionOfAllTasks:enqueueTasks]; + }]; +} + +- (BFTask *)_saveChildrenInBackgroundWithCurrentUser:(PFUser *)currentUser sessionToken:(NSString *)sessionToken { + @synchronized (lock) { + return [[self class] _deepSaveAsync:_estimatedData.dictionaryRepresentation + withCurrentUser:currentUser + sessionToken:sessionToken]; + } +} + +///-------------------------------------- +#pragma mark - Dirtiness helper +///-------------------------------------- + +- (BOOL)isDirty:(BOOL)considerChildren { + @synchronized (lock) { + [self checkForChangesToMutableContainers]; + if (self._state.deleted || dirty || [self _hasChanges]) { + return YES; + } + + if (considerChildren) { + NSMutableSet *seen = [NSMutableSet set]; + return [self _areChildrenDirty:seen]; + } + + return NO; + } +} + +- (void)_setDirty:(BOOL)aDirty { + @synchronized (lock) { + dirty = aDirty; + } +} + +- (BOOL)_areChildrenDirty:(NSMutableSet *)seenObjects { + if ([seenObjects containsObject:self]) { + return NO; + } + [seenObjects addObject:self]; + + @synchronized(lock) { + [self checkpointAllMutableContainers]; + if (self._state.deleted || dirty || [self _hasChanges]) { + return YES; + } + + // We only need to consider the currently estimated children here, + // because they're the only ones that might need to be saved in a + // subsequent call to save, which is the meaning of "dirtiness". + __block BOOL retValue = NO; + [_estimatedData enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + if ([obj isKindOfClass:[PFObject class]] && [obj _areChildrenDirty:seenObjects]) { + retValue = YES; + *stop = YES; + } + }]; + return retValue; + } +} + +///-------------------------------------- +#pragma mark - Mutable container management +///-------------------------------------- + +- (void)checkpointAllMutableContainers { + @synchronized (lock) { + [_estimatedData enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + [self checkpointMutableContainer:obj]; + }]; + } +} + +- (void)checkpointMutableContainer:(id)object { + @synchronized (lock) { + if (PFObjectValueIsKindOfMutableContainerClass(object)) { + [hashedObjectsCache setObject:[PFJSONCacheItem cacheFromObject:object] + forKey:[NSValue valueWithNonretainedObject:object]]; + } + } +} + +- (void)checkForChangesToMutableContainer:(id)object forKey:(NSString *)key { + @synchronized (lock) { + // If this is a mutable container, we should check its contents. + if (PFObjectValueIsKindOfMutableContainerClass(object)) { + PFJSONCacheItem *oldCacheItem = [hashedObjectsCache objectForKey:[NSValue valueWithNonretainedObject:object]]; + if (!oldCacheItem) { + [NSException raise:NSInternalInconsistencyException + format:@"PFObject contains container item that isn't cached."]; + } else { + PFJSONCacheItem *newCacheItem = [PFJSONCacheItem cacheFromObject:object]; + if (![oldCacheItem isEqual:newCacheItem]) { + // A mutable container changed out from under us. Treat it as a set operation. + [self setObject:object forKey:key]; + } + } + } else { + [hashedObjectsCache removeObjectForKey:[NSValue valueWithNonretainedObject:object]]; + } + } +} + +- (void)checkForChangesToMutableContainers { + @synchronized (lock) { + NSMutableArray *unexaminedCacheKeys = [[hashedObjectsCache allKeys] mutableCopy]; + NSDictionary *reachableData = _estimatedData.dictionaryRepresentation; + [reachableData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [unexaminedCacheKeys removeObject:[NSValue valueWithNonretainedObject:obj]]; + [self checkForChangesToMutableContainer:obj forKey:key]; + }]; + + // Remove unchecked cache entries. + [hashedObjectsCache removeObjectsForKeys:unexaminedCacheKeys]; + } +} + +///-------------------------------------- +#pragma mark - Data Availability +///-------------------------------------- + +// TODO: (nlutsenko) Remove this when rest of PFObject is decoupled. +- (void)setHasBeenFetched:(BOOL)fetched { + @synchronized (lock) { + if (self._state.complete != fetched) { + PFMutableObjectState *state = [_pfinternal_state mutableCopy]; + state.complete = fetched; + self._state = state; + } + } +} + +- (void)_setDeleted:(BOOL)deleted { + @synchronized (lock) { + if (self._state.deleted != deleted) { + PFMutableObjectState *state = [_pfinternal_state mutableCopy]; + state.deleted = deleted; + self._state = state; + } + } +} + +- (BOOL)isDataAvailableForKey:(NSString *)key { + if (!key) { + return NO; + } + + @synchronized (lock) { + if ([self isDataAvailable]) { + return YES; + } + return [_availableKeys containsObject:key]; + } +} + +///-------------------------------------- +#pragma mark - Validations +///-------------------------------------- + +// Validations that are done on save. For now, there is nothing. +- (void)_checkSaveParametersWithCurrentUser:(PFUser *)currentUser { + return; +} + +/*! + Checks if Parse class name could be used to initialize a given instance of PFObject or it's subclass. + */ ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert(className, @"Class name can't be 'nil'."); + PFParameterAssert(![className hasPrefix:@"_"], @"Invalid class name. Class names cannot start with an underscore."); +} + +///-------------------------------------- +#pragma mark - Serialization helpers +///-------------------------------------- + +- (NSString *)getOrCreateLocalId { + @synchronized(lock) { + if (!self.localId) { + PFConsistencyAssert(!self._state.objectId, + @"A localId should not be created for an object with an objectId."); + self.localId = [[Parse _currentManager].coreManager.objectLocalIdStore createLocalId]; + } + } + return self.localId; +} + +- (void)resolveLocalId { + @synchronized (lock) { + PFConsistencyAssert(self.localId, @"Tried to resolve a localId for an object with no localId."); + NSString *newObjectId = [[Parse _currentManager].coreManager.objectLocalIdStore objectIdForLocalId:self.localId]; + + // If we are resolving local ids, then this object is about to go over the network. + // But if it has local ids that haven't been resolved yet, then that's not going to + // be possible. + if (!newObjectId) { + [NSException raise:NSInternalInconsistencyException + format:@"Tried to save an object with a pointer to a new, unsaved object."]; + } + + // Nil out the localId so that the new objectId won't be saved back to the PFObjectLocalIdStore. + self.localId = nil; + self.objectId = newObjectId; + } +} + ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + completeData:(BOOL)completeData { + return [self _objectFromDictionary:dictionary + defaultClassName:defaultClassName + completeData:completeData + decoder:[PFDecoder objectDecoder]]; +} + +// When merging results from a query, ensure that any supplied `selectedKeys` are marked as available. This special +// handling is necessary because keys with an `undefined` value are not guaranteed to be included in the server's +// response data. +// +// See T3336562 ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + selectedKeys:(NSArray *)selectedKeys { + PFObject *result = [self _objectFromDictionary:dictionary + defaultClassName:defaultClassName + completeData:(selectedKeys == nil) + decoder:[PFDecoder objectDecoder]]; + [result->_availableKeys addObjectsFromArray:selectedKeys]; + return result; +} + +/*! + Creates a PFObject from a dictionary object. + + @param dictionary Undecoded dictionary. + @param defaultClassName The className of the resulting object if none is given by the dictionary. + @param completeData Whether to use complete data. + @param decoder Decoder used to decode the dictionary. + */ ++ (id)_objectFromDictionary:(NSDictionary *)dictionary + defaultClassName:(NSString *)defaultClassName + completeData:(BOOL)completeData + decoder:(PFDecoder *)decoder { + NSString *objectId = nil; + NSString *className = nil; + if (dictionary != nil) { + objectId = dictionary[@"objectId"]; + className = dictionary[@"className"] ?: defaultClassName; + } + PFObject *object = [PFObject objectWithoutDataWithClassName:className objectId:objectId]; + [object _mergeAfterFetchWithResult:dictionary decoder:decoder completeData:completeData]; + return object; +} + +/*! + When the app was previously a non-LDS app and want to enable LDS, currentUser and currentInstallation + will be discarded if we don't migrate them. This is a helper method to migrate user/installation + from disk to pin. + + @param fileName the file in which the object was saved. + @param pinName the name of the pin in which the object should be stored. + */ ++ (BFTask *)_migrateObjectInBackgroundFromFile:(NSString *)fileName + toPin:(NSString *)pinName { + return [self _migrateObjectInBackgroundFromFile:fileName toPin:pinName usingMigrationBlock:nil]; +} + +/*! + When the app was previously a non-LDS app and want to enable LDS, currentUser and currentInstallation + will be discarded if we don't migrate them. This is a helper method to migrate user/installation + from disk to pin. + + @param fileName the file in which the object was saved. + @param pinName the name of the pin in which the object should be stored. + @param migrationBlock The block that will be called if there is an object on disk and before the object is pinned. + */ ++ (BFTask *)_migrateObjectInBackgroundFromFile:(NSString *)fileName + toPin:(NSString *)pinName + usingMigrationBlock:(BFContinuationBlock)migrationBlock { + PFObjectFilePersistenceController *controller = [Parse _currentManager].coreManager.objectFilePersistenceController; + BFTask *task = [controller loadPersistentObjectAsyncForKey:fileName]; + if (migrationBlock) { + task = [task continueWithSuccessBlock:^id(BFTask *task) { + PFObject *object = task.result; + if (object) { + return [[task continueWithBlock:migrationBlock] continueWithResult:object]; + } + return task; + }]; + } + return [task continueWithSuccessBlock:^id(BFTask *task) { + PFObject *object = task.result; + return [[object _pinInBackgroundWithName:pinName includeChildren:NO] continueWithBlock:^id(BFTask *task) { + BFTask *resultTask = [BFTask taskWithResult:object]; + + // Only delete if we successfully pin it so that it retries the migration next time. + if (!task.error && !task.exception && !task.cancelled) { + NSString *path = [[Parse _currentManager].fileManager parseDataItemPathForPathComponent:fileName]; + return [[PFFileManager removeItemAtPathAsync:path] continueWithBlock:^id(BFTask *task) { + // We don't care if it fails to delete the file, so return the + return resultTask; + }]; + } + return resultTask; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - REST operations +///-------------------------------------- + +/*! + Encodes parse object into NSDictionary suitable for persisting into LDS. + */ +- (NSDictionary *)RESTDictionaryWithObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs { + @synchronized (lock) { + [self checkForChangesToMutableContainers]; + PFObjectState *state = self._state; + return [self RESTDictionaryWithObjectEncoder:objectEncoder + operationSetUUIDs:operationSetUUIDs + state:state + operationSetQueue:operationSetQueue]; + } +} + +- (NSDictionary *)RESTDictionaryWithObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs + state:(PFObjectState *)state + operationSetQueue:(NSArray *)queue { + NSMutableDictionary *result = [[state dictionaryRepresentationWithObjectEncoder:objectEncoder] mutableCopy]; + result[PFObjectClassNameRESTKey] = state.parseClassName; + result[PFObjectCompleteRESTKey] = @(state.complete); + + result[PFObjectIsDeletingEventuallyRESTKey] = @(_deletingEventually); + + // TODO (hallucinogen): based on some note from Android's toRest, we'll need to put this + // stuff somewhere else + NSMutableArray *operations = [NSMutableArray array]; + NSMutableArray *mutableOperationSetUUIDs = [NSMutableArray array]; + for (PFOperationSet *operation in queue) { + NSArray *ooSetUUIDs = nil; + [operations addObject:[operation RESTDictionaryUsingObjectEncoder:objectEncoder + operationSetUUIDs:&ooSetUUIDs]]; + [mutableOperationSetUUIDs addObjectsFromArray:ooSetUUIDs]; + } + + *operationSetUUIDs = mutableOperationSetUUIDs; + + result[PFObjectOperationsRESTKey] = operations; + return result; +} + +- (void)mergeFromRESTDictionary:(NSDictionary *)object withDecoder:(PFDecoder *)decoder { + @synchronized (lock) { + BOOL mergeServerData = NO; + + PFMutableObjectState *state = [self._state mutableCopy]; + + // If LDS has `updatedAt` and we have it - compare, then if stuff is newer - merge. + // If LDS doesn't have `updatedAt` and we don't have it - merge anyway. + NSString *updatedAtString = object[PFObjectUpdatedAtRESTKey]; + if (updatedAtString) { + NSDate *updatedDate = [[PFDateFormatter sharedFormatter] dateFromString:updatedAtString]; + mergeServerData = ([state.updatedAt compare:updatedDate] != NSOrderedDescending); + } else if (!state.updatedAt) { + mergeServerData = YES; + } + [object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([key isEqualToString:PFObjectOperationsRESTKey]) { + PFOperationSet *remoteOperationSet = nil; + NSArray *operations = (NSArray *)obj; + if ([operations count] > 0) { + // Add and enqueue any saveEventually operations, roll forward any other + // operations sets (operations sets here are generally failed/incomplete saves). + PFOperationSet *current = nil; + for (id rawOperationSet in operations) { + PFOperationSet *operationSet = [PFOperationSet operationSetFromRESTDictionary:rawOperationSet + usingDecoder:decoder]; + if (operationSet.saveEventually) { + if (current != nil) { + [[self unsavedChanges] mergeOperationSet:current]; + current = nil; + } + + // Check if queue already contains this operation set and discard it if does + if (![self _containsOperationSet:operationSet]) { + // Insert the `saveEventually` operationSet before the last operation set at all times. + NSUInteger index = ([operationSetQueue count] == 0 ? 0 : [operationSetQueue count] - 1); + [operationSetQueue insertObject:operationSet atIndex:index]; + [self _enqueueSaveEventuallyOperationAsync:operationSet]; + } + + continue; + } + + if (current != nil) { + [operationSet mergeOperationSet:current]; + } + current = operationSet; + } + if (current != nil) { + remoteOperationSet = current; + } + } + + PFOperationSet *localOperationSet = [self unsavedChanges]; + if (localOperationSet.updatedAt != nil && + [localOperationSet.updatedAt compare:remoteOperationSet.updatedAt] != NSOrderedAscending) { + [localOperationSet mergeOperationSet:remoteOperationSet]; + } else { + PFConsistencyAssert(remoteOperationSet, @"'remoteOperationSet' should not be nil."); + NSUInteger index = [operationSetQueue indexOfObject:localOperationSet]; + [remoteOperationSet mergeOperationSet:localOperationSet]; + [operationSetQueue replaceObjectAtIndex:index withObject:remoteOperationSet]; + } + + return; + } + + if ([key isEqualToString:PFObjectCompleteRESTKey]) { + // If server data is complete, consider this object to be fetched + state.complete = state.complete || [obj boolValue]; + return; + } + if ([key isEqualToString:PFObjectIsDeletingEventuallyRESTKey]) { + _deletingEventually = [obj intValue]; + return; + } + + [_availableKeys addObject:key]; + + // If server data in dictionary is older - don't merge it. + if (!mergeServerData) { + return; + } + + if ([key isEqualToString:PFObjectTypeRESTKey] || [key isEqualToString:PFObjectClassNameRESTKey]) { + return; + } + if ([key isEqualToString:PFObjectObjectIdRESTKey]) { + state.objectId = obj; + return; + } + if ([key isEqualToString:PFObjectCreatedAtRESTKey]) { + [state setCreatedAtFromString:obj]; + return; + } + if ([key isEqualToString:PFObjectUpdatedAtRESTKey]) { + [state setUpdatedAtFromString:obj]; + return; + } + + if ([key isEqualToString:PFObjectACLRESTKey]) { + PFACL *acl = [PFACL ACLWithDictionary:obj]; + [state setServerDataObject:acl forKey:PFObjectACLRESTKey]; + [self checkpointMutableContainer:acl]; + return; + } + + // Should be decoded + id decodedObject = [decoder decodeObject:obj]; + if (PFObjectValueIsKindOfMutableContainerClass(decodedObject)) { + [self checkpointMutableContainer:decodedObject]; + } + [state setServerDataObject:decodedObject forKey:key]; + }]; + if (state.updatedAt == nil && state.createdAt != nil) { + state.updatedAt = state.createdAt; + } + BOOL previousDirtyState = dirty; + self._state = state; + dirty = previousDirtyState; + + if (mergeServerData) { + if ([object[PFObjectCompleteRESTKey] boolValue]) { + [self removeOldKeysAfterFetch:object]; + } else { + // Unmark the object as fetched, because we merged from incomplete new data. + [self setHasBeenFetched:NO]; + } + } + [self rebuildEstimatedData]; + [self checkpointAllMutableContainers]; + } +} + +///-------------------------------------- +#pragma mark - Eventually Helper +///-------------------------------------- + +/*! + Enqueues saveEventually operation asynchronously. + + @returns A task which result is a saveEventually task. + */ +- (BFTask *)_enqueueSaveEventuallyWithChildren:(BOOL)saveChildren { + return [_eventuallyTaskQueue enqueue:^BFTask *(BFTask *toAwait) { + PFUser *currentUser = [PFUser currentUser]; + NSString *sessionToken = currentUser.sessionToken; + return [[toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [self _validateSaveEventuallyAsync]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @synchronized (lock) { + [self _objectWillSave]; + if (![self isDirty:NO]) { + return [BFTask taskWithResult:@YES]; + } + } + + BFTask *saveChildrenTask = nil; + if (saveChildren) { + saveChildrenTask = [[self class] _enqueueSaveEventuallyChildrenOfObject:self currentUser:currentUser]; + } else { + saveChildrenTask = [BFTask taskWithResult:nil]; + } + + return [saveChildrenTask continueWithSuccessBlock:^id(BFTask *task) { + BFTask *saveTask = nil; + @synchronized (lock) { + // Snapshot the current set of changes, and push a new changeset into the queue. + PFOperationSet *changes = [self unsavedChanges]; + changes.saveEventually = YES; + [self startSave]; + [self _checkSaveParametersWithCurrentUser:currentUser]; + PFRESTCommand *command = [self _constructSaveCommandForChanges:changes + sessionToken:sessionToken + objectEncoder:[PFPointerOrLocalIdObjectEncoder objectEncoder]]; + + // Enqueue the eventually operation! + saveTask = [[Parse _currentManager].eventuallyQueue enqueueCommandInBackground:command withObject:self]; + [self _enqueueSaveEventuallyOperationAsync:changes]; + } + saveTask = [saveTask continueWithBlock:^id(BFTask *task) { + @try { + if (!task.isCancelled && !task.exception && !task.error) { + PFCommandResult *result = task.result; + // PFPinningEventuallyQueue handle save result directly. + if (![Parse _currentManager].offlineStoreLoaded) { + return [self handleSaveResultAsync:result.result]; + } + } + return task; + } @finally { + [[Parse _currentManager].eventuallyQueue _notifyTestHelperObjectUpdated]; + } + }]; + return [BFTask taskWithResult:saveTask]; + }]; + }]; + }]; +} + + +/*! + Enqueues the saveEventually PFOperationSet in PFObject taskQueue + */ +- (BFTask *)_enqueueSaveEventuallyOperationAsync:(PFOperationSet *)operationSet { + if (!operationSet.isSaveEventually) { + NSString *message = @"This should only be used to enqueue saveEventually operation sets"; + NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException + reason:message + userInfo:nil]; + return [BFTask taskWithException:exception]; + } + + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + // Use default priority background to break a chain and make sure this operation is truly asynchronous + return [toAwait continueWithExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id(BFTask *task) { + PFEventuallyQueue *queue = [Parse _currentManager].eventuallyQueue; + id queueSubClass = (id)queue; + return [queueSubClass _waitForOperationSet:operationSet eventuallyPin:nil]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Data model manipulation +///-------------------------------------- + +- (NSMutableDictionary *)_convertToDictionaryForSaving:(PFOperationSet *)changes + withObjectEncoder:(PFEncoder *)encoder { + @synchronized (lock) { + [self checkForChangesToMutableContainers]; + + NSMutableDictionary *serialized = [NSMutableDictionary dictionary]; + [changes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + serialized[key] = obj; + }]; + return [encoder encodeObject:serialized]; + } +} + +/*! + performOperation:forKey: is like setObject:forKey, but instead of just taking a + new value, it takes a PFFieldOperation that modifies the value. + */ +- (void)performOperation:(PFFieldOperation *)operation forKey:(NSString *)key { + @synchronized (lock) { + id newValue = [_estimatedData applyFieldOperation:operation forKey:key]; + + PFFieldOperation *oldOperation = [[self unsavedChanges] objectForKey:key]; + PFFieldOperation *newOperation = [operation mergeWithPrevious:oldOperation]; + [[self unsavedChanges] setObject:newOperation forKey:key]; + [self checkpointMutableContainer:newValue]; + [_availableKeys addObject:key]; + } +} + +- (BOOL)_containsOperationSet:(PFOperationSet *)operationSet { + @synchronized (lock) { + for (PFOperationSet *existingOperationSet in operationSetQueue) { + if (existingOperationSet == operationSet || + [existingOperationSet.uuid isEqualToString:operationSet.uuid]) { + return YES; + } + } + } + return NO; +} + +/*! + Returns the set of PFFieldOperations that will be sent in the next save. + */ +- (PFOperationSet *)unsavedChanges { + @synchronized (lock) { + return [operationSetQueue lastObject]; + } +} + +/*! + @returns YES if there's unsaved changes in this object. This complements ivar `dirty` for `isDirty` check. + */ +- (BOOL)_hasChanges { + @synchronized (lock) { + return [[self unsavedChanges] count] > 0; + } +} + +/*! + @returns YES if this PFObject has operations in operationSetQueue that haven't been completed yet, + NO if there are no operations in the operationSetQueue. + */ +- (BOOL)_hasOutstandingOperations { + @synchronized (lock) { + // > 1 since 1 is unsaved changes. + return [operationSetQueue count] > 1; + } +} + +- (void)rebuildEstimatedData { + @synchronized (lock) { + _estimatedData = [PFObjectEstimatedData estimatedDataFromServerData:self._state.serverData + operationSetQueue:operationSetQueue]; + } +} + +- (PFObject *)mergeFromObject:(PFObject *)other { + @synchronized (lock) { + if (self == other) { + // If they point to the same instance, then don't merge. + return self; + } + + PFMutableObjectState *state = [self._state mutableCopy]; + state.objectId = other.objectId; + state.createdAt = other.createdAt; + state.updatedAt = other.updatedAt; + state.serverData = [other._state.serverData mutableCopy]; + self._state = state; + [self checkpointAllMutableContainers]; + + dirty = NO; + + [self rebuildEstimatedData]; + return self; + } +} + +- (void)_mergeAfterFetchWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder completeData:(BOOL)completeData { + @synchronized (lock) { + [self checkForChangesToMutableContainers]; + [self _mergeFromServerWithResult:result decoder:decoder completeData:completeData]; + if (completeData) { + [self removeOldKeysAfterFetch:result]; + } + [self rebuildEstimatedData]; + [self checkpointAllMutableContainers]; + } +} + +- (void)removeOldKeysAfterFetch:(NSDictionary *)result { + @synchronized (lock) { + PFMutableObjectState *state = [self._state mutableCopy]; + + NSMutableDictionary *removedDictionary = [NSMutableDictionary dictionaryWithDictionary:state.serverData]; + [removedDictionary removeObjectsForKeys:[result allKeys]]; + + NSArray *removedKeys = [removedDictionary allKeys]; + [state removeServerDataObjectsForKeys:removedKeys]; + [_availableKeys minusSet:[NSSet setWithArray:removedKeys]]; + + self._state = state; + } +} + +- (void)_mergeAfterSaveWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder { + @synchronized (lock) { + PFOperationSet *operationsBeforeSave = operationSetQueue[0]; + [operationSetQueue removeObjectAtIndex:0]; + + if (!result) { + // Merge the data from the failed save into the next save. + PFOperationSet *operationsForNextSave = operationSetQueue[0]; + [operationsForNextSave mergeOperationSet:operationsBeforeSave]; + } else { + // Merge the data from the save and the data from the server into serverData. + [self checkForChangesToMutableContainers]; + + PFMutableObjectState *state = [self._state mutableCopy]; + [state applyOperationSet:operationsBeforeSave]; + self._state = state; + + [self _mergeFromServerWithResult:result decoder:decoder completeData:NO]; + [self rebuildEstimatedData]; + [self checkpointAllMutableContainers]; + } + } +} + +- (void)_mergeFromServerWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder completeData:(BOOL)completeData { + @synchronized (lock) { + PFMutableObjectState *state = [self._state mutableCopy]; + + // If the server's data is complete, consider this object to be fetched. + state.complete |= completeData; + + [result enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([key isEqualToString:PFObjectObjectIdRESTKey]) { + state.objectId = obj; + } else if ([key isEqualToString:PFObjectCreatedAtRESTKey]) { + // These dates can be passed in as NSDate or as NSString, + // depending on whether they were wrapped inside JSONObject with __type: Date or not. + if ([obj isKindOfClass:[NSDate class]]) { + state.createdAt = obj; + } else { + [state setCreatedAtFromString:obj]; + } + } else if ([key isEqualToString:PFObjectUpdatedAtRESTKey]) { + // These dates can be passed in as NSDate or as NSString, + // depending on whether they were wrapped inside JSONObject with __type: Date or not. + if ([obj isKindOfClass:[NSDate class]]) { + state.updatedAt = obj; + } else { + [state setUpdatedAtFromString:obj]; + } + } else if ([key isEqualToString:PFObjectACLRESTKey]) { + PFACL *acl = [PFACL ACLWithDictionary:obj]; + [state setServerDataObject:acl forKey:key]; + [self checkpointMutableContainer:acl]; + } else { + [state setServerDataObject:[decoder decodeObject:obj] forKey:key]; + } + }]; + if (state.updatedAt == nil && state.createdAt != nil) { + state.updatedAt = state.createdAt; + } + self._state = state; + [_availableKeys addObjectsFromArray:[result allKeys]]; + + dirty = NO; + } +} + +///-------------------------------------- +#pragma mark - Command handlers +///-------------------------------------- + +// We can't get rid of these handlers, because subclasses override them +// to add special actions after operations. + +- (BFTask *)handleSaveResultAsync:(NSDictionary *)result { + BFTask *task = [BFTask taskWithResult:nil]; + + NSDictionary *fetchedObjects = [self _collectFetchedObjects]; + + [task continueWithBlock:^id(BFTask *task) { + PFKnownParseObjectDecoder *decoder = [PFKnownParseObjectDecoder decoderWithFetchedObjects:fetchedObjects]; + @synchronized (self.lock) { + // TODO (hallucinogen): t5611821 we need to make mergeAfterSave that accepts decoder and operationBeforeSave + [self _mergeAfterSaveWithResult:result decoder:decoder]; + } + return nil; + }]; + + PFOfflineStore *store = [Parse _currentManager].offlineStore; + if (store != nil) { + task = [task continueWithBlock:^id(BFTask *task) { + return [store updateDataForObjectAsync:self]; + }]; + } + + return [task continueWithBlock:^id(BFTask *task) { + @synchronized (lock) { + if (self.saveDelegate) { + [self.saveDelegate invoke:self error:nil]; + } + return [BFTask taskWithResult:@(!!result)]; + } + }]; +} + +///-------------------------------------- +#pragma mark - Asynchronous operations +///-------------------------------------- + +- (void)startSave { + @synchronized (lock) { + [operationSetQueue addObject:[[PFOperationSet alloc] init]]; + } +} + +- (BFTask *)saveAsync:(BFTask *)toAwait { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentObjectAsync] continueWithBlock:^id(BFTask *task) { + PFUser *currentUser = task.result; + NSString *sessionToken = currentUser.sessionToken; + + BFTask *await = toAwait ?: [BFTask taskWithResult:nil]; + return [[await continueAsyncWithBlock:^id(BFTask *task) { + PFOfflineStore *offlineStore = [Parse _currentManager].offlineStore; + if (offlineStore != nil) { + return [offlineStore fetchObjectLocallyAsync:self]; + } + return nil; + }] continueWithBlock:^id(BFTask *task) { + @synchronized (lock) { + if (![self isDirty:YES]) { + return [BFTask taskWithResult:@YES]; + } + + [self _objectWillSave]; + + // Snapshot the current set of changes, and push a new changeset into the queue. + PFOperationSet *changes = [self unsavedChanges]; + + [self startSave]; + BFTask *childrenTask = [self _saveChildrenInBackgroundWithCurrentUser:currentUser + sessionToken:sessionToken]; + if (!dirty && ![changes count]) { + return childrenTask; + } + return [[childrenTask continueWithSuccessBlock:^id(BFTask *task) { + [self _checkSaveParametersWithCurrentUser:currentUser]; + PFRESTCommand *command = [self _constructSaveCommandForChanges:changes + sessionToken:sessionToken + objectEncoder:[PFPointerObjectEncoder objectEncoder]]; + return [[Parse _currentManager].commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueAsyncWithBlock:^id(BFTask *task) { + if (task.isCancelled || task.exception || task.error) { + // If there was an error, we want to roll forward the save changes before rethrowing. + BFTask *commandRunnerTask = task; + return [[self handleSaveResultAsync:nil] continueWithBlock:^id(BFTask *task) { + return commandRunnerTask; + }]; + } + PFCommandResult *result = task.result; + return [self handleSaveResultAsync:result.result]; + }]; + } + }]; + }]; +} + +- (BFTask *)fetchAsync:(BFTask *)toAwait { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[[self class] objectController] fetchObjectAsync:self withSessionToken:sessionToken]; + }]; + }]; +} + +- (BFTask *)deleteAsync:(BFTask *)toAwait { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[[self class] objectController] deleteObjectAsync:self withSessionToken:sessionToken]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Command constructors +///-------------------------------------- + +- (PFRESTCommand *)_constructSaveCommandForChanges:(PFOperationSet *)changes + sessionToken:(NSString *)sessionToken + objectEncoder:(PFEncoder *)encoder { + @synchronized (lock) { + NSDictionary *parameters = [self _convertToDictionaryForSaving:changes withObjectEncoder:encoder]; + + if (self._state.objectId) { + return [PFRESTObjectCommand updateObjectCommandForObjectState:self._state + changes:parameters + operationSetUUID:changes.uuid + sessionToken:sessionToken]; + } + + return [PFRESTObjectCommand createObjectCommandForObjectState:self._state + changes:parameters + operationSetUUID:changes.uuid + sessionToken:sessionToken]; + + } +} + +- (PFRESTCommand *)_currentDeleteCommandWithSessionToken:(NSString *)sessionToken { + return [PFRESTObjectCommand deleteObjectCommandForObjectState:self._state withSessionToken:sessionToken]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)_setObject:(id)object forKey:(NSString *)key onlyIfDifferent:(BOOL)onlyIfDifferent { + PFParameterAssert(object != nil && key != nil, + @"Can't use nil for keys or values on PFObject. Use NSNull for values."); + PFParameterAssert([key isKindOfClass:[NSString class]], @"PFObject keys must be NSStrings."); + + if (onlyIfDifferent) { + id currentObject = self[key]; + if (currentObject == object || + [currentObject isEqual:object]) { + return; + } + } + + @synchronized (lock) { + if ([object isKindOfClass:[PFFieldOperation class]]) { + [self performOperation:object forKey:key]; + return; + } + + PFObjectAssertValueIsKindOfValidClass(object); + [self performOperation:[PFSetOperation setWithValue:object] forKey:key]; + } +} + +///-------------------------------------- +#pragma mark - Misc helpers +///-------------------------------------- + +- (NSString *)displayObjectId { + return self._state.objectId ?: @"new"; +} + +- (NSString *)displayClassName { + return self._state.parseClassName; +} + +- (void)registerSaveListener:(void (^)(id result, NSError *error))callback { + @synchronized (lock) { + if (!self.saveDelegate) { + self.saveDelegate = [[PFMulticastDelegate alloc] init]; + } + [self.saveDelegate subscribe:callback]; + } +} + +- (void)unregisterSaveListener:(void (^)(id result, NSError *error))callback { + @synchronized (lock) { + if (!self.saveDelegate) { + self.saveDelegate = [[PFMulticastDelegate alloc] init]; + } + [self.saveDelegate unsubscribe:callback]; + } +} + +- (PFACL *)ACLWithoutCopying { + @synchronized (lock) { + return _estimatedData[@"ACL"]; + } +} + +// Overriden by classes which want to ignore the default ACL. +- (void)setDefaultValues { + if ([self needsDefaultACL]) { + PFACL *defaultACL = [PFACL defaultACL]; + if (defaultACL) { + self.ACL = defaultACL; + } + } +} + +- (BOOL)needsDefaultACL { + return YES; +} + +- (NSDictionary *)_collectFetchedObjects { + NSMutableDictionary *fetchedObjects = [NSMutableDictionary dictionary]; + @synchronized (lock) { + NSDictionary *dictionary = _estimatedData.dictionaryRepresentation; + [PFInternalUtils traverseObject:dictionary usingBlock:^id(id obj) { + if ([obj isKindOfClass:[PFObject class]]) { + PFObject *object = obj; + NSString *objectId = object.objectId; + if (objectId && [object isDataAvailable]) { + fetchedObjects[objectId] = object; + } + } + return obj; + }]; + } + return fetchedObjects; +} + +@end + +@implementation PFObject + +@synthesize _availableKeys = _availableKeys; + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + if (!_pfinternal_state) { + PFConsistencyAssert([self conformsToProtocol:@protocol(PFSubclassing)], + @"Can only call -[PFObject init] on subclasses conforming to PFSubclassing."); + [PFObject assertSubclassIsRegistered:[self class]]; + _pfinternal_state = [[self class] _newObjectStateWithParseClassName:[[self class] parseClassName] + objectId:nil + isComplete:YES]; + } + [[self class] _assertValidInstanceClassName:_pfinternal_state.parseClassName]; + + lock = [[NSObject alloc] init]; + operationSetQueue = [NSMutableArray arrayWithObject:[[PFOperationSet alloc] init]]; + _estimatedData = [PFObjectEstimatedData estimatedDataFromServerData:_pfinternal_state.serverData + operationSetQueue:operationSetQueue]; + _availableKeys = [NSMutableSet set]; + hashedObjectsCache = [[NSMutableDictionary alloc] init]; + self.taskQueue = [[PFTaskQueue alloc] init]; + _eventuallyTaskQueue = [[PFTaskQueue alloc] init]; + + if (_pfinternal_state.complete) { + dirty = YES; + [self setDefaultValues]; + } + + return self; +} + +- (instancetype)initWithClassName:(NSString *)className { + PFObjectState *state = [[self class] _newObjectStateWithParseClassName:className objectId:nil isComplete:YES]; + return [self initWithObjectState:state]; +} + +- (instancetype)initWithObjectState:(PFObjectState *)state { + _pfinternal_state = state; + return [self init]; +} + ++ (instancetype)objectWithClassName:(NSString *)className + objectId:(NSString *)objectId + completeData:(BOOL)completeData { + Class class = [[[self class] subclassingController] subclassForParseClassName:className] ?: [PFObject class]; + PFObjectState *state = [class _newObjectStateWithParseClassName:className objectId:objectId isComplete:completeData]; + PFObject *object = [[class alloc] initWithObjectState:state]; + if (!completeData) { + PFConsistencyAssert(![object _hasChanges], + @"The init method of %@ set values on the object, which is not allowed.", class); + } + return object; +} + ++ (instancetype)objectWithClassName:(NSString *)className { + return [self objectWithClassName:className objectId:nil completeData:YES]; +} + ++ (instancetype)objectWithClassName:(NSString *)className dictionary:(NSDictionary *)dictionary { + PFObject *object = [self objectWithClassName:className]; + [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + object[key] = obj; + }]; + return object; +} + ++ (instancetype)objectWithoutDataWithClassName:(NSString *)className objectId:(NSString *)objectId { + // Try get single instance from OfflineStore + PFOfflineStore *store = [Parse _currentManager].offlineStore; + if (store != nil && objectId != nil) { + PFObject *singleObject = [store getOrCreateObjectWithoutDataWithClassName:className objectId:objectId]; + if (singleObject) { + return singleObject; + } + } + + // Local Datastore is not enabled or cannot found the single instance using objectId, let's use the old way + return [self objectWithClassName:className objectId:objectId completeData:NO]; +} + +#pragma mark Subclassing + ++ (instancetype)object { + PFConsistencyAssert([self conformsToProtocol:@protocol(PFSubclassing)], + @"Can only call +object on subclasses conforming to PFSubclassing"); + NSString *className = [(id)self parseClassName]; + Class class = [[self subclassingController] subclassForParseClassName:className] ?: [PFObject class]; + return [class objectWithClassName:className]; +} + ++ (instancetype)objectWithoutDataWithObjectId:(NSString *)objectId { + PFConsistencyAssert([self conformsToProtocol:@protocol(PFSubclassing)], + @"Can only call objectWithoutDataWithObjectId: on subclasses conforming to PFSubclassing"); + return [self objectWithoutDataWithClassName:[(id)self parseClassName] objectId:objectId]; +} + +#pragma mark Private + ++ (instancetype)objectWithoutDataWithClassName:(NSString *)className localId:(NSString *)localId { + PFObject *object = [self objectWithoutDataWithClassName:className objectId:nil]; + object.localId = localId; + return object; +} + +///-------------------------------------- +#pragma mark - PFObjectPrivateSubclass +///-------------------------------------- + +#pragma mark State + ++ (PFObjectState *)_newObjectStateWithParseClassName:(NSString *)className + objectId:(NSString *)objectId + isComplete:(BOOL)complete { + return [PFObjectState stateWithParseClassName:className objectId:objectId isComplete:complete]; +} + +///-------------------------------------- +#pragma mark - Validation +///-------------------------------------- + +- (BFTask PF_GENERIC(PFVoid) *)_validateFetchAsync { + if (!self._state.objectId) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorMissingObjectId + message:@"Can't fetch an object that hasn't been saved to the server."]; + return [BFTask taskWithError:error]; + } + return [BFTask taskWithResult:nil]; +} + +- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync { + return [BFTask taskWithResult:nil]; +} + +- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync { + return [BFTask taskWithResult:nil]; +} + +#pragma mark Object Will Save + +- (void)_objectWillSave { + // Do nothing. +} + +///-------------------------------------- +#pragma mark - Properties +///-------------------------------------- + +- (void)set_state:(PFObjectState *)state { + @synchronized(lock) { + NSString *oldObjectId = _pfinternal_state.objectId; + if (self._state != state) { + _pfinternal_state = [state copy]; + } + + NSString *newObjectId = _pfinternal_state.objectId; + if (![PFObjectUtilities isObject:oldObjectId equalToObject:newObjectId]) { + [self _notifyObjectIdChangedFrom:oldObjectId toObjectId:newObjectId]; + } + } +} + +- (PFObjectState *)_state { + @synchronized(lock) { + return _pfinternal_state; + } +} + +- (PFObjectEstimatedData *)_estimatedData { + @synchronized (lock) { + return _estimatedData; + } +} + +- (void)setObjectId:(NSString *)objectId { + @synchronized (lock) { + NSString *oldObjectId = self._state.objectId; + if ([PFObjectUtilities isObject:oldObjectId equalToObject:objectId]) { + return; + } + + dirty = YES; + + PFMutableObjectState *state = [self._state mutableCopy]; + state.objectId = objectId; + _pfinternal_state = state; + + [self _notifyObjectIdChangedFrom:oldObjectId toObjectId:objectId]; + } +} + +- (NSString *)objectId { + return self._state.objectId; +} + +- (void)_notifyObjectIdChangedFrom:(NSString *)fromObjectId toObjectId:(NSString *)toObjectId { + @synchronized (self.lock) { + // The OfflineStore might raise exception if this object already had a different objectId. + PFOfflineStore *store = [Parse _currentManager].offlineStore; + if (store != nil) { + [store updateObjectIdForObject:self oldObjectId:fromObjectId newObjectId:toObjectId]; + } + if (self.localId) { + [[Parse _currentManager].coreManager.objectLocalIdStore setObjectId:toObjectId forLocalId:self.localId]; + self.localId = nil; + } + } +} + +- (NSString *)parseClassName { + return self._state.parseClassName; +} + +- (NSDate *)updatedAt { + return self._state.updatedAt; +} + +- (NSDate *)createdAt { + return self._state.createdAt; +} + +- (PFACL *)ACL { + return self[@"ACL"]; +} + +- (void)setACL:(PFACL *)ACL { + if (!ACL) { + [self removeObjectForKey:@"ACL"]; + } else { + self[@"ACL"] = ACL; + } +} + +// PFObject(): +@synthesize localId; +@synthesize taskQueue; + +// PFObject(Private): +@synthesize saveDelegate; + +///-------------------------------------- +#pragma mark - PFObject factory methods for Subclassing +///-------------------------------------- + +// Reverse compatibility note: many people may have built PFObject subclasses before +// we officially supported them. Our implementation can do cool stuff, but requires +// the parseClassName class method. ++ (void)registerSubclass { + [[self subclassingController] registerSubclass:self]; +} + ++ (PFQuery *)query { + PFConsistencyAssert([self conformsToProtocol:@protocol(PFSubclassing)], + @"+[PFObject query] can only be called on subclasses conforming to PFSubclassing."); + [PFObject assertSubclassIsRegistered:self]; + return [PFQuery queryWithClassName:[(id)self parseClassName]]; +} + ++ (PFQuery *)queryWithPredicate:(NSPredicate *)predicate { + PFConsistencyAssert([self conformsToProtocol:@protocol(PFSubclassing)], + @"+[PFObject queryWithPredicate:] can only be called on subclasses conforming to PFSubclassing."); + [PFObject assertSubclassIsRegistered:[self class]]; + return [PFQuery queryWithClassName:[(id)self parseClassName] predicate:predicate]; +} + ++ (void)assertSubclassIsRegistered:(Class)subclass { + // If people hacked their own subclass together before we supported it officially, we shouldn't break their app. + if ([subclass conformsToProtocol:@protocol(PFSubclassing)]) { + Class registration = [[self subclassingController] subclassForParseClassName:[subclass parseClassName]]; + + // It's OK to subclass a subclass (i.e. custom PFUser implementation) + PFConsistencyAssert(registration && (registration == subclass || [registration isSubclassOfClass:subclass]), + @"The class %@ must be registered with registerSubclass before using Parse.", subclass); + } +} + +///-------------------------------------- +#pragma mark - Delete commands +///-------------------------------------- + +- (BOOL)delete { + return [self delete:nil]; +} + +- (BOOL)delete:(NSError **)error { + return [[[self deleteInBackground] waitForResult:error] boolValue]; +} + +- (BFTask *)deleteInBackground { + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [[self deleteAsync:toAwait] continueWithSuccessResult:@YES]; + }]; +} + +- (void)deleteInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +- (void)deleteInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self deleteInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Save commands +///-------------------------------------- + +- (BOOL)save { + return [self save:nil]; +} + +- (BOOL)save:(NSError **)error { + return [[[self saveInBackground] waitForResult:error] boolValue]; +} + +- (BFTask *)saveInBackground { + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [self saveAsync:toAwait]; + }]; +} + +- (void)saveInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self saveInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (BFTask *)saveEventually { + return [[self _enqueueSaveEventuallyWithChildren:YES] continueWithSuccessBlock:^id(BFTask *task) { + // The result of the previous task will be an instance of BFTask. + // Returning it here will trigger the whole task stack become an actual save task. + return task.result; + }]; +} + +- (void)saveEventually:(PFBooleanResultBlock)block { + [[self saveEventually] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (BFTask *)deleteEventually { + return [[[_eventuallyTaskQueue enqueue:^BFTask *(BFTask *toAwait) { + NSString *sessionToken = [PFUser currentSessionToken]; + return [[toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [self _validateDeleteAsync]; + }] continueWithSuccessBlock:^id(BFTask *task) { + @synchronized (lock) { + _deletingEventually += 1; + + PFOfflineStore *store = [Parse _currentManager].offlineStore; + BFTask *updateDataTask = store ? [store updateDataForObjectAsync:self] : [BFTask taskWithResult:nil]; + + PFRESTCommand *command = [self _currentDeleteCommandWithSessionToken:sessionToken]; + BFTask *deleteTask = [updateDataTask continueWithBlock:^id(BFTask *task) { + return [[Parse _currentManager].eventuallyQueue enqueueCommandInBackground:command withObject:self]; + }]; + deleteTask = [deleteTask continueWithSuccessBlock:^id(BFTask *task) { + // PFPinningEventuallyQueue handles delete result directly. + if (![Parse _currentManager].offlineStoreLoaded) { + PFCommandResult *result = task.result; + return [[[self class] objectController] processDeleteResultAsync:result.result forObject:self]; + } + return task; + }]; + return deleteTask; + } + }]; + }] continueWithSuccessBlock:^id(BFTask *task) { + // The result of the previous task will be an instance of BFTask. + // Returning it here will trigger the whole task stack become an actual save task. + return task.result; + }] continueWithSuccessResult:@YES]; +} + +///-------------------------------------- +#pragma mark - Dirtiness +///-------------------------------------- + +- (BOOL)isDirty { + return [self isDirty:YES]; +} + +- (BOOL)isDirtyForKey:(NSString *)key { + @synchronized (lock) { + [self checkForChangesToMutableContainer:_estimatedData[key] forKey:key]; + return !![[self unsavedChanges] objectForKey:key]; + } +} + +///-------------------------------------- +#pragma mark - Fetch +///-------------------------------------- + +- (BOOL)isDataAvailable { + return self._state.complete; +} + +- (instancetype)refresh { + return [self fetch]; +} + +- (instancetype)refresh:(NSError **)error { + return [self fetch:error]; +} + +- (void)refreshInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self fetchInBackgroundWithTarget:target selector:selector]; +} + +- (void)refreshInBackgroundWithBlock:(PFObjectResultBlock)block { + [self fetchInBackgroundWithBlock:block]; +} + +- (instancetype)fetch { + return [self fetch:nil]; +} + +- (instancetype)fetch:(NSError **)error { + return [[self fetchInBackground] waitForResult:error]; +} + +- (BFTask *)fetchInBackground { + if (!self._state.objectId) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorMissingObjectId + message:@"Can't refresh an object that hasn't been saved to the server."]; + return [BFTask taskWithError:error]; + } + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [self fetchAsync:toAwait]; + }]; +} + +- (void)fetchInBackgroundWithBlock:(PFObjectResultBlock)block { + [[self fetchInBackground] thenCallBackOnMainThreadAsync:block]; +} + +- (void)fetchInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:object object:error]; + }]; +} + +- (instancetype)fetchIfNeeded { + return [self fetchIfNeeded:nil]; +} + +- (instancetype)fetchIfNeeded:(NSError **)error { + return [[self fetchIfNeededInBackground] waitForResult:error]; +} + +- (BFTask *)fetchIfNeededInBackground { + if ([self isDataAvailable]) { + return [BFTask taskWithResult:self]; + } + return [self fetchInBackground]; +} + +- (void)fetchIfNeededInBackgroundWithBlock:(PFObjectResultBlock)block { + [[self fetchIfNeededInBackground] thenCallBackOnMainThreadAsync:block]; +} + +- (void)fetchIfNeededInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:object object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Fetching Many Objects +///-------------------------------------- + ++ (NSArray *)fetchAll:(NSArray *)objects { + return [self fetchAll:objects error:nil]; +} + ++ (NSArray *)fetchAllIfNeeded:(NSArray *)objects { + return [self fetchAllIfNeeded:objects error:nil]; +} + ++ (NSArray *)fetchAll:(NSArray *)objects error:(NSError **)error { + return [[self fetchAllInBackground:objects] waitForResult:error]; +} + ++ (NSArray *)fetchAllIfNeeded:(NSArray *)objects error:(NSError **)error { + return [[self fetchAllIfNeededInBackground:objects] waitForResult:error]; +} + ++ (BFTask *)fetchAllInBackground:(NSArray *)objects { + // Snapshot the objects array. + NSArray *fetchObjects = [objects copy]; + + if (fetchObjects.count == 0) { + return [BFTask taskWithResult:fetchObjects]; + } + NSArray *uniqueObjects = [PFObjectBatchController uniqueObjectsArrayFromArray:fetchObjects omitObjectsWithData:NO]; + return [[[[self currentUserController] getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [PFObject _enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[self objectBatchController] fetchObjectsAsync:uniqueObjects withSessionToken:sessionToken]; + }]; + } forObjects:uniqueObjects]; + }] continueWithSuccessResult:fetchObjects]; +} + ++ (void)fetchAllInBackground:(NSArray *)objects target:(id)target selector:(SEL)selector { + [self fetchAllInBackground:objects block:^(NSArray *objects, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:objects object:error]; + }]; +} + ++ (void)fetchAllInBackground:(NSArray *)objects block:(PFArrayResultBlock)block { + [[self fetchAllInBackground:objects] thenCallBackOnMainThreadAsync:block]; +} + ++ (BFTask *)fetchAllIfNeededInBackground:(NSArray *)objects { + NSArray *fetchObjects = [objects copy]; + if (fetchObjects.count == 0) { + return [BFTask taskWithResult:fetchObjects]; + } + NSArray *uniqueObjects = [PFObjectBatchController uniqueObjectsArrayFromArray:fetchObjects omitObjectsWithData:YES]; + return [[[[self currentUserController] getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [PFObject _enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[self objectBatchController] fetchObjectsAsync:uniqueObjects withSessionToken:sessionToken]; + }]; + } forObjects:uniqueObjects]; + }] continueWithSuccessResult:fetchObjects]; +} + ++ (void)fetchAllIfNeededInBackground:(NSArray *)objects target:(id)target selector:(SEL)selector { + [self fetchAllIfNeededInBackground:objects block:^(NSArray *objects, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:objects object:error]; + }]; +} + ++ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(PFArrayResultBlock)block { + [[self fetchAllIfNeededInBackground:objects] thenCallBackOnMainThreadAsync:block]; +} + +///-------------------------------------- +#pragma mark - Fetch From Local Datastore +///-------------------------------------- + +- (instancetype)fetchFromLocalDatastore { + return [self fetchFromLocalDatastore:nil]; +} + +- (instancetype)fetchFromLocalDatastore:(NSError **)error { + return [[self fetchFromLocalDatastoreInBackground] waitForResult:error]; +} + +- (void)fetchFromLocalDatastoreInBackgroundWithBlock:(PFObjectResultBlock)block { + [[self fetchFromLocalDatastoreInBackground] thenCallBackOnMainThreadAsync:block]; +} + +- (BFTask *)fetchFromLocalDatastoreInBackground { + PFOfflineStore *store = [Parse _currentManager].offlineStore; + PFConsistencyAssert(store != nil, @"You must enable the local datastore before calling fetchFromLocalDatastore()."); + return [store fetchObjectLocallyAsync:self]; +} + +///-------------------------------------- +#pragma mark - Key/Value Accessors +///-------------------------------------- + +- (void)setObject:(id)object forKey:(NSString *)key { + [self _setObject:object forKey:key onlyIfDifferent:NO]; +} + +- (void)setObject:(id)object forKeyedSubscript:(NSString *)key { + [self setObject:object forKey:key]; +} + +- (id)objectForKey:(NSString *)key { + @synchronized (lock) { + PFConsistencyAssert([self isDataAvailableForKey:key], + @"Key \"%@\" has no data. Call fetchIfNeeded before getting its value.", key); + + id result = _estimatedData[key]; + if ([key isEqualToString:PFObjectACLRESTKey] && [result isKindOfClass:[PFACL class]]) { + PFACL *acl = result; + if ([acl isShared]) { + PFACL *copy = [acl createUnsharedCopy]; + self[PFObjectACLRESTKey] = copy; + return copy; + } + } + + // A relation may be deserialized without a parent or key. Either way, make sure it's consistent. + // TODO: (nlutsenko) This should be removable after we clean up the serialization code. + if ([result isKindOfClass:[PFRelation class]]) { + [result ensureParentIs:self andKeyIs:key]; + } + + return result; + } +} + +- (id)objectForKeyedSubscript:(NSString *)key { + return [self objectForKey:key]; +} + +- (void)removeObjectForKey:(NSString *)key { + @synchronized (lock) { + if ([self objectForKey:key]) { + PFDeleteOperation *operation = [[PFDeleteOperation alloc] init]; + [self performOperation:operation forKey:key]; + } + } +} + +- (void)revert { + @synchronized (self.lock) { + if ([self isDirty]) { + NSMutableSet *persistentKeys = [NSMutableSet setWithArray:[self._state.serverData allKeys]]; + + PFOperationSet *unsavedChanges = [self unsavedChanges]; + for (PFOperationSet *operationSet in operationSetQueue) { + if (operationSet != unsavedChanges) { + [persistentKeys addObjectsFromArray:[operationSet.keyEnumerator allObjects]]; + } + } + + [unsavedChanges removeAllObjects]; + [_availableKeys intersectSet:persistentKeys]; + + [self rebuildEstimatedData]; + [self checkpointAllMutableContainers]; + } + } +} + +- (void)revertObjectForKey:(NSString *)key { + @synchronized (self.lock) { + if ([self isDirtyForKey:key]) { + [[self unsavedChanges] removeObjectForKey:key]; + [self rebuildEstimatedData]; + [_availableKeys removeObject:key]; + [self checkpointAllMutableContainers]; + } + } +} + +#pragma mark Relations + +- (PFRelation *)relationforKey:(NSString *)key { + return [self relationForKey:key]; +} + +- (PFRelation *)relationForKey:(NSString *)key { + @synchronized (lock) { + // All the sanity checking is done when addObject or + // removeObject is called on the relation. + PFRelation *relation = [PFRelation relationForObject:self forKey:key]; + + id object = _estimatedData[key]; + if ([object isKindOfClass:[PFRelation class]]) { + relation.targetClass = ((PFRelation *)object).targetClass; + } + return relation; + } +} + +#pragma mark Array + +- (void)addObject:(id)object forKey:(NSString *)key { + [self addObjectsFromArray:@[ object ] forKey:key]; +} + +- (void)addObjectsFromArray:(NSArray *)objects forKey:(NSString *)key { + [self performOperation:[PFAddOperation addWithObjects:objects] forKey:key]; +} + +- (void)addUniqueObject:(id)object forKey:(NSString *)key { + [self addUniqueObjectsFromArray:@[ object ] forKey:key]; +} + +- (void)addUniqueObjectsFromArray:(NSArray *)objects forKey:(NSString *)key { + [self performOperation:[PFAddUniqueOperation addUniqueWithObjects:objects] forKey:key]; +} + +- (void)removeObject:(id)object forKey:(NSString *)key { + [self removeObjectsInArray:@[ object ] forKey:key]; +} + +- (void)removeObjectsInArray:(NSArray *)objects forKey:(NSString *)key { + [self performOperation:[PFRemoveOperation removeWithObjects:objects] forKey:key]; +} + +#pragma mark Increment + +- (void)incrementKey:(NSString *)key { + [self incrementKey:key byAmount:@1]; +} + +- (void)incrementKey:(NSString *)key byAmount:(NSNumber *)amount { + [self performOperation:[PFIncrementOperation incrementWithAmount:amount] forKey:key]; +} + +///-------------------------------------- +#pragma mark - Key Value Coding +///-------------------------------------- + +- (id)valueForUndefinedKey:(NSString *)key { + return self[key]; +} + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key { + self[key] = value; +} + +///-------------------------------------- +#pragma mark - Misc +///-------------------------------------- + +- (NSArray *)allKeys { + @synchronized (lock) { + return [_estimatedData allKeys]; + } +} + +- (NSString *)description { + static NSString *descriptionKey = @"PFObject-PrintingDescription"; + + NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary; + if ([threadDictionary[descriptionKey] boolValue]) { + return [self _flatDescription]; + } + threadDictionary[descriptionKey] = @YES; + NSString *description = [self _recursiveDescription]; + [threadDictionary removeObjectForKey:descriptionKey]; + return description; +} + +- (NSString *)_recursiveDescription { + @synchronized (lock) { + return [NSString stringWithFormat:@"%@ %@", + [self _flatDescription], [_estimatedData.dictionaryRepresentation description]]; + } +} + +- (NSString *)_flatDescription { + @synchronized (lock) { + return [NSString stringWithFormat:@"<%@: %p, objectId: %@, localId: %@>", + self.displayClassName, self, [self displayObjectId], localId]; + } +} + +///-------------------------------------- +#pragma mark - Save all +///-------------------------------------- + ++ (BOOL)saveAll:(NSArray *)objects { + return [PFObject saveAll:objects error:nil]; +} + ++ (BOOL)saveAll:(NSArray *)objects error:(NSError **)error { + return [[[self saveAllInBackground:objects] waitForResult:error] boolValue]; +} + ++ (BFTask *)saveAllInBackground:(NSArray *)objects { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentObjectAsync] continueWithBlock:^id(BFTask *task) { + PFUser *currentUser = task.result; + NSString *sessionToken = currentUser.sessionToken; + return [PFObject _deepSaveAsync:objects withCurrentUser:currentUser sessionToken:sessionToken]; + }]; +} + ++ (void)saveAllInBackground:(NSArray *)objects target:(id)target selector:(SEL)selector { + [PFObject saveAllInBackground:objects block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + ++ (void)saveAllInBackground:(NSArray *)objects block:(PFBooleanResultBlock)block { + [[PFObject saveAllInBackground:objects] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Delete all +///-------------------------------------- + ++ (BOOL)deleteAll:(NSArray *)objects { + return [PFObject deleteAll:objects error:nil]; +} + ++ (BOOL)deleteAll:(NSArray *)objects error:(NSError **)error { + return [[[self deleteAllInBackground:objects] waitForResult:error] boolValue]; +} + ++ (BFTask PF_GENERIC(NSNumber *) *)deleteAllInBackground:(NSArray *)objects { + NSArray *deleteObjects = [objects copy]; // Snapshot the objects. + if (deleteObjects.count == 0) { + return [BFTask PF_GENERIC(NSNumber *) taskWithResult:@YES]; + } + return [[[[self currentUserController] getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + + NSArray *uniqueObjects = [PFObjectBatchController uniqueObjectsArrayFromArray:deleteObjects usingFilter:^BOOL(PFObject *object) { + return (object.objectId != nil); + }]; + NSMutableArray PF_GENERIC(BFTask *) *validationTasks = [NSMutableArray array]; + for (PFObject *object in uniqueObjects) { + [validationTasks addObject:[object _validateDeleteAsync]]; + } + return [[BFTask taskForCompletionOfAllTasks:validationTasks] continueWithSuccessBlock:^id(BFTask *task) { + return [self _enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[self objectBatchController] deleteObjectsAsync:uniqueObjects + withSessionToken:sessionToken]; + }]; + } forObjects:uniqueObjects]; + }]; + }] continueWithSuccessResult:@YES]; +} + ++ (void)deleteAllInBackground:(NSArray *)objects target:(id)target selector:(SEL)selector { + [PFObject deleteAllInBackground:objects block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + ++ (void)deleteAllInBackground:(NSArray *)objects block:(PFBooleanResultBlock)block { + [[self deleteAllInBackground:objects] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Dynamic synthesizers +///-------------------------------------- + +// NOTE: The ONLY reason this needs to exist is to support mocking PFObject subclasses. +// +// The reason mocking doesn't work is because OCMClassMock looks for methods that exist on the class already, and will +// not be able to use our dynamic instance-level method resolving. By implementing this, we give this method a signature +// once, and then tell the runtime to forward that message on from there. +// +// Note that by implementing it this way, we no longer need to implement -methodSignatureForSelector: or +// -respondsToSelector:, as the method will be dynamically resolved by the runtime when either of those methods is +// invoked. ++ (BOOL)resolveInstanceMethod:(SEL)sel { + if (self == [PFObject class]) { + return NO; + } + + NSMethodSignature *signature = [[self subclassingController] forwardingMethodSignatureForSelector:sel ofClass:self]; + if (!signature) { + return NO; + } + + // Convert the method signature *back* into a objc type string (sidenote, why isn't this a built in?). + NSMutableString *typeString = [NSMutableString stringWithFormat:@"%s", [signature methodReturnType]]; + for (NSUInteger argumentIndex = 0; argumentIndex < [signature numberOfArguments]; argumentIndex++) { + [typeString appendFormat:@"%s", [signature getArgumentTypeAtIndex:argumentIndex]]; + } + + // TODO: (richardross) Support stret return here (will need to introspect the method signature to do so). + class_addMethod(self, sel, _objc_msgForward, [typeString UTF8String]); + + return YES; +} + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + if (![[[self class] subclassingController] forwardObjectInvocation:anInvocation + withObject:(PFObject *)self]) { + [self doesNotRecognizeSelector:anInvocation.selector]; + } +} + +///-------------------------------------- +#pragma mark - Pinning +///-------------------------------------- + +- (BOOL)pin { + return [self pin:nil]; +} + +- (BOOL)pin:(NSError **)error { + return [self pinWithName:PFObjectDefaultPin error:error]; +} + +- (BFTask *)pinInBackground { + return [self pinInBackgroundWithName:PFObjectDefaultPin]; +} + +- (void)pinInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self pinInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (BOOL)pinWithName:(NSString *)name { + return [self pinWithName:name error:nil]; +} + +- (BOOL)pinWithName:(NSString *)name error:(NSError **)error { + return [[[self pinInBackgroundWithName:name] waitForResult:error] boolValue]; +} + +- (void)pinInBackgroundWithName:(NSString *)name block:(PFBooleanResultBlock)block { + [[self pinInBackgroundWithName:name] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (BFTask *)pinInBackgroundWithName:(NSString *)name { + return [self _pinInBackgroundWithName:name includeChildren:YES]; +} + +- (BFTask *)_pinInBackgroundWithName:(NSString *)name includeChildren:(BOOL)includeChildren { + return [[self class] _pinAllInBackground:@[ self ] withName:name includeChildren:includeChildren]; +} + +///-------------------------------------- +#pragma mark - Pinning Many Objects +///-------------------------------------- + ++ (BOOL)pinAll:(NSArray *)objects { + return [self pinAll:objects error:nil]; +} + ++ (BOOL)pinAll:(NSArray *)objects error:(NSError **)error { + return [self pinAll:objects withName:PFObjectDefaultPin error:error]; +} + ++ (BFTask *)pinAllInBackground:(NSArray *)objects { + return [self pinAllInBackground:objects withName:PFObjectDefaultPin]; +} + ++ (void)pinAllInBackground:(NSArray *)objects + block:(PFBooleanResultBlock)block { + [[self pinAllInBackground:objects] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BOOL)pinAll:(NSArray *)objects withName:(NSString *)name { + return [self pinAll:objects withName:name error:nil]; +} + ++ (BOOL)pinAll:(NSArray *)objects withName:(NSString *)name error:(NSError **)error { + return [[[self pinAllInBackground:objects withName:name] waitForResult:error] boolValue]; +} + ++ (BFTask *)pinAllInBackground:(NSArray *)objects withName:(NSString *)name { + return [self _pinAllInBackground:objects withName:name includeChildren:YES]; +} + ++ (void)pinAllInBackground:(NSArray *)objects + withName:(NSString *)name + block:(PFBooleanResultBlock)block { + [[self pinAllInBackground:objects withName:name] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BFTask *)_pinAllInBackground:(NSArray *)objects + withName:(NSString *)name + includeChildren:(BOOL)includeChildren { + return [[self pinningObjectStore] pinObjectsAsync:objects + withPinName:name + includeChildren:includeChildren]; +} + +///-------------------------------------- +#pragma mark - Unpinning +///-------------------------------------- + +- (BOOL)unpin { + return [self unpinWithName:PFObjectDefaultPin]; +} + +- (BOOL)unpin:(NSError **)error { + return [self unpinWithName:PFObjectDefaultPin error:error]; +} + +- (BFTask *)unpinInBackground { + return [self unpinInBackgroundWithName:PFObjectDefaultPin]; +} + +- (void)unpinInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self unpinInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +- (BOOL)unpinWithName:(NSString *)name { + return [self unpinWithName:name error:nil]; +} + +- (BOOL)unpinWithName:(NSString *)name error:(NSError **)error { + return [[[self unpinInBackgroundWithName:name] waitForResult:error] boolValue]; +} + +- (BFTask *)unpinInBackgroundWithName:(NSString *)name { + return [[self class] unpinAllInBackground:@[ self ] withName:name]; +} + +- (void)unpinInBackgroundWithName:(NSString *)name block:(PFBooleanResultBlock)block { + [[self unpinInBackgroundWithName:name] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Unpinning Many Objects +///-------------------------------------- + ++ (BOOL)unpinAllObjects { + return [self unpinAllObjects:nil]; +} + ++ (BOOL)unpinAllObjects:(NSError **)error { + return [self unpinAllObjectsWithName:PFObjectDefaultPin error:error]; +} + ++ (BFTask *)unpinAllObjectsInBackground { + return [self unpinAllObjectsInBackgroundWithName:PFObjectDefaultPin]; +} + ++ (void)unpinAllObjectsInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self unpinAllObjectsInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BOOL)unpinAllObjectsWithName:(NSString *)name { + return [self unpinAllObjectsWithName:name error:nil]; +} + ++ (BOOL)unpinAllObjectsWithName:(NSString *)name error:(NSError **)error { + return [[[self unpinAllObjectsInBackgroundWithName:name] waitForResult:error] boolValue]; +} + ++ (void)unpinAllObjectsInBackgroundWithName:(NSString *)name block:(PFBooleanResultBlock)block { + [[self unpinAllObjectsInBackgroundWithName:name] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BFTask *)unpinAllObjectsInBackgroundWithName:(NSString *)name { + return [[self pinningObjectStore] unpinAllObjectsAsyncWithPinName:name]; +} + ++ (BOOL)unpinAll:(NSArray *)objects { + return [self unpinAll:objects error:nil]; +} + ++ (BOOL)unpinAll:(NSArray *)objects error:(NSError **)error { + return [self unpinAll:objects withName:PFObjectDefaultPin error:error]; +} + ++ (BFTask *)unpinAllInBackground:(NSArray *)objects { + return [self unpinAllInBackground:objects withName:PFObjectDefaultPin]; +} + ++ (void)unpinAllInBackground:(NSArray *)objects block:(PFBooleanResultBlock)block { + [[self unpinAllInBackground:objects] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (BOOL)unpinAll:(NSArray *)objects withName:(NSString *)name { + return [self unpinAll:objects withName:name error:nil]; +} + ++ (BOOL)unpinAll:(NSArray *)objects withName:(NSString *)name error:(NSError **)error { + return [[[self unpinAllInBackground:objects withName:name] waitForResult:error] boolValue]; +} + ++ (BFTask *)unpinAllInBackground:(NSArray *)objects withName:(NSString *)name { + return [[self pinningObjectStore] unpinObjectsAsync:objects withPinName:name]; +} + ++ (void)unpinAllInBackground:(NSArray *)objects + withName:(NSString *)name + block:(PFBooleanResultBlock)block { + [[self unpinAllInBackground:objects withName:name] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +///-------------------------------------- +#pragma mark - Data Source +///-------------------------------------- + ++ (id)objectController { + return [Parse _currentManager].coreManager.objectController; +} + ++ (PFObjectFileCodingLogic *)objectFileCodingLogic { + return [PFObjectFileCodingLogic codingLogic]; +} + ++ (PFObjectBatchController *)objectBatchController { + return [Parse _currentManager].coreManager.objectBatchController; +} + ++ (PFPinningObjectStore *)pinningObjectStore { + return [Parse _currentManager].coreManager.pinningObjectStore; +} + ++ (PFCurrentUserController *)currentUserController { + return [Parse _currentManager].coreManager.currentUserController; +} + ++ (PFObjectSubclassingController *)subclassingController { + return [PFObjectSubclassingController defaultController]; +} + +@end diff --git a/Pods/Parse/Parse/PFProduct.h b/Pods/Parse/Parse/PFProduct.h new file mode 100644 index 0000000..e895d6b --- /dev/null +++ b/Pods/Parse/Parse/PFProduct.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFProduct` class represents an in-app purchase product on the Parse server. + By default, products can only be created via the Data Browser. Saving a `PFProduct` will result in error. + However, the products' metadata information can be queried and viewed. + + This class is currently for iOS only. + */ +PF_WATCH_UNAVAILABLE @interface PFProduct : PFObject + +///-------------------------------------- +/// @name Product-specific Properties +///-------------------------------------- + +/*! + @abstract The product identifier of the product. + + @discussion This should match the product identifier in iTunes Connect exactly. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *productIdentifier; + +/*! + @abstract The icon of the product. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) PFFile *icon; + +/*! + @abstract The title of the product. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *title; + +/*! + @abstract The subtitle of the product. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *subtitle; + +/*! + @abstract The order in which the product information is displayed in . + + @discussion The product with a smaller order is displayed earlier in the . + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSNumber *order; + +/*! + @abstract The name of the associated download. + + @discussion If there is no downloadable asset, it should be `nil`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong, readonly) NSString *downloadName; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFProduct.m b/Pods/Parse/Parse/PFProduct.m new file mode 100644 index 0000000..4eb2f2b --- /dev/null +++ b/Pods/Parse/Parse/PFProduct.m @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFProduct.h" +#import "PFProduct+Private.h" + +#import "PFAssert.h" +#import "PFObject+Subclass.h" + +@implementation PFProduct + +@dynamic productIdentifier; +@dynamic icon; +@dynamic title; +@dynamic subtitle; +@dynamic order; +@dynamic downloadName; + +///-------------------------------------- +#pragma mark - PFSubclassing +///-------------------------------------- + +// Validates a class name. We override this to only allow the product class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[PFProduct parseClassName]], + @"Cannot initialize a PFProduct with a custom class name."); +} + ++ (NSString *)parseClassName { + return @"_Product"; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +@dynamic price; +@dynamic priceLocale; +@dynamic contentPath; +@dynamic progress; + +@end diff --git a/Pods/Parse/Parse/PFPurchase.h b/Pods/Parse/Parse/PFPurchase.h new file mode 100644 index 0000000..b681cc1 --- /dev/null +++ b/Pods/Parse/Parse/PFPurchase.h @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import +#import + +@class PFProduct; + +PF_ASSUME_NONNULL_BEGIN + +typedef void (^PFPurchaseProductObservationBlock)(SKPaymentTransaction *transaction); +typedef void (^PFPurchaseBuyProductResultBlock)(NSError *PF_NULLABLE_S error); +typedef void (^PFPurchaseDownloadAssetResultBlock)(NSString *PF_NULLABLE_S filePath, NSError *PF_NULLABLE_S error); + +/*! + `PFPurchase` provides a set of APIs for working with in-app purchases. + + This class is currently for iOS only. + */ +@interface PFPurchase : NSObject + +/*! + @abstract Add application logic block which is run when buying a product. + + @discussion This method should be called once for each product, and should be called before + calling . All invocations to should happen within + the same method, and on the main thread. It is recommended to place all invocations of this method + in `application:didFinishLaunchingWithOptions:`. + + @param productIdentifier the product identifier + @param block The block to be run when buying a product. + */ ++ (void)addObserverForProduct:(NSString *)productIdentifier block:(PFPurchaseProductObservationBlock)block; + +/*! + @abstract *Asynchronously* initiates the purchase for the product. + + @param productIdentifier the product identifier + @param block the completion block. + */ ++ (void)buyProduct:(NSString *)productIdentifier block:(PFPurchaseBuyProductResultBlock)block; + +/*! + @abstract *Asynchronously* download the purchased asset, which is stored on Parse's server. + + @discussion Parse verifies the receipt with Apple and delivers the content only if the receipt is valid. + + @param transaction the transaction, which contains the receipt. + @param completion the completion block. + */ ++ (void)downloadAssetForTransaction:(SKPaymentTransaction *)transaction + completion:(PFPurchaseDownloadAssetResultBlock)completion; + +/*! + @abstract *Asynchronously* download the purchased asset, which is stored on Parse's server. + + @discussion Parse verifies the receipt with Apple and delivers the content only if the receipt is valid. + + @param transaction the transaction, which contains the receipt. + @param completion the completion block. + @param progress the progress block, which is called multiple times to reveal progress of the download. + */ ++ (void)downloadAssetForTransaction:(SKPaymentTransaction *)transaction + completion:(PFPurchaseDownloadAssetResultBlock)completion + progress:(PF_NULLABLE PFProgressBlock)progress; + +/*! + @abstract *Asynchronously* restore completed transactions for the current user. + + @discussion Only nonconsumable purchases are restored. If observers for the products have been added before + calling this method, invoking the method reruns the application logic associated with the purchase. + + @warning This method is only important to developers who want to preserve purchase states across + different installations of the same app. + */ ++ (void)restore; + +/*! + @abstract Returns a content path of the asset of a product, if it was purchased and downloaded. + + @discussion To download and verify purchases use . + + @warning This method will return `nil`, if the purchase wasn't verified or if the asset was not downloaded. + */ ++ (PF_NULLABLE NSString *)assetContentPathForProduct:(PFProduct *)product; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFPurchase.m b/Pods/Parse/Parse/PFPurchase.m new file mode 100644 index 0000000..52ee0f8 --- /dev/null +++ b/Pods/Parse/Parse/PFPurchase.m @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPurchase.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFConstants.h" +#import "PFPaymentTransactionObserver.h" +#import "PFProduct.h" +#import "PFPurchaseController.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +@implementation PFPurchase + +///-------------------------------------- +#pragma mark - Public +///-------------------------------------- + ++ (void)addObserverForProduct:(NSString *)productIdentifier block:(PFPurchaseProductObservationBlock)block { + // We require the following method to run on the main thread because we want to add the observer + // *after* all products handlers have been added. Developers might be calling this method multiple + // times; and if the observer is added after the first call, the observer might not know how to + // handle some purchases. + + PFConsistencyAssert([NSThread isMainThread], @"%@ must be called on the main thread.", NSStringFromSelector(_cmd)); + PFParameterAssert(productIdentifier, @"You must pass in a valid product identifier."); + PFParameterAssert(block, @"You must pass in a valid block for the product."); + + [[Parse _currentManager].purchaseController.transactionObserver handle:productIdentifier block:block]; +} + ++ (void)buyProduct:(NSString *)productIdentifier block:(PFPurchaseBuyProductResultBlock)completion { + [[[self _purchaseController] buyProductAsyncWithIdentifier:productIdentifier] continueWithBlock:^id(BFTask *task) { + if (completion) { + completion(task.error); + } + return nil; + }]; +} + ++ (void)restore { + [[self _purchaseController].paymentQueue restoreCompletedTransactions]; +} + ++ (void)downloadAssetForTransaction:(SKPaymentTransaction *)transaction + completion:(PFPurchaseDownloadAssetResultBlock)completion { + [self downloadAssetForTransaction:transaction completion:completion progress:nil]; +} + ++ (void)downloadAssetForTransaction:(SKPaymentTransaction *)transaction + completion:(PFPurchaseDownloadAssetResultBlock)completion + progress:(PFProgressBlock)progress { + @weakify(self); + [[[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + @strongify(self); + NSString *sessionToken = task.result; + return [[self _purchaseController] downloadAssetAsyncForTransaction:transaction + withProgressBlock:progress + sessionToken:sessionToken]; + }] continueWithMainThreadResultBlock:completion executeIfCancelled:YES]; +} + ++ (NSString *)assetContentPathForProduct:(PFProduct *)product { + NSString *path = [[self _purchaseController] assetContentPathForProductWithIdentifier:product.productIdentifier + fileName:product.downloadName]; + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + return path; + } + + return nil; +} + +///-------------------------------------- +#pragma mark - Purchase Controller +///-------------------------------------- + ++ (PFPurchaseController *)_purchaseController { + return [Parse _currentManager].purchaseController; +} + +@end diff --git a/Pods/Parse/Parse/PFPush.h b/Pods/Parse/Parse/PFPush.h new file mode 100644 index 0000000..6cff85b --- /dev/null +++ b/Pods/Parse/Parse/PFPush.h @@ -0,0 +1,532 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import + +@class PFQuery PF_GENERIC(PFGenericObject : PFObject *); + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFPush` class defines a push notification that can be sent from a client device. + + The preferred way of modifying or retrieving channel subscriptions is to use + the class, instead of the class methods in `PFPush`. + */ +@interface PFPush : NSObject + +///-------------------------------------- +/// @name Creating a Push Notification +///-------------------------------------- + ++ (instancetype)push; + +///-------------------------------------- +/// @name Configuring a Push Notification +///-------------------------------------- + +/*! + @abstract Sets the channel on which this push notification will be sent. + + @param channel The channel to set for this push. + The channel name must start with a letter and contain only letters, numbers, dashes, and underscores. + */ +- (void)setChannel:(PF_NULLABLE NSString *)channel; + +/*! + @abstract Sets the array of channels on which this push notification will be sent. + + @param channels The array of channels to set for this push. + Each channel name must start with a letter and contain only letters, numbers, dashes, and underscores. + */ +- (void)setChannels:(PF_NULLABLE NSArray PF_GENERIC(NSString *) *)channels; + +/*! + @abstract Sets an installation query to which this push notification will be sent. + + @discussion The query should be created via <[PFInstallation query]> and should not specify a skip, limit, or order. + + @param query The installation query to set for this push. + */ +- (void)setQuery:(PF_NULLABLE PFQuery PF_GENERIC(PFInstallation *) *)query; + +/*! + @abstract Sets an alert message for this push notification. + + @warning This will overwrite any data specified in setData. + + @param message The message to send in this push. + */ +- (void)setMessage:(PF_NULLABLE NSString *)message; + +/*! + @abstract Sets an arbitrary data payload for this push notification. + + @discussion See the guide for information about the dictionary structure. + + @warning This will overwrite any data specified in setMessage. + + @param data The data to send in this push. + */ +- (void)setData:(PF_NULLABLE NSDictionary *)data; + +/*! + @abstract Sets whether this push will go to Android devices. + + @param pushToAndroid Whether this push will go to Android devices. + + @deprecated Please use a `[PFInstallation query]` with a constraint on deviceType instead. + */ +- (void)setPushToAndroid:(BOOL)pushToAndroid PARSE_DEPRECATED("Please use a [PFInstallation query] with a constraint on deviceType. This method is deprecated and won't do anything."); + +/*! + @abstract Sets whether this push will go to iOS devices. + + @param pushToIOS Whether this push will go to iOS devices. + + @deprecated Please use a `[PFInstallation query]` with a constraint on deviceType instead. + */ +- (void)setPushToIOS:(BOOL)pushToIOS PARSE_DEPRECATED("Please use a [PFInstallation query] with a constraint on deviceType. This method is deprecated and won't do anything."); + +/*! + @abstract Sets the expiration time for this notification. + + @discussion The notification will be sent to devices which are either online + at the time the notification is sent, or which come online before the expiration time is reached. + Because device clocks are not guaranteed to be accurate, + most applications should instead use . + + @see expireAfterTimeInterval: + + @param date The time at which the notification should expire. + */ +- (void)expireAtDate:(PF_NULLABLE NSDate *)date; + +/*! + @abstract Sets the time interval after which this notification should expire. + + @discussion This notification will be sent to devices which are either online at + the time the notification is sent, or which come online within the given + time interval of the notification being received by Parse's server. + An interval which is less than or equal to zero indicates that the + message should only be sent to devices which are currently online. + + @param timeInterval The interval after which the notification should expire. + */ +- (void)expireAfterTimeInterval:(NSTimeInterval)timeInterval; + +/*! + @abstract Clears both expiration values, indicating that the notification should never expire. + */ +- (void)clearExpiration; + +///-------------------------------------- +/// @name Sending Push Notifications +///-------------------------------------- + +/*! + @abstract *Synchronously* send a push message to a channel. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param message The message to send. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the send succeeded. + */ ++ (BOOL)sendPushMessageToChannel:(NSString *)channel + withMessage:(NSString *)message + error:(NSError **)error; + +/*! + @abstract *Asynchronously* send a push message to a channel. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param message The message to send. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message; + +/*! + @abstract *Asynchronously* sends a push message to a channel and calls the given block. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param message The message to send. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract *Asynchronously* send a push message to a channel. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param message The message to send. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Send a push message to a query. + + @param query The query to send to. The query must be a query created with <[PFInstallation query]>. + @param message The message to send. + @param error Pointer to an NSError that will be set if necessary. + + @returns Returns whether the send succeeded. + */ ++ (BOOL)sendPushMessageToQuery:(PFQuery PF_GENERIC(PFInstallation *) *)query + withMessage:(NSString *)message + error:(NSError **)error; + +/*! + @abstract *Asynchronously* send a push message to a query. + + @param query The query to send to. The query must be a query created with <[PFInstallation query]>. + @param message The message to send. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)sendPushMessageToQueryInBackground:(PFQuery PF_GENERIC(PFInstallation *) *)query + withMessage:(NSString *)message; + +/*! + @abstract *Asynchronously* sends a push message to a query and calls the given block. + + @param query The query to send to. The query must be a PFInstallation query + created with [PFInstallation query]. + @param message The message to send. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)sendPushMessageToQueryInBackground:(PFQuery PF_GENERIC(PFInstallation *) *)query + withMessage:(NSString *)message + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract *Synchronously* send this push message. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the send succeeded. + */ +- (BOOL)sendPush:(NSError **)error; + +/*! + @abstract *Asynchronously* send this push message. + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)sendPushInBackground; + +/*! + @abstract *Asynchronously* send this push message and executes the given callback block. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)sendPushInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract *Asynchronously* send this push message and calls the given callback. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ +- (void)sendPushInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract *Synchronously* send a push message with arbitrary data to a channel. + + @discussion See the guide for information about the dictionary structure. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param data The data to send. + @param error Pointer to an NSError that will be set if necessary. + + @returns Returns whether the send succeeded. + */ ++ (BOOL)sendPushDataToChannel:(NSString *)channel + withData:(NSDictionary *)data + error:(NSError **)error; + +/*! + @abstract *Asynchronously* send a push message with arbitrary data to a channel. + + @discussion See the guide for information about the dictionary structure. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param data The data to send. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)sendPushDataToChannelInBackground:(NSString *)channel + withData:(NSDictionary *)data; + +/*! + @abstract Asynchronously sends a push message with arbitrary data to a channel and calls the given block. + + @discussion See the guide for information about the dictionary structure. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param data The data to send. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)sendPushDataToChannelInBackground:(NSString *)channel + withData:(NSDictionary *)data + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract *Asynchronously* send a push message with arbitrary data to a channel. + + @discussion See the guide for information about the dictionary structure. + + @param channel The channel to send to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param data The data to send. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)sendPushDataToChannelInBackground:(NSString *)channel + withData:(NSDictionary *)data + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract *Synchronously* send a push message with arbitrary data to a query. + + @discussion See the guide for information about the dictionary structure. + + @param query The query to send to. The query must be a query + created with <[PFInstallation query]>. + @param data The data to send. + @param error Pointer to an NSError that will be set if necessary. + + @returns Returns whether the send succeeded. + */ ++ (BOOL)sendPushDataToQuery:(PFQuery PF_GENERIC(PFInstallation *) *)query + withData:(NSDictionary *)data + error:(NSError **)error; + +/*! + @abstract Asynchronously send a push message with arbitrary data to a query. + + @discussion See the guide for information about the dictionary structure. + + @param query The query to send to. The query must be a query + created with <[PFInstallation query]>. + @param data The data to send. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)sendPushDataToQueryInBackground:(PFQuery PF_GENERIC(PFInstallation *) *)query + withData:(NSDictionary *)data; + +/*! + @abstract *Asynchronously* sends a push message with arbitrary data to a query and calls the given block. + + @discussion See the guide for information about the dictionary structure. + + @param query The query to send to. The query must be a query + created with <[PFInstallation query]>. + @param data The data to send. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)sendPushDataToQueryInBackground:(PFQuery PF_GENERIC(PFInstallation *) *)query + withData:(NSDictionary *)data + block:(PF_NULLABLE PFBooleanResultBlock)block; + +///-------------------------------------- +/// @name Handling Notifications +///-------------------------------------- + +/*! + @abstract A default handler for push notifications while the app is active that + could be used to mimic the behavior of iOS push notifications while the app is backgrounded or not running. + + @discussion Call this from `application:didReceiveRemoteNotification:`. + If push has a dictionary containing loc-key and loc-args in the alert, + we support up to 10 items in loc-args (`NSRangeException` if limit exceeded). + + @warning This method is available only on iOS. + + @param userInfo The userInfo dictionary you get in `appplication:didReceiveRemoteNotification:`. + */ ++ (void)handlePush:(PF_NULLABLE NSDictionary *)userInfo NS_AVAILABLE_IOS(3_0) PF_EXTENSION_UNAVAILABLE(""); + +///-------------------------------------- +/// @name Managing Channel Subscriptions +///-------------------------------------- + +/*! + @abstract Store the device token locally for push notifications. + + @discussion Usually called from you main app delegate's `didRegisterForRemoteNotificationsWithDeviceToken:`. + + @param deviceToken Either as an `NSData` straight from `application:didRegisterForRemoteNotificationsWithDeviceToken:` + or as an `NSString` if you converted it yourself. + */ ++ (void)storeDeviceToken:(id)deviceToken; + +/*! + @abstract *Synchronously* get all the channels that this device is subscribed to. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns an `NSSet` containing all the channel names this device is subscribed to. + */ ++ (PF_NULLABLE NSSet *)getSubscribedChannels:(NSError **)error; + +/*! + @abstract *Asynchronously* get all the channels that this device is subscribed to. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSSet *)*)getSubscribedChannelsInBackground; + +/*! + @abstract *Asynchronously* get all the channels that this device is subscribed to. + @param block The block to execute. + It should have the following argument signature: `^(NSSet *channels, NSError *error)`. + */ ++ (void)getSubscribedChannelsInBackgroundWithBlock:(PFSetResultBlock)block; + +/* + @abstract *Asynchronously* get all the channels that this device is subscribed to. + + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSSet *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + */ ++ (void)getSubscribedChannelsInBackgroundWithTarget:(id)target + selector:(SEL)selector; + +/*! + @abstract *Synchrnously* subscribes the device to a channel of push notifications. + + @param channel The channel to subscribe to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the subscribe succeeded. + */ ++ (BOOL)subscribeToChannel:(NSString *)channel error:(NSError **)error; + +/*! + @abstract *Asynchronously* subscribes the device to a channel of push notifications. + + @param channel The channel to subscribe to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)subscribeToChannelInBackground:(NSString *)channel; + +/*! + @abstract *Asynchronously* subscribes the device to a channel of push notifications and calls the given block. + + @param channel The channel to subscribe to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)` + */ ++ (void)subscribeToChannelInBackground:(NSString *)channel + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract *Asynchronously* subscribes the device to a channel of push notifications and calls the given callback. + + @param channel The channel to subscribe to. The channel name must start with + a letter and contain only letters, numbers, dashes, and underscores. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)subscribeToChannelInBackground:(NSString *)channel + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract *Synchronously* unsubscribes the device to a channel of push notifications. + + @param channel The channel to unsubscribe from. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns whether the unsubscribe succeeded. + */ ++ (BOOL)unsubscribeFromChannel:(NSString *)channel error:(NSError **)error; + +/*! + @abstract *Asynchronously* unsubscribes the device from a channel of push notifications. + + @param channel The channel to unsubscribe from. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)unsubscribeFromChannelInBackground:(NSString *)channel; + +/*! + @abstract *Asynchronously* unsubscribes the device from a channel of push notifications and calls the given block. + + @param channel The channel to unsubscribe from. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)unsubscribeFromChannelInBackground:(NSString *)channel + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/* + @abstract *Asynchronously* unsubscribes the device from a channel of push notifications and calls the given callback. + + @param channel The channel to unsubscribe from. + @param target The object to call selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)unsubscribeFromChannelInBackground:(NSString *)channel + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFPush.m b/Pods/Parse/Parse/PFPush.m new file mode 100644 index 0000000..e5101dd --- /dev/null +++ b/Pods/Parse/Parse/PFPush.m @@ -0,0 +1,464 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFPush.h" +#import "PFPushPrivate.h" + +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFEncoder.h" +#import "PFHash.h" +#import "PFInstallationPrivate.h" +#import "PFKeychainStore.h" +#import "PFMacros.h" +#import "PFMutablePushState.h" +#import "PFMutableQueryState.h" +#import "PFPushChannelsController.h" +#import "PFPushController.h" +#import "PFPushManager.h" +#import "PFPushUtilities.h" +#import "PFQueryPrivate.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +static Class _pushInternalUtilClass = nil; + +@interface PFPush () + +@property (nonatomic, strong) PFMutablePushState *state; +@property (nonatomic, strong) PFQuery PF_GENERIC(PFInstallation *) *query; + +@end + +@implementation PFPush (Private) + ++ (Class)pushInternalUtilClass { + return _pushInternalUtilClass ?: [PFPushUtilities class]; +} + ++ (void)setPushInternalUtilClass:(Class)utilClass { + if (utilClass) { + PFParameterAssert([utilClass conformsToProtocol:@protocol(PFPushInternalUtils)], + @"utilClass must conform to PFPushInternalUtils protocol"); + } + _pushInternalUtilClass = utilClass; +} + +@end + +@implementation PFPush + +///-------------------------------------- +#pragma mark - Instance +///-------------------------------------- + +#pragma mark Init + ++ (instancetype)push { + return [[self alloc] init]; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _state = [[PFMutablePushState alloc] init]; + + return self; +} + +#pragma mark Accessors + +- (void)setQuery:(PFQuery *)query { + PFParameterAssert(!self.state.channels || !query, @"Can't set both the query and channel(s) properties."); + _query = query; +} + +- (void)setChannelSet:(NSSet *)channelSet { + PFParameterAssert(!self.query || !channelSet, @"Can't set both the query and channel(s) properties."); + self.state.channels = channelSet; +} + +- (void)setChannel:(NSString *)channel { + self.channelSet = PF_SET(channel); +} + +- (void)setChannels:(NSArray *)channels { + self.channelSet = [NSSet setWithArray:channels]; +} + +- (void)setMessage:(NSString *)message { + [self.state setPayloadWithMessage:message]; +} + +- (void)expireAtDate:(NSDate *)date { + self.state.expirationDate = date; + self.state.expirationTimeInterval = nil; +} + +- (void)expireAfterTimeInterval:(NSTimeInterval)timeInterval { + self.state.expirationDate = nil; + self.state.expirationTimeInterval = @(timeInterval); +} + +- (void)clearExpiration { + self.state.expirationDate = nil; + self.state.expirationTimeInterval = nil; +} + +- (void)setData:(NSDictionary *)data { + self.state.payload = data; +} + +#pragma mark Sending + +- (BOOL)sendPush:(NSError **)error { + return [[[self sendPushInBackground] waitForResult:error] boolValue]; +} + +- (BFTask *)sendPushInBackground { + if (self.query) { + PFParameterAssert(!self.query.state.sortKeys, @"Cannot send push notifications to an ordered query."); + PFParameterAssert(self.query.state.limit == -1, @"Cannot send push notifications to a limit query."); + PFParameterAssert(self.query.state.skip == 0, @"Cannot send push notifications to a skip query."); + } + + // Capture state first. + PFPushController *pushController = [[self class] pushController]; + PFPushState *state = [self _currentStateCopy]; + return [[PFUser _getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [pushController sendPushNotificationAsyncWithState:state sessionToken:sessionToken]; + }]; +} + +- (void)sendPushInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self sendPushInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +- (void)sendPushInBackgroundWithBlock:(PFBooleanResultBlock)block { + [[self sendPushInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + +#pragma mark Command + +- (PFPushState *)_currentStateCopy { + if (self.query) { + PFMutablePushState *state = [self.state mutableCopy]; + state.queryState = self.query.state; + return [state copy]; + } + return [self.state copy]; +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + PFPush *push = [[PFPush allocWithZone:zone] init]; + push.state = [self.state mutableCopy]; + return push; +} + +///-------------------------------------- +#pragma mark - NSObject +///-------------------------------------- + +- (NSUInteger)hash { + return PFIntegerPairHash([self.query hash], [self.state hash]); +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[PFPush class]]) { + return NO; + } + + PFPush *push = (PFPush *)object; + return (((self.query == nil && push.query == nil) || + [self.query isEqual:push.query]) && + [self.state isEqual:push.state]); +} + +///-------------------------------------- +#pragma mark - Sending Push Notifications +///-------------------------------------- + +#pragma mark To Channel + ++ (BOOL)sendPushMessageToChannel:(NSString *)channel + withMessage:(NSString *)message + error:(NSError **)error { + return [[[self sendPushMessageToChannelInBackground:channel withMessage:message] waitForResult:error] boolValue]; +} + ++ (BFTask *)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message { + NSDictionary *data = @{ @"alert" : message }; + return [self sendPushDataToChannelInBackground:channel withData:data]; +} + ++ (void)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message + block:(PFBooleanResultBlock)block { + [[self sendPushMessageToChannelInBackground:channel + withMessage:message] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (void)sendPushMessageToChannelInBackground:(NSString *)channel + withMessage:(NSString *)message + target:(id)target + selector:(SEL)selector { + [self sendPushMessageToChannelInBackground:channel withMessage:message block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + ++ (BOOL)sendPushDataToChannel:(NSString *)channel + withData:(NSDictionary *)data + error:(NSError **)error { + return [[[PFPush sendPushDataToChannelInBackground:channel withData:data] waitForResult:error] boolValue]; +} + ++ (BFTask *)sendPushDataToChannelInBackground:(NSString *)channel withData:(NSDictionary *)data { + PFPush *push = [self push]; + [push setChannel:channel]; + [push setData:data]; + return [push sendPushInBackground]; +} + ++ (void)sendPushDataToChannelInBackground:(NSString *)channel + withData:(NSDictionary *)data + block:(PFBooleanResultBlock)block { + [[self sendPushDataToChannelInBackground:channel withData:data] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (void)sendPushDataToChannelInBackground:(NSString *)channel + withData:(NSDictionary *)data + target:(id)target + selector:(SEL)selector { + [self sendPushDataToChannelInBackground:channel withData:data block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +#pragma mark To Query + ++ (BOOL)sendPushMessageToQuery:(PFQuery *)query + withMessage:(NSString *)message + error:(NSError **)error { + PFPush *push = [PFPush push]; + push.query = query; + push.message = message; + return [push sendPush:error]; +} + ++ (BFTask *)sendPushMessageToQueryInBackground:(PFQuery *)query + withMessage:(NSString *)message { + PFPush *push = [PFPush push]; + push.query = query; + push.message = message; + return [push sendPushInBackground]; +} + ++ (void)sendPushMessageToQueryInBackground:(PFQuery *)query + withMessage:(NSString *)message + block:(PFBooleanResultBlock)block { + PFPush *push = [PFPush push]; + push.query = query; + push.message = message; + [push sendPushInBackgroundWithBlock:block]; +} + + ++ (BOOL)sendPushDataToQuery:(PFQuery *)query + withData:(NSDictionary *)data + error:(NSError **)error { + PFPush *push = [PFPush push]; + push.query = query; + push.data = data; + return [push sendPush:error]; +} + ++ (BFTask *)sendPushDataToQueryInBackground:(PFQuery *)query + withData:(NSDictionary *)data { + PFPush *push = [PFPush push]; + push.query = query; + push.data = data; + return [push sendPushInBackground]; +} + ++ (void)sendPushDataToQueryInBackground:(PFQuery *)query + withData:(NSDictionary *)data + block:(PFBooleanResultBlock)block { + PFPush *push = [PFPush push]; + push.query = query; + push.data = data; + [push sendPushInBackgroundWithBlock:block]; +} + +///-------------------------------------- +#pragma mark - Channels +///-------------------------------------- + +#pragma mark Get + ++ (NSSet *)getSubscribedChannels:(NSError **)error { + return [[self getSubscribedChannelsInBackground] waitForResult:error]; +} + ++ (BFTask *)getSubscribedChannelsInBackground { + return [[self channelsController] getSubscribedChannelsAsync]; +} + ++ (void)getSubscribedChannelsInBackgroundWithBlock:(PFSetResultBlock)block { + [[self getSubscribedChannelsInBackground] thenCallBackOnMainThreadAsync:block]; +} + ++ (void)getSubscribedChannelsInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self getSubscribedChannelsInBackgroundWithBlock:^(NSSet *channels, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:channels object:error]; + }]; +} + +#pragma mark Subscribe + ++ (BOOL)subscribeToChannel:(NSString *)channel error:(NSError **)error { + return [[[self subscribeToChannelInBackground:channel] waitForResult:error] boolValue]; +} + ++ (BFTask *)subscribeToChannelInBackground:(NSString *)channel { + return [[self channelsController] subscribeToChannelAsyncWithName:channel]; +} + ++ (void)subscribeToChannelInBackground:(NSString *)channel block:(PFBooleanResultBlock)block { + [[self subscribeToChannelInBackground:channel] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (void)subscribeToChannelInBackground:(NSString *)channel target:(id)target selector:(SEL)selector { + [self subscribeToChannelInBackground:channel block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +#pragma mark Unsubscribe + ++ (BOOL)unsubscribeFromChannel:(NSString *)channel error:(NSError **)error { + return [[[self unsubscribeFromChannelInBackground:channel] waitForResult:error] boolValue]; +} + ++ (BFTask *)unsubscribeFromChannelInBackground:(NSString *)channel { + return [[self channelsController] unsubscribeFromChannelAsyncWithName:channel]; +} + ++ (void)unsubscribeFromChannelInBackground:(NSString *)channel block:(PFBooleanResultBlock)block { + [[self unsubscribeFromChannelInBackground:channel] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (void)unsubscribeFromChannelInBackground:(NSString *)channel target:(id)target selector:(SEL)selector { + [self unsubscribeFromChannelInBackground:channel block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Handling Notifications +///-------------------------------------- + +#if PARSE_IOS_ONLY ++ (void)handlePush:(NSDictionary *)userInfo { + UIApplication *application = [UIApplication sharedApplication]; + if ([application applicationState] != UIApplicationStateActive) { + return; + } + + NSDictionary *aps = userInfo[@"aps"]; + id alert = aps[@"alert"]; + + if (alert) { + NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey]; + NSString *message = nil; + if ([alert isKindOfClass:[NSString class]]) { + message = alert; + } else if ([alert isKindOfClass:[NSDictionary class]]) { + NSDictionary *alertDict = alert; + NSString *locKey = alertDict[@"loc-key"]; + if (locKey) { + NSString *format = [[NSBundle mainBundle] localizedStringForKey:locKey value:@"" table:nil]; + message = [PFInternalUtils _stringWithFormat:format arguments:alertDict[@"loc-args"]]; + } + } + if (message) { + [[self pushInternalUtilClass] showAlertViewWithTitle:appName message:message]; + } + } + + NSNumber *badgeNumber = aps[@"badge"]; + if (badgeNumber) { + NSInteger number = [aps[@"badge"] integerValue]; + [application setApplicationIconBadgeNumber:number]; + } + + NSString *soundName = aps[@"sound"]; + + // Vibrate or play sound only if `sound` is specified. + if ([soundName isKindOfClass:[NSString class]] && soundName.length != 0) { + // Vibrate if the sound is `default`, otherwise - play the sound name. + if ([soundName isEqualToString:@"default"]) { + [[self pushInternalUtilClass] playVibrate]; + } else { + [[self pushInternalUtilClass] playAudioWithName:soundName]; + } + } +} +#endif + +///-------------------------------------- +#pragma mark - Store Token +///-------------------------------------- + ++ (void)storeDeviceToken:(id)deviceToken { + NSString *deviceTokenString = [[self pushInternalUtilClass] convertDeviceTokenToString:deviceToken]; + [PFInstallation currentInstallation].deviceToken = deviceTokenString; +} + +///-------------------------------------- +#pragma mark - Deprecated +///-------------------------------------- + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (void)setPushToIOS:(BOOL)pushToIOS { +} + +- (void)setPushToAndroid:(BOOL)pushToAndroid { +} +#pragma clang diagnostic pop + +///-------------------------------------- +#pragma mark - Push Manager +///-------------------------------------- + ++ (PFPushController *)pushController { + return [Parse _currentManager].pushManager.pushController; +} + ++ (PFPushChannelsController *)channelsController { + return [Parse _currentManager].pushManager.channelsController; +} + +@end diff --git a/Pods/Parse/Parse/PFQuery.h b/Pods/Parse/Parse/PFQuery.h new file mode 100644 index 0000000..661399a --- /dev/null +++ b/Pods/Parse/Parse/PFQuery.h @@ -0,0 +1,892 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFQuery` class defines a query that is used to query for s. + */ +@interface PFQuery PF_GENERIC(PFGenericObject : PFObject *) : NSObject + +///-------------------------------------- +/// @name Blocks +///-------------------------------------- + +typedef void (^PFQueryArrayResultBlock)(NSArray PF_GENERIC(PFGenericObject) * PF_NULLABLE_S objects, NSError * PF_NULLABLE_S error); + +///-------------------------------------- +/// @name Creating a Query for a Class +///-------------------------------------- + +/*! + @abstract Initializes the query with a class name. + + @param className The class name. + */ +- (instancetype)initWithClassName:(NSString *)className; + +/*! + @abstract Returns a `PFQuery` for a given class. + + @param className The class to query on. + + @returns A `PFQuery` object. + */ ++ (instancetype)queryWithClassName:(NSString *)className; + +/*! + @abstract Creates a PFQuery with the constraints given by predicate. + + @discussion The following types of predicates are supported: + + - Simple comparisons such as `=`, `!=`, `<`, `>`, `<=`, `>=`, and `BETWEEN` with a key and a constant. + - Containment predicates, such as `x IN {1, 2, 3}`. + - Key-existence predicates, such as `x IN SELF`. + - BEGINSWITH expressions. + - Compound predicates with `AND`, `OR`, and `NOT`. + - SubQueries with `key IN %@`, subquery. + + The following types of predicates are NOT supported: + + - Aggregate operations, such as `ANY`, `SOME`, `ALL`, or `NONE`. + - Regular expressions, such as `LIKE`, `MATCHES`, `CONTAINS`, or `ENDSWITH`. + - Predicates comparing one key to another. + - Complex predicates with many ORed clauses. + + @param className The class to query on. + @param predicate The predicate to create conditions from. + */ ++ (instancetype)queryWithClassName:(NSString *)className predicate:(PF_NULLABLE NSPredicate *)predicate; + +/*! + The class name to query for. + */ +@property (nonatomic, strong) NSString *parseClassName; + +///-------------------------------------- +/// @name Adding Basic Constraints +///-------------------------------------- + +/*! + @abstract Make the query include PFObjects that have a reference stored at the provided key. + + @discussion This has an effect similar to a join. You can use dot notation to specify which fields in + the included object are also fetch. + + @param key The key to load child s for. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)includeKey:(NSString *)key; + +/*! + @abstract Make the query restrict the fields of the returned s to include only the provided keys. + + @discussion If this is called multiple times, then all of the keys specified in each of the calls will be included. + + @param keys The keys to include in the result. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)selectKeys:(NSArray PF_GENERIC(NSString *) *)keys; + +/*! + @abstract Add a constraint that requires a particular key exists. + + @param key The key that should exist. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKeyExists:(NSString *)key; + +/*! + @abstract Add a constraint that requires a key not exist. + + @param key The key that should not exist. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKeyDoesNotExist:(NSString *)key; + +/*! + @abstract Add a constraint to the query that requires a particular key's object to be equal to the provided object. + + @param key The key to be constrained. + @param object The object that must be equalled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key equalTo:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's object to be less than the provided object. + + @param key The key to be constrained. + @param object The object that provides an upper bound. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key lessThan:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's object + to be less than or equal to the provided object. + + @param key The key to be constrained. + @param object The object that must be equalled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key lessThanOrEqualTo:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's object + to be greater than the provided object. + + @param key The key to be constrained. + @param object The object that must be equalled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key greaterThan:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's + object to be greater than or equal to the provided object. + + @param key The key to be constrained. + @param object The object that must be equalled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key greaterThanOrEqualTo:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's object + to be not equal to the provided object. + + @param key The key to be constrained. + @param object The object that must not be equalled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key notEqualTo:(id)object; + +/*! + @abstract Add a constraint to the query that requires a particular key's object + to be contained in the provided array. + + @param key The key to be constrained. + @param array The possible values for the key's object. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key containedIn:(NSArray *)array; + +/*! + @abstract Add a constraint to the query that requires a particular key's object + not be contained in the provided array. + + @param key The key to be constrained. + @param array The list of values the key's object should not be. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key notContainedIn:(NSArray *)array; + +/*! + @abstract Add a constraint to the query that requires a particular key's array + contains every element of the provided array. + + @param key The key to be constrained. + @param array The array of values to search for. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key containsAllObjectsInArray:(NSArray *)array; + +///-------------------------------------- +/// @name Adding Location Constraints +///-------------------------------------- + +/*! + @abstract Add a constraint to the query that requires a particular key's coordinates (specified via ) + be near a reference point. + + @discussion Distance is calculated based on angular distance on a sphere. Results will be sorted by distance + from reference point. + + @param key The key to be constrained. + @param geopoint The reference point represented as a . + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key nearGeoPoint:(PFGeoPoint *)geopoint; + +/*! + @abstract Add a constraint to the query that requires a particular key's coordinates (specified via ) + be near a reference point and within the maximum distance specified (in miles). + + @discussion Distance is calculated based on a spherical coordinate system. + Results will be sorted by distance (nearest to farthest) from the reference point. + + @param key The key to be constrained. + @param geopoint The reference point represented as a . + @param maxDistance Maximum distance in miles. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + nearGeoPoint:(PFGeoPoint *)geopoint + withinMiles:(double)maxDistance; + +/*! + @abstract Add a constraint to the query that requires a particular key's coordinates (specified via ) + be near a reference point and within the maximum distance specified (in kilometers). + + @discussion Distance is calculated based on a spherical coordinate system. + Results will be sorted by distance (nearest to farthest) from the reference point. + + @param key The key to be constrained. + @param geopoint The reference point represented as a . + @param maxDistance Maximum distance in kilometers. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + nearGeoPoint:(PFGeoPoint *)geopoint + withinKilometers:(double)maxDistance; + +/*! + Add a constraint to the query that requires a particular key's coordinates (specified via ) be near + a reference point and within the maximum distance specified (in radians). Distance is calculated based on + angular distance on a sphere. Results will be sorted by distance (nearest to farthest) from the reference point. + + @param key The key to be constrained. + @param geopoint The reference point as a . + @param maxDistance Maximum distance in radians. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + nearGeoPoint:(PFGeoPoint *)geopoint + withinRadians:(double)maxDistance; + +/*! + @abstract Add a constraint to the query that requires a particular key's coordinates (specified via ) be + contained within a given rectangular geographic bounding box. + + @param key The key to be constrained. + @param southwest The lower-left inclusive corner of the box. + @param northeast The upper-right inclusive corner of the box. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key withinGeoBoxFromSouthwest:(PFGeoPoint *)southwest toNortheast:(PFGeoPoint *)northeast; + +///-------------------------------------- +/// @name Adding String Constraints +///-------------------------------------- + +/*! + @abstract Add a regular expression constraint for finding string values that match the provided regular expression. + + @warning This may be slow for large datasets. + + @param key The key that the string to match is stored in. + @param regex The regular expression pattern to match. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key matchesRegex:(NSString *)regex; + +/*! + @abstract Add a regular expression constraint for finding string values that match the provided regular expression. + + @warning This may be slow for large datasets. + + @param key The key that the string to match is stored in. + @param regex The regular expression pattern to match. + @param modifiers Any of the following supported PCRE modifiers: + - `i` - Case insensitive search + - `m` - Search across multiple lines of input + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + matchesRegex:(NSString *)regex + modifiers:(PF_NULLABLE NSString *)modifiers; + +/*! + @abstract Add a constraint for finding string values that contain a provided substring. + + @warning This will be slow for large datasets. + + @param key The key that the string to match is stored in. + @param substring The substring that the value must contain. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key containsString:(PF_NULLABLE NSString *)substring; + +/*! + @abstract Add a constraint for finding string values that start with a provided prefix. + + @discussion This will use smart indexing, so it will be fast for large datasets. + + @param key The key that the string to match is stored in. + @param prefix The substring that the value must start with. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key hasPrefix:(PF_NULLABLE NSString *)prefix; + +/*! + @abstract Add a constraint for finding string values that end with a provided suffix. + + @warning This will be slow for large datasets. + + @param key The key that the string to match is stored in. + @param suffix The substring that the value must end with. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key hasSuffix:(PF_NULLABLE NSString *)suffix; + +///-------------------------------------- +/// @name Adding Subqueries +///-------------------------------------- + +/*! + Returns a `PFQuery` that is the `or` of the passed in queries. + + @param queries The list of queries to or together. + + @returns An instance of `PFQuery` that is the `or` of the passed in queries. + */ ++ (instancetype)orQueryWithSubqueries:(NSArray PF_GENERIC(PFQuery *) *)queries; + +/*! + @abstract Adds a constraint that requires that a key's value matches a value in another key + in objects returned by a sub query. + + @param key The key that the value is stored. + @param otherKey The key in objects in the returned by the sub query whose value should match. + @param query The query to run. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + matchesKey:(NSString *)otherKey + inQuery:(PFQuery *)query; + +/*! + @abstract Adds a constraint that requires that a key's value `NOT` match a value in another key + in objects returned by a sub query. + + @param key The key that the value is stored. + @param otherKey The key in objects in the returned by the sub query whose value should match. + @param query The query to run. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key + doesNotMatchKey:(NSString *)otherKey + inQuery:(PFQuery *)query; + +/*! + @abstract Add a constraint that requires that a key's value matches a `PFQuery` constraint. + + @warning This only works where the key's values are s or arrays of s. + + @param key The key that the value is stored in + @param query The query the value should match + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key matchesQuery:(PFQuery *)query; + +/*! + @abstract Add a constraint that requires that a key's value to not match a `PFQuery` constraint. + + @warning This only works where the key's values are s or arrays of s. + + @param key The key that the value is stored in + @param query The query the value should not match + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)whereKey:(NSString *)key doesNotMatchQuery:(PFQuery *)query; + +///-------------------------------------- +/// @name Sorting +///-------------------------------------- + +/*! + @abstract Sort the results in *ascending* order with the given key. + + @param key The key to order by. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)orderByAscending:(NSString *)key; + +/*! + @abstract Additionally sort in *ascending* order by the given key. + + @discussion The previous keys provided will precedence over this key. + + @param key The key to order by. + */ +- (instancetype)addAscendingOrder:(NSString *)key; + +/*! + @abstract Sort the results in *descending* order with the given key. + + @param key The key to order by. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)orderByDescending:(NSString *)key; + +/*! + @abstract Additionally sort in *descending* order by the given key. + + @discussion The previous keys provided will precedence over this key. + + @param key The key to order by. + */ +- (instancetype)addDescendingOrder:(NSString *)key; + +/*! + @abstract Sort the results using a given sort descriptor. + + @warning If a `sortDescriptor` has custom `selector` or `comparator` - they aren't going to be used. + + @param sortDescriptor The `NSSortDescriptor` to use to sort the results of the query. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)orderBySortDescriptor:(NSSortDescriptor *)sortDescriptor; + +/*! + @abstract Sort the results using a given array of sort descriptors. + + @warning If a `sortDescriptor` has custom `selector` or `comparator` - they aren't going to be used. + + @param sortDescriptors An array of `NSSortDescriptor` objects to use to sort the results of the query. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)orderBySortDescriptors:(PF_NULLABLE NSArray PF_GENERIC(NSSortDescriptor *) *)sortDescriptors; + +///-------------------------------------- +/// @name Getting Objects by ID +///-------------------------------------- + +/*! + @abstract Returns a with a given class and id. + + @param objectClass The class name for the object that is being requested. + @param objectId The id of the object that is being requested. + + @returns The if found. Returns `nil` if the object isn't found, or if there was an error. + */ ++ (PF_NULLABLE PFGenericObject)getObjectOfClass:(NSString *)objectClass + objectId:(NSString *)objectId PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Returns a with a given class and id and sets an error if necessary. + + @param objectClass The class name for the object that is being requested. + @param objectId The id of the object that is being requested. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns The if found. Returns `nil` if the object isn't found, or if there was an `error`. + */ ++ (PF_NULLABLE PFGenericObject)getObjectOfClass:(NSString *)objectClass + objectId:(NSString *)objectId + error:(NSError **)error; + +/*! + @abstract Returns a with the given id. + + @warning This method mutates the query. + It will reset limit to `1`, skip to `0` and remove all conditions, leaving only `objectId`. + + @param objectId The id of the object that is being requested. + + @returns The if found. Returns nil if the object isn't found, or if there was an error. + */ +- (PF_NULLABLE PFGenericObject)getObjectWithId:(NSString *)objectId PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Returns a with the given id and sets an error if necessary. + + @warning This method mutates the query. + It will reset limit to `1`, skip to `0` and remove all conditions, leaving only `objectId`. + + @param objectId The id of the object that is being requested. + @param error Pointer to an `NSError` that will be set if necessary. + + @returns The if found. Returns nil if the object isn't found, or if there was an error. + */ +- (PF_NULLABLE PFGenericObject)getObjectWithId:(NSString *)objectId error:(NSError **)error; + +/*! + @abstract Gets a asynchronously and calls the given block with the result. + + @warning This method mutates the query. + It will reset limit to `1`, skip to `0` and remove all conditions, leaving only `objectId`. + + @param objectId The id of the object that is being requested. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(PFGenericObject) *)getObjectInBackgroundWithId:(NSString *)objectId; + +/*! + @abstract Gets a asynchronously and calls the given block with the result. + + @warning This method mutates the query. + It will reset limit to `1`, skip to `0` and remove all conditions, leaving only `objectId`. + + @param objectId The id of the object that is being requested. + @param block The block to execute. + The block should have the following argument signature: `^(NSArray *object, NSError *error)` + */ +- (void)getObjectInBackgroundWithId:(NSString *)objectId + block:(PF_NULLABLE void(^)(PFGenericObject PF_NULLABLE_S object, NSError *PF_NULLABLE_S error))block; + +/* + @abstract Gets a asynchronously. + + This mutates the PFQuery. It will reset limit to `1`, skip to `0` and remove all conditions, leaving only `objectId`. + + @param objectId The id of the object being requested. + @param target The target for the callback selector. + @param selector The selector for the callback. + It should have the following signature: `(void)callbackWithResult:(id)result error:(NSError *)error`. + Result will be `nil` if error is set and vice versa. + */ +- (void)getObjectInBackgroundWithId:(NSString *)objectId + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Getting User Objects +///-------------------------------------- + +/*! + @abstract Returns a with a given id. + + @param objectId The id of the object that is being requested. + + @returns The PFUser if found. Returns nil if the object isn't found, or if there was an error. + */ ++ (PF_NULLABLE PFUser *)getUserObjectWithId:(NSString *)objectId PF_SWIFT_UNAVAILABLE; + +/*! + Returns a PFUser with a given class and id and sets an error if necessary. + @param objectId The id of the object that is being requested. + @param error Pointer to an NSError that will be set if necessary. + @result The PFUser if found. Returns nil if the object isn't found, or if there was an error. + */ ++ (PF_NULLABLE PFUser *)getUserObjectWithId:(NSString *)objectId error:(NSError **)error; + +/*! + @deprecated Please use [PFUser query] instead. + */ ++ (instancetype)queryForUser PARSE_DEPRECATED("Use [PFUser query] instead."); + +///-------------------------------------- +/// @name Getting all Matches for a Query +///-------------------------------------- + +/*! + @abstract Finds objects *synchronously* based on the constructed query. + + @returns Returns an array of objects that were found. + */ +- (PF_NULLABLE NSArray PF_GENERIC(PFGenericObject) *)findObjects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Finds objects *synchronously* based on the constructed query and sets an error if there was one. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns an array of objects that were found. + */ +- (PF_NULLABLE NSArray PF_GENERIC(PFGenericObject) *)findObjects:(NSError **)error; + +/*! + @abstract Finds objects *asynchronously* and sets the `NSArray` of objects as a result of the task. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSArray *)*)findObjectsInBackground; + +/*! + @abstract Finds objects *asynchronously* and calls the given block with the results. + + @param block The block to execute. + It should have the following argument signature: `^(NSArray *objects, NSError *error)` + */ +- (void)findObjectsInBackgroundWithBlock:(PF_NULLABLE PFQueryArrayResultBlock)block; + +/* + @abstract Finds objects *asynchronously* and calls the given callback with the results. + + @param target The object to call the selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(id)result error:(NSError *)error`. + Result will be `nil` if error is set and vice versa. + */ +- (void)findObjectsInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Getting the First Match in a Query +///-------------------------------------- + +/*! + @abstract Gets an object *synchronously* based on the constructed query. + + @warning This method mutates the query. It will reset the limit to `1`. + + @returns Returns a , or `nil` if none was found. + */ +- (PF_NULLABLE PFGenericObject)getFirstObject PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Gets an object *synchronously* based on the constructed query and sets an error if any occurred. + + @warning This method mutates the query. It will reset the limit to `1`. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns a , or `nil` if none was found. + */ +- (PF_NULLABLE PFGenericObject)getFirstObject:(NSError **)error; + +/*! + @abstract Gets an object *asynchronously* and sets it as a result of the task. + + @warning This method mutates the query. It will reset the limit to `1`. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(PFGenericObject) *)getFirstObjectInBackground; + +/*! + @abstract Gets an object *asynchronously* and calls the given block with the result. + + @warning This method mutates the query. It will reset the limit to `1`. + + @param block The block to execute. + It should have the following argument signature: `^(PFObject *object, NSError *error)`. + `result` will be `nil` if `error` is set OR no object was found matching the query. + `error` will be `nil` if `result` is set OR if the query succeeded, but found no results. + */ +- (void)getFirstObjectInBackgroundWithBlock:(PF_NULLABLE void(^)(PFGenericObject PF_NULLABLE_S object, NSError *PF_NULLABLE_S error))block; + +/* + @abstract Gets an object *asynchronously* and calls the given callback with the results. + + @warning This method mutates the query. It will reset the limit to `1`. + + @param target The object to call the selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(PFObject *)result error:(NSError *)error`. + `result` will be `nil` if `error` is set OR no object was found matching the query. + `error` will be `nil` if `result` is set OR if the query succeeded, but found no results. + */ +- (void)getFirstObjectInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Counting the Matches in a Query +///-------------------------------------- + +/*! + @abstract Counts objects *synchronously* based on the constructed query. + + @returns Returns the number of objects that match the query, or `-1` if there is an error. + */ +- (NSInteger)countObjects PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Counts objects *synchronously* based on the constructed query and sets an error if there was one. + + @param error Pointer to an `NSError` that will be set if necessary. + + @returns Returns the number of objects that match the query, or `-1` if there is an error. + */ +- (NSInteger)countObjects:(NSError **)error; + +/*! + @abstract Counts objects *asynchronously* and sets `NSNumber` with count as a result of the task. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)countObjectsInBackground; + +/*! + @abstract Counts objects *asynchronously* and calls the given block with the counts. + + @param block The block to execute. + It should have the following argument signature: `^(int count, NSError *error)` + */ +- (void)countObjectsInBackgroundWithBlock:(PF_NULLABLE PFIntegerResultBlock)block; + +/* + @abstract Counts objects *asynchronously* and calls the given callback with the count. + + @param target The object to call the selector on. + @param selector The selector to call. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + */ +- (void)countObjectsInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Cancelling a Query +///-------------------------------------- + +/*! + @abstract Cancels the current network request (if any). Ensures that callbacks won't be called. + */ +- (void)cancel; + +///-------------------------------------- +/// @name Paginating Results +///-------------------------------------- + +/*! + @abstract A limit on the number of objects to return. The default limit is `100`, with a + maximum of 1000 results being returned at a time. + + @warning If you are calling `findObjects` with `limit = 1`, you may find it easier to use `getFirst` instead. + */ +@property (nonatomic, assign) NSInteger limit; + +/*! + @abstract The number of objects to skip before returning any. + */ +@property (nonatomic, assign) NSInteger skip; + +///-------------------------------------- +/// @name Controlling Caching Behavior +///-------------------------------------- + +/*! + @abstract The cache policy to use for requests. + + Not allowed when Pinning is enabled. + + @see fromLocalDatastore + @see fromPin + @see fromPinWithName: + */ +@property (assign, readwrite) PFCachePolicy cachePolicy; + +/*! + @abstract The age after which a cached value will be ignored + */ +@property (assign, readwrite) NSTimeInterval maxCacheAge; + +/*! + @abstract Returns whether there is a cached result for this query. + + @result `YES` if there is a cached result for this query, otherwise `NO`. + */ +- (BOOL)hasCachedResult; + +/*! + @abstract Clears the cached result for this query. If there is no cached result, this is a noop. + */ +- (void)clearCachedResult; + +/*! + @abstract Clears the cached results for all queries. + */ ++ (void)clearAllCachedResults; + +///-------------------------------------- +/// @name Query Source +///-------------------------------------- + +/*! + @abstract Change the source of this query to all pinned objects. + + @warning Requires Local Datastore to be enabled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + + @see cachePolicy + */ +- (instancetype)fromLocalDatastore; + +/*! + @abstract Change the source of this query to the default group of pinned objects. + + @warning Requires Local Datastore to be enabled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + + @see PFObjectDefaultPin + @see cachePolicy + */ +- (instancetype)fromPin; + +/*! + @abstract Change the source of this query to a specific group of pinned objects. + + @warning Requires Local Datastore to be enabled. + + @param name The pinned group. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + + @see PFObjectDefaultPin + @see cachePolicy + */ +- (instancetype)fromPinWithName:(PF_NULLABLE NSString *)name; + +/*! + @abstract Ignore ACLs when querying from the Local Datastore. + + @discussion This is particularly useful when querying for objects with Role based ACLs set on them. + + @warning Requires Local Datastore to be enabled. + + @returns The same instance of `PFQuery` as the receiver. This allows method chaining. + */ +- (instancetype)ignoreACLs; + +///-------------------------------------- +/// @name Advanced Settings +///-------------------------------------- + +/*! + @abstract Whether or not performance tracing should be done on the query. + + @warning This should not be set to `YES` in most cases. + */ +@property (nonatomic, assign) BOOL trace; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFQuery.m b/Pods/Parse/Parse/PFQuery.m new file mode 100644 index 0000000..0fb3995 --- /dev/null +++ b/Pods/Parse/Parse/PFQuery.m @@ -0,0 +1,1136 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFQuery.h" + +#import +#import + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCoreManager.h" +#import "PFCurrentUserController.h" +#import "PFGeoPointPrivate.h" +#import "PFInternalUtils.h" +#import "PFKeyValueCache.h" +#import "PFMutableQueryState.h" +#import "PFObject.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFPin.h" +#import "PFQueryController.h" +#import "PFQueryUtilities.h" +#import "PFRESTQueryCommand.h" +#import "PFUserPrivate.h" +#import "ParseInternal.h" +#import "Parse_Private.h" + +NSString *const PFQueryKeyNotEqualTo = @"$ne"; +NSString *const PFQueryKeyLessThan = @"$lt"; +NSString *const PFQueryKeyLessThanEqualTo = @"$lte"; +NSString *const PFQueryKeyGreaterThan = @"$gt"; +NSString *const PFQueryKeyGreaterThanOrEqualTo = @"$gte"; +NSString *const PFQueryKeyContainedIn = @"$in"; +NSString *const PFQueryKeyNotContainedIn = @"$nin"; +NSString *const PFQueryKeyContainsAll = @"$all"; +NSString *const PFQueryKeyNearSphere = @"$nearSphere"; +NSString *const PFQueryKeyWithin = @"$within"; +NSString *const PFQueryKeyRegex = @"$regex"; +NSString *const PFQueryKeyExists = @"$exists"; +NSString *const PFQueryKeyInQuery = @"$inQuery"; +NSString *const PFQueryKeyNotInQuery = @"$notInQuery"; +NSString *const PFQueryKeySelect = @"$select"; +NSString *const PFQueryKeyDontSelect = @"$dontSelect"; +NSString *const PFQueryKeyRelatedTo = @"$relatedTo"; +NSString *const PFQueryKeyOr = @"$or"; +NSString *const PFQueryKeyQuery = @"query"; +NSString *const PFQueryKeyKey = @"key"; +NSString *const PFQueryKeyObject = @"object"; + +NSString *const PFQueryOptionKeyMaxDistance = @"$maxDistance"; +NSString *const PFQueryOptionKeyBox = @"$box"; +NSString *const PFQueryOptionKeyRegexOptions = @"$options"; + +/*! + Checks if an object can be used as value for query equality clauses. + */ +static void PFQueryAssertValidEqualityClauseClass(id object) { + static NSArray *classes; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + classes = @[ [NSString class], [NSNumber class], [NSDate class], [NSNull class], + [PFObject class], [PFGeoPoint class] ]; + }); + + for (Class class in classes) { + if ([object isKindOfClass:class]) { + return; + } + } + + PFParameterAssert(NO, @"Cannot do a comparison query for type: %@", [object class]); +} + +/*! + Checks if an object can be used as value for query ordering clauses. + */ +static void PFQueryAssertValidOrderingClauseClass(id object) { + static NSArray *classes; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + classes = @[ [NSString class], [NSNumber class], [NSDate class] ]; + }); + + for (Class class in classes) { + if ([object isKindOfClass:class]) { + return; + } + } + + PFParameterAssert(NO, @"Cannot do a query that requires ordering for type: %@", [object class]); +} + +@interface PFQuery () { + BFCancellationTokenSource *_cancellationTokenSource; +} + +@property (nonatomic, strong, readwrite) PFMutableQueryState *state; + +@end + +@implementation PFQuery + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithState:(PFQueryState *)state { + self = [super init]; + if (!self) return nil; + + _state = [state mutableCopy]; + + return self; +} + +- (instancetype)initWithClassName:(NSString *)className { + self = [super init]; + if (!self) return nil; + + _state = [PFMutableQueryState stateWithParseClassName:className]; + + return self; +} + +///-------------------------------------- +#pragma mark - Public Accessors +///-------------------------------------- + +#pragma mark Basic + +- (NSString *)parseClassName { + return self.state.parseClassName; +} + +- (void)setParseClassName:(NSString *)parseClassName { + [self checkIfCommandIsRunning]; + self.state.parseClassName = parseClassName; +} + +#pragma mark Limit + +- (void)setLimit:(NSInteger)limit { + self.state.limit = limit; +} + +- (NSInteger)limit { + return self.state.limit; +} + +#pragma mark Skip + +- (void)setSkip:(NSInteger)skip { + self.state.skip = skip; +} + +- (NSInteger)skip { + return self.state.skip; +} + +#pragma mark Cache Policy + +- (void)setCachePolicy:(PFCachePolicy)cachePolicy { + [self _checkPinningEnabled:NO]; + [self checkIfCommandIsRunning]; + + self.state.cachePolicy = cachePolicy; +} + +- (PFCachePolicy)cachePolicy { + [self _checkPinningEnabled:NO]; + [self checkIfCommandIsRunning]; + + return self.state.cachePolicy; +} + +#pragma mark Cache Policy + +- (void)setMaxCacheAge:(NSTimeInterval)maxCacheAge { + self.state.maxCacheAge = maxCacheAge; +} + +- (NSTimeInterval)maxCacheAge { + return self.state.maxCacheAge; +} + +#pragma mark Trace + +- (void)setTrace:(BOOL)trace { + self.state.trace = trace; +} + +- (BOOL)trace { + return self.state.trace; +} + +///-------------------------------------- +#pragma mark - Order +///-------------------------------------- + +- (instancetype)orderByAscending:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state sortByKey:key ascending:YES]; + return self; +} + +- (instancetype)addAscendingOrder:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state addSortKey:key ascending:YES]; + return self; +} + +- (instancetype)orderByDescending:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state sortByKey:key ascending:NO]; + return self; +} + +- (instancetype)addDescendingOrder:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state addSortKey:key ascending:NO]; + return self; +} + +- (instancetype)orderBySortDescriptor:(NSSortDescriptor *)sortDescriptor { + NSString *key = sortDescriptor.key; + if (key) { + if (sortDescriptor.ascending) { + [self orderByAscending:key]; + } else { + [self orderByDescending:key]; + } + } + return self; +} + +- (instancetype)orderBySortDescriptors:(NSArray *)sortDescriptors { + [self.state addSortKeysFromSortDescriptors:sortDescriptors]; + return self; +} + +///-------------------------------------- +#pragma mark - Conditions +///-------------------------------------- + +// Helper for condition queries. +- (instancetype)whereKey:(NSString *)key condition:(NSString *)condition object:(id)object { + [self checkIfCommandIsRunning]; + [self.state setConditionType:condition withObject:object forKey:key]; + return self; +} + +- (instancetype)whereKey:(NSString *)key equalTo:(id)object { + [self checkIfCommandIsRunning]; + PFQueryAssertValidEqualityClauseClass(object); + [self.state setEqualityConditionWithObject:object forKey:key]; + return self; +} + +- (instancetype)whereKey:(NSString *)key greaterThan:(id)object { + PFQueryAssertValidOrderingClauseClass(object); + return [self whereKey:key condition:PFQueryKeyGreaterThan object:object]; +} + +- (instancetype)whereKey:(NSString *)key greaterThanOrEqualTo:(id)object { + PFQueryAssertValidOrderingClauseClass(object); + return [self whereKey:key condition:PFQueryKeyGreaterThanOrEqualTo object:object]; +} + +- (instancetype)whereKey:(NSString *)key lessThan:(id)object { + PFQueryAssertValidOrderingClauseClass(object); + return [self whereKey:key condition:PFQueryKeyLessThan object:object]; +} + +- (instancetype)whereKey:(NSString *)key lessThanOrEqualTo:(id)object { + PFQueryAssertValidOrderingClauseClass(object); + return [self whereKey:key condition:PFQueryKeyLessThanEqualTo object:object]; +} + +- (instancetype)whereKey:(NSString *)key notEqualTo:(id)object { + PFQueryAssertValidEqualityClauseClass(object); + return [self whereKey:key condition:PFQueryKeyNotEqualTo object:object]; +} + +- (instancetype)whereKey:(NSString *)key containedIn:(NSArray *)inArray { + return [self whereKey:key condition:PFQueryKeyContainedIn object:inArray]; +} + +- (instancetype)whereKey:(NSString *)key notContainedIn:(NSArray *)inArray { + return [self whereKey:key condition:PFQueryKeyNotContainedIn object:inArray]; +} + +- (instancetype)whereKey:(NSString *)key containsAllObjectsInArray:(NSArray *)array { + return [self whereKey:key condition:PFQueryKeyContainsAll object:array]; +} + +- (instancetype)whereKey:(NSString *)key nearGeoPoint:(PFGeoPoint *)geopoint { + return [self whereKey:key condition:PFQueryKeyNearSphere object:geopoint]; +} + +- (instancetype)whereKey:(NSString *)key nearGeoPoint:(PFGeoPoint *)geopoint withinRadians:(double)maxDistance { + return [[self whereKey:key condition:PFQueryKeyNearSphere object:geopoint] + whereKey:key condition:PFQueryOptionKeyMaxDistance object:@(maxDistance)]; +} + +- (instancetype)whereKey:(NSString *)key nearGeoPoint:(PFGeoPoint *)geopoint withinMiles:(double)maxDistance { + return [self whereKey:key nearGeoPoint:geopoint withinRadians:(maxDistance/EARTH_RADIUS_MILES)]; +} + +- (instancetype)whereKey:(NSString *)key nearGeoPoint:(PFGeoPoint *)geopoint withinKilometers:(double)maxDistance { + return [self whereKey:key nearGeoPoint:geopoint withinRadians:(maxDistance/EARTH_RADIUS_KILOMETERS)]; +} + +- (instancetype)whereKey:(NSString *)key withinGeoBoxFromSouthwest:(PFGeoPoint *)southwest toNortheast:(PFGeoPoint *)northeast { + NSArray *array = @[ southwest, northeast ]; + NSDictionary *dictionary = @{ PFQueryOptionKeyBox : array }; + return [self whereKey:key condition:PFQueryKeyWithin object:dictionary]; +} + +- (instancetype)whereKey:(NSString *)key matchesRegex:(NSString *)regex { + return [self whereKey:key condition:PFQueryKeyRegex object:regex]; +} + +- (instancetype)whereKey:(NSString *)key matchesRegex:(NSString *)regex modifiers:(NSString *)modifiers { + [self checkIfCommandIsRunning]; + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:2]; + dictionary[PFQueryKeyRegex] = regex; + if ([modifiers length]) { + dictionary[PFQueryOptionKeyRegexOptions] = modifiers; + } + [self.state setEqualityConditionWithObject:dictionary forKey:key]; + return self; +} + +- (instancetype)whereKey:(NSString *)key containsString:(NSString *)substring { + NSString *regex = [PFQueryUtilities regexStringForString:substring]; + return [self whereKey:key matchesRegex:regex]; +} + +- (instancetype)whereKey:(NSString *)key hasPrefix:(NSString *)prefix { + NSString *regex = [NSString stringWithFormat:@"^%@", [PFQueryUtilities regexStringForString:prefix]]; + return [self whereKey:key matchesRegex:regex]; +} + +- (instancetype)whereKey:(NSString *)key hasSuffix:(NSString *)suffix { + NSString *regex = [NSString stringWithFormat:@"%@$", [PFQueryUtilities regexStringForString:suffix]]; + return [self whereKey:key matchesRegex:regex]; +} + +- (instancetype)whereKeyExists:(NSString *)key { + return [self whereKey:key condition:PFQueryKeyExists object:@YES]; +} + +- (instancetype)whereKeyDoesNotExist:(NSString *)key { + return [self whereKey:key condition:PFQueryKeyExists object:@NO]; +} + +- (instancetype)whereKey:(NSString *)key matchesQuery:(PFQuery *)query { + return [self whereKey:key condition:PFQueryKeyInQuery object:query]; +} + +- (instancetype)whereKey:(NSString *)key doesNotMatchQuery:(PFQuery *)query { + return [self whereKey:key condition:PFQueryKeyNotInQuery object:query]; +} + +- (instancetype)whereKey:(NSString *)key matchesKey:(NSString *)otherKey inQuery:(PFQuery *)query { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:2]; + dict[PFQueryKeyQuery] = query; + dict[PFQueryKeyKey] = otherKey; + return [self whereKey:key condition:PFQueryKeySelect object:dict]; +} + +- (instancetype)whereKey:(NSString *)key doesNotMatchKey:(NSString *)otherKey inQuery:(PFQuery *)query { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:2]; + dict[PFQueryKeyQuery] = query; + dict[PFQueryKeyKey] = otherKey; + return [self whereKey:key condition:PFQueryKeyDontSelect object:dict]; +} + +- (instancetype)whereRelatedToObject:(PFObject *)parent fromKey:(NSString *)key { + [self.state setRelationConditionWithObject:parent forKey:key]; + return self; +} + +- (void)redirectClassNameForKey:(NSString *)key { + [self.state redirectClassNameForKey:key]; +} + +///-------------------------------------- +#pragma mark - Include +///-------------------------------------- + +- (instancetype)includeKey:(NSString *)key { + [self checkIfCommandIsRunning]; + [self.state includeKey:key]; + return self; +} + +///-------------------------------------- +#pragma mark - Select +///-------------------------------------- + +- (instancetype)selectKeys:(NSArray *)keys { + [self checkIfCommandIsRunning]; + [self.state selectKeys:keys]; + return self; +} + +///-------------------------------------- +#pragma mark - NSPredicate helper methods +///-------------------------------------- + ++ (void)assertKeyPathConstant:(NSComparisonPredicate *)predicate { + PFConsistencyAssert(predicate.leftExpression.expressionType == NSKeyPathExpressionType && + predicate.rightExpression.expressionType == NSConstantValueExpressionType, + @"This predicate must have a key path and a constant. %@", predicate); +} + +// Adds the conditions from an NSComparisonPredicate to a PFQuery. +- (void)whereComparisonPredicate:(NSComparisonPredicate *)predicate { + NSExpression *left = predicate.leftExpression; + NSExpression *right = predicate.rightExpression; + + switch (predicate.predicateOperatorType) { + case NSEqualToPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath equalTo:(right.constantValue ?: [NSNull null])]; + return; + } + case NSNotEqualToPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath notEqualTo:(right.constantValue ?: [NSNull null])]; + return; + } + case NSLessThanPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath lessThan:right.constantValue]; + return; + } + case NSLessThanOrEqualToPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath lessThanOrEqualTo:right.constantValue]; + return; + } + case NSGreaterThanPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath greaterThan:right.constantValue]; + return; + } + case NSGreaterThanOrEqualToPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath greaterThanOrEqualTo:right.constantValue]; + return; + } + case NSInPredicateOperatorType: { + if (left.expressionType == NSKeyPathExpressionType && + right.expressionType == NSConstantValueExpressionType) { + if ([right.constantValue isKindOfClass:[PFQuery class]]) { + // Like "value IN subquery + [self whereKey:left.keyPath matchesQuery:right.constantValue]; + } else { + // Like "value IN %@", @{@1, @2, @3, @4} + [self whereKey:left.keyPath containedIn:right.constantValue]; + } + } else if (left.expressionType == NSKeyPathExpressionType && + right.expressionType == NSAggregateExpressionType && + [right.constantValue isKindOfClass:[NSArray class]]) { + // Like "value IN {1, 2, 3, 4}" + NSArray *constants = right.constantValue; + NSMutableArray *values = [NSMutableArray arrayWithCapacity:constants.count]; + for (NSExpression *expression in constants) { + [values addObject:expression.constantValue]; + } + [self whereKey:left.keyPath containedIn:values]; + } else if (right.expressionType == NSEvaluatedObjectExpressionType && + left.expressionType == NSKeyPathExpressionType) { + // Like "value IN SELF" + [self whereKeyExists:left.keyPath]; + } else { + [NSException raise:NSInternalInconsistencyException + format:@"An IN predicate must have a key path and a constant."]; + } + return; + } + case NSCustomSelectorPredicateOperatorType: { + if (predicate.customSelector != NSSelectorFromString(@"notContainedIn:")) { + [NSException raise:NSInternalInconsistencyException + format:@"Predicates with custom selectors are not supported."]; + } + + if (right.expressionType == NSConstantValueExpressionType && + left.expressionType == NSKeyPathExpressionType) { + if ([right.constantValue isKindOfClass:[PFQuery class]]) { + // Like "NOT (value IN subquery)" + [self whereKey:left.keyPath doesNotMatchQuery:right.constantValue]; + } else { + // Like "NOT (value in %@)", @{@1, @2, @3} + [self whereKey:left.keyPath notContainedIn:right.constantValue]; + } + } else if (left.expressionType == NSKeyPathExpressionType && + right.expressionType == NSAggregateExpressionType && + [right.constantValue isKindOfClass:[NSArray class]]) { + // Like "NOT (value IN {1, 2, 3, 4})" + NSArray *constants = right.constantValue; + NSMutableArray *values = [NSMutableArray arrayWithCapacity:constants.count]; + for (NSExpression *expression in constants) { + [values addObject:expression.constantValue]; + } + [self whereKey:left.keyPath notContainedIn:values]; + } else if (right.expressionType == NSEvaluatedObjectExpressionType && + left.expressionType == NSKeyPathExpressionType) { + // Like "NOT (value IN SELF)" + [self whereKeyDoesNotExist:left.keyPath]; + } else { + [NSException raise:NSInternalInconsistencyException + format:@"A NOT IN predicate must have a key path and a constant array."]; + } + return; + } + case NSBeginsWithPredicateOperatorType: { + [[self class] assertKeyPathConstant:predicate]; + [self whereKey:left.keyPath hasPrefix:right.constantValue]; + return; + } + case NSContainsPredicateOperatorType: { + [NSException raise:NSInternalInconsistencyException + format:@"Regex queries are not supported with " + "[PFQuery queryWithClassName:predicate:]. Please try to structure your " + "data so that you can use an equalTo or containedIn query."]; + } + case NSEndsWithPredicateOperatorType: { + [NSException raise:NSInternalInconsistencyException + format:@"Regex queries are not supported with " + "[PFQuery queryWithClassName:predicate:]. Please try to structure your " + "data so that you can use an equalTo or containedIn query."]; + } + case NSMatchesPredicateOperatorType: { + [NSException raise:NSInternalInconsistencyException + format:@"Regex queries are not supported with " + "[PFQuery queryWithClassName:predicate:]. Please try to structure your " + "data so that you can use an equalTo or containedIn query."]; + } + case NSLikePredicateOperatorType: { + [NSException raise:NSInternalInconsistencyException + format:@"LIKE is not supported by PFQuery."]; + } + default: { + [NSException raise:NSInternalInconsistencyException + format:@"This comparison predicate is not supported. (%zd)", predicate.predicateOperatorType]; + } + } +} + +/*! + Creates a PFQuery with the constraints given by predicate. + This method assumes the predicate has already been normalized. + */ ++ (instancetype)queryWithClassName:(NSString *)className normalizedPredicate:(NSPredicate *)predicate { + if ([predicate isKindOfClass:[NSComparisonPredicate class]]) { + PFQuery *query = [self queryWithClassName:className]; + [query whereComparisonPredicate:(NSComparisonPredicate *)predicate]; + return query; + } else if ([predicate isKindOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; + switch (compound.compoundPredicateType) { + case NSAndPredicateType: { + PFQuery *query = nil; + NSMutableArray *subpredicates = [NSMutableArray array]; + // If there's an OR query in here, we'll start with it. + for (NSPredicate *subpredicate in compound.subpredicates) { + if ([subpredicate isKindOfClass:[NSCompoundPredicate class]] && + ((NSCompoundPredicate *)subpredicate).compoundPredicateType == NSOrPredicateType) { + if (query) { + [NSException raise:NSInternalInconsistencyException + format:@"A query had 2 ORs in an AND after normalization. %@", + predicate]; + } + query = [self queryWithClassName:className normalizedPredicate:subpredicate]; + } else { + [subpredicates addObject:subpredicate]; + } + } + // If there was no OR query, then start with an empty query. + if (!query) { + query = [self queryWithClassName:className]; + } + for (NSPredicate *subpredicate in subpredicates) { + if (![subpredicate isKindOfClass:[NSComparisonPredicate class]]) { + // This should never happen. + [NSException raise:NSInternalInconsistencyException + format:@"A predicate had a non-comparison predicate inside an AND " + "after normalization. %@", predicate]; + } + NSComparisonPredicate *comparison = (NSComparisonPredicate *)subpredicate; + [query whereComparisonPredicate:comparison]; + } + return query; + } + case NSOrPredicateType: { + NSMutableArray *subqueries = [NSMutableArray arrayWithCapacity:compound.subpredicates.count]; + if (compound.subpredicates.count > 4) { + [NSException raise:NSInternalInconsistencyException + format:@"This query is too complex. It had an OR with >4 subpredicates " + "after normalization."]; + } + for (NSPredicate *subpredicate in compound.subpredicates) { + [subqueries addObject:[self queryWithClassName:className normalizedPredicate:subpredicate]]; + } + return [self orQueryWithSubqueries:subqueries]; + } + default: { + // This should never happen. + [NSException raise:NSInternalInconsistencyException + format:@"A predicate had a NOT after normalization. %@", predicate]; + return nil; + } + } + } else { + [NSException raise:NSInternalInconsistencyException format:@"Unknown predicate type."]; + return nil; + } +} + +///-------------------------------------- +#pragma mark - Helpers +///-------------------------------------- + +- (void)checkIfCommandIsRunning { + @synchronized (self) { + if (_cancellationTokenSource) { + [NSException raise:NSInternalInconsistencyException + format:@"This query has an outstanding network connection. You have to wait until it's done."]; + } + } +} + +- (void)markAsRunning:(BFCancellationTokenSource *)source { + [self checkIfCommandIsRunning]; + @synchronized (self) { + _cancellationTokenSource = source; + } +} + +///-------------------------------------- +#pragma mark - Constructors +///-------------------------------------- + ++ (instancetype)queryWithClassName:(NSString *)className { + return [[self alloc] initWithClassName:className]; +} + ++ (instancetype)queryWithClassName:(NSString *)className predicate:(NSPredicate *)predicate { + if (!predicate) { + return [self queryWithClassName:className]; + } + + NSPredicate *normalizedPredicate = [PFQueryUtilities predicateByNormalizingPredicate:predicate]; + return [self queryWithClassName:className normalizedPredicate:normalizedPredicate]; +} + ++ (instancetype)orQueryWithSubqueries:(NSArray *)queries { + NSMutableArray *array = [NSMutableArray array]; + NSString *className = nil; + for (id object in queries) { + PFParameterAssert([object isKindOfClass:[PFQuery class]], + @"All elements should be instances of `PFQuery` class."); + + PFQuery *query = (PFQuery *)object; + if (!className) { + className = query.parseClassName; + } else { + PFParameterAssert([query.parseClassName isEqualToString:className], + @"All sub queries of an `or` query should be on the same class."); + } + + [array addObject:query]; + } + PFQuery *query = [self queryWithClassName:className]; + [query.state setEqualityConditionWithObject:array forKey:PFQueryKeyOr]; + return query; +} + +///-------------------------------------- +#pragma mark - Get with objectId +///-------------------------------------- + ++ (PFObject *)getObjectOfClass:(NSString *)objectClass objectId:(NSString *)objectId { + return [self getObjectOfClass:objectClass objectId:objectId error:nil]; +} + ++ (PFObject *)getObjectOfClass:(NSString *)objectClass + objectId:(NSString *)objectId + error:(NSError **)error { + PFQuery *query = [self queryWithClassName:objectClass]; + return [query getObjectWithId:objectId error:error]; +} + +// TODO (hallucinogen): we may want to remove this in 2.0 since we can just use the static counterpart +- (PFObject *)getObjectWithId:(NSString *)objectId { + return [self getObjectWithId:objectId error:nil]; +} + +- (PFObject *)getObjectWithId:(NSString *)objectId error:(NSError **)error { + return [[self getObjectInBackgroundWithId:objectId] waitForResult:error]; +} + +- (BFTask *)getObjectInBackgroundWithId:(NSString *)objectId { + if ([objectId length] == 0) { + return [BFTask taskWithResult:nil]; + } + + PFConsistencyAssert(self.state.cachePolicy != kPFCachePolicyCacheThenNetwork, + @"kPFCachePolicyCacheThenNetwork can only be used with methods that have a callback."); + return [self _getObjectWithIdAsync:objectId cachePolicy:self.state.cachePolicy after:nil]; +} + +- (void)getObjectInBackgroundWithId:(NSString *)objectId block:(PFObjectResultBlock)block { + @synchronized (self) { + if (!self.state.queriesLocalDatastore && self.state.cachePolicy == kPFCachePolicyCacheThenNetwork) { + BFTask *cacheTask = [[self _getObjectWithIdAsync:objectId + cachePolicy:kPFCachePolicyCacheOnly + after:nil] thenCallBackOnMainThreadAsync:block]; + [[self _getObjectWithIdAsync:objectId + cachePolicy:kPFCachePolicyNetworkOnly + after:cacheTask] thenCallBackOnMainThreadAsync:block]; + } else { + [[self getObjectInBackgroundWithId:objectId] thenCallBackOnMainThreadAsync:block]; + } + } +} + +- (void)getObjectInBackgroundWithId:(NSString *)objectId target:(id)target selector:(SEL)selector { + [self getObjectInBackgroundWithId:objectId block:^(PFObject *object, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:object object:error]; + }]; +} + +- (BFTask *)_getObjectWithIdAsync:(NSString *)objectId cachePolicy:(PFCachePolicy)cachePolicy after:(BFTask *)task { + self.limit = 1; + self.skip = 0; + [self.state removeAllConditions]; + [self.state setEqualityConditionWithObject:objectId forKey:@"objectId"]; + + PFQueryState *state = [self _queryStateCopyWithCachePolicy:cachePolicy]; + return [[self _findObjectsAsyncForQueryState:state + after:task] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *objects = task.result; + if (objects.count == 0) { + return [BFTask taskWithError:[PFQueryUtilities objectNotFoundError]]; + } + + return [BFTask taskWithResult:objects.lastObject]; + }]; +} + +///-------------------------------------- +#pragma mark - Get Users (Deprecated) +///-------------------------------------- + ++ (PFUser *)getUserObjectWithId:(NSString *)objectId { + return [self getUserObjectWithId:objectId error:nil]; +} + ++ (PFUser *)getUserObjectWithId:(NSString *)objectId error:(NSError **)error { + PFQuery *query = [PFUser query]; + PFUser *object = (PFUser *)[query getObjectWithId:objectId error:error]; + + return object; +} + ++ (instancetype)queryForUser { + return [PFUser query]; +} + +///-------------------------------------- +#pragma mark - Find Objects +///-------------------------------------- + +- (NSArray *)findObjects { + return [self findObjects:nil]; +} + +- (NSArray *)findObjects:(NSError **)error { + return [[self findObjectsInBackground] waitForResult:error]; +} + +- (BFTask *)findObjectsInBackground { + PFQueryState *state = [self _queryStateCopy]; + + PFConsistencyAssert(state.cachePolicy != kPFCachePolicyCacheThenNetwork, + @"kPFCachePolicyCacheThenNetwork can only be used with methods that have a callback."); + return [self _findObjectsAsyncForQueryState:state after:nil]; +} + +- (void)findObjectsInBackgroundWithBlock:(PFQueryArrayResultBlock)block { + @synchronized (self) { + if (!self.state.queriesLocalDatastore && self.state.cachePolicy == kPFCachePolicyCacheThenNetwork) { + PFQueryState *cacheQueryState = [self _queryStateCopyWithCachePolicy:kPFCachePolicyCacheOnly]; + BFTask *cacheTask = [[self _findObjectsAsyncForQueryState:cacheQueryState + after:nil] thenCallBackOnMainThreadAsync:block]; + + PFQueryState *remoteQueryState = [self _queryStateCopyWithCachePolicy:kPFCachePolicyNetworkOnly]; + [[self _findObjectsAsyncForQueryState:remoteQueryState + after:cacheTask] thenCallBackOnMainThreadAsync:block]; + } else { + [[self findObjectsInBackground] thenCallBackOnMainThreadAsync:block]; + } + } +} + +- (void)findObjectsInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:objects object:error]; + }]; +} + +- (BFTask *)_findObjectsAsyncForQueryState:(PFQueryState *)queryState after:(BFTask *)previous { + BFCancellationTokenSource *cancellationTokenSource = _cancellationTokenSource; + if (!previous) { + cancellationTokenSource = [BFCancellationTokenSource cancellationTokenSource]; + [self markAsRunning:cancellationTokenSource]; + } + + BFTask *start = (previous ?: [BFTask taskWithResult:nil]); + + [self _validateQueryState]; + @weakify(self); + return [[[start continueWithBlock:^id(BFTask *task) { + @strongify(self); + return [[self class] _getCurrentUserForQueryState:queryState]; + }] continueWithBlock:^id(BFTask *task) { + @strongify(self); + PFUser *user = task.result; + return [[[self class] queryController] findObjectsAsyncForQueryState:queryState + withCancellationToken:cancellationTokenSource.token + user:user]; + }] continueWithBlock:^id(BFTask *task) { + @strongify(self); + if (!self) { + return task; + } + @synchronized (self) { + if (_cancellationTokenSource == cancellationTokenSource) { + _cancellationTokenSource = nil; + } + } + return task; + }]; +} + +///-------------------------------------- +#pragma mark - Get Object +///-------------------------------------- + +- (PFObject *)getFirstObject { + return [self getFirstObject:nil]; +} + +- (PFObject *)getFirstObject:(NSError **)error { + return [[self getFirstObjectInBackground] waitForResult:error]; +} + +- (BFTask *)getFirstObjectInBackground { + PFConsistencyAssert(self.state.cachePolicy != kPFCachePolicyCacheThenNetwork, + @"kPFCachePolicyCacheThenNetwork can only be used with methods that have a callback."); + return [self _getFirstObjectAsyncWithCachePolicy:self.state.cachePolicy after:nil]; +} + +- (void)getFirstObjectInBackgroundWithBlock:(PFObjectResultBlock)block { + @synchronized (self) { + if (!self.state.queriesLocalDatastore && self.state.cachePolicy == kPFCachePolicyCacheThenNetwork) { + BFTask *cacheTask = [[self _getFirstObjectAsyncWithCachePolicy:kPFCachePolicyCacheOnly + after:nil] thenCallBackOnMainThreadAsync:block]; + [[self _getFirstObjectAsyncWithCachePolicy:kPFCachePolicyNetworkOnly + after:cacheTask] thenCallBackOnMainThreadAsync:block]; + } else { + [[self getFirstObjectInBackground] thenCallBackOnMainThreadAsync:block]; + } + } +} + +- (void)getFirstObjectInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self getFirstObjectInBackgroundWithBlock:^(PFObject *result, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:result object:error]; + }]; +} + +- (BFTask *)_getFirstObjectAsyncWithCachePolicy:(PFCachePolicy)cachePolicy after:(BFTask *)task { + self.limit = 1; + + PFQueryState *state = [self _queryStateCopyWithCachePolicy:cachePolicy]; + return [[self _findObjectsAsyncForQueryState:state after:task] continueWithSuccessBlock:^id(BFTask *task) { + NSArray *objects = task.result; + if (objects.count == 0) { + return [BFTask taskWithError:[PFQueryUtilities objectNotFoundError]]; + } + + return [BFTask taskWithResult:objects.lastObject]; + }]; +} + +///-------------------------------------- +#pragma mark - Count Objects +///-------------------------------------- + +- (NSInteger)countObjects { + return [self countObjects:nil]; +} + +- (NSInteger)countObjects:(NSError **)error { + NSNumber *count = [[self countObjectsInBackground] waitForResult:error]; + if (!count) { + // TODO: (nlutsenko) It's really weird that we are inconsistent in sync vs async methods. + // Leaving for now since some devs might be relying on this. + return -1; + } + + return [count integerValue]; +} + +- (BFTask *)countObjectsInBackground { + PFConsistencyAssert(self.state.cachePolicy != kPFCachePolicyCacheThenNetwork, + @"kPFCachePolicyCacheThenNetwork can only be used with methods that have a callback."); + return [self _countObjectsAsyncForQueryState:[self _queryStateCopy] after:nil]; +} + +- (void)countObjectsInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self countObjectsInBackgroundWithBlock:^(int number, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(number) object:error]; + }]; +} + +- (void)countObjectsInBackgroundWithBlock:(PFIntegerResultBlock)block { + PFIdResultBlock callback = nil; + if (block) { + callback = ^(id result, NSError *error) { + block([result intValue], error); + }; + } + + @synchronized (self) { + if (!self.state.queriesLocalDatastore && self.state.cachePolicy == kPFCachePolicyCacheThenNetwork) { + PFQueryState *cacheQueryState = [self _queryStateCopyWithCachePolicy:kPFCachePolicyCacheOnly]; + BFTask *cacheTask = [[self _countObjectsAsyncForQueryState:cacheQueryState + after:nil] thenCallBackOnMainThreadAsync:callback]; + + PFQueryState *remoteQueryState = [self _queryStateCopyWithCachePolicy:kPFCachePolicyNetworkOnly]; + [[self _countObjectsAsyncForQueryState:remoteQueryState + after:cacheTask] thenCallBackOnMainThreadAsync:callback]; + } else { + [[self countObjectsInBackground] thenCallBackOnMainThreadAsync:callback]; + } + } +} + +- (BFTask *)_countObjectsAsyncForQueryState:(PFQueryState *)queryState after:(BFTask *)previousTask { + BFCancellationTokenSource *cancellationTokenSource = _cancellationTokenSource; + if (!previousTask) { + cancellationTokenSource = [BFCancellationTokenSource cancellationTokenSource]; + [self markAsRunning:cancellationTokenSource]; + } + + BFTask *start = (previousTask ?: [BFTask taskWithResult:nil]); + + [self _validateQueryState]; + @weakify(self); + return [[[start continueWithBlock:^id(BFTask *task) { + return [[self class] _getCurrentUserForQueryState:queryState]; + }] continueWithBlock:^id(BFTask *task) { + @strongify(self); + PFUser *user = task.result; + return [[[self class] queryController] countObjectsAsyncForQueryState:queryState + withCancellationToken:cancellationTokenSource.token + user:user]; + }] continueWithBlock:^id(BFTask *task) { + @synchronized(self) { + if (_cancellationTokenSource == cancellationTokenSource) { + _cancellationTokenSource = nil; + } + } + return task; + }]; +} + +///-------------------------------------- +#pragma mark - Cancel +///-------------------------------------- + +- (void)cancel { + @synchronized (self) { + if (_cancellationTokenSource) { + [_cancellationTokenSource cancel]; + _cancellationTokenSource = nil; + } + } +} + +///-------------------------------------- +#pragma mark - NSCopying +///-------------------------------------- + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithState:self.state]; +} + +///-------------------------------------- +#pragma mark NSObject +///-------------------------------------- + +- (NSUInteger)hash { + return [self.state hash]; +} + +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[PFQuery class]]) { + return NO; + } + + return [self.state isEqual:((PFQuery *)object).state]; +} + +///-------------------------------------- +#pragma mark - Caching +///-------------------------------------- + +- (BOOL)hasCachedResult { + return [[[self class] queryController] hasCachedResultForQueryState:self.state + sessionToken:[PFUser currentSessionToken]]; +} + +- (void)clearCachedResult { + [[[self class] queryController] clearCachedResultForQueryState:self.state + sessionToken:[PFUser currentSessionToken]]; +} + ++ (void)clearAllCachedResults { + [[self queryController] clearAllCachedResults]; +} + +///-------------------------------------- +#pragma mark - Check Pinning Status +///-------------------------------------- + +/*! + If `enabled` is YES, raise an exception if OfflineStore is not enabled. If `enabled` is NO, raise + an exception if OfflineStore is enabled. + */ +- (void)_checkPinningEnabled:(BOOL)enabled { + BOOL loaded = [Parse _currentManager].offlineStoreLoaded; + if (enabled) { + PFConsistencyAssert(loaded, @"Method requires Pinning enabled."); + } else { + PFConsistencyAssert(!loaded, @"Method not allowed when Pinning is enabled."); + } +} + +///-------------------------------------- +#pragma mark - Query Source +///-------------------------------------- + +- (instancetype)fromLocalDatastore { + return [self fromPinWithName:nil]; +} + +- (instancetype)fromPin { + return [self fromPinWithName:PFObjectDefaultPin]; +} + +- (instancetype)fromPinWithName:(NSString *)name { + [self _checkPinningEnabled:YES]; + [self checkIfCommandIsRunning]; + + self.state.queriesLocalDatastore = YES; + self.state.localDatastorePinName = [name copy]; + + return self; +} + +- (instancetype)ignoreACLs { + [self _checkPinningEnabled:YES]; + [self checkIfCommandIsRunning]; + + self.state.shouldIgnoreACLs = YES; + + return self; +} + +///-------------------------------------- +#pragma mark - Query State +///-------------------------------------- + +- (PFQueryState *)_queryStateCopy { + return [self.state copy]; +} + +- (PFQueryState *)_queryStateCopyWithCachePolicy:(PFCachePolicy)cachePolicy { + PFMutableQueryState *state = [self.state mutableCopy]; + state.cachePolicy = cachePolicy; + return state; +} + +- (void)_validateQueryState { + PFConsistencyAssert(self.state.queriesLocalDatastore || !self.state.shouldIgnoreACLs, + @"`ignoreACLs` can only be used with Local Datastore queries."); +} + +///-------------------------------------- +#pragma mark - Query Controller +///-------------------------------------- + ++ (PFQueryController *)queryController { + return [Parse _currentManager].coreManager.queryController; +} + +///-------------------------------------- +#pragma mark - User +///-------------------------------------- + ++ (BFTask *)_getCurrentUserForQueryState:(PFQueryState *)state { + if (state.shouldIgnoreACLs) { + return [BFTask taskWithResult:nil]; + } + return [[Parse _currentManager].coreManager.currentUserController getCurrentObjectAsync]; +} + +@end diff --git a/Pods/Parse/Parse/PFRelation.h b/Pods/Parse/Parse/PFRelation.h new file mode 100644 index 0000000..af64ff9 --- /dev/null +++ b/Pods/Parse/Parse/PFRelation.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFRelation` class that is used to access all of the children of a many-to-many relationship. + Each instance of `PFRelation` is associated with a particular parent object and key. + */ +@interface PFRelation : NSObject + +/*! + @abstract The name of the class of the target child objects. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy) NSString *targetClass; + +///-------------------------------------- +/// @name Accessing Objects +///-------------------------------------- + +/*! + @abstract Returns a object that can be used to get objects in this relation. + */ +- (PF_NULLABLE PFQuery *)query; + +///-------------------------------------- +/// @name Modifying Relations +///-------------------------------------- + +/*! + @abstract Adds a relation to the passed in object. + + @param object A object to add relation to. + */ +- (void)addObject:(PFObject *)object; + +/*! + @abstract Removes a relation to the passed in object. + + @param object A object to add relation to. + */ +- (void)removeObject:(PFObject *)object; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFRelation.m b/Pods/Parse/Parse/PFRelation.m new file mode 100644 index 0000000..a6fdb68 --- /dev/null +++ b/Pods/Parse/Parse/PFRelation.m @@ -0,0 +1,236 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRelation.h" +#import "PFRelationPrivate.h" + +#import + +#import "PFAssert.h" +#import "PFFieldOperation.h" +#import "PFInternalUtils.h" +#import "PFMacros.h" +#import "PFMutableRelationState.h" +#import "PFObjectPrivate.h" +#import "PFQueryPrivate.h" + +NSString *const PFRelationKeyClassName = @"className"; +NSString *const PFRelationKeyType = @"__type"; +NSString *const PFRelationKeyObjects = @"objects"; + +@interface PFRelation () { + // + // Use this queue as follows: + // Because state is defined as an atomic property, there's no need to use the queue if you're only reading from + // self.state once during the method. + // + // If you ever need to use self.state more than once, either take a copy at the top of the function, or use a + // dispatch_sync block. + // + // If you are ever changing the state variable, you should use dispatch_sync. + // + dispatch_queue_t _stateAccessQueue; +} + +@property (atomic, copy) PFMutableRelationState *state; + +@end + +@implementation PFRelation + +@dynamic targetClass; + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + + _stateAccessQueue = dispatch_queue_create("com.parse.relation.state.access", DISPATCH_QUEUE_SERIAL); + _state = [[PFMutableRelationState alloc] init]; + + return self; +} + +- (instancetype)initWithParent:(PFObject *)newParent key:(NSString *)newKey { + self = [self init]; + if (!self) return nil; + + _state.parent = newParent; + _state.key = newKey; + + return self; +} + +- (instancetype)initWithTargetClass:(NSString *)newTargetClass { + self = [self init]; + if (!self) return nil; + + _state.targetClass = newTargetClass; + + return self; +} + +- (instancetype)initFromDictionary:(NSDictionary *)dictionary withDecoder:(PFDecoder *)decoder { + self = [self init]; + if (!self) return nil; + + NSArray *array = dictionary[PFRelationKeyObjects]; + NSMutableSet *known = [[NSMutableSet alloc] initWithCapacity:array.count]; + + // Decode the result + for (id encodedObject in array) { + [known addObject:[decoder decodeObject:encodedObject]]; + } + + _state.targetClass = dictionary[PFRelationKeyClassName]; + [_state.knownObjects setSet:known]; + + return self; +} + ++ (PFRelation *)relationForObject:(PFObject *)parent forKey:(NSString *)key { + return [[PFRelation alloc] initWithParent:parent key:key]; +} + ++ (PFRelation *)relationWithTargetClass:(NSString *)targetClass { + return [[PFRelation alloc] initWithTargetClass:targetClass]; +} + ++ (PFRelation *)relationFromDictionary:(NSDictionary *)dictionary withDecoder:(PFDecoder *)decoder { + return [[PFRelation alloc] initFromDictionary:dictionary withDecoder:decoder]; +} + +- (void)ensureParentIs:(PFObject *)someParent andKeyIs:(NSString *)someKey { + pf_sync_with_throw(_stateAccessQueue, ^{ + __strong PFObject *sparent = self.state.parent; + + if (!sparent) { + sparent = self.state.parent = someParent; + } + + if (!self.state.key) { + self.state.key = someKey; + } + + PFConsistencyAssert(sparent == someParent, + @"Internal error. One PFRelation retrieved from two different PFObjects."); + + PFConsistencyAssert([self.state.key isEqualToString:someKey], + @"Internal error. One PFRelation retrieved from two different keys."); + }); +} + +- (NSString *)description { + PFRelationState *state = [self.state copy]; + + return [NSString stringWithFormat:@"<%@: %p, %p.%@ -> %@>", + [self class], + self, + state.parent, + state.key, + state.targetClass]; +} + +- (PFQuery *)query { + PFRelationState *state = [self.state copy]; + __strong PFObject *sparent = state.parent; + + PFQuery *query = nil; + if (state.targetClass) { + query = [PFQuery queryWithClassName:state.targetClass]; + } else { + query = [PFQuery queryWithClassName:state.parentClassName]; + [query redirectClassNameForKey:state.key]; + } + if (sparent) { + [query whereRelatedToObject:sparent fromKey:state.key]; + } else if (state.parentClassName) { + PFObject *object = [PFObject objectWithoutDataWithClassName:state.parentClassName + objectId:state.parentObjectId]; + [query whereRelatedToObject:object fromKey:state.key]; + } + + return query; +} + +- (NSString *)targetClass { + return self.state.targetClass; +} + +- (void)setTargetClass:(NSString *)targetClass { + dispatch_sync(_stateAccessQueue, ^{ + self.state.targetClass = targetClass; + }); +} + +- (void)addObject:(PFObject *)object { + pf_sync_with_throw(_stateAccessQueue, ^{ + PFRelationState *state = self.state; + + PFRelationOperation *op = [PFRelationOperation addRelationToObjects:@[ object ]]; + [state.parent performOperation:op forKey:state.key]; + + self.state.targetClass = op.targetClass; + [self.state.knownObjects addObject:object]; + }); +} + +- (void)removeObject:(PFObject *)object { + pf_sync_with_throw(_stateAccessQueue, ^{ + PFRelationState *state = self.state; + + PFRelationOperation *op = [PFRelationOperation removeRelationToObjects:@[ object ]]; + [state.parent performOperation:op forKey:state.key]; + + self.state.targetClass = op.targetClass; + [self.state.knownObjects removeObject:object]; + }); +} + +- (NSDictionary *)encodeIntoDictionary { + PFRelationState *state = [self.state copy]; + NSMutableArray *encodedObjects = [NSMutableArray arrayWithCapacity:state.knownObjects.count]; + + for (PFObject *knownObject in state.knownObjects) { + [encodedObjects addObject:[[PFPointerObjectEncoder objectEncoder] encodeObject:knownObject]]; + } + + return @{ + PFRelationKeyType : @"Relation", + PFRelationKeyClassName : state.targetClass, + PFRelationKeyObjects : encodedObjects + }; +} + +/*! + Returns true if and only if this object was ever known to be in the relation. + This is used for offline caching. + */ +- (BOOL)_hasKnownObject:(PFObject *)object { + __block BOOL results = NO; + + dispatch_sync(_stateAccessQueue, ^{ + results = [self.state.knownObjects containsObject:object]; + }); + + return results; +} + +- (void)_addKnownObject:(PFObject *)object { + dispatch_sync(_stateAccessQueue, ^{ + [self.state.knownObjects addObject:object]; + }); +} + +- (void)_removeKnownObject:(PFObject *)object { + dispatch_sync(_stateAccessQueue, ^{ + [self.state.knownObjects removeObject:object]; + }); +} + +@end diff --git a/Pods/Parse/Parse/PFRole.h b/Pods/Parse/Parse/PFRole.h new file mode 100644 index 0000000..18d21c9 --- /dev/null +++ b/Pods/Parse/Parse/PFRole.h @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `PFRole` class represents a Role on the Parse server. + `PFRoles` represent groupings of objects for the purposes of granting permissions + (e.g. specifying a for a ). + Roles are specified by their sets of child users and child roles, + all of which are granted any permissions that the parent role has. + + Roles must have a name (which cannot be changed after creation of the role), and must specify an ACL. + */ +@interface PFRole : PFObject + +///-------------------------------------- +/// @name Creating a New Role +///-------------------------------------- + +/*! + @abstract Constructs a new `PFRole` with the given name. + If no default ACL has been specified, you must provide an ACL for the role. + + @param name The name of the Role to create. + */ +- (instancetype)initWithName:(NSString *)name; + +/*! + @abstract Constructs a new `PFRole` with the given name. + + @param name The name of the Role to create. + @param acl The ACL for this role. Roles must have an ACL. + */ +- (instancetype)initWithName:(NSString *)name acl:(PF_NULLABLE PFACL *)acl; + +/*! + @abstract Constructs a new `PFRole` with the given name. + + @discussion If no default ACL has been specified, you must provide an ACL for the role. + + @param name The name of the Role to create. + */ ++ (instancetype)roleWithName:(NSString *)name; + +/*! + @abstract Constructs a new `PFRole` with the given name. + + @param name The name of the Role to create. + @param acl The ACL for this role. Roles must have an ACL. + */ ++ (instancetype)roleWithName:(NSString *)name acl:(PF_NULLABLE PFACL *)acl; + +///-------------------------------------- +/// @name Role-specific Properties +///-------------------------------------- + +/*! + @abstract Gets or sets the name for a role. + + @discussion This value must be set before the role has been saved to the server, + and cannot be set once the role has been saved. + + @warning A role's name can only contain alphanumeric characters, `_`, `-`, and spaces. + */ +@property (nonatomic, copy) NSString *name; + +/*! + @abstract Gets the for the objects that are direct children of this role. + + @discussion These users are granted any privileges that this role has been granted + (e.g. read or write access through ACLs). You can add or remove users from + the role through this relation. + */ +@property (nonatomic, strong, readonly) PFRelation *users; + +/*! + @abstract Gets the for the `PFRole` objects that are direct children of this role. + + @discussion These roles' users are granted any privileges that this role has been granted + (e.g. read or write access through ACLs). You can add or remove child roles + from this role through this relation. + */ +@property (nonatomic, strong, readonly) PFRelation *roles; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFRole.m b/Pods/Parse/Parse/PFRole.m new file mode 100644 index 0000000..bceec77 --- /dev/null +++ b/Pods/Parse/Parse/PFRole.m @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFRole.h" + +#import + +#import "PFAssert.h" +#import "PFObject+Subclass.h" +#import "PFObjectPrivate.h" +#import "PFQuery.h" + +@implementation PFRole + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithName:(NSString *)name { + return [self initWithName:name acl:nil]; +} + +- (instancetype)initWithName:(NSString *)name acl:(PFACL *)acl { + self = [super init]; + if (!self) return nil; + + self.name = name; + self.ACL = acl; + + return self; +} + ++ (instancetype)roleWithName:(NSString *)name { + return [[self alloc] initWithName:name]; +} + ++ (instancetype)roleWithName:(NSString *)name acl:(PFACL *)acl { + return [[self alloc] initWithName:name acl:acl]; +} + +///-------------------------------------- +#pragma mark - Role-specific Properties +///-------------------------------------- + +@dynamic name; + +// Dynamic synthesizers would use objectForKey, not relationForKey +- (PFRelation *)roles { + return [self relationForKey:@keypath(PFRole, roles)]; +} + +- (PFRelation *)users { + return [self relationForKey:@keypath(PFRole, users)]; +} + +///-------------------------------------- +#pragma mark - PFObject Overrides +///-------------------------------------- + +- (void)setObject:(id)object forKey:(NSString *)key { + if ([key isEqualToString:@keypath(PFRole, name)]) { + PFConsistencyAssert(!self.objectId, @"A role's name can only be set before it has been saved."); + PFParameterAssert([object isKindOfClass:[NSString class]], @"A role's name must be an NSString."); + PFParameterAssert([object rangeOfString:@"^[0-9a-zA-Z_\\- ]+$" options:NSRegularExpressionSearch].location != NSNotFound, + @"A role's name can only contain alphanumeric characters, _, -, and spaces."); + } + [super setObject:object forKey:key]; +} + +- (BFTask *)saveInBackground { + PFConsistencyAssert(self.objectId || self.name, @"New roles must specify a name."); + return [super saveInBackground]; +} + +// Validates a class name. We override this to only allow the role class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[self parseClassName]], + @"Cannot initialize a PFRole with a custom class name."); +} + ++ (NSString *)parseClassName { + return @"_Role"; +} + +@end diff --git a/Pods/Parse/Parse/PFSession.h b/Pods/Parse/Parse/PFSession.h new file mode 100644 index 0000000..3b5c00c --- /dev/null +++ b/Pods/Parse/Parse/PFSession.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +@class PFSession; + +typedef void(^PFSessionResultBlock)(PFSession *PF_NULLABLE_S session, NSError *PF_NULLABLE_S error); + +/*! + `PFSession` is a local representation of a session. + This class is a subclass of a , + and retains the same functionality as any other subclass of . + */ +@interface PFSession : PFObject + +/*! + @abstract The session token string for this session. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy, readonly) NSString *sessionToken; + +/*! + *Asynchronously* fetches a `PFSession` object related to the current user. + + @returns A task that is `completed` with an instance of `PFSession` class or is `faulted` if the operation fails. + */ ++ (BFTask PF_GENERIC(PFSession *)*)getCurrentSessionInBackground; + +/*! + *Asynchronously* fetches a `PFSession` object related to the current user. + + @param block The block to execute when the operation completes. + It should have the following argument signature: `^(PFSession *session, NSError *error)`. + */ ++ (void)getCurrentSessionInBackgroundWithBlock:(PF_NULLABLE PFSessionResultBlock)block; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFSession.m b/Pods/Parse/Parse/PFSession.m new file mode 100644 index 0000000..a4af8e8 --- /dev/null +++ b/Pods/Parse/Parse/PFSession.m @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFSession.h" + +#import "BFTask+Private.h" +#import "PFAssert.h" +#import "PFCoreManager.h" +#import "PFCurrentUserController.h" +#import "PFObject+Subclass.h" +#import "PFObjectPrivate.h" +#import "PFSessionController.h" +#import "PFUserPrivate.h" +#import "Parse_Private.h" + +static BOOL _PFSessionIsWritablePropertyForKey(NSString *key) { + static NSSet *protectedKeys; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + protectedKeys = [NSSet setWithObjects: + @"sessionToken", + @"restricted", + @"createdWith", + @"installationId", + @"user", + @"expiresAt", nil]; + }); + return ![protectedKeys containsObject:key]; +} + +@implementation PFSession + +@dynamic sessionToken; + +///-------------------------------------- +#pragma mark - PFSubclassing +///-------------------------------------- + ++ (NSString *)parseClassName { + return @"_Session"; +} + +- (BOOL)needsDefaultACL { + return NO; +} + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[PFSession parseClassName]], + @"Cannot initialize a PFSession with a custom class name."); +} + +#pragma mark Get Current Session + ++ (BFTask *)getCurrentSessionInBackground { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) { + NSString *sessionToken = task.result; + return [[self sessionController] getCurrentSessionAsyncWithSessionToken:sessionToken]; + }]; +} + ++ (void)getCurrentSessionInBackgroundWithBlock:(PFSessionResultBlock)block { + [[self getCurrentSessionInBackground] thenCallBackOnMainThreadAsync:block]; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setObject:(id)object forKey:(NSString *)key { + PFParameterAssert(_PFSessionIsWritablePropertyForKey(key), + @"Can't change the '%@' field of a PFSession.", key); + [super setObject:object forKey:key]; +} + +- (void)removeObjectForKey:(NSString *)key { + PFParameterAssert(_PFSessionIsWritablePropertyForKey(key), + @"Can't remove the '%@' field of a PFSession.", key); + [super removeObjectForKey:key]; +} + +- (void)removeObjectsInArray:(NSArray *)objects forKey:(NSString *)key { + PFParameterAssert(_PFSessionIsWritablePropertyForKey(key), + @"Can't remove any object from '%@' field of a PFSession.", key); + [super removeObjectsInArray:objects forKey:key]; +} + +///-------------------------------------- +#pragma mark - Session Controller +///-------------------------------------- + ++ (PFSessionController *)sessionController { + return [Parse _currentManager].coreManager.sessionController; +} + +@end diff --git a/Pods/Parse/Parse/PFSubclassing.h b/Pods/Parse/Parse/PFSubclassing.h new file mode 100644 index 0000000..c07743d --- /dev/null +++ b/Pods/Parse/Parse/PFSubclassing.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +@class PFQuery PF_GENERIC(PFGenericObject : PFObject *); + +PF_ASSUME_NONNULL_BEGIN + +/*! + If a subclass of conforms to `PFSubclassing` and calls , + Parse framework will be able to use that class as the native class for a Parse cloud object. + + Classes conforming to this protocol should subclass and + include `PFObject+Subclass.h` in their implementation file. + This ensures the methods in the Subclass category of are exposed in its subclasses only. + */ +@protocol PFSubclassing + +/*! + @abstract Constructs an object of the most specific class known to implement . + + @discussion This method takes care to help subclasses be subclassed themselves. + For example, `[PFUser object]` returns a by default but will return an + object of a registered subclass instead if one is known. + A default implementation is provided by which should always be sufficient. + + @returns Returns the object that is instantiated. + */ ++ (instancetype)object; + +/*! + @abstract Creates a reference to an existing PFObject for use in creating associations between PFObjects. + + @discussion Calling <[PFObject isDataAvailable]> on this object will return `NO` + until <[PFObject fetchIfNeeded]> has been called. No network request will be made. + A default implementation is provided by PFObject which should always be sufficient. + + @param objectId The object id for the referenced object. + + @returns A new without data. + */ ++ (instancetype)objectWithoutDataWithObjectId:(PF_NULLABLE NSString *)objectId; + +/*! + @abstract The name of the class as seen in the REST API. + */ ++ (NSString *)parseClassName; + +/*! + @abstract Create a query which returns objects of this type. + + @discussion A default implementation is provided by which should always be sufficient. + */ ++ (PF_NULLABLE PFQuery *)query; + +/*! + @abstract Returns a query for objects of this type with a given predicate. + + @discussion A default implementation is provided by which should always be sufficient. + + @param predicate The predicate to create conditions from. + + @returns An instance of . + + @see [PFQuery queryWithClassName:predicate:] + */ ++ (PF_NULLABLE PFQuery *)queryWithPredicate:(PF_NULLABLE NSPredicate *)predicate; + +/*! + @abstract Lets Parse know this class should be used to instantiate all objects with class type . + + @warning This method must be called before <[Parse setApplicationId:clientKey:]> + */ ++ (void)registerSubclass; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFUser.h b/Pods/Parse/Parse/PFUser.h new file mode 100644 index 0000000..3ae63f6 --- /dev/null +++ b/Pods/Parse/Parse/PFUser.h @@ -0,0 +1,519 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +#import +#import +#import + +PF_ASSUME_NONNULL_BEGIN + +typedef void(^PFUserSessionUpgradeResultBlock)(NSError *PF_NULLABLE_S error); +typedef void(^PFUserLogoutResultBlock)(NSError *PF_NULLABLE_S error); + +@class PFQuery PF_GENERIC(PFGenericObject : PFObject *); +@protocol PFUserAuthenticationDelegate; + +/*! + The `PFUser` class is a local representation of a user persisted to the Parse Data. + This class is a subclass of a , and retains the same functionality of a , + but also extends it with various user specific methods, like authentication, signing up, and validation uniqueness. + + Many APIs responsible for linking a `PFUser` with Facebook or Twitter have been deprecated in favor of dedicated + utilities for each social network. See , and for more information. + */ + +@interface PFUser : PFObject + +///-------------------------------------- +/// @name Accessing the Current User +///-------------------------------------- + +/*! + @abstract Gets the currently logged in user from disk and returns an instance of it. + + @returns Returns a `PFUser` that is the currently logged in user. If there is none, returns `nil`. + */ ++ (PF_NULLABLE instancetype)currentUser; + +/*! + @abstract The session token for the `PFUser`. + + @discussion This is set by the server upon successful authentication. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, copy, readonly) NSString *sessionToken; + +/*! + @abstract Whether the `PFUser` was just created from a request. + + @discussion This is only set after a Facebook or Twitter login. + */ +@property (assign, readonly) BOOL isNew; + +/*! + @abstract Whether the user is an authenticated object for the device. + + @discussion An authenticated `PFUser` is one that is obtained via a or method. + An authenticated object is required in order to save (with altered values) or delete it. + + @returns Returns whether the user is authenticated. + */ +- (BOOL)isAuthenticated; + +///-------------------------------------- +/// @name Creating a New User +///-------------------------------------- + +/*! + @abstract Creates a new `PFUser` object. + + @returns Returns a new `PFUser` object. + */ ++ (instancetype)user; + +/*! + @abstract Enables automatic creation of anonymous users. + + @discussion After calling this method, will always have a value. + The user will only be created on the server once the user has been saved, + or once an object with a relation to that user or an ACL that refers to the user has been saved. + + @warning <[PFObject saveEventually]> will not work on if an item being saved has a relation + to an automatic user that has never been saved. + */ ++ (void)enableAutomaticUser; + +/*! + @abstract The username for the `PFUser`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *username; + +/**! + @abstract The password for the `PFUser`. + + @discussion This will not be filled in from the server with the password. + It is only meant to be set. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *password; + +/*! + @abstract The email for the `PFUser`. + */ +@property (PF_NULLABLE_PROPERTY nonatomic, strong) NSString *email; + +/*! + @abstract Signs up the user *synchronously*. + + @discussion This will also enforce that the username isn't already taken. + + @warning Make sure that password and username are set before calling this method. + + @returns Returns `YES` if the sign up was successful, otherwise `NO`. + */ +- (BOOL)signUp PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Signs up the user *synchronously*. + + @discussion This will also enforce that the username isn't already taken. + + @warning Make sure that password and username are set before calling this method. + + @param error Error object to set on error. + + @returns Returns whether the sign up was successful. + */ +- (BOOL)signUp:(NSError **)error; + +/*! + @abstract Signs up the user *asynchronously*. + + @discussion This will also enforce that the username isn't already taken. + + @warning Make sure that password and username are set before calling this method. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask PF_GENERIC(NSNumber *)*)signUpInBackground; + +/*! + @abstract Signs up the user *asynchronously*. + + @discussion This will also enforce that the username isn't already taken. + + @warning Make sure that password and username are set before calling this method. + + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ +- (void)signUpInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract Signs up the user *asynchronously*. + + @discussion This will also enforce that the username isn't already taken. + + @warning Make sure that password and username are set before calling this method. + + @param target Target object for the selector. + @param selector The selector that will be called when the asynchrounous request is complete. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ +- (void)signUpInBackgroundWithTarget:(PF_NULLABLE_S id)target selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Logging In +///-------------------------------------- + +/*! + @abstract Makes a *synchronous* request to login a user with specified credentials. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param username The username of the user. + @param password The password of the user. + + @returns Returns an instance of the `PFUser` on success. + If login failed for either wrong password or wrong username, returns `nil`. + */ ++ (PF_NULLABLE instancetype)logInWithUsername:(NSString *)username + password:(NSString *)password PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Makes a *synchronous* request to login a user with specified credentials. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param username The username of the user. + @param password The password of the user. + @param error The error object to set on error. + + @returns Returns an instance of the `PFUser` on success. + If login failed for either wrong password or wrong username, returns `nil`. + */ ++ (PF_NULLABLE instancetype)logInWithUsername:(NSString *)username + password:(NSString *)password + error:(NSError **)error; + +/*! + @abstract Makes an *asynchronous* request to login a user with specified credentials. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param username The username of the user. + @param password The password of the user. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(__kindof PFUser *)*)logInWithUsernameInBackground:(NSString *)username + password:(NSString *)password; + +/*! + @abstract Makes an *asynchronous* request to login a user with specified credentials. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param username The username of the user. + @param password The password of the user. + @param target Target object for the selector. + @param selector The selector that will be called when the asynchrounous request is complete. + It should have the following signature: `(void)callbackWithUser:(PFUser *)user error:(NSError *)error`. + */ ++ (void)logInWithUsernameInBackground:(NSString *)username + password:(NSString *)password + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +/*! + @abstract Makes an *asynchronous* request to log in a user with specified credentials. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param username The username of the user. + @param password The password of the user. + @param block The block to execute. + It should have the following argument signature: `^(PFUser *user, NSError *error)`. + */ ++ (void)logInWithUsernameInBackground:(NSString *)username + password:(NSString *)password + block:(PF_NULLABLE PFUserResultBlock)block; + +///-------------------------------------- +/// @name Becoming a User +///-------------------------------------- + +/*! + @abstract Makes a *synchronous* request to become a user with the given session token. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param sessionToken The session token for the user. + + @returns Returns an instance of the `PFUser` on success. + If becoming a user fails due to incorrect token, it returns `nil`. + */ ++ (PF_NULLABLE instancetype)become:(NSString *)sessionToken PF_SWIFT_UNAVAILABLE; + +/*! + @abstract Makes a *synchronous* request to become a user with the given session token. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This will also cache the user locally so that calls to will use the latest logged in user. + + @param sessionToken The session token for the user. + @param error The error object to set on error. + + @returns Returns an instance of the `PFUser` on success. + If becoming a user fails due to incorrect token, it returns `nil`. + */ ++ (PF_NULLABLE instancetype)become:(NSString *)sessionToken error:(NSError **)error; + +/*! + @abstract Makes an *asynchronous* request to become a user with the given session token. + + @discussion Returns an instance of the successfully logged in `PFUser`. + This also caches the user locally so that calls to will use the latest logged in user. + + @param sessionToken The session token for the user. + + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(__kindof PFUser *)*)becomeInBackground:(NSString *)sessionToken; + +/*! + @abstract Makes an *asynchronous* request to become a user with the given session token. + + @discussion Returns an instance of the successfully logged in `PFUser`. This also caches the user locally + so that calls to will use the latest logged in user. + + @param sessionToken The session token for the user. + @param block The block to execute. + The block should have the following argument signature: `^(PFUser *user, NSError *error)`. + */ ++ (void)becomeInBackground:(NSString *)sessionToken block:(PF_NULLABLE PFUserResultBlock)block; + +/*! + @abstract Makes an *asynchronous* request to become a user with the given session token. + + @discussion Returns an instance of the successfully logged in `PFUser`. This also caches the user locally + so that calls to will use the latest logged in user. + + @param sessionToken The session token for the user. + @param target Target object for the selector. + @param selector The selector that will be called when the asynchrounous request is complete. + It should have the following signature: `(void)callbackWithUser:(PFUser *)user error:(NSError *)error`. + */ ++ (void)becomeInBackground:(NSString *)sessionToken + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Revocable Session +///-------------------------------------- + +/*! + @abstract Enables revocable sessions and migrates the currentUser session token to use revocable session if needed. + + @discussion This method is required if you want to use APIs + and you application's 'Require Revocable Session' setting is turned off on `http://parse.com` app settings. + After returned `BFTask` completes - class and APIs will be available for use. + + @returns An instance of `BFTask` that is completed when revocable + sessions are enabled and currentUser token is migrated. + */ ++ (BFTask *)enableRevocableSessionInBackground; + +/*! + @abstract Enables revocable sessions and upgrades the currentUser session token to use revocable session if needed. + + @discussion This method is required if you want to use APIs + and legacy sessions are enabled in your application settings on `http://parse.com/`. + After returned `BFTask` completes - class and APIs will be available for use. + + @param block Block that will be called when revocable sessions are enabled and currentUser token is migrated. + */ ++ (void)enableRevocableSessionInBackgroundWithBlock:(PF_NULLABLE PFUserSessionUpgradeResultBlock)block; + +///-------------------------------------- +/// @name Logging Out +///-------------------------------------- + +/*! + @abstract *Synchronously* logs out the currently logged in user on disk. + */ ++ (void)logOut; + +/*! + @abstract *Asynchronously* logs out the currently logged in user. + + @discussion This will also remove the session from disk, log out of linked services + and all future calls to will return `nil`. This is preferrable to using , + unless your code is already running from a background thread. + + @returns An instance of `BFTask`, that is resolved with `nil` result when logging out completes. + */ ++ (BFTask *)logOutInBackground; + +/*! + @abstract *Asynchronously* logs out the currently logged in user. + + @discussion This will also remove the session from disk, log out of linked services + and all future calls to will return `nil`. This is preferrable to using , + unless your code is already running from a background thread. + + @param block A block that will be called when logging out completes or fails. + */ ++ (void)logOutInBackgroundWithBlock:(PF_NULLABLE PFUserLogoutResultBlock)block; + +///-------------------------------------- +/// @name Requesting a Password Reset +///-------------------------------------- + +/*! + @abstract *Synchronously* Send a password reset request for a specified email. + + @discussion If a user account exists with that email, an email will be sent to that address + with instructions on how to reset their password. + + @param email Email of the account to send a reset password request. + + @returns Returns `YES` if the reset email request is successful. `NO` - if no account was found for the email address. + */ ++ (BOOL)requestPasswordResetForEmail:(NSString *)email PF_SWIFT_UNAVAILABLE; + +/*! + @abstract *Synchronously* send a password reset request for a specified email and sets an error object. + + @discussion If a user account exists with that email, an email will be sent to that address + with instructions on how to reset their password. + + @param email Email of the account to send a reset password request. + @param error Error object to set on error. + @returns Returns `YES` if the reset email request is successful. `NO` - if no account was found for the email address. + */ ++ (BOOL)requestPasswordResetForEmail:(NSString *)email error:(NSError **)error; + +/*! + @abstract Send a password reset request asynchronously for a specified email and sets an + error object. If a user account exists with that email, an email will be sent to + that address with instructions on how to reset their password. + @param email Email of the account to send a reset password request. + @returns The task, that encapsulates the work being done. + */ ++ (BFTask PF_GENERIC(NSNumber *)*)requestPasswordResetForEmailInBackground:(NSString *)email; + +/*! + @abstract Send a password reset request *asynchronously* for a specified email. + + @discussion If a user account exists with that email, an email will be sent to that address + with instructions on how to reset their password. + + @param email Email of the account to send a reset password request. + @param block The block to execute. + It should have the following argument signature: `^(BOOL succeeded, NSError *error)`. + */ ++ (void)requestPasswordResetForEmailInBackground:(NSString *)email + block:(PF_NULLABLE PFBooleanResultBlock)block; + +/*! + @abstract Send a password reset request *asynchronously* for a specified email and sets an error object. + + @discussion If a user account exists with that email, an email will be sent to that address + with instructions on how to reset their password. + + @param email Email of the account to send a reset password request. + @param target Target object for the selector. + @param selector The selector that will be called when the asynchronous request is complete. + It should have the following signature: `(void)callbackWithResult:(NSNumber *)result error:(NSError *)error`. + `error` will be `nil` on success and set if there was an error. + `[result boolValue]` will tell you whether the call succeeded or not. + */ ++ (void)requestPasswordResetForEmailInBackground:(NSString *)email + target:(PF_NULLABLE_S id)target + selector:(PF_NULLABLE_S SEL)selector; + +///-------------------------------------- +/// @name Third-party Authentication +///-------------------------------------- + +/*! + @abstract Registers a third party authentication delegate. + + @note This method shouldn't be invoked directly unless developing a third party authentication library. + @see PFUserAuthenticationDelegate + + @param delegate The third party authenticaiton delegate to be registered. + @param authType The name of the type of third party authentication source. + */ ++ (void)registerAuthenticationDelegate:(id)delegate forAuthType:(NSString *)authType; + +/*! + @abstract Logs in a user with third party authentication credentials. + + @note This method shouldn't be invoked directly unless developing a third party authentication library. + @see PFUserAuthenticationDelegate + + @param authType The name of the type of third party authentication source. + @param authData The user credentials of the third party authentication source. + + @returns A `BFTask` that is resolved to `PFUser` when logging in completes. + */ ++ (BFTask PF_GENERIC(PFUser *) *)logInWithAuthTypeInBackground:(NSString *)authType authData:(NSDictionary *)authData; + +/*! + @abstract Links this user to a third party authentication library. + + @note This method shouldn't be invoked directly unless developing a third party authentication library. + @see PFUserAuthenticationDelegate + + @param authType The name of the type of third party authentication source. + @param authData The user credentials of the third party authentication source. + + @returns A `BFTask` that is resolved to `@YES` if linking succeeds. + */ +- (BFTask PF_GENERIC(NSNumber *) *)linkWithAuthTypeInBackground:(NSString *)authType authData:(NSDictionary *)authData; + +/*! + @abstract Unlinks this user from a third party authentication library. + + @note This method shouldn't be invoked directly unless developing a third party authentication library. + @see PFUserAuthenticationDelegate + + @param authType The name of the type of third party authentication source. + + @returns A `BFTask` that is resolved to `@YES` if unlinking succeeds. + */ +- (BFTask PF_GENERIC(NSNumber *) *)unlinkWithAuthTypeInBackground:(NSString *)authType; + +/*! + @abstract Indicates whether this user is linked with a third party authentication library of a specific type. + + @note This method shouldn't be invoked directly unless developing a third party authentication library. + @see PFUserAuthenticationDelegate + + @param authType The name of the type of third party authentication source. + + @returns `YES` if the user is linked with a provider, otherwise `NO`. + */ +- (BOOL)isLinkedWithAuthType:(NSString *)authType; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/PFUser.m b/Pods/Parse/Parse/PFUser.m new file mode 100644 index 0000000..d7a6eac --- /dev/null +++ b/Pods/Parse/Parse/PFUser.m @@ -0,0 +1,1225 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "PFUser.h" +#import "PFUserPrivate.h" + +#import +#import + +#import "BFTask+Private.h" +#import "PFACLPrivate.h" +#import "PFAnonymousAuthenticationProvider.h" +#import "PFAnonymousUtils_Private.h" +#import "PFAssert.h" +#import "PFCommandResult.h" +#import "PFCommandRunning.h" +#import "PFCoreManager.h" +#import "PFCurrentUserController.h" +#import "PFDecoder.h" +#import "PFErrorUtilities.h" +#import "PFFileManager.h" +#import "PFKeychainStore.h" +#import "PFMultiProcessFileLockController.h" +#import "PFMutableUserState.h" +#import "PFObject+Subclass.h" +#import "PFObjectConstants.h" +#import "PFObjectFilePersistenceController.h" +#import "PFObjectPrivate.h" +#import "PFOfflineStore.h" +#import "PFOperationSet.h" +#import "PFQueryPrivate.h" +#import "PFRESTUserCommand.h" +#import "PFSessionUtilities.h" +#import "PFTaskQueue.h" +#import "PFUserAuthenticationController.h" +#import "PFUserConstants.h" +#import "PFUserController.h" +#import "PFUserFileCodingLogic.h" +#import "Parse_Private.h" + +NSString *const PFUserCurrentUserFileName = @"currentUser"; +NSString *const PFUserCurrentUserPinName = @"_currentUser"; +NSString *const PFUserCurrentUserKeychainItemName = @"currentUser"; + +static BOOL _PFUserIsWritablePropertyForKey(NSString *key) { + return ![PFUserSessionTokenRESTKey isEqualToString:key]; +} + +static BOOL _PFUserIsRemovablePropertyForKey(NSString *key) { + return _PFUserIsWritablePropertyForKey(key) && ![PFUserUsernameRESTKey isEqualToString:key]; +} + +@interface PFUser () + +@property (nonatomic, copy) PFUserState *_state; + +@end + +@implementation PFUser (Private) + +static BOOL revocableSessionEnabled_; + +- (void)setDefaultValues { + [super setDefaultValues]; + self.isCurrentUser = NO; +} + +- (BOOL)needsDefaultACL { + return NO; +} + +///-------------------------------------- +#pragma mark - Current User +///-------------------------------------- + +// Returns the session token for the current user. ++ (NSString *)currentSessionToken { + return [[self _getCurrentUserSessionTokenAsync] waitForResult:nil withMainThreadWarning:NO]; +} + ++ (BFTask *)_getCurrentUserSessionTokenAsync { + return [[self currentUserController] getCurrentUserSessionTokenAsync]; +} + +///-------------------------------------- +#pragma mark - PFObject +///-------------------------------------- + +#pragma mark Validation + +- (BFTask PF_GENERIC(PFVoid) *)_validateDeleteAsync { + return [[super _validateDeleteAsync] continueWithSuccessBlock:^id(BFTask PF_GENERIC(PFVoid) *task) { + if (!self.isAuthenticated) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorUserCannotBeAlteredWithoutSession + message:@"User cannot be deleted unless they have been authenticated."]; + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + +- (BFTask PF_GENERIC(PFVoid) *)_validateSaveEventuallyAsync { + return [[super _validateSaveEventuallyAsync] continueWithSuccessBlock:^id(BFTask PF_GENERIC(PFVoid) *task) { + if ([self isDirtyForKey:PFUserPasswordRESTKey]) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorOperationForbidden + message:@"Unable to saveEventually a PFUser with dirty password."]; + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + +#pragma mark Else + +- (NSString *)displayClassName { + if ([self isMemberOfClass:[PFUser class]]) { + return @"PFUser"; + } + return NSStringFromClass([self class]); +} + +// Validates a class name. We override this to only allow the user class name. ++ (void)_assertValidInstanceClassName:(NSString *)className { + PFParameterAssert([className isEqualToString:[PFUser parseClassName]], + @"Cannot initialize a PFUser with a custom class name."); +} + +// Checks the properties on the object before saving. +- (void)_checkSaveParametersWithCurrentUser:(PFUser *)currentUser { + @synchronized ([self lock]) { + PFConsistencyAssert(self.objectId || self.isLazy, + @"User cannot be saved unless they are already signed up. Call signUp first."); + + PFConsistencyAssert([self _isAuthenticatedWithCurrentUser:currentUser] || + [self.objectId isEqualToString:currentUser.objectId], + @"User cannot be saved unless they have been authenticated via logIn or signUp", nil); + } +} + +// Checks the properties on the object before signUp. +- (BFTask *)_validateSignUpAsync { + return [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + NSError *error = nil; + @synchronized (self.lock) { + if (!self.username) { + error = [PFErrorUtilities errorWithCode:kPFErrorUsernameMissing + message:@"Cannot sign up without a username."]; + } else if (!self.password) { + error = [PFErrorUtilities errorWithCode:kPFErrorUserPasswordMissing + message:@"Cannot sign up without a password."]; + } else if (![self isDirty:NO] || self.objectId) { + error = [PFErrorUtilities errorWithCode:kPFErrorUsernameTaken + message:@"Cannot sign up an existing user."]; + } + } + if (error) { + return [BFTask taskWithError:error]; + } + return nil; + }]; +} + +- (NSMutableDictionary *)_convertToDictionaryForSaving:(PFOperationSet *)changes + withObjectEncoder:(PFEncoder *)encoder { + @synchronized ([self lock]) { + NSMutableDictionary *serialized = [super _convertToDictionaryForSaving:changes withObjectEncoder:encoder]; + if ([self.authData count] > 0) { + serialized[PFUserAuthDataRESTKey] = [self.authData copy]; + } + return serialized; + } +} + +- (BFTask *)handleSaveResultAsync:(NSDictionary *)result { + return [[super handleSaveResultAsync:result] continueWithSuccessBlock:^id(BFTask *saveTask) { + if (self.isCurrentUser) { + [self cleanUpAuthData]; + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller saveCurrentObjectAsync:self] continueWithBlock:^id(BFTask *task) { + return saveTask.result; + }]; + } + return saveTask; + }]; +} + +///-------------------------------------- +#pragma mark - Sign Up +///-------------------------------------- + +- (PFRESTCommand *)_currentSignUpCommandForChanges:(PFOperationSet *)changes { + @synchronized ([self lock]) { + NSDictionary *parameters = [self _convertToDictionaryForSaving:changes + withObjectEncoder:[PFPointerObjectEncoder objectEncoder]]; + return [PFRESTUserCommand signUpUserCommandWithParameters:parameters + revocableSession:[[self class] _isRevocableSessionEnabled] + sessionToken:self.sessionToken]; + } +} + +///-------------------------------------- +#pragma mark - Service Login +///-------------------------------------- + +// Constructs the command for user_signup_or_login. This is used for Facebook, Twitter, and other linking services. +- (PFRESTCommand *)_currentServiceLoginCommandForChanges:(PFOperationSet *)changes { + @synchronized ([self lock]) { + NSDictionary *parameters = [self _convertToDictionaryForSaving:changes + withObjectEncoder:[PFPointerObjectEncoder objectEncoder]]; + return [PFRESTUserCommand serviceLoginUserCommandWithParameters:parameters + revocableSession:[[self class] _isRevocableSessionEnabled] + sessionToken:self.sessionToken]; + } +} + +- (BFTask *)_handleServiceLoginCommandResult:(PFCommandResult *)result { + return [BFTask taskFromExecutor:[BFExecutor defaultExecutor] withBlock:^id{ + NSDictionary *resultDictionary = result.result; + return [[self handleSaveResultAsync:resultDictionary] continueWithBlock:^id(BFTask *task) { + BOOL new = (result.httpResponse.statusCode == 201); // 201 means Created + @synchronized (self.lock) { + if (self._state.isNew != new) { + PFMutableUserState *state = [self._state mutableCopy]; + state.isNew = new; + self._state = state; + } + if (resultDictionary) { + self.isLazy = NO; + + // Serialize the object to disk so we can later access it via currentUser + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller saveCurrentObjectAsync:self] continueAsyncWithBlock:^id(BFTask *task) { + [self.saveDelegate invoke:self error:nil]; + return self; + }]; + } + return [BFTask taskWithResult:self]; + } + }]; + }]; +} + +// Override the save result handling with custom user functionality +- (BFTask *)handleSignUpResultAsync:(BFTask *)task { + @synchronized ([self lock]) { + PFCommandResult *commandResult = task.result; + NSDictionary *result = commandResult.result; + BFTask *signUpTask = task; + + // Bail-out early, but still make sure that super class handled the result + if (task.error || task.cancelled || task.exception) { + return [[super handleSaveResultAsync:nil] continueWithBlock:^id(BFTask *task) { + return signUpTask; + }]; + } + __block BOOL saveResult = NO; + return [[[super handleSaveResultAsync:result] continueWithBlock:^id(BFTask *task) { + saveResult = [task.result boolValue]; + if (saveResult) { + @synchronized (self.lock) { + // Save the session information + PFMutableUserState *state = [self._state mutableCopy]; + state.sessionToken = result[PFUserSessionTokenRESTKey]; + state.isNew = YES; + self._state = state; + self.isLazy = NO; + } + } + return signUpTask; + }] continueWithBlock:^id(BFTask *task) { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller saveCurrentObjectAsync:self] continueWithResult:@(saveResult)]; + }]; + } +} + +- (void)cleanUpAuthData { + @synchronized ([self lock]) { + for (NSString *key in [self.authData copy]) { + id linkData = [self.authData objectForKey:key]; + if (!linkData || linkData == [NSNull null]) { + [self.authData removeObjectForKey:key]; + [self.linkedServiceNames removeObject:key]; + + [[[[self class] authenticationController] restoreAuthenticationAsyncWithAuthData:nil + forAuthType:key] waitForResult:nil withMainThreadWarning:NO]; + } + } + } +} + +/*! + Copies special PFUser fields from another user. + */ +- (PFObject *)mergeFromObject:(PFUser *)other { + @synchronized ([self lock]) { + [super mergeFromObject:other]; + + if (self == other) { + // If they point to the same instance, then don't merge. + return self; + } + + PFMutableUserState *state = [self._state mutableCopy]; + state.sessionToken = other.sessionToken; + state.isNew = other._state.isNew; + self._state = state; + + [self.authData removeAllObjects]; + [self.authData addEntriesFromDictionary:other.authData]; + + [self.linkedServiceNames removeAllObjects]; + [self.linkedServiceNames unionSet:other.linkedServiceNames]; + + return self; + } +} + +/* + Merges custom fields from JSON associated with a PFUser: + { + "session_token": string, + "is_new": boolean, + "auth_data": { + "facebook": { + "id": string, + "access_token": string, + "expiration_date": string (represents date) + } + } + } + */ +- (void)_mergeFromServerWithResult:(NSDictionary *)result decoder:(PFDecoder *)decoder completeData:(BOOL)completeData { + @synchronized ([self lock]) { + // save the session token + + PFMutableUserState *state = [self._state mutableCopy]; + + NSString *newSessionToken = result[PFUserSessionTokenRESTKey]; + if (newSessionToken) { + // Save the session token + state.sessionToken = newSessionToken; + } + + self._state = state; + + // Merge the linked service metadata + NSDictionary *newAuthData = [decoder decodeObject:result[PFUserAuthDataRESTKey]]; + if (newAuthData) { + [self.authData removeAllObjects]; + [self.linkedServiceNames removeAllObjects]; + [newAuthData enumerateKeysAndObjectsUsingBlock:^(id key, id linkData, BOOL *stop) { + if (linkData != [NSNull null]) { + [self.authData setObject:linkData forKey:key]; + [self.linkedServiceNames addObject:key]; + [self synchronizeAuthDataWithAuthType:key]; + } else { + [self.authData removeObjectForKey:key]; + [self.linkedServiceNames removeObject:key]; + [self synchronizeAuthDataWithAuthType:key]; + } + }]; + } + + // Strip authData and sessionToken from the data, as those keys are saved in a custom way + NSMutableDictionary *serverData = [result mutableCopy]; + [serverData removeObjectForKey:PFUserSessionTokenRESTKey]; + [serverData removeObjectForKey:PFUserAuthDataRESTKey]; + + // The public fields are handled by the regular mergeFromServer + [super _mergeFromServerWithResult:serverData decoder:decoder completeData:completeData]; + } +} + +- (void)synchronizeAuthDataWithAuthType:(NSString *)authType { + @synchronized ([self lock]) { + if (!self.isCurrentUser) { + return; + } + + NSDictionary *data = self.authData[authType]; + BFTask *restoreTask = [[[self class] authenticationController] restoreAuthenticationAsyncWithAuthData:data + forAuthType:authType]; + [restoreTask waitForResult:nil withMainThreadWarning:NO]; + if (restoreTask.faulted || ![restoreTask.result boolValue]) { // TODO: (nlutsenko) Maybe chain this method? + [self unlinkWithAuthTypeInBackground:authType]; + } + } +} + +- (void)synchronizeAllAuthData { + @synchronized ([self lock]) { + // Ensures that all auth providers have auth data (e.g. access tokens, etc.) that matches this user. + if (self.authData) { + [self.authData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [self synchronizeAuthDataWithAuthType:key]; + }]; + } + } +} + +- (BFTask *)resolveLazinessAsync:(BFTask *)toAwait { + @synchronized ([self lock]) { + if (!self.isLazy) { + return [BFTask taskWithResult:self]; + } + if (self.linkedServiceNames.count == 0) { + // If there are no linked services, treat this like a sign-up. + return [[self signUpAsync:toAwait] continueAsyncWithSuccessBlock:^id(BFTask *task) { + self.isLazy = NO; + return self; + }]; + } + + // Otherwise, treat this as a SignUpOrLogIn + PFRESTCommand *command = [self _currentServiceLoginCommandForChanges:[self unsavedChanges]]; + [self startSave]; + + return [[toAwait continueAsyncWithBlock:^id(BFTask *task) { + return [[Parse _currentManager].commandRunner runCommandAsync:command withOptions:0]; + }] continueAsyncWithBlock:^id(BFTask *task) { + PFCommandResult *result = task.result; + + if (task.error || task.cancelled) { + // If there was an error, we want to roll forward the save changes, but return the original task. + return [[self _handleServiceLoginCommandResult:result] continueAsyncWithBlock:^id(BFTask *unused) { + // Return the original task, instead of the new one (in order to have a proper error) + return task; + }]; + } + + if ([result.httpResponse statusCode] == 201) { + return [self _handleServiceLoginCommandResult:result]; + } else { + // Otherwise, treat this as a fresh login, and switch the current user to the new user. + PFUser *newUser = [[self class] _objectFromDictionary:result.result + defaultClassName:[self parseClassName] + completeData:YES]; + @synchronized ([newUser lock]) { + [newUser startSave]; + return [newUser _handleServiceLoginCommandResult:result]; + } + } + }]; + } +} + +- (BFTask *)_logOutAsyncWithAuthType:(NSString *)authType { + return [[[self class] authenticationController] deauthenticateAsyncWithAuthType:authType]; +} + ++ (instancetype)logInLazyUserWithAuthType:(NSString *)authType authData:(NSDictionary *)authData { + PFUser *user = [self user]; + @synchronized ([user lock]) { + [user setIsCurrentUser:YES]; + user.isLazy = YES; + [user.authData setObject:authData forKey:authType]; + [user.linkedServiceNames addObject:authType]; + } + return user; +} + +- (BFTask *)signUpAsync:(BFTask *)toAwait { + PFUser *currentUser = [[self class] currentUser]; + NSString *token = currentUser.sessionToken; + @synchronized ([self lock]) { + if (self.objectId) { + // For anonymous users, there may be an objectId. Setting the userName + // will have removed the anonymous link and set the value in the authData + // object to [NSNull null], so we can just treat it like a save operation. + if (self.authData[PFAnonymousUserAuthenticationType] == [NSNull null]) { + return [self saveAsync:toAwait]; + } + + // Otherwise, return an error + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorUsernameTaken + message:@"Cannot sign up a user that has already signed up."]; + return [BFTask taskWithError:error]; + } + + // If the operationSetQueue is has operation sets in it, then a save or signUp is in progress. + // If there is a signUp or save already in progress, don't allow another one to start. + if ([self _hasOutstandingOperations]) { + NSError *error = [PFErrorUtilities errorWithCode:kPFErrorUsernameTaken + message:@"Cannot sign up a user that is already signing up."]; + return [BFTask taskWithError:error]; + } + + return [[self _validateSignUpAsync] continueWithSuccessBlock:^id(BFTask *task) { + if (currentUser && [PFAnonymousUtils isLinkedWithUser:currentUser]) { + // self doesn't have any outstanding saves, so we can safely merge its operations + // into the current user. + + PFConsistencyAssert(!isCurrentUser, @"Attempt to merge currentUser with itself."); + + [self checkForChangesToMutableContainers]; + @synchronized ([currentUser lock]) { + NSString *oldUsername = [currentUser.username copy]; + NSString *oldPassword = [currentUser.password copy]; + NSArray *oldAnonymousData = currentUser.authData[PFAnonymousUserAuthenticationType]; + + [currentUser checkForChangesToMutableContainers]; + + // Move the changes to this object over to the currentUser object. + PFOperationSet *selfOperations = operationSetQueue[0]; + [operationSetQueue removeAllObjects]; + [operationSetQueue addObject:[[PFOperationSet alloc] init]]; + for (NSString *key in selfOperations) { + [currentUser setObject:[selfOperations objectForKey:key] forKey:key]; + } + + currentUser->dirty = YES; + currentUser.password = self.password; + currentUser.username = self.username; + + [self rebuildEstimatedData]; + [currentUser rebuildEstimatedData]; + + return [[[[currentUser saveInBackground] continueWithBlock:^id(BFTask *task) { + if (task.error || task.cancelled || task.exception) { + @synchronized ([currentUser lock]) { + if (oldUsername) { + currentUser.username = oldUsername; + } + currentUser.password = oldPassword; + [currentUser restoreAnonymity:oldAnonymousData]; + } + + @synchronized(self.lock) { + [operationSetQueue replaceObjectAtIndex:0 withObject:selfOperations]; + [self rebuildEstimatedData]; + } + } + return task; + }] continueWithSuccessBlock:^id(BFTask *task) { + if ([Parse _currentManager].offlineStoreLoaded) { + return [[Parse _currentManager].offlineStore deleteDataForObjectAsync:currentUser]; + } + return nil; + }] continueWithSuccessBlock:^id(BFTask *task) { + [self mergeFromObject:currentUser]; + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller saveCurrentObjectAsync:self] continueWithResult:@YES]; + }]; + } + } + // Use a nil session token for objects saved during a signup. + BFTask *saveChildren = [self _saveChildrenInBackgroundWithCurrentUser:currentUser sessionToken:token]; + PFOperationSet *changes = [self unsavedChanges]; + [self startSave]; + + return [[[toAwait continueWithBlock:^id(BFTask *task) { + return saveChildren; + }] continueWithSuccessBlock:^id(BFTask *task) { + // We need to construct the signup command lazily, because saving the children + // may change the way the object itself is serialized. + PFRESTCommand *command = [self _currentSignUpCommandForChanges:changes]; + return [[Parse _currentManager].commandRunner runCommandAsync:command + withOptions:PFCommandRunningOptionRetryIfFailed]; + }] continueWithBlock:^id(BFTask *task) { + return [self handleSignUpResultAsync:task]; + }]; + }]; + } +} + +- (void)stripAnonymity { + @synchronized ([self lock]) { + if ([PFAnonymousUtils isLinkedWithUser:self]) { + NSString *authType = PFAnonymousUserAuthenticationType; + + [self.linkedServiceNames removeObject:authType]; + + if (self.objectId) { + self.authData[authType] = [NSNull null]; + } else { + [self.authData removeObjectForKey:authType]; + } + dirty = YES; + } + } +} + +- (void)restoreAnonymity:(id)anonymousData { + @synchronized ([self lock]) { + if (anonymousData && anonymousData != [NSNull null]) { + NSString *authType = PFAnonymousUserAuthenticationType; + [self.linkedServiceNames addObject:authType]; + self.authData[authType] = anonymousData; + } + } +} + +///-------------------------------------- +#pragma mark - Saving +///-------------------------------------- + +- (PFRESTCommand *)_constructSaveCommandForChanges:(PFOperationSet *)changes + sessionToken:(NSString *)token + objectEncoder:(PFEncoder *)encoder { + // If we are curent user - use the latest available session token, as it might have been changed since + // this command was enqueued. + if ([self isCurrentUser]) { + token = self.sessionToken; + } + return [super _constructSaveCommandForChanges:changes + sessionToken:token + objectEncoder:encoder]; +} + +///-------------------------------------- +#pragma mark - REST operations +///-------------------------------------- + +- (void)mergeFromRESTDictionary:(NSDictionary *)object withDecoder:(PFDecoder *)decoder { + @synchronized ([self lock]) { + NSMutableDictionary *restDictionary = [object mutableCopy]; + + PFMutableUserState *state = [self._state mutableCopy]; + if (object[PFUserSessionTokenRESTKey] != nil) { + state.sessionToken = object[PFUserSessionTokenRESTKey]; + [restDictionary removeObjectForKey:PFUserSessionTokenRESTKey]; + } + + if (object[PFUserAuthDataRESTKey] != nil) { + NSDictionary *newAuthData = object[PFUserAuthDataRESTKey]; + [newAuthData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + self.authData[key] = obj; + if (obj != nil) { + [self.linkedServiceNames addObject:key]; + } + [self synchronizeAuthDataWithAuthType:key]; + }]; + + [restDictionary removeObjectForKey:PFUserAuthDataRESTKey]; + } + + self._state = state; + + [super mergeFromRESTDictionary:restDictionary withDecoder:decoder]; + } +} + +- (NSDictionary *)RESTDictionaryWithObjectEncoder:(PFEncoder *)objectEncoder + operationSetUUIDs:(NSArray **)operationSetUUIDs + state:(PFObjectState *)state + operationSetQueue:(NSArray *)queue { + @synchronized (self.lock) { + NSMutableArray *cleanQueue = [queue mutableCopy]; + [queue enumerateObjectsUsingBlock:^(PFOperationSet *operationSet, NSUInteger idx, BOOL *stop) { + // Remove operations for `password` field, to not let it persist to LDS. + if (operationSet[PFUserPasswordRESTKey]) { + operationSet = [operationSet copy]; + [operationSet removeObjectForKey:PFUserPasswordRESTKey]; + + cleanQueue[idx] = operationSet; + } + }]; + return [super RESTDictionaryWithObjectEncoder:objectEncoder + operationSetUUIDs:operationSetUUIDs + state:state + operationSetQueue:cleanQueue]; + } +} + +///-------------------------------------- +#pragma mark - Revocable Session +///-------------------------------------- + ++ (dispatch_queue_t)_revocableSessionSynchronizationQueue { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("com.parse.user.revocableSession", DISPATCH_QUEUE_CONCURRENT); + }); + return queue; +} + ++ (BOOL)_isRevocableSessionEnabled { + __block BOOL value = NO; + dispatch_sync([self _revocableSessionSynchronizationQueue], ^{ + value = revocableSessionEnabled_; + }); + return value; +} + ++ (void)_setRevocableSessionEnabled:(BOOL)enabled { + dispatch_barrier_sync([self _revocableSessionSynchronizationQueue], ^{ + revocableSessionEnabled_ = enabled; + }); +} + ++ (BFTask *)_upgradeToRevocableSessionInBackground { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentUserAsyncWithOptions:0] continueWithSuccessBlock:^id(BFTask *task) { + PFUser *currentUser = task.result; + NSString *sessionToken = currentUser.sessionToken; + + // Bail-out early if session token is already revocable. + if ([PFSessionUtilities isSessionTokenRevocable:sessionToken]) { + return [BFTask taskWithResult:currentUser]; + } + return [currentUser _upgradeToRevocableSessionInBackground]; + }]; +} + +- (BFTask *)_upgradeToRevocableSessionInBackground { + @weakify(self); + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + @strongify(self); + + NSString *token = nil; + @synchronized(self.lock) { + token = self.sessionToken; + } + + // Check session token here as well, to make sure we didn't upgrade the token in between. + if ([PFSessionUtilities isSessionTokenRevocable:token]) { + return [BFTask taskWithResult:self]; + } + + PFRESTCommand *command = [PFRESTUserCommand upgradeToRevocableSessionCommandWithSessionToken:token]; + return [[[Parse _currentManager].commandRunner runCommandAsync:command + withOptions:0] continueWithSuccessBlock:^id(BFTask *task) { + NSDictionary *dictionary = [task.result result]; + PFSession *session = [PFSession _objectFromDictionary:dictionary + defaultClassName:[PFSession parseClassName] + completeData:YES]; + @synchronized(self.lock) { + PFMutableUserState *state = [self._state mutableCopy]; + state.sessionToken = session.sessionToken; + self._state = state; + } + PFCurrentUserController *controller = [[self class] currentUserController]; + return [controller saveCurrentObjectAsync:self]; + }]; + }]; + }]; +} + +///-------------------------------------- +#pragma mark - Data Source +///-------------------------------------- + ++ (PFObjectFileCodingLogic *)objectFileCodingLogic { + return [PFUserFileCodingLogic codingLogic]; +} + ++ (PFUserAuthenticationController *)authenticationController { + return [Parse _currentManager].coreManager.userAuthenticationController; +} + ++ (PFUserController *)userController { + return [Parse _currentManager].coreManager.userController; +} + +@end + +@implementation PFUser + +@dynamic _state; + +// PFUser: +@dynamic username; +@dynamic email; +@dynamic password; + +// PFUser (Private): +@dynamic authData; +@dynamic linkedServiceNames; +@dynamic isLazy; + ++ (NSString *)parseClassName { + return @"_User"; +} + ++ (instancetype)currentUser { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller getCurrentObjectAsync] waitForResult:nil withMainThreadWarning:NO]; +} + +- (BOOL)isCurrentUser { + @synchronized (self.lock) { + return isCurrentUser; + } +} + +- (void)setIsCurrentUser:(BOOL)aBool { + @synchronized (self.lock) { + isCurrentUser = aBool; + } +} + +///-------------------------------------- +#pragma mark - Log In +///-------------------------------------- + ++ (instancetype)logInWithUsername:(NSString *)username password:(NSString *)password { + return [self logInWithUsername:username password:password error:nil]; +} + ++ (instancetype)logInWithUsername:(NSString *)username password:(NSString *)password error:(NSError **)error { + return [[self logInWithUsernameInBackground:username password:password] waitForResult:error]; +} + ++ (BFTask *)logInWithUsernameInBackground:(NSString *)username password:(NSString *)password { + return [[self userController] logInCurrentUserAsyncWithUsername:username + password:password + revocableSession:[self _isRevocableSessionEnabled]]; +} + ++ (void)logInWithUsernameInBackground:(NSString *)username + password:(NSString *)password + block:(PFUserResultBlock)block { + [[self logInWithUsernameInBackground:username password:password] thenCallBackOnMainThreadAsync:block]; +} + ++ (void)logInWithUsernameInBackground:(NSString *)username + password:(NSString *)password + target:(id)target + selector:(SEL)selector { + [self logInWithUsernameInBackground:username password:password block:^(PFUser *user, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:user object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Third-party Authentication +///-------------------------------------- + ++ (void)registerAuthenticationDelegate:(id)delegate forAuthType:(NSString *)authType { + [[self authenticationController] registerAuthenticationDelegate:delegate forAuthType:authType]; +} + +#pragma mark Log In + ++ (BFTask *)logInWithAuthTypeInBackground:(NSString *)authType authData:(NSDictionary *)authData { + PFParameterAssert(authType, @"Can't log in without `authType`."); + PFParameterAssert(authData, @"Can't log in without `authData`."); + PFUserAuthenticationController *controller = [self authenticationController]; + PFConsistencyAssert([controller authenticationDelegateForAuthType:authType], + @"No registered authentication delegate found for `%@` authentication type. " + @"Register a delegate first via PFUser.registerAuthenticationDelegate(delegate, forAuthType:)", authType); + return [[self authenticationController] logInUserAsyncWithAuthType:authType authData:authData]; +} + +#pragma mark Link + +- (BFTask *)linkWithAuthTypeInBackground:(NSString *)authType authData:(NSDictionary *)newAuthData { + PFParameterAssert(authType, @"Can't link without `authType`."); + PFParameterAssert(authData, @"Can't link without `authData`."); + PFUserAuthenticationController *controller = [[self class] authenticationController]; + PFConsistencyAssert([controller authenticationDelegateForAuthType:authType], + @"No registered authentication delegate found for `%@` authentication type. " + @"Register a delegate first via PFUser.registerAuthenticationDelegate(delegate, forAuthType:)", authType); + + @weakify(self); + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [toAwait continueWithBlock:^id(BFTask *task) { + @strongify(self); + + NSDictionary *oldAnonymousData = nil; + + @synchronized (self.lock) { + self.authData[authType] = newAuthData; + [self.linkedServiceNames addObject:authType]; + + oldAnonymousData = self.authData[PFAnonymousUserAuthenticationType]; + [self stripAnonymity]; + + dirty = YES; + } + + return [[self saveAsync:nil] continueAsyncWithBlock:^id(BFTask *task) { + if (task.result) { + [self synchronizeAuthDataWithAuthType:authType]; + } else { + @synchronized (self.lock) { + [self.authData removeObjectForKey:authType]; + [self.linkedServiceNames removeObject:authType]; + [self restoreAnonymity:oldAnonymousData]; + } + } + return task; + }]; + }]; + }]; +} + +#pragma mark Unlink + +- (BFTask *)unlinkWithAuthTypeInBackground:(NSString *)authType { + return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ + @synchronized (self.lock) { + if (self.authData[authType]) { + self.authData[authType] = [NSNull null]; + dirty = YES; + return [self saveInBackground]; + } + } + return @YES; + }]; +} + +#pragma mark Linked + +- (BOOL)isLinkedWithAuthType:(NSString *)authType { + PFParameterAssert(authType, @"Authentication type can't be `nil`."); + @synchronized(self.lock) { + return [self.linkedServiceNames containsObject:authType]; + } +} + +#pragma mark Private + ++ (void)_unregisterAuthenticationDelegateForAuthType:(NSString *)authType { + [[[self class] authenticationController] unregisterAuthenticationDelegateForAuthType:authType]; +} + +///-------------------------------------- +#pragma mark - Become +///-------------------------------------- + ++ (instancetype)become:(NSString *)sessionToken { + return [self become:sessionToken error:nil]; +} + ++ (instancetype)become:(NSString *)sessionToken error:(NSError **)error { + return [[self becomeInBackground:sessionToken] waitForResult:error]; +} + ++ (BFTask *)becomeInBackground:(NSString *)sessionToken { + PFParameterAssert(sessionToken, @"Session Token must be provided for login."); + return [[self userController] logInCurrentUserAsyncWithSessionToken:sessionToken]; +} + ++ (void)becomeInBackground:(NSString *)sessionToken block:(PFUserResultBlock)block { + [[self becomeInBackground:sessionToken] thenCallBackOnMainThreadAsync:block]; +} + ++ (void)becomeInBackground:(NSString *)sessionToken target:(id)target selector:(SEL)selector { + [self becomeInBackground:sessionToken block:^(PFUser *user, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:user object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Revocable Sessions +///-------------------------------------- + ++ (BFTask *)enableRevocableSessionInBackground { + if ([self _isRevocableSessionEnabled]) { + return [BFTask taskWithResult:nil]; + } + [self _setRevocableSessionEnabled:YES]; + return [self _upgradeToRevocableSessionInBackground]; +} + ++ (void)enableRevocableSessionInBackgroundWithBlock:(PFUserSessionUpgradeResultBlock)block { + [[self enableRevocableSessionInBackground] continueWithBlock:^id(BFTask *task) { + block(task.error); + return nil; + }]; +} + +///-------------------------------------- +#pragma mark - Request Password Reset +///-------------------------------------- + ++ (BOOL)requestPasswordResetForEmail:(NSString *)email { + return [self requestPasswordResetForEmail:email error:nil]; +} + ++ (BOOL)requestPasswordResetForEmail:(NSString *)email error:(NSError **)error { + return [[[self requestPasswordResetForEmailInBackground:email] waitForResult:error] boolValue]; +} + ++ (BFTask *)requestPasswordResetForEmailInBackground:(NSString *)email { + PFParameterAssert(email, @"Email should be provided to request password reset."); + return [[[self userController] requestPasswordResetAsyncForEmail:email] continueWithSuccessResult:@YES]; +} + ++ (void)requestPasswordResetForEmailInBackground:(NSString *)email block:(PFBooleanResultBlock)block { + [[self requestPasswordResetForEmailInBackground:email] thenCallBackOnMainThreadWithBoolValueAsync:block]; +} + ++ (void)requestPasswordResetForEmailInBackground:(NSString *)email target:(id)target selector:(SEL)selector { + [self requestPasswordResetForEmailInBackground:email block:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +///-------------------------------------- +#pragma mark - Logging out +///-------------------------------------- + ++ (void)logOut { + [[self logOutInBackground] waitForResult:nil withMainThreadWarning:NO]; +} + ++ (BFTask *)logOutInBackground { + PFCurrentUserController *controller = [[self class] currentUserController]; + return [controller logOutCurrentUserAsync]; +} + ++ (void)logOutInBackgroundWithBlock:(PFUserLogoutResultBlock)block { + [[self logOutInBackground] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) { + block(task.error); + return nil; + }]; +} + +- (BFTask *)_logOutAsync { + //TODO: (nlutsenko) Maybe add this to `taskQueue`? + + NSString *token = nil; + NSMutableArray *tasks = [NSMutableArray array]; + @synchronized(self.lock) { + [self.authData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + BFTask *task = [self _logOutAsyncWithAuthType:key]; + [tasks addObject:task]; + }]; + + self.isCurrentUser = NO; + + token = [self.sessionToken copy]; + + PFMutableUserState *state = [self._state mutableCopy]; + state.sessionToken = nil; + self._state = state; + } + + BFTask *task = [BFTask taskForCompletionOfAllTasks:tasks]; + + if ([PFSessionUtilities isSessionTokenRevocable:token]) { + return [task continueWithExecutor:[BFExecutor defaultExecutor] withBlock:^id(BFTask *task) { + return [[[self class] userController] logOutUserAsyncWithSessionToken:token]; + }]; + } + return task; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setObject:(id)object forKey:(NSString *)key { + PFParameterAssert(_PFUserIsWritablePropertyForKey(key), + @"Can't remove the '%@' field of a PFUser.", key); + if ([key isEqualToString:PFUserUsernameRESTKey]) { + [self stripAnonymity]; + } + [super setObject:object forKey:key]; +} + +- (void)removeObjectForKey:(NSString *)key { + PFParameterAssert(_PFUserIsRemovablePropertyForKey(key), + @"Can't remove the '%@' field of a PFUser.", key); + [super removeObjectForKey:key]; +} + +- (NSMutableDictionary *)authData { + @synchronized ([self lock]) { + if (!authData) { + authData = [[NSMutableDictionary alloc] init]; + } + } + return authData; +} + +- (NSMutableSet *)linkedServiceNames { + @synchronized ([self lock]) { + if (!linkedServiceNames) { + linkedServiceNames = [[NSMutableSet alloc] init]; + } + } + return linkedServiceNames; +} + ++ (instancetype)user { + return [self object]; +} + +- (BFTask *)saveAsync:(BFTask *)toAwait { + if (!toAwait) { + toAwait = [BFTask taskWithResult:nil]; + } + + // This breaks a rare deadlock scenario where on one thread, user.lock is acquired before taskQueue.lock sometimes, + // but not always. Using continueAsyncWithBlock unlocks from the taskQueue, and solves the proplem. + return [toAwait continueAsyncWithBlock:^id(BFTask *task) { + @synchronized ([self lock]) { + if (self.isLazy) { + return [[self resolveLazinessAsync:toAwait] continueAsyncWithSuccessBlock:^id(BFTask *task) { + return @(!!task.result); + }]; + } + } + + return [super saveAsync:toAwait]; + }]; +} + +- (BFTask *)fetchAsync:(BFTask *)toAwait { + if ([self isLazy]) { + return [BFTask taskWithResult:@YES]; + } + + return [[super fetchAsync:toAwait] continueAsyncWithSuccessBlock:^id(BFTask *fetchAsyncTask) { + if ([self isCurrentUser]) { + [self cleanUpAuthData]; + PFCurrentUserController *controller = [[self class] currentUserController]; + return [[controller saveCurrentObjectAsync:self] continueAsyncWithBlock:^id(BFTask *task) { + return fetchAsyncTask.result; + }]; + } + return fetchAsyncTask.result; + }]; +} + +- (instancetype)fetch:(NSError **)error { + if (self.isLazy) { + return self; + } + return [super fetch:error]; +} + +- (void)fetchInBackgroundWithBlock:(PFObjectResultBlock)block { + if (self.isLazy) { + if (block) { + block(self, nil); + return; + } + } + [super fetchInBackgroundWithBlock:^(PFObject *result, NSError *error) { + if (block) { + block(result, error); + } + }]; +} + +- (BOOL)signUp { + return [self signUp:nil]; +} + +- (BOOL)signUp:(NSError **)error { + return [[[self signUpInBackground] waitForResult:error] boolValue]; +} + +- (BFTask *)signUpInBackground { + return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) { + return [self signUpAsync:toAwait]; + }]; +} + +- (void)signUpInBackgroundWithTarget:(id)target selector:(SEL)selector { + [self signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + [PFInternalUtils safePerformSelector:selector withTarget:target object:@(succeeded) object:error]; + }]; +} + +- (BOOL)isAuthenticated { + PFUser *currentUser = [[self class] currentUser]; + return [self _isAuthenticatedWithCurrentUser:currentUser]; +} + +- (BOOL)_isAuthenticatedWithCurrentUser:(PFUser *)currentUser { + @synchronized ([self lock]) { + BOOL authenticated = self.isLazy || self.sessionToken; + if (!authenticated && currentUser != nil) { + authenticated = [self.objectId isEqualToString:currentUser.objectId]; + } else { + authenticated = self.isCurrentUser; + } + return authenticated; + } +} + +- (BOOL)isNew { + return self._state.isNew; +} + +- (NSString *)sessionToken { + return self._state.sessionToken; +} + +- (void)signUpInBackgroundWithBlock:(PFBooleanResultBlock)block { + @synchronized ([self lock]) { + if (self.objectId) { + // For anonymous users, there may be an objectId. Setting the userName + // will have removed the anonymous link and set the value in the authData + // object to [NSNull null], so we can just treat it like a save operation. + if (authData[PFAnonymousUserAuthenticationType] == [NSNull null]) { + [self saveInBackgroundWithBlock:block]; + return; + } + } + [[self signUpInBackground] thenCallBackOnMainThreadWithBoolValueAsync:block]; + } +} + ++ (void)enableAutomaticUser { + [Parse _currentManager].coreManager.currentUserController.automaticUsersEnabled = YES; +} + +///-------------------------------------- +#pragma mark - PFObjectPrivateSubclass +///-------------------------------------- + +#pragma mark State + ++ (PFObjectState *)_newObjectStateWithParseClassName:(NSString *)className + objectId:(NSString *)objectId + isComplete:(BOOL)complete { + return [PFUserState stateWithParseClassName:className objectId:objectId isComplete:complete]; +} + +@end diff --git a/Pods/Parse/Parse/PFUserAuthenticationDelegate.h b/Pods/Parse/Parse/PFUserAuthenticationDelegate.h new file mode 100644 index 0000000..d51c107 --- /dev/null +++ b/Pods/Parse/Parse/PFUserAuthenticationDelegate.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import + +PF_ASSUME_NONNULL_BEGIN + +/*! + Provides a general interface for delegation of third party authentication with s. + */ +@protocol PFUserAuthenticationDelegate + +/*! + @abstract Called when restoring third party authentication credentials that have been serialized, + such as session keys, user id, etc. + + @note This method will be executed on a background thread. + + @param authData The auth data for the provider. This value may be `nil` when unlinking an account. + + @returns `YES` - if the `authData` was succesfully synchronized, + or `NO` if user should not longer be associated because of bad `authData`. + */ +- (BOOL)restoreAuthenticationWithAuthData:(PF_NULLABLE NSDictionary PF_GENERIC(NSString *, NSString *) *)authData; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Parse.h b/Pods/Parse/Parse/Parse.h new file mode 100644 index 0000000..52cd000 --- /dev/null +++ b/Pods/Parse/Parse/Parse.h @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#if !TARGET_OS_WATCH + +#import +#import + +#endif + +#if TARGET_OS_IOS + +#import +#import +#import + +#endif + +PF_ASSUME_NONNULL_BEGIN + +/*! + The `Parse` class contains static functions that handle global configuration for the Parse framework. + */ +@interface Parse : NSObject + +///-------------------------------------- +/// @name Connecting to Parse +///-------------------------------------- + +/*! + @abstract Sets the applicationId and clientKey of your application. + + @param applicationId The application id of your Parse application. + @param clientKey The client key of your Parse application. + */ ++ (void)setApplicationId:(NSString *)applicationId clientKey:(NSString *)clientKey; + +/*! + @abstract The current application id that was used to configure Parse framework. + */ ++ (NSString *)getApplicationId; + +/*! + @abstract The current client key that was used to configure Parse framework. + */ ++ (NSString *)getClientKey; + +///-------------------------------------- +/// @name Enabling Local Datastore +///-------------------------------------- + +/*! + @abstract Enable pinning in your application. This must be called before your application can use + pinning. The recommended way is to call this method before `setApplicationId:clientKey:`. + */ ++ (void)enableLocalDatastore; + +/*! + @abstract Flag that indicates whether Local Datastore is enabled. + + @returns `YES` if Local Datastore is enabled, otherwise `NO`. + */ ++ (BOOL)isLocalDatastoreEnabled; + +///-------------------------------------- +/// @name Enabling Extensions Data Sharing +///-------------------------------------- + +/*! + @abstract Enables data sharing with an application group identifier. + + @discussion After enabling - Local Datastore, `currentUser`, `currentInstallation` and all eventually commands + are going to be available to every application/extension in a group that have the same Parse applicationId. + + @warning This method is required to be called before . + + @param groupIdentifier Application Group Identifier to share data with. + */ ++ (void)enableDataSharingWithApplicationGroupIdentifier:(NSString *)groupIdentifier PF_EXTENSION_UNAVAILABLE("Use `enableDataSharingWithApplicationGroupIdentifier:containingApplication:`.") PF_WATCH_UNAVAILABLE; + +/*! + @abstract Enables data sharing with an application group identifier. + + @discussion After enabling - Local Datastore, `currentUser`, `currentInstallation` and all eventually commands + are going to be available to every application/extension in a group that have the same Parse applicationId. + + @warning This method is required to be called before . + This method can only be used by application extensions. + + @param groupIdentifier Application Group Identifier to share data with. + @param bundleIdentifier Bundle identifier of the containing application. + */ ++ (void)enableDataSharingWithApplicationGroupIdentifier:(NSString *)groupIdentifier + containingApplication:(NSString *)bundleIdentifier PF_WATCH_UNAVAILABLE; + +/*! + @abstract Application Group Identifier for Data Sharing + + @returns `NSString` value if data sharing is enabled, otherwise `nil`. + */ ++ (NSString *)applicationGroupIdentifierForDataSharing PF_WATCH_UNAVAILABLE; + +/*! + @abstract Containing application bundle identifier. + + @returns `NSString` value if data sharing is enabled, otherwise `nil`. + */ ++ (NSString *)containingApplicationBundleIdentifierForDataSharing PF_WATCH_UNAVAILABLE; + +#if PARSE_IOS_ONLY + +///-------------------------------------- +/// @name Configuring UI Settings +///-------------------------------------- + +/*! + @abstract Set whether to show offline messages when using a Parse view or view controller related classes. + + @param enabled Whether a `UIAlertView` should be shown when the device is offline + and network access is required from a view or view controller. + + @deprecated This method has no effect. + */ ++ (void)offlineMessagesEnabled:(BOOL)enabled PARSE_DEPRECATED("This method is deprecated and has no effect."); + +/*! + @abstract Set whether to show an error message when using a Parse view or view controller related classes + and a Parse error was generated via a query. + + @param enabled Whether a `UIAlertView` should be shown when an error occurs. + + @deprecated This method has no effect. + */ ++ (void)errorMessagesEnabled:(BOOL)enabled PARSE_DEPRECATED("This method is deprecated and has no effect."); + +#endif + +///-------------------------------------- +/// @name Logging +///-------------------------------------- + +/*! + @abstract Sets the level of logging to display. + + @discussion By default: + - If running inside an app that was downloaded from iOS App Store - it is set to + - All other cases - it is set to + + @param logLevel Log level to set. + @see PFLogLevel + */ ++ (void)setLogLevel:(PFLogLevel)logLevel; + +/*! + @abstract Log level that will be displayed. + + @discussion By default: + - If running inside an app that was downloaded from iOS App Store - it is set to + - All other cases - it is set to + + @returns A value. + @see PFLogLevel + */ ++ (PFLogLevel)logLevel; + +@end + +PF_ASSUME_NONNULL_END diff --git a/Pods/Parse/Parse/Parse.m b/Pods/Parse/Parse/Parse.m new file mode 100644 index 0000000..4bafd45 --- /dev/null +++ b/Pods/Parse/Parse/Parse.m @@ -0,0 +1,235 @@ +/** + * Copyright (c) 2015-present, Parse, LLC. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "BFTask+Private.h" +#import "Parse.h" +#import "ParseInternal.h" +#import "ParseManager.h" +#import "PFEventuallyPin.h" +#import "PFObject+Subclass.h" +#import "PFOfflineStore.h" +#import "PFPin.h" +#import "PFPinningEventuallyQueue.h" +#import "PFUserPrivate.h" +#import "PFLogger.h" +#import "PFSession.h" +#import "PFFileManager.h" +#import "PFApplication.h" +#import "PFKeychainStore.h" +#import "PFLogging.h" +#import "PFObjectSubclassingController.h" + +#if !TARGET_OS_WATCH +#import "PFInstallationPrivate.h" +#if TARGET_OS_IOS +#import "PFProduct+Private.h" +#endif +#endif + +#import "PFCategoryLoader.h" + +@implementation Parse + +static ParseManager *currentParseManager_; + +static BOOL shouldEnableLocalDatastore_; + +static NSString *applicationGroupIdentifier_; +static NSString *containingApplicationBundleIdentifier_; + ++ (void)initialize { + if (self == [Parse class]) { + // Load all private categories, that we have... + // Without this call - private categories - will require `-ObjC` in linker flags. + // By explicitly calling empty method - we can avoid that. + [PFCategoryLoader loadPrivateCategories]; + } +} + +///-------------------------------------- +#pragma mark - Connect +///-------------------------------------- + ++ (void)setApplicationId:(NSString *)applicationId clientKey:(NSString *)clientKey { + PFConsistencyAssert([applicationId length], @"'applicationId' should not be nil."); + PFConsistencyAssert([clientKey length], @"'clientKey' should not be nil."); + + // Setup new manager first, so it's 100% ready whenever someone sends a request for anything. + ParseManager *manager = [[ParseManager alloc] initWithApplicationId:applicationId clientKey:clientKey]; + [manager configureWithApplicationGroupIdentifier:applicationGroupIdentifier_ + containingApplicationIdentifier:containingApplicationBundleIdentifier_ + enabledLocalDataStore:shouldEnableLocalDatastore_]; + currentParseManager_ = manager; + + shouldEnableLocalDatastore_ = NO; + + PFObjectSubclassingController *subclassingController = [PFObjectSubclassingController defaultController]; + // Register built-in subclasses of PFObject so they get used. + // We're forced to register subclasses directly this way, in order to prevent a deadlock. + // If we ever switch to bundle scanning, this code can go away. + [subclassingController registerSubclass:[PFUser class]]; + [subclassingController registerSubclass:[PFSession class]]; + [subclassingController registerSubclass:[PFRole class]]; + [subclassingController registerSubclass:[PFPin class]]; + [subclassingController registerSubclass:[PFEventuallyPin class]]; +#if !TARGET_OS_WATCH + [subclassingController registerSubclass:[PFInstallation class]]; +#if TARGET_OS_IOS + [subclassingController registerSubclass:[PFProduct class]]; +#endif +#endif + +#if TARGET_OS_IOS + [PFNetworkActivityIndicatorManager sharedManager].enabled = YES; +#endif + + [currentParseManager_ preloadDiskObjectsToMemoryAsync]; + + [[self parseModulesCollection] parseDidInitializeWithApplicationId:applicationId clientKey:clientKey]; +} + ++ (NSString *)getApplicationId { + PFConsistencyAssert(currentParseManager_, + @"You have to call setApplicationId:clientKey: on Parse to configure Parse."); + return currentParseManager_.applicationId; +} + ++ (NSString *)getClientKey { + PFConsistencyAssert(currentParseManager_, + @"You have to call setApplicationId:clientKey: on Parse to configure Parse."); + return currentParseManager_.clientKey; +} + +///-------------------------------------- +#pragma mark - Extensions Data Sharing +///-------------------------------------- + ++ (void)enableDataSharingWithApplicationGroupIdentifier:(NSString *)groupIdentifier { + PFConsistencyAssert(!currentParseManager_, + @"'enableDataSharingWithApplicationGroupIdentifier:' must be called before 'setApplicationId:clientKey'"); + PFParameterAssert([groupIdentifier length], @"'groupIdentifier' should not be nil."); + PFConsistencyAssert(![PFApplication currentApplication].extensionEnvironment, @"This method cannot be used in application extensions."); + PFConsistencyAssert([PFFileManager isApplicationGroupContainerReachableForGroupIdentifier:groupIdentifier], + @"ApplicationGroupContainer is unreachable. Please double check your Xcode project settings."); + applicationGroupIdentifier_ = [groupIdentifier copy]; +} + ++ (void)enableDataSharingWithApplicationGroupIdentifier:(NSString *)groupIdentifier + containingApplication:(NSString *)bundleIdentifier { + PFConsistencyAssert(!currentParseManager_, + @"'enableDataSharingWithApplicationGroupIdentifier:containingApplication:' must be called before 'setApplicationId:clientKey'"); + PFParameterAssert([groupIdentifier length], @"'groupIdentifier' should not be nil."); + PFParameterAssert([bundleIdentifier length], @"Containing application bundle identifier should not be nil."); + PFConsistencyAssert([PFApplication currentApplication].extensionEnvironment, @"This method can only be used in application extensions."); + PFConsistencyAssert([PFFileManager isApplicationGroupContainerReachableForGroupIdentifier:groupIdentifier], + @"ApplicationGroupContainer is unreachable. Please double check your Xcode project settings."); + + applicationGroupIdentifier_ = groupIdentifier; + containingApplicationBundleIdentifier_ = bundleIdentifier; +} + ++ (NSString *)applicationGroupIdentifierForDataSharing { + return applicationGroupIdentifier_; +} + ++ (NSString *)containingApplicationBundleIdentifierForDataSharing { + return containingApplicationBundleIdentifier_; +} + ++ (void)_resetDataSharingIdentifiers { + applicationGroupIdentifier_ = nil; + containingApplicationBundleIdentifier_ = nil; +} + +///-------------------------------------- +#pragma mark - Local Datastore +///-------------------------------------- + ++ (void)enableLocalDatastore { + PFConsistencyAssert(!currentParseManager_, + @"'enableLocalDataStore' must be called before 'setApplicationId:clientKey:'"); + + // Lazily enableLocalDatastore after init. We can't use ParseModule because + // ParseModule isn't processed in main thread and may cause race condition. + shouldEnableLocalDatastore_ = YES; +} + ++ (BOOL)isLocalDatastoreEnabled { + if (!currentParseManager_) { + return shouldEnableLocalDatastore_; + } + return currentParseManager_.offlineStoreLoaded; +} + +///-------------------------------------- +#pragma mark - User Interface +///-------------------------------------- + +#if PARSE_IOS_ONLY + ++ (void)offlineMessagesEnabled:(BOOL)enabled { + // Deprecated method - shouldn't do anything. +} + ++ (void)errorMessagesEnabled:(BOOL)enabled { + // Deprecated method - shouldn't do anything. +} + +#endif + +///-------------------------------------- +#pragma mark - Logging +///-------------------------------------- + ++ (void)setLogLevel:(PFLogLevel)logLevel { + [PFLogger sharedLogger].logLevel = logLevel; +} + ++ (PFLogLevel)logLevel { + return [PFLogger sharedLogger].logLevel; +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + ++ (ParseManager *)_currentManager { + return currentParseManager_; +} + ++ (void)_clearCurrentManager { + currentParseManager_ = nil; +} + +///-------------------------------------- +#pragma mark - Modules +///-------------------------------------- + ++ (void)enableParseModule:(id)module { + [[self parseModulesCollection] addParseModule:module]; +} + ++ (void)disableParseModule:(id)module { + [[self parseModulesCollection] removeParseModule:module]; +} + ++ (BOOL)isModuleEnabled:(id)module { + return [[self parseModulesCollection] containsModule:module]; +} + ++ (ParseModuleCollection *)parseModulesCollection { + static ParseModuleCollection *collection; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + collection = [[ParseModuleCollection alloc] init]; + }); + return collection; +} + +@end diff --git a/Pods/Parse/Parse/Resources/en.lproj/Parse.strings b/Pods/Parse/Parse/Resources/en.lproj/Parse.strings new file mode 100644 index 0000000..c1c3414 Binary files /dev/null and b/Pods/Parse/Parse/Resources/en.lproj/Parse.strings differ diff --git a/Pods/Parse/README.md b/Pods/Parse/README.md new file mode 100644 index 0000000..c8e506b --- /dev/null +++ b/Pods/Parse/README.md @@ -0,0 +1,106 @@ +# Parse SDK for iOS/OS X + +[![Build Status][build-status-svg]][build-status-link] +[![Coverage Status][coverage-status-svg]][coverage-status-link] +[![Podspec][podspec-svg]][podspec-link] +[![License][license-svg]][license-link] +![Platforms][platforms-svg] +[![Dependencies][dependencies-svg]][dependencies-link] +[![References][references-svg]][references-link] + +A library that gives you access to the powerful Parse cloud platform from your iOS or OS X app. +For more information Parse and its features, see [the website][parse.com] and [getting started][docs]. + +## Other Parse Projects + + - [ParseUI for iOS][parseui-ios-link] + - [Parse SDK for Android][android-sdk-link] + +## Getting Started + +To use parse, head on over to the [releases][releases] page, and download the latest build. +And you're off! Take a look at the public [documentation][docs] and start building. + +**Other Installation Options** + + 1. **CocoaPods** + + Add the following line to your podfile: + + pod 'Parse' + + Run pod install, and you should now have the latest parse release. + + 2. **Compiling for yourself** + + If you want to manually compile the SDK, clone it locally, and run the following commands in the root directory of the repository: + + # To pull in extra dependencies (Bolts and OCMock) + git submodule update --init --recursive + + # To install all the gems + bundle install + + # Build & Package the Frameworks + rake package:frameworks + + Compiled frameworks will be in 2 archives: `Parse-iOS.zip` and `Parse-OSX.zip` inside the `build/release` folder, and you can link them as you'd please. + + 3. **Using Parse as a sub-project** + + You can also include parse as a subproject inside of your application if you'd prefer, although we do not recommend this, as it will increase your indexing time significantly. To do so, just drag and drop the Parse.xcodeproj file into your workspace. Note that unit tests will be unavailable if you use Parse like this, as OCMock will be unable to be found. + +## How Do I Contribute? + +We want to make contributing to this project as easy and transparent as possible. Please refer to the [Contribution Guidelines][contributing]. + +## Dependencies + +We use the following libraries as dependencies inside of Parse: + + - [Bolts][bolts-framework], for task management. + - [OCMock][ocmock-framework], for unit testing. + +## License + +``` +Copyright (c) 2015-present, Parse, LLC. +All rights reserved. + +This source code is licensed under the BSD-style license found in the +LICENSE file in the root directory of this source tree. An additional grant +of patent rights can be found in the PATENTS file in the same directory. +``` + + [parse.com]: https://www.parse.com/products/ios + [docs]: https://www.parse.com/docs/ios/guide + [blog]: https://blog.parse.com/ + + [parseui-ios-link]: https://github.com/ParsePlatform/ParseUI-iOS + [android-sdk-link]: https://github.com/ParsePlatform/Parse-SDK-Android + + [releases]: https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/releases + [contributing]: https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/blob/master/CONTRIBUTING.md + + [bolts-framework]: https://github.com/BoltsFramework/Bolts-iOS + [ocmock-framework]: http://ocmock.org + + [build-status-svg]: https://travis-ci.org/ParsePlatform/Parse-SDK-iOS-OSX.svg + [build-status-link]: https://travis-ci.org/ParsePlatform/Parse-SDK-iOS-OSX/branches + + [coverage-status-svg]: https://codecov.io/github/ParsePlatform/Parse-SDK-iOS-OSX/coverage.svg?branch=master + [coverage-status-link]: https://codecov.io/github/ParsePlatform/Parse-SDK-iOS-OSX?branch=master + + [license-svg]: https://img.shields.io/badge/license-BSD-lightgrey.svg + [license-link]: https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/blob/master/LICENSE + + [podspec-svg]: https://img.shields.io/cocoapods/v/Parse.svg + [podspec-link]: https://cocoapods.org/pods/Parse + + [platforms-svg]: https://img.shields.io/badge/platform-ios%20%7C%20osx-lightgrey.svg + + [dependencies-svg]: https://img.shields.io/badge/dependencies-2-yellowgreen.svg + [dependencies-link]: https://github.com/ParsePlatform/Parse-SDK-iOS-OSX/blob/master/Vendor + + [references-svg]: https://www.versioneye.com/objective-c/parse/reference_badge.svg + [references-link]: https://www.versioneye.com/objective-c/parse/references diff --git a/Pods/ParseUI/LICENSE b/Pods/ParseUI/LICENSE new file mode 100644 index 0000000..31425ea --- /dev/null +++ b/Pods/ParseUI/LICENSE @@ -0,0 +1,17 @@ +Copyright (c) 2014, Parse, LLC. All rights reserved. + +You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +copy, modify, and distribute this software in source code or binary form for use +in connection with the web services and APIs provided by Parse. + +As with any software that integrates with the Parse platform, your use of +this software is subject to the Parse Terms of Service +[https://www.parse.com/about/terms]. This copyright notice shall be +included in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.h b/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.h new file mode 100644 index 0000000..71b3642 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +@class PFImageView; +@class PFObject; + +/*! + The `PFCollectionViewCell` class represents a collection view cell which can + download and display remote images stored on Parse as well as has a default simple text label. + */ +@interface PFCollectionViewCell : UICollectionViewCell + +/*! + @abstract A simple lazy-loaded label for the collection view cell. + */ +@property (nonatomic, strong, readonly) UILabel *textLabel; + +/*! + @abstract The lazy-loaded imageView of the collection view cell. + + @see PFImageView + */ +@property (nonatomic, strong, readonly) PFImageView *imageView; + +/*! + @abstract This method should update all the relevant information inside a subclass of `PFCollectionViewCell`. + + @discussion This method is automatically called by whenever the cell + should display new information. By default this method does nothing. + + @param object An instance of `PFObject` to update from. + */ +- (void)updateFromObject:(PFUI_NULLABLE PFObject *)object; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.m b/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.m new file mode 100644 index 0000000..6fb1f46 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFCollectionViewCell.m @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFCollectionViewCell.h" + +#import "PFImageView.h" +#import "PFRect.h" + +@implementation PFCollectionViewCell + +@synthesize imageView = _imageView; +@synthesize textLabel = _textLabel; + +#pragma mark - +#pragma mark UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + const CGRect bounds = self.contentView.bounds; + + CGRect imageViewFrame = CGRectZero; + if (_imageView && _imageView.image){ + imageViewFrame = PFRectMakeWithSizeCenteredInRect(PFSizeMin(_imageView.image.size, bounds.size), + bounds); + } + CGRect textLabelFrame = CGRectZero; + if (_textLabel) { + CGSize maxImageViewSize = CGSizeMake(CGRectGetWidth(bounds), CGRectGetHeight(bounds) / 3.0f * 2.0f); + CGSize imageViewSize = PFSizeMin(imageViewFrame.size, maxImageViewSize); + + imageViewFrame = PFRectMakeWithSizeCenteredInRect(imageViewSize, PFRectMakeWithSize(maxImageViewSize)); + CGFloat textLabelTopInset = (CGRectIsEmpty(imageViewFrame) ? 0.0f : CGRectGetMaxY(imageViewFrame)); + + textLabelFrame = PFRectMakeWithOriginSize(CGPointMake(0.0f, textLabelTopInset), + CGSizeMake(CGRectGetWidth(bounds), CGRectGetHeight(bounds) - textLabelTopInset)); + } + + // Adapt content mode of _imageView to fit the image in bounds if the layout frame is smaller or center if it's bigger. + if (!CGRectIsEmpty(imageViewFrame)) { + if (CGRectContainsRect(PFRectMakeWithSize(_imageView.image.size), PFRectMakeWithSize(imageViewFrame.size))) { + _imageView.contentMode = UIViewContentModeScaleAspectFit; + } else { + _imageView.contentMode = UIViewContentModeCenter; + } + } + + _imageView.frame = CGRectIntegral(imageViewFrame); + _textLabel.frame = CGRectIntegral(textLabelFrame); +} + +#pragma mark - +#pragma mark Update + +- (void)updateFromObject:(PFObject *)object { + // Do nothing +} + +#pragma mark - +#pragma mark Accessors + +- (PFImageView *)imageView { + if (!_imageView) { + _imageView = [[PFImageView alloc] initWithFrame:CGRectZero]; + [self.contentView addSubview:_imageView]; + } + return _imageView; +} + +- (UILabel *)textLabel { + if (!_textLabel) { + _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _textLabel.numberOfLines = 0; + [self.contentView addSubview:_textLabel]; + } + return _textLabel; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.h b/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.h new file mode 100644 index 0000000..eb80487 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + An enum that represents states of the PFPurchaseTableViewCell. + @see PFPurchaseTableViewCell + */ +typedef NS_ENUM(uint8_t, PFPurchaseTableViewCellState) { + /*! Normal state of the cell. */ + PFPurchaseTableViewCellStateNormal = 0, + /*! Downloading state of the cell. */ + PFPurchaseTableViewCellStateDownloading, + /*! State of the cell, when the product was downloaded. */ + PFPurchaseTableViewCellStateDownloaded +}; + +/*! + `PFPurchaseTableViewCell` is a subclass that is used to show + products in a . + + @see PFProductTableViewController + */ +@interface PFPurchaseTableViewCell : PFTableViewCell + +/*! + @abstract State of the cell. + @see PFPurchaseTableViewCellState + */ +@property (nonatomic, assign) PFPurchaseTableViewCellState state; + +/*! + @abstract Label where price of the product is displayed. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UILabel *priceLabel; + +/*! + @abstract Progress view that is shown, when the product is downloading. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIProgressView *progressView; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.m b/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.m new file mode 100644 index 0000000..4a7968b --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFPurchaseTableViewCell.m @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFPurchaseTableViewCell.h" + +#import "PFLocalization.h" +#import "PFRect.h" + +@interface PFPurchaseTableViewCell () + +@property (nonatomic, strong) UILabel *priceLabel; +@property (nonatomic, strong) UIProgressView *progressView; + +@end + +@implementation PFPurchaseTableViewCell + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + self.backgroundView = [[UIView alloc] initWithFrame:CGRectZero]; + + self.imageView.layer.shadowColor = [UIColor blackColor].CGColor; + self.imageView.layer.shadowOffset = CGSizeMake(0.0f, 1.0f); + self.imageView.layer.shadowRadius = 1.0f; + self.imageView.layer.shadowOpacity = 1.0f; + + self.textLabel.backgroundColor = [UIColor clearColor]; + self.detailTextLabel.backgroundColor = [UIColor clearColor]; + self.detailTextLabel.numberOfLines = 2; + self.detailTextLabel.font = [UIFont systemFontOfSize:12.0f]; + + self.priceLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + self.priceLabel.backgroundColor = [UIColor colorWithWhite:242.0f/255.0f alpha:1.0f]; + self.priceLabel.textColor = [UIColor grayColor]; + self.priceLabel.shadowColor = [UIColor whiteColor]; + self.priceLabel.shadowOffset = CGSizeMake(0.0f, -1.0f); + self.priceLabel.font = [UIFont boldSystemFontOfSize:12.0f]; + self.priceLabel.layer.borderColor = [UIColor grayColor].CGColor; + self.priceLabel.layer.borderWidth = 1.0f; + self.priceLabel.layer.cornerRadius = 3.0f; + self.priceLabel.lineBreakMode = NSLineBreakByWordWrapping; + self.priceLabel.numberOfLines = 0; + [self.contentView addSubview:self.priceLabel]; + + self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; + self.state = PFPurchaseTableViewCellStateNormal; + } + return self; +} +#pragma mark - +#pragma mark UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + const CGRect bounds = self.contentView.bounds; + + CGFloat iconWidth = floorf(0.8f * CGRectGetHeight(bounds)); + CGFloat iconMarginY = floorf((CGRectGetHeight(bounds) - iconWidth)/2.0f); + CGFloat iconMarginX = iconMarginY; + CGFloat x = iconMarginX; + CGFloat y = iconMarginY; + self.imageView.frame = CGRectMake(x, y, iconWidth, iconWidth); + x += self.imageView.frame.size.width + iconMarginX; + + self.priceLabel.frame = CGRectZero; // this is necessary for sizeToFit to work correctly + [self.priceLabel sizeToFit]; + CGFloat priceLabelRightInset = 10.0f; + CGFloat priceLabelX = CGRectGetWidth(bounds) - CGRectGetWidth(self.priceLabel.frame) - priceLabelRightInset; + CGFloat priceLabelY = floorf((CGRectGetHeight(self.textLabel.frame) - CGRectGetHeight(self.priceLabel.frame))/2.0f) + iconMarginY; + + self.priceLabel.frame = PFRectMakeWithOriginSize(CGPointMake(priceLabelX, priceLabelY), self.priceLabel.frame.size); + + CGFloat titleWidth = self.contentView.frame.size.width - self.imageView.frame.size.width - iconMarginX - 100.0f; + CGFloat titleHeight = self.textLabel.frame.size.height; + self.textLabel.frame = CGRectMake(x, y, titleWidth, titleHeight); + + CGFloat textMarginBottom = 5.0f; + y += self.textLabel.frame.size.height + textMarginBottom; + + CGFloat detailTextLabelWidth = CGRectGetWidth(bounds) - x - 50.0f; + self.detailTextLabel.frame = CGRectMake(x, y, detailTextLabelWidth, CGRectGetWidth(self.detailTextLabel.frame)); + self.progressView.frame = CGRectMake(x, CGRectGetHeight(bounds) - CGRectGetHeight(self.progressView.frame) - iconMarginY - 2.0f, + detailTextLabelWidth, CGRectGetHeight(self.progressView.frame)); +} + +#pragma mark - +#pragma mark PFPurchaseTableViewCell + +- (void)setState:(PFPurchaseTableViewCellState)state { + if (self.state == state) { + return; + } + + _state = state; + + switch (state) { + case PFPurchaseTableViewCellStateNormal: + { + self.detailTextLabel.numberOfLines = 2; + } + break; + case PFPurchaseTableViewCellStateDownloading: + { + self.detailTextLabel.numberOfLines = 1; + self.priceLabel.backgroundColor = [UIColor colorWithRed:132.0f/255.0f green:175.0f/255.0f blue:230.0f/255.0f alpha:1.0f]; + NSString *downloadingText = NSLocalizedString(@"DOWNLOADING", @"DOWNLOADING"); + self.priceLabel.text = [NSString stringWithFormat:@" %@ ", downloadingText]; + self.priceLabel.textColor = [UIColor whiteColor]; + self.priceLabel.shadowColor = [UIColor blackColor]; + self.priceLabel.shadowOffset = CGSizeMake(0.0f, -1.0f); + [self.contentView addSubview:self.progressView]; + } + break; + case PFPurchaseTableViewCellStateDownloaded: + { + self.detailTextLabel.numberOfLines = 2; + NSString *installedText = NSLocalizedString(@"INSTALLED", @"INSTALLED"); + self.priceLabel.text = [NSString stringWithFormat:@" %@ ", installedText]; + self.priceLabel.textColor = [UIColor whiteColor]; + self.priceLabel.shadowColor = [UIColor blackColor]; + self.priceLabel.shadowOffset = CGSizeMake(0.0f, -1.0f); + self.priceLabel.backgroundColor = [UIColor colorWithRed:160.0f/255.0f green:200.0f/255.0f blue:120.0f/255.0f alpha:1.0f]; + [self.progressView removeFromSuperview]; + } + break; + default: + break; + } + [self setNeedsLayout]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.h b/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.h new file mode 100644 index 0000000..9238d99 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + The `PFTableViewCell` class represents a table view cell which can download and display remote images stored on Parse. + + When used in a - downloading and displaying of the remote images + are automatically managed by the . + */ +@interface PFTableViewCell : UITableViewCell + +/*! + @abstract The imageView of the table view cell. + + @see PFImageView + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFImageView *imageView; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.m b/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.m new file mode 100644 index 0000000..65f67cd --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Cells/PFTableViewCell.m @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFTableViewCell.h" + +#import "PFRect.h" + +@interface PFTableViewCell () + +@property (nonatomic, assign) UITableViewCellStyle style; +@property (nonatomic, strong) PFImageView *customImageView; + +@end + +@implementation PFTableViewCell + +#pragma mark - +#pragma mark NSObject + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + _style = style; + + _customImageView = [[PFImageView alloc] initWithFrame:CGRectZero]; + [self.contentView addSubview:_customImageView]; + } + return self; +} + +#pragma mark - +#pragma mark UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + // We cannot depend on the parent class to lay out things perfectly because + // UITableViewCell layoutSubviews use its internal imageView member rather than via + // its self.imageView property, so we need to lay out things manually + + // Don't relayout anything if there is no file/image + if (!self.imageView.file && !self.imageView.image) { + return; + } + + // Value2 ignores imageView entirely + if (self.style == UITableViewCellStyleValue2) { + return; + } + + const CGRect bounds = self.contentView.bounds; + + CGFloat imageHeight = MIN(CGRectGetWidth(bounds), CGRectGetHeight(bounds)); + CGFloat imageWidth = floorf(13.0f * imageHeight / 9.0f); // Default is 13/9 aspect ratio + _customImageView.frame = PFRectMakeWithSize(CGSizeMake(imageWidth, imageHeight)); + + CGFloat imageViewRightInset = 10.0f; + CGFloat textOrigin = CGRectGetMaxX(_customImageView.frame) + imageViewRightInset; + + CGRect textLabelFrame = self.textLabel.frame; + CGRect detailTextLabelFrame = self.detailTextLabel.frame; + + switch (self.style) { + case UITableViewCellStyleDefault: + case UITableViewCellStyleSubtitle: + { + CGFloat originalTextLabelInset = CGRectGetMinX(textLabelFrame); + CGFloat originalDetailTextLabelInset = CGRectGetMinX(detailTextLabelFrame); + + CGFloat maxTextLabelWidth = CGRectGetMaxX(bounds) - textOrigin - originalTextLabelInset; + CGFloat maxDetailTextLabelWidth = CGRectGetMaxX(bounds) - textOrigin - originalDetailTextLabelInset; + + textLabelFrame.origin.x = textOrigin; + textLabelFrame.size.width = MIN(maxTextLabelWidth, CGRectGetWidth(textLabelFrame)); + + detailTextLabelFrame.origin.x = textOrigin; + detailTextLabelFrame.size.width = MIN(maxDetailTextLabelWidth, CGRectGetWidth(detailTextLabelFrame)); + } + break; + case UITableViewCellStyleValue1: + { + CGFloat maxTextLabelWidth = CGRectGetMinX(detailTextLabelFrame) - textOrigin; + + textLabelFrame.origin.x = textOrigin; + textLabelFrame.size.width = MIN(maxTextLabelWidth, CGRectGetWidth(textLabelFrame)); + } + break; + default: + break; + } + self.textLabel.frame = textLabelFrame; + self.detailTextLabel.frame = detailTextLabelFrame; +} + +#pragma mark - +#pragma mark PFImageTableViewCell + +- (PFImageView *)imageView { + return _customImageView; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.h b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.h new file mode 100644 index 0000000..91f15fa --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +/*! + The `PFActivityIndicatorCollectionReusableView` class represents a collection footer + that has a simple text label and displays UIActivityIndicatorView if property is set to `YES`. + An instance of this class is used as a default next page button inside . + */ +@interface PFActivityIndicatorCollectionReusableView : UICollectionReusableView + +@property (nonatomic, strong, readonly) UILabel *textLabel; + +@property (nonatomic, assign, getter=isAnimating) BOOL animating; + +- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents; +- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.m b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.m new file mode 100644 index 0000000..3385edd --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.m @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFActivityIndicatorCollectionReusableView.h" + +#import "PFRect.h" + +@interface PFActivityIndicatorCollectionReusableView () { + UIActivityIndicatorView *_activityIndicator; + UIButton *_actionButton; +} + +@end + +@implementation PFActivityIndicatorCollectionReusableView + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (!self) return nil; + + _actionButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _actionButton.backgroundColor = self.backgroundColor; + [self addSubview:_actionButton]; + + _textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _textLabel.numberOfLines = 0; + _textLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:_textLabel]; + + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityIndicator.hidesWhenStopped = YES; + [self addSubview:_activityIndicator]; + + return self; +} + +#pragma mark - +#pragma mark Dealloc + +- (void)dealloc { + [self removeTarget:nil action:nil forControlEvents:UIControlEventAllEvents]; +} + +#pragma mark - +#pragma mark UIView + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + [super setBackgroundColor:backgroundColor]; + _actionButton.backgroundColor = backgroundColor; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + const CGRect bounds = self.bounds; + + _actionButton.frame = bounds; + + _textLabel.frame = PFRectMakeWithSizeCenteredInRect([_textLabel sizeThatFits:bounds.size], bounds); + _activityIndicator.frame = PFRectMakeWithSizeCenteredInRect([_activityIndicator sizeThatFits:bounds.size], bounds); +} + +#pragma mark - +#pragma mark Accessors + +- (void)setAnimating:(BOOL)animating { + if (self.animating != animating) { + + if (animating) { + [_activityIndicator startAnimating]; + _textLabel.alpha = 0.0f; + } else { + [_activityIndicator stopAnimating]; + _textLabel.alpha = 1.0f; + } + } +} + +- (BOOL)isAnimating { + return [_activityIndicator isAnimating]; +} + +#pragma mark - +#pragma mark Actions + +- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { + [_actionButton addTarget:target action:action forControlEvents:controlEvents]; +} + +- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents { + [_actionButton removeTarget:target action:action forControlEvents:controlEvents]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.h b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.h new file mode 100644 index 0000000..fea50a7 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +/*! + The `PFActivityIndicatorTableViewCell` class represents a table view cell + that displays UIActivityIndicatorView as the accessory view. + */ +@interface PFActivityIndicatorTableViewCell : PFTableViewCell + +@property (nonatomic, assign, getter=isAnimating) BOOL animating; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.m b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.m new file mode 100644 index 0000000..b825bd1 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.m @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFActivityIndicatorTableViewCell.h" + +@interface PFActivityIndicatorTableViewCell () + +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; + +@end + +@implementation PFActivityIndicatorTableViewCell + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self) { + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityIndicator.hidesWhenStopped = YES; + self.accessoryView = _activityIndicator; + } + return self; +} + +#pragma mark - +#pragma mark Accessors + +- (void)setAnimating:(BOOL)animating { + if (self.animating != animating) { + + if (animating) { + [_activityIndicator startAnimating]; + } else { + [_activityIndicator stopAnimating]; + } + } +} + +- (BOOL)isAnimating { + return [_activityIndicator isAnimating]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.h b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.h new file mode 100644 index 0000000..bcf4f10 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFColor : UIColor + +///-------------------------------------- +/// @name Common +///-------------------------------------- + ++ (UIColor *)commonBackgroundColor; + +///-------------------------------------- +/// @name TextFields +///-------------------------------------- + ++ (UIColor *)textFieldBackgroundColor; ++ (UIColor *)textFieldTextColor; ++ (UIColor *)textFieldPlaceholderColor; ++ (UIColor *)textFieldSeparatorColor; + +///-------------------------------------- +/// @name Buttons +///-------------------------------------- + ++ (UIColor *)loginButtonBackgroundColor; ++ (UIColor *)signupButtonBackgroundColor; ++ (UIColor *)facebookButtonBackgroundColor; ++ (UIColor *)twitterButtonBackgroundColor; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.m b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.m new file mode 100644 index 0000000..467d9ee --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFColor.m @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFColor.h" + +@implementation PFColor + +#pragma mark - +#pragma mark Common + ++ (UIColor *)commonBackgroundColor { + return [UIColor colorWithRed:249/255.0f + green:251.0f/255.0f + blue:1.0f + alpha:1.0f]; +} + +#pragma mark - +#pragma mark TextField + ++ (UIColor *)textFieldBackgroundColor { + return [UIColor whiteColor]; +} + ++ (UIColor *)textFieldTextColor { + return [UIColor blackColor]; +} + ++ (UIColor *)textFieldPlaceholderColor { + return [UIColor colorWithWhite:194.0f/255.0f alpha:1.0f]; +} + ++ (UIColor *)textFieldSeparatorColor { + return [UIColor colorWithWhite:227.0f/255.0f alpha:1.0f]; +} + +#pragma mark - +#pragma mark Buttons + ++ (UIColor *)loginButtonBackgroundColor { + return [UIColor colorWithRed:97.0f/255.0f + green:106.f/255.0f + blue:116.0f/255.0f + alpha:1.0f]; +} + ++ (UIColor *)signupButtonBackgroundColor { + return [UIColor colorWithRed:108.0f/255.0f + green:150.0f/255.0f + blue:249.0f/255.0f + alpha:1.0f]; +} + ++ (UIColor *)facebookButtonBackgroundColor { + return [UIColor colorWithRed:58.0f/255.0f + green:89.0f/255.0f + blue:152.0f/255.0f + alpha:1.0f]; +} + ++ (UIColor *)twitterButtonBackgroundColor { + return [UIColor colorWithRed:45.0f/255.0f + green:170.0f/255.0f + blue:1.0f + alpha:1.0f]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.h b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.h new file mode 100644 index 0000000..b7d5222 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFImage : UIImage + ++ (UIImage *)imageWithColor:(UIColor *)color; ++ (UIImage *)imageWithColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius; + ++ (UIImage *)imageNamed:(NSString *)name; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.m b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.m new file mode 100644 index 0000000..ac01c1e --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFImage.m @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFImage.h" + +#import "PFColor.h" +#import "PFRect.h" +#import "PFResources.h" + +@implementation PFImage + ++ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size +{ + UIGraphicsBeginImageContext(size); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, color.CGColor); + CGContextFillRect(context, (CGRect){.size = size}); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + ++ (UIImage *)imageWithColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius { + CGSize size = CGSizeMake(cornerRadius * 2.0f + 1.0f, cornerRadius * 2.0f + 1.0f); + + UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); + + [color setFill]; + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:PFRectMakeWithSize(size) cornerRadius:cornerRadius]; + [path fill]; + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(cornerRadius, + cornerRadius, + cornerRadius, + cornerRadius) + resizingMode:UIImageResizingModeStretch]; + + return image; +} + ++ (UIImage *)imageWithColor:(UIColor *)color { + return [self imageWithColor:color size:CGSizeMake(1.0f, 1.0f)]; +} + ++ (UIImage *)imageNamed:(NSString *)imageName { + UIImage *image = [UIImage imageNamed:imageName]; + if (image) { + // If there is an external override for the image at the given path, use it. + return image; + } + + NSString *fileExtension = [imageName pathExtension]; + NSMutableString *filenameWithoutExtension = [[imageName stringByDeletingPathExtension] mutableCopy]; + [filenameWithoutExtension replaceOccurrencesOfString:@"-\\." + withString:@"_" + options:NSRegularExpressionSearch + range:NSMakeRange(0, [filenameWithoutExtension length])]; + + NSData *data = nil; + + int imageScale = (int)ceilf([UIScreen mainScreen].scale); + while (data == nil && imageScale > 1) { + NSString *selectorName = [filenameWithoutExtension stringByAppendingFormat:@"%dx_%@", + imageScale, + fileExtension]; + SEL selector = NSSelectorFromString(selectorName); + if ([PFResources respondsToSelector:selector]) { + data = (NSData *)[PFResources performSelector:selector]; + } + if (data == nil) { + imageScale--; + } + } + if (!data) { + NSString *selectorName = [filenameWithoutExtension stringByAppendingFormat:@"_%@", fileExtension]; + SEL selector = NSSelectorFromString(selectorName); + data = (NSData *)[PFResources performSelector:selector]; + } + image = [[UIImage alloc] initWithData:data]; + + // we need to indicate to the framework that the data is already a 2x image, otherwise the framework + // stretches the image by 2x again. To do that, we drop down to CGImage layer to take advantage of + // +[UIImage imageWithCGImage:scale:orientation] + return [UIImage imageWithCGImage:image.CGImage scale:imageScale orientation:image.imageOrientation]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.h b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.h new file mode 100644 index 0000000..e6c1f05 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import +#import + +extern CGRect PFRectMakeWithOriginSize(CGPoint origin, CGSize size); +extern CGRect PFRectMakeWithOrigin(CGPoint origin); +extern CGRect PFRectMakeWithSize(CGSize size); + +extern CGRect PFRectMakeWithSizeCenteredInRect(CGSize size, CGRect rect); +extern CGSize PFSizeMin(CGSize size1, CGSize size2); diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.m b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.m new file mode 100644 index 0000000..0aeb75c --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFRect.m @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFRect.h" + +CGRect PFRectMakeWithOriginSize(CGPoint origin, CGSize size) { + return CGRectMake(origin.x, origin.y, size.width, size.height); +} + +CGRect PFRectMakeWithOrigin(CGPoint origin) { + return PFRectMakeWithOriginSize(origin, CGSizeZero); +} + +CGRect PFRectMakeWithSize(CGSize size) { + return PFRectMakeWithOriginSize(CGPointZero, size); +} + +CGRect PFRectMakeWithSizeCenteredInRect(CGSize size, CGRect rect) { + CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); + CGPoint origin = CGPointMake(floorf(center.x - size.width / 2.0f), + floorf(center.y - size.height / 2.0f)); + return PFRectMakeWithOriginSize(origin, size); +} + +CGSize PFSizeMin(CGSize size1, CGSize size2) { + CGSize size = CGSizeZero; + size.width = MIN(size1.width, size2.width); + size.height = MIN(size1.height, size2.height); + return size; +} diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.h b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.h new file mode 100644 index 0000000..3478c95 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFUIAlertView : UIAlertView + ++ (void)showAlertViewWithTitle:(NSString *)title + error:(NSError *)error; ++ (void)showAlertViewWithTitle:(NSString *)title + message:(NSString *)message; ++ (void)showAlertViewWithTitle:(NSString *)title + message:(NSString *)message + cancelButtonTitle:(NSString *)cancelButtonTitle; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.m b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.m new file mode 100644 index 0000000..e958829 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Extensions/PFUIAlertView.m @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFUIAlertView.h" + +#import "PFLocalization.h" + +@implementation PFUIAlertView + ++ (void)showAlertViewWithTitle:(NSString *)title error:(NSError *)error { + NSString *message = error.userInfo[@"error"]; + if (!message) { + message = [error.userInfo[@"originalError"] localizedDescription]; + } + if (!message) { + message = [error localizedDescription]; + } + [self showAlertViewWithTitle:title message:message]; +} + ++ (void)showAlertViewWithTitle:(NSString *)title message:(NSString *)message { + [self showAlertViewWithTitle:title + message:message + cancelButtonTitle:NSLocalizedString(@"OK", @"OK")]; +} + ++ (void)showAlertViewWithTitle:(NSString *)title + message:(NSString *)message + cancelButtonTitle:(NSString *)cancelButtonTitle { + UIAlertView *alertView = [[self alloc] initWithTitle:title + message:message + delegate:nil + cancelButtonTitle:cancelButtonTitle + otherButtonTitles:nil]; + [alertView show]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.h b/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.h new file mode 100644 index 0000000..36c2337 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +/*! + A memory cache for UIImage, based on NSCache + */ +@interface PFImageCache : NSCache + ++ (instancetype)sharedCache; + +- (void)setImage:(UIImage *)image forURL:(NSURL *)url; +- (UIImage *)imageForURL:(NSURL *)url; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.m b/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.m new file mode 100644 index 0000000..260cd50 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/PFImageCache.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFImageCache.h" + +static NSString *PFImageCacheKeyFromURL(NSURL *url) { + return [url absoluteString]; +} + +@implementation PFImageCache + ++ (instancetype)sharedCache { + static dispatch_once_t onceToken; + static PFImageCache *sharedCache; + dispatch_once(&onceToken, ^{ + sharedCache = [[self alloc] init]; + }); + return sharedCache; +} + +- (void)setImage:(UIImage *)image forURL:(NSURL *)url { + [self setObject:image forKey:PFImageCacheKeyFromURL(url)]; +} + +- (UIImage *)imageForURL:(NSURL *)url { + return [self objectForKey:PFImageCacheKeyFromURL(url)]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/PFLocalization.h b/Pods/ParseUI/ParseUI/Classes/Internal/PFLocalization.h new file mode 100644 index 0000000..8477d8f --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/PFLocalization.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef ParseUI_PFLocalization_h +#define ParseUI_PFLocalization_h + +#undef NSLocalizedString +#define NSLocalizedString(key, comment) \ +[[NSBundle bundleForClass:[self class]] localizedStringForKey:key value:nil table:@"ParseUI"] + +#endif diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.h b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.h new file mode 100644 index 0000000..c9772ef --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +typedef NS_ENUM(uint8_t, PFActionButtonStyle) +{ + PFActionButtonStyleNormal, + PFActionButtonStyleWide +}; + +@class PFActionButtonConfiguration; + +@interface PFActionButton : UIButton + +@property (nonatomic, assign, getter=isLoading) BOOL loading; + +@property (nonatomic, assign) PFActionButtonStyle buttonStyle; + +///-------------------------------------- +/// @name Class +///-------------------------------------- + ++ (NSString *)titleForButtonStyle:(PFActionButtonStyle)buttonStyle; + +///-------------------------------------- +/// @name Init +///-------------------------------------- + +- (instancetype)initWithConfiguration:(PFActionButtonConfiguration *)configuration + buttonStyle:(PFActionButtonStyle)buttonStyle NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; + +@end + +@interface PFActionButtonConfiguration : NSObject + +@property (nonatomic, strong, readonly) UIColor *backgroundImageColor; +@property (nonatomic, strong, readonly) UIImage *image; + +- (instancetype)initWithBackgroundImageColor:(UIColor *)backgroundImageColor + image:(UIImage *)image NS_DESIGNATED_INITIALIZER; + +- (void)setTitle:(NSString *)title forButtonStyle:(PFActionButtonStyle)style; +- (NSString *)titleForButtonStyle:(PFActionButtonStyle)style; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.m b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.m new file mode 100644 index 0000000..f741d35 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFActionButton.m @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFActionButton.h" + +#import "PFImage.h" +#import "PFRect.h" + +static const UIEdgeInsets PFActionButtonContentEdgeInsets = { .top = 0.0f, .left = 12.0f, .bottom = 0.0f, .right = 0.0f }; + +@interface PFActionButton () +{ + UIActivityIndicatorView *_activityIndicatorView; +} + +@property (nonatomic, strong) PFActionButtonConfiguration *configuration; + +- (instancetype)initWithCoder:(nonnull NSCoder *)decoder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; + +@end + +@implementation PFActionButton + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + return [super initWithFrame:frame]; +} + +- (instancetype)initWithCoder:(nonnull NSCoder *)decoder { + return [super initWithCoder:decoder]; +} + +- (instancetype)initWithConfiguration:(PFActionButtonConfiguration *)configuration + buttonStyle:(PFActionButtonStyle)buttonStyle { + self = [super initWithFrame:CGRectZero]; + if (!self) return nil; + + _buttonStyle = buttonStyle; + _configuration = configuration; + + self.backgroundColor = [UIColor clearColor]; + self.titleLabel.font = [UIFont systemFontOfSize:16.0f]; + + self.contentEdgeInsets = UIEdgeInsetsZero; + self.imageEdgeInsets = UIEdgeInsetsZero; + + UIImage *backgroundImage = [PFImage imageWithColor:configuration.backgroundImageColor cornerRadius:4.0f]; + [self setBackgroundImage:backgroundImage forState:UIControlStateNormal]; + + [self setImage:configuration.image forState:UIControlStateNormal]; + + [self setTitle:[configuration titleForButtonStyle:buttonStyle] + forState:UIControlStateNormal]; + + return self; +} + +#pragma mark - +#pragma mark Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + + _activityIndicatorView.center = self.imageView.center; + self.imageView.alpha = (self.loading ? 0.0f : 1.0f); +} + +- (CGSize)sizeThatFits:(CGSize)boundingSize { + CGSize size = CGSizeZero; + size.width = MAX([super sizeThatFits:boundingSize].width, boundingSize.width); + size.height = MIN(44.0f, boundingSize.height); + return size; +} + +- (CGRect)imageRectForContentRect:(CGRect)contentRect { + CGRect imageRect = PFRectMakeWithSize([self imageForState:UIControlStateNormal].size); + imageRect.origin.x = PFActionButtonContentEdgeInsets.left; + imageRect.origin.y = CGRectGetMidY(contentRect) - CGRectGetMidY(imageRect); + return imageRect; +} + +- (CGRect)titleRectForContentRect:(CGRect)contentRect { + contentRect.origin.x = CGRectGetMaxX([self imageRectForContentRect:contentRect]); + contentRect.size.width = CGRectGetWidth(self.bounds) - CGRectGetMaxX([self imageRectForContentRect:contentRect]); + + CGSize size = [super titleRectForContentRect:contentRect].size; + CGRect rect = PFRectMakeWithSizeCenteredInRect(size, contentRect); + return rect; +} + +#pragma mark - +#pragma mark Configuration + ++ (UIColor *)backgroundImageColor { + return [UIColor clearColor]; +} + ++ (NSString *)titleForButtonStyle:(PFActionButtonStyle)buttonStyle { + return nil; +} + +#pragma mark - +#pragma mark Accessors + +- (void)setLoading:(BOOL)loading { + if (self.loading != loading) { + if (loading) { + if (!_activityIndicatorView) { + _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + } + + [_activityIndicatorView startAnimating]; + [self addSubview:_activityIndicatorView]; + [self setNeedsLayout]; + } else { + [_activityIndicatorView stopAnimating]; + [_activityIndicatorView removeFromSuperview]; + } + + self.imageView.alpha = (loading ? 0.0f : 1.0f); + } +} + +- (BOOL)isLoading { + return [_activityIndicatorView isAnimating]; +} + +- (void)setButtonStyle:(PFActionButtonStyle)buttonStyle { + if (self.buttonStyle != buttonStyle) { + _buttonStyle = buttonStyle; + + [self setTitle:[self.configuration titleForButtonStyle:self.buttonStyle] forState:UIControlStateNormal]; + } +} + +@end + +@interface PFActionButtonConfiguration () { + NSMutableDictionary *_titlesDictionary; +} + +@property (nonatomic, strong, readwrite) UIColor *backgroundImageColor; +@property (nonatomic, strong, readwrite) UIImage *image; + +@end + +@implementation PFActionButtonConfiguration + +#pragma mark - +#pragma mark Init + +- (instancetype)init { + return [self initWithBackgroundImageColor:nil image:nil]; +} + +- (instancetype)initWithBackgroundImageColor:(UIColor *)backgroundImageColor + image:(UIImage *)image { + self = [super init]; + if (!self) return nil; + + _backgroundImageColor = backgroundImageColor; + _image = image; + + return self; +} + +#pragma mark - +#pragma mark Title + +- (void)setTitle:(NSString *)title forButtonStyle:(PFActionButtonStyle)style { + if (!_titlesDictionary) { + _titlesDictionary = [NSMutableDictionary dictionaryWithCapacity:style]; + } + _titlesDictionary[@(style)] = title; +} + +- (NSString *)titleForButtonStyle:(PFActionButtonStyle)style { + return _titlesDictionary[@(style)]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.h b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.h new file mode 100644 index 0000000..c34e4db --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFDismissButton : UIButton + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.m b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.m new file mode 100644 index 0000000..ba40367 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.m @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFDismissButton.h" + +#import "PFRect.h" + +@implementation PFDismissButton + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (!self) return nil; + + [self setImage:[self _defaultImage] forState:UIControlStateNormal]; + + return self; +} + +#pragma mark - +#pragma mark Init + +- (UIImage *)_defaultImage { + CGRect imageRect = PFRectMakeWithSize(CGSizeMake(22.0f, 22.0f)); + + UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0f); + + [[UIColor colorWithRed:91.0f/255.0f green:107.0f/255.0f blue:118.0f/255.0f alpha:1.0f] setStroke]; + + UIBezierPath *path = [UIBezierPath bezierPath]; + + [path moveToPoint:CGPointZero]; + [path addLineToPoint:CGPointMake(CGRectGetMaxX(imageRect), CGRectGetMaxY(imageRect))]; + + [path moveToPoint:CGPointMake(CGRectGetMaxX(imageRect), CGRectGetMinY(imageRect))]; + [path addLineToPoint:CGPointMake(CGRectGetMinX(imageRect), CGRectGetMaxY(imageRect))]; + + path.lineWidth = 2.0f; + + [path stroke]; + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +#pragma mark - +#pragma mark UIView + +- (CGSize)sizeThatFits:(CGSize)boundingSize { + CGSize size = CGSizeZero; + size.width = MIN(22.0f, boundingSize.width); + size.height = MIN(22.0f, boundingSize.height); + return size; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + CGRect bigBounds = CGRectInset(self.bounds, -22.0f, -22.0f); + return CGRectContainsPoint(bigBounds, point); +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.h b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.h new file mode 100644 index 0000000..8517180 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFPrimaryButton : UIButton + +@property (nonatomic, assign, getter=isLoading) BOOL loading; + +- (instancetype)initWithBackgroundImageColor:(UIColor *)color NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.m b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.m new file mode 100644 index 0000000..5c5a076 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.m @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFPrimaryButton.h" + +#import "PFImage.h" +#import "PFRect.h" + +@interface PFPrimaryButton () +{ + UIActivityIndicatorView *_activityIndicatorView; +} + +- (instancetype)initWithCoder:(nonnull NSCoder *)decoder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; + +@end + +@implementation PFPrimaryButton + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + return [super initWithFrame:frame]; +} + +- (instancetype)initWithCoder:(nonnull NSCoder *)decoder { + return [super initWithCoder:decoder]; +} + +- (instancetype)initWithBackgroundImageColor:(UIColor *)color { + self = [super initWithFrame:CGRectZero]; + if (!self) return nil; + + [self setBackgroundImage:[PFImage imageWithColor:color] forState:UIControlStateNormal]; + + self.titleLabel.font = [UIFont systemFontOfSize:20.0f]; + self.contentVerticalAlignment = UIControlContentHorizontalAlignmentCenter; + self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; + + return self; +} + +#pragma mark - +#pragma mark Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGFloat activityIndicatorRightInset = 12.0f; + + CGRect activityIndicatorFrame = PFRectMakeWithSizeCenteredInRect(_activityIndicatorView.bounds.size, self.bounds); + activityIndicatorFrame.origin.x = (CGRectGetMinX(self.titleLabel.frame) + - CGRectGetWidth(activityIndicatorFrame) + - activityIndicatorRightInset); + _activityIndicatorView.frame = activityIndicatorFrame; +} + +- (CGSize)sizeThatFits:(CGSize)boundingSize { + CGSize size = CGSizeZero; + size.width = boundingSize.width; + size.height = MIN(56.0f, boundingSize.height); + return size; +} + +#pragma mark - +#pragma mark Accessors + +- (void)setLoading:(BOOL)loading { + if (self.loading != loading) { + if (loading) { + if (!_activityIndicatorView) { + _activityIndicatorView = [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + } + + [_activityIndicatorView startAnimating]; + [self addSubview:_activityIndicatorView]; + [self setNeedsLayout]; + } else { + [_activityIndicatorView stopAnimating]; + [_activityIndicatorView removeFromSuperview]; + } + } +} + +- (BOOL)isLoading { + return [_activityIndicatorView isAnimating]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.h b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.h new file mode 100644 index 0000000..75db8f2 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFTextButton : UIButton + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.m b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.m new file mode 100644 index 0000000..9ada726 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/Buttons/PFTextButton.m @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFTextButton.h" + +@implementation PFTextButton + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (!self) return nil; + + self.titleLabel.font = [UIFont systemFontOfSize:16.0f]; + [self setTitleColor:[UIColor colorWithRed:82.0f/255.0f + green:152.0f/255.0f + blue:252.0f/255.0f + alpha:1.0f] + forState:UIControlStateNormal]; + + return self; +} + +#pragma mark - +#pragma mark UIView + +- (CGSize)sizeThatFits:(CGSize)boundingSize { + CGSize size = [super sizeThatFits:boundingSize]; + size.width = MAX(32.0f, boundingSize.width); + size.height = MIN(32.0f, boundingSize.height); + return size; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.h b/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.h new file mode 100644 index 0000000..02b2a90 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +/*! + A loading view that is used to show users that data is being loaded before any data is available. + */ +@interface PFLoadingView : UIView + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.m b/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.m new file mode 100644 index 0000000..a9b7688 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Internal/Views/PFLoadingView.m @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFLoadingView.h" + +#import "PFRect.h" +#import "PFLocalization.h" + +@interface PFLoadingView () + +@property (nonatomic, strong) UILabel *loadingLabel; +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; + +@end + +@implementation PFLoadingView + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + [_activityIndicator startAnimating]; + [self addSubview:_activityIndicator]; + + _loadingLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _loadingLabel.text = NSLocalizedString(@"Loading...", @"Loading message of PFQueryTableViewController"); + _loadingLabel.backgroundColor = [UIColor clearColor]; + _loadingLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + _loadingLabel.shadowColor = [UIColor whiteColor]; + [_loadingLabel sizeToFit]; + [self addSubview:_loadingLabel]; + } + return self; +} + +#pragma mark - +#pragma mark UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + const CGRect bounds = self.bounds; + + CGFloat viewsInset = 4.0f; + CGFloat startX = floorf((CGRectGetMaxX(bounds) + - CGRectGetWidth(_loadingLabel.frame) + - CGRectGetWidth(_activityIndicator.frame) + - viewsInset) + / 2.0f); + + CGRect activityIndicatorFrame = PFRectMakeWithSizeCenteredInRect(_activityIndicator.frame.size, bounds); + activityIndicatorFrame.origin.x = startX; + _activityIndicator.frame = activityIndicatorFrame; + + CGRect loadingLabelFrame = PFRectMakeWithSizeCenteredInRect(_loadingLabel.frame.size, bounds); + loadingLabelFrame.origin.x = CGRectGetMaxX(activityIndicatorFrame) + viewsInset; + _loadingLabel.frame = loadingLabelFrame; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.h b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.h new file mode 100644 index 0000000..ee71b02 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + `PFLogInFields` bitmask specifies the log in elements which are enabled in the view. + + @see PFLogInViewController + @see PFLogInView + */ +typedef NS_OPTIONS(NSInteger, PFLogInFields) { + /*! No fields. */ + PFLogInFieldsNone = 0, + /*! Username and password fields. */ + PFLogInFieldsUsernameAndPassword = 1 << 0, + /*! Forgot password button. */ + PFLogInFieldsPasswordForgotten = 1 << 1, + /*! Login button. */ + PFLogInFieldsLogInButton = 1 << 2, + /*! Button to login with Facebook. */ + PFLogInFieldsFacebook = 1 << 3, + /*! Button to login with Twitter. */ + PFLogInFieldsTwitter = 1 << 4, + /*! Signup Button. */ + PFLogInFieldsSignUpButton = 1 << 5, + /*! Dismiss Button. */ + PFLogInFieldsDismissButton = 1 << 6, + + /*! Default value. Combines Username, Password, Login, Signup, Forgot Password and Dismiss buttons. */ + PFLogInFieldsDefault = (PFLogInFieldsUsernameAndPassword | + PFLogInFieldsLogInButton | + PFLogInFieldsSignUpButton | + PFLogInFieldsPasswordForgotten | + PFLogInFieldsDismissButton) +}; + +@class PFTextField; + +/*! + The `PFLogInView` class provides a standard log in interface for authenticating a . + */ +@interface PFLogInView : UIScrollView + +///-------------------------------------- +/// @name Creating Log In View +///-------------------------------------- + +/*! + @abstract Initializes the view with the specified log in elements. + + @param fields A bitmask specifying the log in elements which are enabled in the view + + @returns An initialized `PFLogInView` object or `nil` if the object couldn't be created. + + @see PFLogInFields + */ +- (instancetype)initWithFields:(PFLogInFields)fields; + +/*! + @abstract The view controller that will present this view. + + @discussion Used to lay out elements correctly when the presenting view controller has translucent elements. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, weak) UIViewController *presentingViewController; + +///-------------------------------------- +/// @name Customizing the Logo +///-------------------------------------- + +/// The logo. By default, it is the Parse logo. +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) UIView *logo; + +///-------------------------------------- +/// @name Configure Username Behaviour +///-------------------------------------- + +/*! + @abstract If email should be used to log in, instead of username + + @discussion By default, this is set to `NO`. + */ +@property (nonatomic, assign) BOOL emailAsUsername; + +///-------------------------------------- +/// @name Log In Elements +///-------------------------------------- + +/*! + @abstract The bitmask which specifies the enabled log in elements in the view. + */ +@property (nonatomic, assign, readonly) PFLogInFields fields; + +/*! + @abstract The username text field. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *usernameField; + +/*! + @abstract The password text field. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *passwordField; + +/*! + @abstract The password forgotten button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *passwordForgottenButton; + +/*! + @abstract The log in button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *logInButton; + +/*! + @abstract The Facebook button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *facebookButton; + +/*! + @abstract The Twitter button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *twitterButton; + +/*! + @abstract The sign up button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *signUpButton; + +/*! + @abstract It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *dismissButton; + +/*! + @abstract The facebook/twitter login label. + + @deprecated This property is deprecated and will always be nil. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UILabel *externalLogInLabel __attribute__(PARSE_UI_DEPRECATED("This property is deprecated and will always be nil.")); + +/*! + @abstract The sign up label. + + @deprecated This property is deprecated and will always be nil. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UILabel *signUpLabel __attribute__(PARSE_UI_DEPRECATED("This property is deprecated and will always be nil.")); + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.m b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.m new file mode 100644 index 0000000..4aed98d --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView.m @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFLogInView.h" + +#import "PFActionButton.h" +#import "PFColor.h" +#import "PFDismissButton.h" +#import "PFImage.h" +#import "PFLocalization.h" +#import "PFPrimaryButton.h" +#import "PFRect.h" +#import "PFTextButton.h" +#import "PFTextField.h" + +static NSString *const PFLogInViewDefaultLogoImageName = @"parse_logo.png"; +static NSString *const PFLogInViewDefaultFacebookButtonImageName = @"facebook_icon.png"; +static NSString *const PFLogInViewDefaultTwitterButtonImageName = @"twitter_icon.png"; + +@implementation PFLogInView + +///-------------------------------------- +#pragma mark - Class +///-------------------------------------- + ++ (PFActionButtonConfiguration *)_defaultSignUpButtonConfiguration { + PFActionButtonConfiguration *configuration = [[PFActionButtonConfiguration alloc] initWithBackgroundImageColor:[PFColor signupButtonBackgroundColor] + image:nil]; + NSString *title = NSLocalizedString(@"Sign Up", @"Sign Up"); + [configuration setTitle:title forButtonStyle:PFActionButtonStyleNormal]; + [configuration setTitle:title forButtonStyle:PFActionButtonStyleWide]; + + return configuration; +} + ++ (PFActionButtonConfiguration *)_defaultFacebookButtonConfiguration { + PFActionButtonConfiguration *configuration = [[PFActionButtonConfiguration alloc] initWithBackgroundImageColor:[PFColor facebookButtonBackgroundColor] + image:[PFImage imageNamed:PFLogInViewDefaultFacebookButtonImageName]]; + + [configuration setTitle:NSLocalizedString(@"Facebook", @"Facebook") + forButtonStyle:PFActionButtonStyleNormal]; + [configuration setTitle:NSLocalizedString(@"Log In with Facebook", @"Log In with Facebook") + + forButtonStyle:PFActionButtonStyleWide]; + + return configuration; +} + ++ (PFActionButtonConfiguration *)_defaultTwitterButtonConfiguration { + PFActionButtonConfiguration *configuration = [[PFActionButtonConfiguration alloc] initWithBackgroundImageColor:[PFColor twitterButtonBackgroundColor] + image:[PFImage imageNamed:PFLogInViewDefaultTwitterButtonImageName]]; + + [configuration setTitle:NSLocalizedString(@"Twitter", @"Twitter") + forButtonStyle:PFActionButtonStyleNormal]; + [configuration setTitle:NSLocalizedString(@"Log In with Twitter", @"Log In with Twitter") + + forButtonStyle:PFActionButtonStyleWide]; + + return configuration; +} + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)initWithFields:(PFLogInFields)otherFields { + self = [super initWithFrame:CGRectZero]; + if (!self) return nil; + + [PFLogInView _validateFields:otherFields]; + + self.opaque = YES; + self.backgroundColor = [PFColor commonBackgroundColor]; + + _fields = otherFields; + + _logo = [[UIImageView alloc] initWithImage:[PFImage imageNamed:PFLogInViewDefaultLogoImageName]]; + _logo.contentMode = UIViewContentModeScaleAspectFit; + [self addSubview:_logo]; + + [self _updateAllFields]; + + return self; +} + +///-------------------------------------- +#pragma mark - Fields +///-------------------------------------- + +- (void)_updateAllFields { + if (_fields & PFLogInFieldsDismissButton) { + if (!_dismissButton) { + _dismissButton = [[PFDismissButton alloc] initWithFrame:CGRectZero]; + [self addSubview:_dismissButton]; + } + } else { + [_dismissButton removeFromSuperview]; + _dismissButton = nil; + } + + if (_fields & PFLogInFieldsUsernameAndPassword) { + if (!_usernameField) { + _usernameField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:(PFTextFieldSeparatorStyleTop | + PFTextFieldSeparatorStyleBottom)]; + _usernameField.autocorrectionType = UITextAutocorrectionTypeNo; + _usernameField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _usernameField.returnKeyType = UIReturnKeyNext; + [self addSubview:_usernameField]; + [self _updateUsernameFieldStyle]; + } + + if (!_passwordField) { + _passwordField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:PFTextFieldSeparatorStyleBottom]; + _passwordField.placeholder = NSLocalizedString(@"Password", @"Password"); + _passwordField.secureTextEntry = YES; + _passwordField.autocorrectionType = UITextAutocorrectionTypeNo; + _passwordField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _passwordField.returnKeyType = UIReturnKeyDone; + [self addSubview:_passwordField]; + } + } else { + [_usernameField removeFromSuperview]; + _usernameField = nil; + + [_passwordField removeFromSuperview]; + _passwordField = nil; + } + + if (_fields & PFLogInFieldsSignUpButton) { + if (!_signUpButton) { + _signUpButton = [[PFActionButton alloc] initWithConfiguration:[[self class] _defaultSignUpButtonConfiguration] + buttonStyle:PFActionButtonStyleNormal]; + [self addSubview:_signUpButton]; + } + } else { + [_signUpButton removeFromSuperview]; + _signUpButton = nil; + } + + if (_fields & PFLogInFieldsPasswordForgotten) { + if (!_passwordForgottenButton) { + _passwordForgottenButton = [[PFTextButton alloc] initWithFrame:CGRectZero]; + [_passwordForgottenButton setTitle:NSLocalizedString(@"Forgot Password?", "Forgot Password?") + forState:UIControlStateNormal]; + [self addSubview:_passwordForgottenButton]; + } + } else { + [_passwordForgottenButton removeFromSuperview]; + _passwordForgottenButton = nil; + } + + if (_fields & PFLogInFieldsLogInButton) { + if (!_logInButton) { + _logInButton = [[PFPrimaryButton alloc] initWithBackgroundImageColor:[PFColor loginButtonBackgroundColor]]; + [_logInButton setTitle:NSLocalizedString(@"Log In", @"Log In") forState:UIControlStateNormal]; + [self addSubview:_logInButton]; + } + } else { + [_logInButton removeFromSuperview]; + _logInButton = nil; + } + + if (_fields & PFLogInFieldsFacebook) { + if (!_facebookButton) { + _facebookButton = [[PFActionButton alloc] initWithConfiguration:[[self class] _defaultFacebookButtonConfiguration] + buttonStyle:PFActionButtonStyleNormal]; + [self addSubview:_facebookButton]; + } + } else { + [_facebookButton removeFromSuperview]; + _facebookButton = nil; + } + + if (_fields & PFLogInFieldsTwitter) { + if (!_twitterButton) { + _twitterButton = [[PFActionButton alloc] initWithConfiguration:[[self class] _defaultTwitterButtonConfiguration] + buttonStyle:PFActionButtonStyleNormal]; + [self addSubview:_twitterButton]; + } + } else { + [_twitterButton removeFromSuperview]; + _twitterButton = nil; + } +} + +///-------------------------------------- +#pragma mark - UIView +///-------------------------------------- + +- (void)layoutSubviews { + [super layoutSubviews]; + + const CGRect bounds = PFRectMakeWithOriginSize(CGPointZero, self.bounds.size); + + if (_dismissButton) { + CGPoint origin = CGPointMake(16.0f, 16.0f); + + // In iOS 7+, if this view is presented fullscreen, it's top edge will be behind the status bar. + // This lets us move down the dismiss button a bit so that it's not covered by the status bar. + if ([self.presentingViewController respondsToSelector:@selector(topLayoutGuide)]) { + origin.y += self.presentingViewController.topLayoutGuide.length; + } + + CGRect frame = PFRectMakeWithOriginSize(origin, [_dismissButton sizeThatFits:bounds.size]); + _dismissButton.frame = frame; + } + + CGRect contentRect = PFRectMakeWithSizeCenteredInRect(PFSizeMin(bounds.size, [self _maxContentSize]), + bounds); + const CGSize contentSizeScale = [self _contentSizeScaleForContentSize:contentRect.size]; + + CGFloat socialButtonsDefaultInset = 16.0f; + UIEdgeInsets socialButtonsRectInsets = UIEdgeInsetsZero; + if (CGRectGetMinX(contentRect) <= CGRectGetMinX(bounds)) { + socialButtonsRectInsets = UIEdgeInsetsMake(0.0f, + socialButtonsDefaultInset, + 0.0f, + socialButtonsDefaultInset); + } + CGRect socialButtonsRect = UIEdgeInsetsInsetRect(contentRect, socialButtonsRectInsets); + + if (_signUpButton) { + CGSize buttonSize = [_signUpButton sizeThatFits:socialButtonsRect.size]; + [(PFActionButton *)_signUpButton setButtonStyle:PFActionButtonStyleWide]; + + CGRect frame = PFRectMakeWithSizeCenteredInRect(buttonSize, socialButtonsRect); + frame.origin.y = CGRectGetMaxY(socialButtonsRect) - CGRectGetHeight(frame) - socialButtonsRectInsets.left; + _signUpButton.frame = frame; + + contentRect.size.height = CGRectGetMinY(frame) - CGRectGetMinY(contentRect); + socialButtonsRect = UIEdgeInsetsInsetRect(contentRect, socialButtonsRectInsets); + } + + if (_facebookButton && _twitterButton) { + CGSize buttonSize = [_facebookButton sizeThatFits:socialButtonsRect.size]; + buttonSize.width = (socialButtonsRect.size.width - socialButtonsDefaultInset) / 2.0f; + + CGRect frame = PFRectMakeWithOriginSize(socialButtonsRect.origin, buttonSize); + frame.origin.y = CGRectGetMaxY(socialButtonsRect) - buttonSize.height - socialButtonsDefaultInset; + [(PFActionButton *)_facebookButton setButtonStyle:PFActionButtonStyleNormal]; + _facebookButton.frame = frame; + + frame.origin.x = CGRectGetMaxX(frame) + socialButtonsDefaultInset; + [(PFActionButton *)_twitterButton setButtonStyle:PFActionButtonStyleNormal]; + _twitterButton.frame = frame; + + contentRect.size.height = CGRectGetMinY(frame) - CGRectGetMinY(contentRect); + socialButtonsRect = UIEdgeInsetsInsetRect(contentRect, socialButtonsRectInsets); + } else if (_facebookButton) { + CGSize buttonSize = [_facebookButton sizeThatFits:socialButtonsRect.size]; + CGRect frame = PFRectMakeWithOriginSize(socialButtonsRect.origin, buttonSize); + frame.origin.y = CGRectGetMaxY(socialButtonsRect) - buttonSize.height - socialButtonsDefaultInset; + _facebookButton.frame = frame; + + [(PFActionButton *)_facebookButton setButtonStyle:PFActionButtonStyleWide]; + + contentRect.size.height = CGRectGetMinY(frame) - CGRectGetMinY(contentRect); + socialButtonsRect = UIEdgeInsetsInsetRect(contentRect, socialButtonsRectInsets); + } else if (_twitterButton) { + CGSize buttonSize = [_twitterButton sizeThatFits:socialButtonsRect.size]; + CGRect frame = PFRectMakeWithOriginSize(socialButtonsRect.origin, buttonSize); + frame.origin.y = CGRectGetMaxY(socialButtonsRect) - buttonSize.height - socialButtonsDefaultInset; + _twitterButton.frame = frame; + + [(PFActionButton *)_twitterButton setButtonStyle:PFActionButtonStyleWide]; + + contentRect.size.height = CGRectGetMinY(frame) - CGRectGetMinY(contentRect); + socialButtonsRect = UIEdgeInsetsInsetRect(contentRect, socialButtonsRectInsets); + } + + if (_signUpButton || _facebookButton || _twitterButton) { + contentRect.size.height -= socialButtonsDefaultInset; + } + + const CGRect loginContentRect = PFRectMakeWithSizeCenteredInRect([self _loginContentSizeThatFits:contentRect.size + withContentSizeScale:contentSizeScale], + contentRect); + const CGSize loginContentSize = loginContentRect.size; + CGFloat currentY = CGRectGetMinY(loginContentRect); + + if (_logo) { + CGFloat logoTopInset = (CGRectGetMinX(contentRect) > 0.0f ? 36.0f : 88.0f) * contentSizeScale.height; + CGFloat logoBottomInset = floorf(36.0f * contentSizeScale.height); + + CGFloat logoAvailableHeight = floorf(68.0f * contentSizeScale.height); + + CGSize logoSize = [_logo sizeThatFits:CGSizeMake(loginContentSize.width, logoAvailableHeight)]; + logoSize.width = MIN(loginContentSize.width, logoSize.width); + logoSize.height = MIN(logoAvailableHeight, logoSize.height); + + CGRect frame = PFRectMakeWithSizeCenteredInRect(logoSize, loginContentRect); + frame.origin.y = CGRectGetMinY(loginContentRect) + logoTopInset; + _logo.frame = CGRectIntegral(frame); + + currentY = floorf(CGRectGetMaxY(frame) + logoBottomInset); + } + + if (_usernameField) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_usernameField sizeThatFits:loginContentSize], + loginContentRect); + frame.origin.y = currentY; + _usernameField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_passwordField) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_passwordField sizeThatFits:loginContentSize], + loginContentRect); + frame.origin.y = currentY; + _passwordField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_logInButton) { + CGFloat loginButtonTopInset = floorf(24.0f * contentSizeScale.height); + + CGRect frame = PFRectMakeWithSizeCenteredInRect([_logInButton sizeThatFits:loginContentSize], loginContentRect); + frame.origin.y = currentY + loginButtonTopInset; + _logInButton.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_passwordForgottenButton) { + CGFloat forgotPasswordInset = floorf(12.0f * contentSizeScale.height); + + CGSize buttonSize = [_passwordForgottenButton sizeThatFits:loginContentSize]; + CGRect frame = PFRectMakeWithSizeCenteredInRect(buttonSize, loginContentRect); + frame.origin.y = currentY + forgotPasswordInset; + _passwordForgottenButton.frame = frame; + } +} + +- (CGSize)_loginContentSizeThatFits:(CGSize)boundingSize withContentSizeScale:(CGSize)contentSizeScale { + CGSize size = boundingSize; + size.height = 0.0f; + if (_logo) { + CGFloat logoTopInset = floorf(36.0f * contentSizeScale.height); + CGFloat logoBottomInset = floorf(36.0f * contentSizeScale.height); + + CGFloat logoAvailableHeight = floorf(68.0f * contentSizeScale.height); + + CGFloat scale = MAX(contentSizeScale.width, contentSizeScale.height); + + CGSize logoSize = [_logo sizeThatFits:CGSizeMake(boundingSize.width, logoAvailableHeight)]; + logoSize.height *= scale; + logoSize.width *= scale; + + size.height += logoSize.height + logoTopInset + logoBottomInset; + } + if (_usernameField) { + CGSize fieldSize = [_usernameField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_passwordField) { + CGSize fieldSize = [_passwordField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_logInButton) { + CGFloat loginButtonTopInset = 24.0f * contentSizeScale.height; + + CGSize buttonSize = [_logInButton sizeThatFits:boundingSize]; + + size.height += buttonSize.height + loginButtonTopInset; + } + if (_passwordForgottenButton) { + CGFloat forgotPasswordInset = 12.0f * contentSizeScale.height; + + UIView *button = _signUpButton ?: _passwordForgottenButton; + CGSize buttonSize = [button sizeThatFits:boundingSize]; + + size.height += buttonSize.height + forgotPasswordInset * 2.0f; + } + size.width = floorf(size.width); + size.height = floorf(size.height); + + return size; +} + +- (CGSize)_maxContentSize { + return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? CGSizeMake(420.0f, 550.0f) : CGSizeMake(500.0f, 800.0f)); +} + +- (CGSize)_contentSizeScaleForContentSize:(CGSize)contentSize { + CGSize maxContentSize = [self _maxContentSize]; + if (maxContentSize.width < contentSize.width && + maxContentSize.height < contentSize.height) { + return CGSizeMake(1.0f, 1.0f); + } + + CGSize contentSizeScale = CGSizeMake(contentSize.width / maxContentSize.width, + contentSize.height / maxContentSize.height); + return contentSizeScale; +} + +///-------------------------------------- +#pragma mark - Accessors +///-------------------------------------- + +- (void)setFields:(PFLogInFields)fields { + if (_fields != fields) { + _fields = fields; + [self _updateAllFields]; + [self setNeedsLayout]; + } +} + +- (void)setLogo:(UIView *)logo { + if (self.logo != logo) { + [_logo removeFromSuperview]; + _logo = logo; + [self addSubview:_logo]; + + [self setNeedsLayout]; + } +} + +- (void)setEmailAsUsername:(BOOL)otherEmailAsUsername { + if (_emailAsUsername != otherEmailAsUsername) { + _emailAsUsername = otherEmailAsUsername; + + [self _updateUsernameFieldStyle]; + } +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + ++ (void)_validateFields:(PFLogInFields)fields { + if (fields == PFLogInFieldsNone) { + [NSException raise:NSInvalidArgumentException + format:@"Fields must be set before initializing PFLogInView."]; + } + + if (fields & PFLogInFieldsLogInButton) { + if (!(fields & PFLogInFieldsUsernameAndPassword)) { + [NSException raise:NSInvalidArgumentException + format:@"Username and password must be enabled if done button is enabled."]; + } + } + + if (fields & PFLogInFieldsPasswordForgotten) { + if (!(fields & PFLogInFieldsUsernameAndPassword)) { + [NSException raise:NSInvalidArgumentException + format:@"Username and password must be enabled if password forgotten button is enabled."]; + } + } +} + +- (void)_updateUsernameFieldStyle { + UIKeyboardType keyboardType = UIKeyboardTypeDefault; + NSString *usernamePlaceholder = nil; + if (!_emailAsUsername) { + keyboardType = UIKeyboardTypeDefault; + usernamePlaceholder = NSLocalizedString(@"Username", @"Username"); + } else { + keyboardType = UIKeyboardTypeEmailAddress; + usernamePlaceholder = NSLocalizedString(@"Email", @"Email"); + } + + _usernameField.placeholder = usernamePlaceholder; + _usernameField.keyboardType = keyboardType; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.h b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.h new file mode 100644 index 0000000..9df9112 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import +#import + +PFUI_ASSUME_NONNULL_BEGIN + +@class PFSignUpViewController; +@class PFUser; +@protocol PFLogInViewControllerDelegate; + +/*! + The `PFLogInViewController` class presents and manages a standard authentication interface for logging in a . + */ +@interface PFLogInViewController : UIViewController + +///-------------------------------------- +/// @name Configuring Log In Elements +///-------------------------------------- + +/*! + @abstract A bitmask specifying the log in elements which are enabled in the view. + + @see PFLogInFields + */ +@property (nonatomic, assign) PFLogInFields fields; + + +/*! + @abstract The log in view. It contains all the enabled log in elements. + + @see PFLogInView + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFLogInView *logInView; + +///-------------------------------------- +/// @name Configuring Log In Behaviors +///-------------------------------------- + +/*! + @abstract The delegate that responds to the control events of `PFLogInViewController`. + + @see PFLogInViewControllerDelegate + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, weak) id delegate; + +/*! + @abstract The facebook permissions that Facebook log in requests for. + + @discussion If unspecified, the default is basic facebook permissions. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy) NSArray *facebookPermissions; + +/*! + @abstract The sign up controller if sign up is enabled. + + @discussion Use this to configure the sign up view, and the transition animation to the sign up view. + The default is a sign up view with a username, a password, a dismiss button and a sign up button. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) PFSignUpViewController *signUpController; + +/*! + @abstract Whether to prompt for the email as username on the login view. + + @discussion If set to `YES`, we'll prompt for the email in the username field. + This property value propagates to the attached . + By default, this is set to `NO`. + */ +@property (nonatomic, assign) BOOL emailAsUsername; + +@end + +///-------------------------------------- +/// @name Notifications +///-------------------------------------- + +/*! + @abstract The notification is posted immediately after the log in succeeds. + */ +extern NSString *const PFLogInSuccessNotification; + +/*! + @abstract The notification is posted immediately after the log in fails. + @discussion If the delegate prevents the log in from starting, the notification is not sent. + */ +extern NSString *const PFLogInFailureNotification; + +/*! + @abstract The notification is posted immediately after the log in is cancelled. + */ +extern NSString *const PFLogInCancelNotification; + +///-------------------------------------- +/// @name PFLogInViewControllerDelegate +///-------------------------------------- + +/*! + The `PFLogInViewControllerDelegate` protocol defines methods a delegate of a should implement. + All methods of this protocol are optional. + */ +@protocol PFLogInViewControllerDelegate + +@optional + +///-------------------------------------- +/// @name Customizing Behavior +///-------------------------------------- + +/*! + @abstract Sent to the delegate to determine whether the log in request should be submitted to the server. + + @param logInController The login view controller that is requesting the data. + @param username the username the user tries to log in with. + @param password the password the user tries to log in with. + + @returns A `BOOL` indicating whether the log in should proceed. + */ +- (BOOL)logInViewController:(PFLogInViewController *)logInController +shouldBeginLogInWithUsername:(NSString *)username + password:(NSString *)password; + +///-------------------------------------- +/// @name Responding to Actions +///-------------------------------------- + +/*! + @abstract Sent to the delegate when a is logged in. + + @param logInController The login view controller where login finished. + @param user object that is a result of the login. + */ +- (void)logInViewController:(PFLogInViewController *)logInController didLogInUser:(PFUser *)user; + +/*! + @abstract Sent to the delegate when the log in attempt fails. + + @discussion If you implement this method, PFLoginViewController will not automatically show its default + login failure alert view. Instead, you should show your custom alert view in your implementation. + + @param logInController The login view controller where login failed. + @param error `NSError` object representing the error that occured. + */ +- (void)logInViewController:(PFLogInViewController *)logInController + didFailToLogInWithError:(PFUI_NULLABLE NSError *)error; + +/*! + @abstract Sent to the delegate when the log in screen is cancelled. + + @param logInController The login view controller where login was cancelled. + */ +- (void)logInViewControllerDidCancelLogIn:(PFLogInViewController *)logInController; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.m b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.m new file mode 100644 index 0000000..a6dceae --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInViewController.m @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFLogInViewController.h" + +#import + +#import "PFActionButton.h" +#import "PFUIAlertView.h" +#import "PFLocalization.h" +#import "PFPrimaryButton.h" +#import "PFSignUpViewController.h" +#import "PFTextField.h" +#import "PFLogInView_Private.h" + +NSString *const PFLogInSuccessNotification = @"com.parse.ui.login.success"; +NSString *const PFLogInFailureNotification = @"com.parse.ui.login.failure"; +NSString *const PFLogInCancelNotification = @"com.parse.ui.login.cancel"; + +/*! + This protocol exists so that we can weakly refer to messages to pass to PFFacebookUtils without + actually taking a dependency on the symbols. + */ +@protocol WeaklyReferencedFBUtils + +// FBSDKv3 ++ (void)logInWithPermissions:(NSArray *)permissions block:(PFUserResultBlock)block; +// FBSDKv4 ++ (void)logInInBackgroundWithReadPermissions:(NSArray *)permissions block:(PFUserResultBlock)block; ++ (void)logInInBackgroundWithPublishPermissions:(NSArray *)permissions block:(PFUserResultBlock)block; + +@end + +@protocol WeaklyReferenceTwitterUtils + ++ (void)logInWithBlock:(PFUserResultBlock)block; + +@end + +@interface PFLogInViewController () { + struct { + BOOL shouldBeginLogIn : YES; + BOOL didLogInUser : YES; + BOOL didFailToLogIn : YES; + BOOL didCancelLogIn : YES; + } _delegateExistingMethods; +} + +@property (nonatomic, strong, readwrite) PFLogInView *logInView; +@property (nonatomic, assign) BOOL loading; + +@property (nonatomic, assign) CGFloat visibleKeyboardHeight; + +@end + +@implementation PFLogInViewController + +///-------------------------------------- +#pragma mark - Init +///-------------------------------------- + +- (instancetype)init { + if (self = [super init]) { + [self _commonInit]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + [self _commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super initWithCoder:decoder]) { + [self _commonInit]; + } + return self; +} + +- (void)_commonInit { + self.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + self.modalPresentationStyle = UIModalPresentationFormSheet; + _fields = PFLogInFieldsDefault; + + if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { + self.automaticallyAdjustsScrollViewInsets = NO; + } +} + +///-------------------------------------- +#pragma mark - Dealloc +///-------------------------------------- + +- (void)dealloc { + // Unregister from all notifications + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +///-------------------------------------- +#pragma mark - UIViewController +///-------------------------------------- + +- (void)loadView { + _logInView = [[PFLogInView alloc] initWithFields:_fields]; + [_logInView setPresentingViewController:self]; + self.view = _logInView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self setupHandlers]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self _registerForKeyboardNotifications]; + + if (self.navigationController && + self.fields & PFLogInFieldsDismissButton) { + self.fields = self.fields & ~PFLogInFieldsDismissButton; + + [_logInView.dismissButton removeFromSuperview]; + } +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + return UIInterfaceOrientationMaskAll; + } + + return UIInterfaceOrientationMaskPortrait; +} + +///-------------------------------------- +#pragma mark - PFLogInViewController +///-------------------------------------- + +- (PFLogInView *)logInView { + return (PFLogInView *)self.view; // self.view will call loadView if the view is nil +} + +- (void)setEmailAsUsername:(BOOL)otherEmailAsUsername { + self.logInView.emailAsUsername = otherEmailAsUsername; +} + +- (BOOL)emailAsUsername { + return self.logInView.emailAsUsername; +} + +- (void)setFields:(PFLogInFields)fields { + if (_fields != fields) { + _fields = fields; + + // Avoid force loading logInView + if (_logInView) { + _logInView.fields = fields; + } + } +} + +- (void)setDelegate:(id)delegate { + if (self.delegate != delegate) { + _delegate = delegate; + + _delegateExistingMethods.shouldBeginLogIn = [delegate respondsToSelector:@selector(logInViewController: + shouldBeginLogInWithUsername: + password:)]; + _delegateExistingMethods.didLogInUser = [delegate respondsToSelector:@selector(logInViewController: + didLogInUser:)]; + _delegateExistingMethods.didFailToLogIn = [delegate respondsToSelector:@selector(logInViewController: + didFailToLogInWithError:)]; + _delegateExistingMethods.didCancelLogIn = [delegate + respondsToSelector:@selector(logInViewControllerDidCancelLogIn:)]; + } +} + +- (PFSignUpViewController *)signUpController { + if (!_signUpController) { + _signUpController = [[PFSignUpViewController alloc] init]; + _signUpController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + _signUpController.emailAsUsername = self.emailAsUsername; + } + return _signUpController; +} + +- (void)setLoading:(BOOL)loading { + if (self.loading != loading) { + _loading = loading; + + _logInView.usernameField.enabled = !self.loading; + _logInView.passwordField.enabled = !self.loading; + _logInView.passwordForgottenButton.enabled = !self.loading; + _logInView.dismissButton.enabled = !self.loading; + } +} + +///-------------------------------------- +#pragma mark - UITextFieldDelegate +///-------------------------------------- + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + if (textField == _logInView.usernameField) { + [_logInView.passwordField becomeFirstResponder]; + } + if (textField == _logInView.passwordField) { + [self _dismissKeyboard]; + [self _loginAction]; + } + + return YES; +} + +///-------------------------------------- +#pragma mark - UIAlertViewDelegate +///-------------------------------------- + +- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex { + if (buttonIndex != [alertView cancelButtonIndex]) { + NSString *email = [alertView textFieldAtIndex:0].text; + [self _requestPasswordResetWithEmail:email]; + } +} + +///-------------------------------------- +#pragma mark - Private +///-------------------------------------- + +- (void)setupHandlers { + [_logInView.dismissButton addTarget:self + action:@selector(_dismissAction) + forControlEvents:UIControlEventTouchUpInside]; + + _logInView.usernameField.delegate = self; + _logInView.passwordField.delegate = self; + [_logInView.logInButton addTarget:self action:@selector(_loginAction) forControlEvents:UIControlEventTouchUpInside]; + [_logInView.passwordForgottenButton addTarget:self + action:@selector(_forgotPasswordAction) + forControlEvents:UIControlEventTouchUpInside]; + + [_logInView.facebookButton addTarget:self + action:@selector(_loginWithFacebook) + forControlEvents:UIControlEventTouchUpInside]; + [_logInView.twitterButton addTarget:self + action:@selector(_loginWithTwitter) + forControlEvents:UIControlEventTouchUpInside]; + + [_logInView.signUpButton addTarget:self + action:@selector(_signupAction) + forControlEvents:UIControlEventTouchUpInside]; + + UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(_dismissKeyboard)]; + [_logInView addGestureRecognizer:gestureRecognizer]; + gestureRecognizer.cancelsTouchesInView = NO; +} + +- (void)_dismissAction { + [self cancelLogIn]; + + // Normally the role of dismissing a modal controller lies on the presenting controller. + // Here we violate the principle so that the presenting modal log in controller is especially easy. + // Cons of this design is that it makes working with non-modally presented log in controller hard; + // but this concern is mitigated by the fact that navigationally presented controller should not have + // dismiss button. + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)_forgotPasswordAction PF_EXTENSION_UNAVAILABLE("") { + NSString *title = NSLocalizedString(@"Reset Password", @"Forgot password request title in PFLogInViewController"); + NSString *message = NSLocalizedString(@"Please enter the email address for your account.", + @"Email request message in PFLogInViewController"); + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title + message:message + delegate:self + cancelButtonTitle:NSLocalizedString(@"Cancel", @"Cancel") + otherButtonTitles:NSLocalizedString(@"OK", @"OK"), nil]; + alertView.alertViewStyle = UIAlertViewStylePlainTextInput; + + UITextField *textField = [alertView textFieldAtIndex:0]; + textField.placeholder = NSLocalizedString(@"Email", @"Email"); + textField.keyboardType = UIKeyboardTypeEmailAddress; + textField.returnKeyType = UIReturnKeyDone; + + [alertView show]; +} + +- (void)_requestPasswordResetWithEmail:(NSString *)email { + [PFUser requestPasswordResetForEmailInBackground:email block:^(BOOL success, NSError *error) { + if (success) { + NSString *title = NSLocalizedString(@"Password Reset", + @"Password reset success alert title in PFLogInViewController."); + NSString *message = [NSString stringWithFormat:NSLocalizedString(@"An email with reset instructions has been sent to '%@'.", + @"Password reset message in PFLogInViewController"), email]; + [PFUIAlertView showAlertViewWithTitle:title + message:message + cancelButtonTitle:NSLocalizedString(@"OK", @"OK")]; + } else { + NSString *title = NSLocalizedString(@"Password Reset Failed", + @"Password reset error alert title in PFLogInViewController."); + [PFUIAlertView showAlertViewWithTitle:title error:error]; + } + }]; +} + +#pragma mark Log In With Facebook + +- (void)_loginWithFacebook { + if (self.loading) { + return; + } + + self.loading = YES; + if ([_logInView.facebookButton isKindOfClass:[PFActionButton class]]) { + [(PFActionButton *)_logInView.facebookButton setLoading:YES]; + } + + __weak typeof(self) wself = self; + PFUserResultBlock resultBlock = ^(PFUser *user, NSError *error) { + __strong typeof(wself) sself = wself; + sself.loading = NO; + if ([_logInView.facebookButton isKindOfClass:[PFActionButton class]]) { + [(PFActionButton *)_logInView.facebookButton setLoading:NO]; + } + + if (user) { + [sself _loginDidSucceedWithUser:user]; + } else if (error) { + [sself _loginDidFailWithError:error]; + } else { + // User cancelled login. + } + }; + + Class fbUtils = NSClassFromString(@"PFFacebookUtils"); + if ([fbUtils respondsToSelector:@selector(logInWithPermissions:block:)]) { + // Facebook SDK v3 Login + [fbUtils logInWithPermissions:_facebookPermissions block:resultBlock]; + } else if ([fbUtils respondsToSelector:@selector(logInInBackgroundWithReadPermissions:block:)]) { + // Facebook SDK v4 Login + if ([self _permissionsContainsFacebookPublishPermission:_facebookPermissions]) { + [fbUtils logInInBackgroundWithPublishPermissions:_facebookPermissions block:resultBlock]; + } else { + [fbUtils logInInBackgroundWithReadPermissions:_facebookPermissions block:resultBlock]; + } + } else { + [NSException raise:NSInternalInconsistencyException + format:@"Can't find PFFacebookUtils. Please link with ParseFacebookUtils or ParseFacebookUtilsV4 to enable login with Facebook."]; + } +} + +- (BOOL)_permissionsContainsFacebookPublishPermission:(NSArray *)permissions { + for (NSString *permission in permissions) { + if ([permission hasPrefix:@"publish"] || + [permission hasPrefix:@"manage"] || + [permission isEqualToString:@"ads_management"] || + [permission isEqualToString:@"create_event"] || + [permission isEqualToString:@"rsvp_event"]) { + return YES; + } + } + return NO; +} + +#pragma mark Log In With Twitter + +- (void)_loginWithTwitter { + if (self.loading) { + return; + } + + if ([_logInView.facebookButton isKindOfClass:[PFActionButton class]]) { + [(PFActionButton *)_logInView.twitterButton setLoading:YES]; + } + self.loading = YES; + + Class twitterUtils = NSClassFromString(@"PFTwitterUtils"); + if (twitterUtils && [twitterUtils respondsToSelector:@selector(logInWithBlock:)]) { + [twitterUtils logInWithBlock:^(PFUser *user, NSError *error) { + self.loading = NO; + if ([_logInView.facebookButton isKindOfClass:[PFActionButton class]]) { + [(PFActionButton *)_logInView.twitterButton setLoading:NO]; + } + + if (user) { + [self _loginDidSucceedWithUser:user]; + } else if (error) { + [self _loginDidFailWithError:error]; + } else { + // User cancelled login. + } + }]; + } else { + [NSException raise:NSInternalInconsistencyException + format:@"Can't find PFTwitterUtils. Please link with ParseTwitterUtils to enable login with Twitter."]; + } +} + +#pragma mark Log In + +- (void)_loginAction { + if (self.loading) { + return; + } + + NSString *username = _logInView.usernameField.text ?: @""; + NSString *password = _logInView.passwordField.text ?: @""; + + if (_delegateExistingMethods.shouldBeginLogIn) { + if (![_delegate logInViewController:self shouldBeginLogInWithUsername:username password:password]) { + return; + } + } + + self.loading = YES; + if ([_logInView.logInButton isKindOfClass:[PFPrimaryButton class]]) { + [(PFActionButton *)_logInView.logInButton setLoading:YES]; + } + + [PFUser logInWithUsernameInBackground:username password:password block:^(PFUser *user, NSError *error) { + self.loading = NO; + if ([_logInView.logInButton isKindOfClass:[PFPrimaryButton class]]) { + [(PFPrimaryButton *)_logInView.logInButton setLoading:NO]; + } + + if (user) { + [self _loginDidSucceedWithUser:user]; + } else { + [self _loginDidFailWithError:error]; + } + }]; +} + +- (void)_signupAction { + if (self.loading) { + return; + } + [self presentViewController:self.signUpController animated:YES completion:nil]; +} + +- (void)_loginDidSucceedWithUser:(PFUser *)user { + if (_delegateExistingMethods.didLogInUser) { + [_delegate logInViewController:self didLogInUser:user]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFLogInSuccessNotification object:self]; +} + +- (void)_loginDidFailWithError:(NSError *)error { + if (_delegateExistingMethods.didFailToLogIn) { + [_delegate logInViewController:self didFailToLogInWithError:error]; + } else { + NSString *title = NSLocalizedString(@"Login Failed", @"Login failed alert title in PFLogInViewController"); + NSString *message = nil; + if (error.code == kPFErrorObjectNotFound) { + message = NSLocalizedString(@"The username and password you entered don't match", @"Invalid login credentials alert message in PFLogInViewController"); + } else { + message = NSLocalizedString(@"Please try again", @"Generic login failed alert message in PFLogInViewController"); + } + [PFUIAlertView showAlertViewWithTitle:title message:message]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFLogInFailureNotification object:self]; +} + +- (void)cancelLogIn { + if (_delegateExistingMethods.didCancelLogIn) { + [_delegate logInViewControllerDidCancelLogIn:self]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFLogInCancelNotification object:self]; +} + +///-------------------------------------- +#pragma mark - Keyboard +///-------------------------------------- + +- (UIView *)currentFirstResponder { + if ([_logInView.usernameField isFirstResponder]) { + return _logInView.usernameField; + } + if ([_logInView.passwordField isFirstResponder]) { + return _logInView.passwordField; + } + return nil; +} + +- (void)_dismissKeyboard { + [self.view endEditing:YES]; +} + +- (void)_registerForKeyboardNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardWillShow:) + name:UIKeyboardWillShowNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardWillHide:) + name:UIKeyboardWillHideNotification + object:nil]; +} + +- (void)_keyboardWillShow:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; + CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; + + CGRect keyboardFrame = [self.view convertRect:endFrame fromView:self.view.window]; + CGFloat visibleKeyboardHeight = CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(keyboardFrame); + + [self setVisibleKeyboardHeight:visibleKeyboardHeight + animationDuration:duration + animationOptions:curve << 16]; +} + +- (void)_keyboardWillHide:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; + [self setVisibleKeyboardHeight:0.0 + animationDuration:duration + animationOptions:curve << 16]; +} + +- (void)setVisibleKeyboardHeight:(CGFloat)visibleKeyboardHeight + animationDuration:(NSTimeInterval)animationDuration + animationOptions:(UIViewAnimationOptions)animationOptions { + + dispatch_block_t animationsBlock = ^{ + self.visibleKeyboardHeight = visibleKeyboardHeight; + }; + + if (animationDuration == 0.0) { + animationsBlock(); + } else { + [UIView animateWithDuration:animationDuration + delay:0.0 + options:animationOptions | UIViewAnimationOptionBeginFromCurrentState + animations:animationsBlock + completion:nil]; + } +} + +- (void)setVisibleKeyboardHeight:(CGFloat)visibleKeyboardHeight { + if (self.visibleKeyboardHeight != visibleKeyboardHeight) { + _visibleKeyboardHeight = visibleKeyboardHeight; + [self _updateViewContentOffsetAnimated:NO]; + } +} + +- (void)_updateViewContentOffsetAnimated:(BOOL)animated { + CGPoint contentOffset = CGPointZero; + if (self.visibleKeyboardHeight > 0.0f) { + // Scroll the view to keep fields visible + CGFloat offsetForScrollingTextFieldToTop = CGRectGetMinY([self currentFirstResponder].frame); + + UIView *lowestView; + if (_logInView.passwordForgottenButton) { + lowestView = _logInView.passwordForgottenButton; + } else if (_logInView.logInButton) { + lowestView = _logInView.logInButton; + } else { + lowestView = _logInView.passwordField; + } + + CGFloat offsetForScrollingLowestViewToBottom = 0.0f; + offsetForScrollingLowestViewToBottom += self.visibleKeyboardHeight; + offsetForScrollingLowestViewToBottom += CGRectGetMaxY(lowestView.frame); + offsetForScrollingLowestViewToBottom -= CGRectGetHeight(_logInView.bounds); + + if (offsetForScrollingLowestViewToBottom < 0) { + return; // No scrolling required + } + + contentOffset = CGPointMake(0.0f, MIN(offsetForScrollingTextFieldToTop, offsetForScrollingLowestViewToBottom)); + } + + [_logInView setContentOffset:contentOffset animated:animated]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView_Private.h b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView_Private.h new file mode 100644 index 0000000..7069832 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/LogInViewController/PFLogInView_Private.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +@interface PFLogInView (Private) + +@property (nonatomic, assign, readwrite) PFLogInFields fields; + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.h b/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.h new file mode 100644 index 0000000..fa5ce55 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + `PFProductTableViewController` displays in-app purchase products stored on Parse. + In addition to setting up in-app purchases in iTunes Connect, the app developer needs + to register product information on Parse, in the Product class. + */ +@interface PFProductTableViewController : PFQueryTableViewController + +/*! + @abstract Initializes a product table view controller. + + @param style The UITableViewStyle for the table + + @returns An initialized `PFProductTableViewController` object or `nil` if the object couldn't be created. + */ +- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.m b/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.m new file mode 100644 index 0000000..34da3ca --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/ProductTableViewController/PFProductTableViewController.m @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFProductTableViewController.h" + +#import +#import +#import + +#import "PFUIAlertView.h" +#import "PFLocalization.h" +#import "PFPurchaseTableViewCell.h" + +static NSString *const PFProductMetadataPriceKey = @"price"; +static NSString *const PFProductMetadataPriceLocaleKey = @"priceLocale"; + +@interface PFProductTableViewController () { + NSMutableDictionary *_productMetadataDictionary; + NSMutableDictionary *_productProgressDictionary; + + SKProductsRequest *_storeProductsRequest; +} + +@end + +@implementation PFProductTableViewController + +#pragma mark - +#pragma mark NSObject + +- (instancetype)initWithStyle:(UITableViewStyle)style { + if (self = [super initWithStyle:UITableViewStylePlain className:[PFProduct parseClassName]]) { + self.pullToRefreshEnabled = NO; + self.paginationEnabled = NO; + + _productMetadataDictionary = [NSMutableDictionary dictionary]; + _productProgressDictionary = [NSMutableDictionary dictionary]; + } + return self; +} + +- (instancetype)initWithStyle:(UITableViewStyle)style className:(NSString *)className { + return [self initWithStyle:style]; +} + +#pragma mark - +#pragma mark UIViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView.rowHeight = 84.0f; +} + +- (void)objectsDidLoad:(NSError *)error { + [super objectsDidLoad:error]; + if (error) { + return; + } + + [self.objects enumerateObjectsUsingBlock:^(PFProduct *product, NSUInteger idx, BOOL *stop) { + // No download for this product - just continue + if (!product.downloadName) { + return; + } + + [PFPurchase addObserverForProduct:product.productIdentifier block:^(SKPaymentTransaction *transaction) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:0]; + PFPurchaseTableViewCell *cell = (PFPurchaseTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]; + + cell.state = PFPurchaseTableViewCellStateDownloading; + [PFPurchase downloadAssetForTransaction:transaction + completion:^(NSString *filePath, NSError *downloadError) { + if (!downloadError) { + cell.state = PFPurchaseTableViewCellStateDownloaded; + } else { + cell.state = PFPurchaseTableViewCellStateNormal; + + NSString *title = NSLocalizedString(@"Download Error", + @"Download Error"); + [PFUIAlertView showAlertViewWithTitle:title error:downloadError]; + } + } + progress:^(int percentDone) { + _productProgressDictionary[product.productIdentifier] = @(percentDone); + [cell.progressView setProgress:percentDone/100.0f animated:YES]; + }]; + }]; + }]; +} + +#pragma mark - +#pragma mark UITableViewDataSource + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath + object:(PFProduct *)product { + static NSString *cellIdentifier = @"PFPurchaseTableViewCell"; + + PFPurchaseTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (!cell) { + cell = [[PFPurchaseTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle + reuseIdentifier:cellIdentifier]; + } + + if (indexPath.row % 2 == 0) { + cell.backgroundView.backgroundColor = [UIColor colorWithWhite:242.0f/255.0f alpha:1.0f]; + cell.textLabel.shadowOffset = CGSizeZero; + cell.textLabel.shadowColor = [UIColor whiteColor]; + cell.priceLabel.shadowOffset = CGSizeZero; + cell.priceLabel.shadowColor = [UIColor whiteColor]; + + } else { + cell.backgroundView.backgroundColor = [UIColor colorWithWhite:232.0f/255.0f alpha:1.0f]; + cell.textLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + cell.textLabel.shadowColor = [UIColor whiteColor]; + cell.priceLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); + cell.priceLabel.shadowColor = [UIColor whiteColor]; + } + + cell.imageView.file = product.icon; + cell.textLabel.text = product.title; + cell.detailTextLabel.text = product.subtitle; + + NSString *price = [self _priceForProduct:product]; + if (price) { + cell.priceLabel.text = [NSString stringWithFormat:@" $%@ ", price]; + } + + if (product.downloadName) { + NSString *contentPath = [PFPurchase assetContentPathForProduct:product]; + if (contentPath) { + cell.state = PFPurchaseTableViewCellStateDownloaded; + } + } else { + int progress = [self _downloadProgressForProduct:product]; + if (progress == 0) { + cell.state = PFPurchaseTableViewCellStateNormal; + } else if (progress == 100) { + cell.state = PFPurchaseTableViewCellStateDownloaded; + } else { + cell.state = PFPurchaseTableViewCellStateDownloading; + } + } + + return cell; +} + +#pragma mark - +#pragma mark UITableViewDelegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.row < self.objects.count) { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + PFProduct *product = self.objects[indexPath.row]; + [PFPurchase buyProduct:product.productIdentifier block:^(NSError *error) { + if (error) { + NSString *title = NSLocalizedString(@"Purchase Error", @"Purchase Error"); + [PFUIAlertView showAlertViewWithTitle:title error:error]; + } + }]; + } +} + +#pragma mark - +#pragma mark Data + +- (NSString *)_priceForProduct:(PFProduct *)product { + return _productMetadataDictionary[product.productIdentifier][PFProductMetadataPriceKey]; +} + +- (int)_downloadProgressForProduct:(PFProduct *)product { + return [_productProgressDictionary[product.productIdentifier] intValue]; +} + +#pragma mark - +#pragma mark PFQueryTableViewController + +- (PFQuery *)queryForTable { + PFQuery *query = [super queryForTable]; + [query orderByAscending:@"order"]; + return query; +} + +#pragma mark - +#pragma mark Querying Store + +- (void)_queryStoreForProductsWithIdentifiers:(NSSet *)identifiers { + _storeProductsRequest.delegate = nil; + _storeProductsRequest = nil; + + _storeProductsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers]; + _storeProductsRequest.delegate = self; + [_storeProductsRequest start]; +} + +#pragma mark - +#pragma mark SKProductsRequestDelegate + +- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { + if (request != _storeProductsRequest) { + return; + } + + NSArray *validProducts = response.products; + if ([validProducts count] == 0) { + return; + } + + [validProducts enumerateObjectsUsingBlock:^(SKProduct *product, NSUInteger idx, BOOL *stop) { + NSDictionary *metadata = @{ PFProductMetadataPriceKey : product.price, + PFProductMetadataPriceLocaleKey : product.priceLocale }; + _productMetadataDictionary[product.productIdentifier] = metadata; + }]; + [self.tableView reloadData]; + + _storeProductsRequest.delegate = nil; +} + +- (void)requestDidFinish:(SKRequest *)request { + _storeProductsRequest.delegate = nil; + _storeProductsRequest = nil; +} + +- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { + _storeProductsRequest.delegate = nil; + _storeProductsRequest = nil; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.h b/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.h new file mode 100644 index 0000000..e658269 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +@class BFTask; +@class PFCollectionViewCell; +@class PFObject; +@class PFQuery; + +/*! + This class allows you to think about a one-to-one mapping between a and a `UICollectionViewCell`, + rather than having to juggle index paths. + + You also get the following features out of the box: + + - Pagination with a cell that can be tapped to load the next page. + - Pull-to-refresh collection view header. + - Automatic downloading and displaying of remote images in cells. + - Loading screen, shown before any data is loaded. + - Automatic loading and management of the objects array. + - Various methods that can be overridden to customize behavior at major events in the data cycle. + + @see PFCollectionViewCell + */ +@interface PFQueryCollectionViewController : UICollectionViewController + +/*! + @abstract The class name of the this collection will use as a datasource. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy) IBInspectable NSString *parseClassName; + +/*! + @abstract Whether the collection should use the default loading view. Default - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL loadingViewEnabled; + +/*! + @abstract Whether the collection should use the built-in pull-to-refresh feature. Defualt - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL pullToRefreshEnabled; + +/*! + @abstract Whether the collection should use the built-in pagination feature. Default - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL paginationEnabled; + +/*! + @abstract The number of objects to show per page. Default - `25`. + */ +@property (nonatomic, assign) IBInspectable NSUInteger objectsPerPage; + +/*! + @abstract Whether the collection is actively loading new data from the server. + */ +@property (nonatomic, assign, getter=isLoading) BOOL loading; + +///-------------------------------------- +/// @name Creating a PFQueryCollectionViewController +///-------------------------------------- + +/*! + @abstract Initializes a view controller with a `UICollectionViewFlowLayout` and a class name + of that will be associated with this collection. + + @param className The class name of the instances of that this table will display. + + @returns An initialized `PFQueryCollectionViewController` object or `nil` if the object couldn't be created. + */ +- (instancetype)initWithClassName:(PFUI_NULLABLE NSString *)className; + +/*! + @abstract Initializes a view controller with a class name of that will be associated with this collection. + + @param layout Layout for collection view to use. + @param className The class name of the instances of that this table will display. + + @returns An initialized `PFQueryCollectionViewController` object or `nil` if the object couldn't be created. + */ +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout + className:(PFUI_NULLABLE NSString *)className NS_DESIGNATED_INITIALIZER; + +///-------------------------------------- +/// @name Responding to Events +///-------------------------------------- + +/*! + Called when objects will be loaded from Parse. If you override this method, you must + call [super objectsWillLoad] in your implementation. + */ +- (void)objectsWillLoad NS_REQUIRES_SUPER; + +/*! + Called when objects have loaded from Parse. If you override this method, you must + call [super objectsDidLoad:] in your implementation. + @param error The Parse error from running the PFQuery, if there was any. + */ +- (void)objectsDidLoad:(PFUI_NULLABLE NSError *)error NS_REQUIRES_SUPER; + +///-------------------------------------- +/// @name Accessing Results +///-------------------------------------- + +/*! + @abstract The array of instances of that is used as a data source. + */ +@property (nonatomic, copy, readonly) NSArray *objects; + +/*! + @abstract Returns an object at a particular indexPath. + + @discussion The default impementation returns the object at `indexPath.item`. + If you want to return objects in a different indexPath order, like for sections, override this method. + + @param indexPath An instance of `NSIndexPath`. + + @returns The object at the specified indexPath. + */ +- (PFUI_NULLABLE PFObject *)objectAtIndexPath:(PFUI_NULLABLE NSIndexPath *)indexPath; + +/*! + @abstract Removes an object at the specified index path, animated. + */ +- (void)removeObjectAtIndexPath:(PFUI_NULLABLE NSIndexPath *)indexPath; + +/*! + @abstract Removes all objects at the specified index paths, animated. + */ +- (void)removeObjectsAtIndexPaths:(PFUI_NULLABLE NSArray *)indexes; + +///-------------------------------------- +/// @name Loading Data +///-------------------------------------- + +/*! + @abstract Clears the collection view and loads the first page of objects. + + @returns An awaitable task that completes when the reload succeeds + */ +- (BFTask *)loadObjects; + +/*! + @abstract Loads the objects of the at the specified page and appends it to the + objects already loaded and refreshes the collection. + + @param page The page of objects to load. + @param clear Whether to clear the collection view after receiving the objects. + + @returns An awaitable task that completes when the reload succeeds + */ +- (BFTask *)loadObjects:(NSInteger)page clear:(BOOL)clear; + +/*! + @abstract Loads the next page of objects, appends to table, and refreshes. + */ +- (void)loadNextPage; + +/*! + @abstract Clears the collection view of all objects. + */ +- (void)clear; + +///-------------------------------------- +/// @name Querying +///-------------------------------------- + +/*! + @abstract Override to construct your own custom to get the objects. + + @returns An instance of that method will use to the objects for this collection. + */ +- (PFQuery *)queryForCollection; + +///-------------------------------------- +/// @name Data Source Methods +///-------------------------------------- + +/*! + @abstract Override this method to customize each cell given a that is loaded. + + @warning The cell should inherit from which is a subclass of `UICollectionViewCell`. + + @param collectionView The collection view object associated with this controller. + @param indexPath The indexPath of the cell. + @param object The that is associated with the cell. + + @returns The cell that represents this object. + */ +- (PFUI_NULLABLE PFCollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath + object:(PFUI_NULLABLE PFObject *)object; + +/*! + @discussion Override this method to customize the view that allows the user to load the + next page when pagination is turned on. + + @param collectionView The collection view object associated with this controller. + + @returns The view that allows the user to paginate. + */ +- (PFUI_NULLABLE UICollectionReusableView *)collectionViewReusableViewForNextPageAction:(UICollectionView *)collectionView; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.m b/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.m new file mode 100644 index 0000000..af1a52a --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.m @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFQueryCollectionViewController.h" + +#import +#import + +#import + +#import "PFActivityIndicatorCollectionReusableView.h" +#import "PFCollectionViewCell.h" +#import "PFImageView.h" +#import "PFLoadingView.h" +#import "PFLocalization.h" + +static NSString *const PFQueryCollectionViewCellIdentifier = @"cell"; +static NSString *const PFQueryCollectionViewNextPageReusableViewIdentifier = @"nextPageView"; + +@interface PFQueryCollectionViewController () { + NSMutableArray *_mutableObjects; + + BOOL _firstLoad; // Whether we have loaded the first set of objects + NSInteger _currentPage; // The last page that was loaded + NSInteger _lastLoadCount; // The count of objects from the last load. +} + +@property (nonatomic, strong) UIRefreshControl *refreshControl; + +@property (nonatomic, strong) PFLoadingView *loadingView; + +@property (nonatomic, strong) PFActivityIndicatorCollectionReusableView *currentNextPageView; + +- (instancetype)initWithCoder:(NSCoder *)decoder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER; + +@end + +@implementation PFQueryCollectionViewController + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithCoder:(NSCoder *)decoder { + // initWithCoder is usually a parallel designated initializer, as is the case here + // It's used by storyboard + self = [super initWithCoder:decoder]; + if (!self) return nil; + + [self _setupWithClassName:nil]; + + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + // This is used by interface builder + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (!self) return nil; + + [self _setupWithClassName:nil]; + + return self; +} + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout { + return[self initWithCollectionViewLayout:layout className:nil]; +} + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout className:(NSString *)className { + self = [super initWithCollectionViewLayout:layout]; + if (!self) return nil; + + [self _setupWithClassName:className]; + + return self; +} + +- (instancetype)initWithClassName:(NSString *)className { + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + return [self initWithCollectionViewLayout:layout className:className]; +} + +- (void)_setupWithClassName:(NSString *)otherClassName { + _mutableObjects = [NSMutableArray array]; + _firstLoad = YES; + + // Set some reasonable defaults + _objectsPerPage = 25; + _loadingViewEnabled = YES; + _paginationEnabled = YES; + _pullToRefreshEnabled = YES; + _lastLoadCount = -1; + + _parseClassName = [otherClassName copy]; +} + +#pragma mark - +#pragma mark UIViewController + +- (void)loadView { + [super loadView]; + + self.collectionView.backgroundColor = [UIColor whiteColor]; + + [self.collectionView registerClass:[PFCollectionViewCell class] + forCellWithReuseIdentifier:PFQueryCollectionViewCellIdentifier]; + [self.collectionView registerClass:[PFActivityIndicatorCollectionReusableView class] + forSupplementaryViewOfKind:UICollectionElementKindSectionFooter + withReuseIdentifier:PFQueryCollectionViewNextPageReusableViewIdentifier]; + + if (self.pullToRefreshEnabled) { + self.refreshControl = [[UIRefreshControl alloc] init]; + [self.refreshControl addTarget:self + action:@selector(_refreshControlValueChanged:) + forControlEvents:UIControlEventValueChanged]; + [self.collectionView addSubview:self.refreshControl]; + self.collectionView.alwaysBounceVertical = YES; + } +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self loadObjects]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + self.loadingView.frame = self.collectionView.bounds; +} + +#pragma mark - +#pragma mark Responding to Events + +- (void)objectsWillLoad { + [self _refreshLoadingView]; +} + +- (void)objectsDidLoad:(NSError *)error { + [self _refreshLoadingView]; + _firstLoad = NO; +} + +#pragma mark - +#pragma mark Accessing Results + +- (NSArray *)objects { + return _mutableObjects; +} + +- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath { + return self.objects[indexPath.row]; +} + +#pragma mark - +#pragma mark Removing Objects + +- (void)removeObjectAtIndexPath:(NSIndexPath *)indexPath { + [self removeObjectsAtIndexPaths:@[ indexPath ]]; +} + +- (void)removeObjectsAtIndexPaths:(NSArray *)indexPaths { + if (indexPaths.count == 0) { + return; + } + + // We need the contents as both an index set and a list of index paths. + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + + for (NSIndexPath *indexPath in indexPaths) { + if (indexPath.section != 0) { + [NSException raise:NSRangeException format:@"Index Path section %lu out of range!", (long)indexPath.section]; + } + + if (indexPath.row >= self.objects.count) { + [NSException raise:NSRangeException format:@"Index Path row %lu out of range!", (long)indexPath.row]; + } + + [indexes addIndex:indexPath.row]; + } + + BFContinuationBlock deletionHandlerBlock = ^id (BFTask *task) { + self.refreshControl.enabled = YES; + + if (task.error) { + [self _handleDeletionError:task.error]; + } + + return nil; + }; + + NSMutableArray *allDeletionTasks = [NSMutableArray arrayWithCapacity:indexes.count]; + NSArray *objectsToRemove = [self.objects objectsAtIndexes:indexes]; + + // Remove the contents from our local cache so we can give the user immediate feedback. + [_mutableObjects removeObjectsInArray:objectsToRemove]; + [self.collectionView deleteItemsAtIndexPaths:indexPaths]; + + for (id obj in objectsToRemove) { + [allDeletionTasks addObject:[obj deleteInBackground]]; + } + + [[BFTask taskForCompletionOfAllTasks:allDeletionTasks] + continueWithBlock:deletionHandlerBlock]; +} + +#pragma mark - +#pragma mark Loading Data + +- (BFTask *)loadObjects { + return [self loadObjects:0 clear:YES]; +} + +- (BFTask *)loadObjects:(NSInteger)page clear:(BOOL)clear { + self.loading = YES; + [self objectsWillLoad]; + + BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource]; + + PFQuery *query = [self queryForCollection]; + [self _alterQuery:query forLoadingPage:page]; + [query findObjectsInBackgroundWithBlock:^(NSArray *foundObjects, NSError *error) { + if (![Parse isLocalDatastoreEnabled] && + query.cachePolicy != kPFCachePolicyCacheOnly && + error.code == kPFErrorCacheMiss) { + // no-op on cache miss + return; + } + + self.loading = NO; + + if (error) { + _lastLoadCount = -1; + _currentNextPageView.animating = NO; + } else { + _currentPage = page; + _lastLoadCount = [foundObjects count]; + + if (clear) { + [_mutableObjects removeAllObjects]; + } + + [_mutableObjects addObjectsFromArray:foundObjects]; + [self.collectionView reloadData]; + } + + [self objectsDidLoad:error]; + [self.refreshControl endRefreshing]; + + [source setError:error]; + }]; + + return source.task; +} + +- (void)loadNextPage { + if (!self.loading) { + [self loadObjects:(_currentPage + 1) clear:NO]; + _currentNextPageView.animating = YES; + } +} + +- (void)clear { + [_mutableObjects removeAllObjects]; + [self.collectionView reloadData]; + _currentPage = 0; +} + +#pragma mark - +#pragma mark Querying + +- (PFQuery *)queryForCollection { + if (!self.parseClassName) { + [NSException raise:NSInternalInconsistencyException + format:@"You need to specify a parseClassName for the PFQueryTableViewController.", nil]; + } + + PFQuery *query = [PFQuery queryWithClassName:self.parseClassName]; + + // If no objects are loaded in memory, we look to the cache first to fill the table + // and then subsequently do a query against the network. + if ([self.objects count] == 0 && ![Parse isLocalDatastoreEnabled]) { + query.cachePolicy = kPFCachePolicyCacheThenNetwork; + } + + [query orderByDescending:@"createdAt"]; + + return query; +} + +- (void)_alterQuery:(PFQuery *)query forLoadingPage:(NSInteger)page { + if (self.paginationEnabled && self.objectsPerPage) { + query.limit = self.objectsPerPage; + query.skip = page * self.objectsPerPage; + } +} + +#pragma mark - +#pragma mark Data Source Methods + +- (PFCollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath + object:(PFObject *)object { + PFCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:PFQueryCollectionViewCellIdentifier + forIndexPath:indexPath]; + [cell updateFromObject:object]; + return cell; +} + +- (UICollectionReusableView *)collectionViewReusableViewForNextPageAction:(UICollectionView *)collectionView { + _currentNextPageView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter + withReuseIdentifier:PFQueryCollectionViewNextPageReusableViewIdentifier + forIndexPath:[self _indexPathForPaginationReusableView]]; + _currentNextPageView.textLabel.text = NSLocalizedString(@"Load more...", @"Load more..."); + [_currentNextPageView addTarget:self action:@selector(loadNextPage) forControlEvents:UIControlEventTouchUpInside]; + _currentNextPageView.animating = self.loading; + return _currentNextPageView; +} + +#pragma mark - +#pragma mark UICollectionViewDataSource + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 1; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return [self.objects count]; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + return [self collectionView:collectionView cellForItemAtIndexPath:indexPath object:[self objectAtIndexPath:indexPath]]; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView + viewForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath { + return [self collectionViewReusableViewForNextPageAction:collectionView]; +} + +#pragma mark - +#pragma mark UICollectionViewDelegateFlowLayout + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section { + if ([self _shouldShowPaginationView]) { + return CGSizeMake(CGRectGetWidth(collectionView.bounds), 50.0f); + } + return CGSizeZero; +} + +#pragma mark - +#pragma mark Pagination + +- (BOOL)_shouldShowPaginationView { + return (self.paginationEnabled && + [self.objects count] != 0 && + (_lastLoadCount == -1 || _lastLoadCount >= (NSInteger)self.objectsPerPage)); +} + +- (NSIndexPath *)_indexPathForPaginationReusableView { + return [NSIndexPath indexPathForItem:0 inSection:[self numberOfSectionsInCollectionView:self.collectionView] - 1]; +} + +#pragma mark - +#pragma mark Error handling + +- (void)_handleDeletionError:(NSError *)error { + // Fully reload on error. + [self loadObjects]; + + NSString *errorMessage = [NSString stringWithFormat:@"%@: \"%@\"", + NSLocalizedString(@"Error occurred during deletion", @"Error occurred during deletion"), + error.localizedDescription]; + + if ([UIAlertController class]) { + UIAlertController *errorController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Error", @"Error") + message:errorMessage + preferredStyle:UIAlertControllerStyleAlert]; + + [errorController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleCancel + handler:nil]]; + + [self presentViewController:errorController animated:YES completion:nil]; + } else { + // Cast to `id` is required for building succesfully for app extensions, + // this code actually never runs in App Extensions, since they are iOS 8.0+, so we are good with just a hack + UIAlertView *alertView = [(id)[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"Error") + message:errorMessage + delegate:nil + cancelButtonTitle:NSLocalizedString(@"OK", @"OK") + otherButtonTitles:nil]; + + [alertView show]; + } +} + +#pragma mark - +#pragma mark Actions + +- (void)_refreshControlValueChanged:(UIRefreshControl *)refreshControl { + if (!self.loading) { + [self loadObjects]; + } +} + +#pragma mark - +#pragma mark Loading View + +- (void)_refreshLoadingView { + BOOL showLoadingView = self.loadingViewEnabled && self.loading && _firstLoad; + + if (showLoadingView) { + [self.collectionView addSubview:self.loadingView]; + [self.view setNeedsLayout]; + } else { + // Avoid loading `loadingView` - just use an ivar. + if (_loadingView) { + [self.loadingView removeFromSuperview]; + self.loadingView = nil; + } + } +} + +- (PFLoadingView *)loadingView { + if (!_loadingView) { + _loadingView = [[PFLoadingView alloc] initWithFrame:CGRectZero]; + } + return _loadingView; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.h b/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.h new file mode 100644 index 0000000..4f73344 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +@class BFTask; +@class PFObject; +@class PFQuery; +@class PFTableViewCell; + +/*! + This class allows you to think about a one-to-one mapping between a and a `UITableViewCell`, + rather than having to juggle index paths. + + You also get the following features out of the box: + + - Pagination with a cell that can be tapped to load the next page. + - Pull-to-refresh table view header. + - Automatic downloading and displaying of remote images in cells. + - Loading screen, shown before any data is loaded. + - Automatic loading and management of the objects array. + - Various methods that can be overridden to customize behavior at major events in the data cycle. + */ +@interface PFQueryTableViewController : UITableViewController + +///-------------------------------------- +/// @name Creating a PFQueryTableViewController +///-------------------------------------- + +/*! + @abstract Initializes with a class name of the that will be associated with this table. + + @param style The UITableViewStyle for the table + @param className The class name of the instances of that this table will display. + + @returns An initialized `PFQueryTableViewController` object or `nil` if the object couldn't be created. + */ +- (instancetype)initWithStyle:(UITableViewStyle)style + className:(PFUI_NULLABLE NSString *)className NS_DESIGNATED_INITIALIZER; + +/*! + @abstract Initializes with a class name of the PFObjects that will be associated with this table. + + @param className The class name of the instances of that this table will display. + + @returns An initialized `PFQueryTableViewController` object or `nil` if the object couldn't be created. + */ +- (instancetype)initWithClassName:(PFUI_NULLABLE NSString *)className; + +///-------------------------------------- +/// @name Configuring Behavior +///-------------------------------------- + +/*! + @abstract The class name of the this table will use as a datasource. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy) IBInspectable NSString *parseClassName; + +/*! + @abstract The key to use to display for the cell text label. + + @discussion This won't apply if you override + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy) IBInspectable NSString *textKey; + +/*! + @abstract The key to use to display for the cell image view. + + @discussion This won't apply if you override + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy) IBInspectable NSString *imageKey; + +/*! + @abstract The image to use as a placeholder for the cell images. + + @discussion This won't apply if you override + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) IBInspectable UIImage *placeholderImage; + +/*! + @abstract Whether the table should use the default loading view. Default - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL loadingViewEnabled; + +/*! + @abstract Whether the table should use the built-in pull-to-refresh feature. Defualt - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL pullToRefreshEnabled; + +/*! + @abstract Whether the table should use the built-in pagination feature. Default - `YES`. + */ +@property (nonatomic, assign) IBInspectable BOOL paginationEnabled; + +/*! + @abstract The number of objects to show per page. Default - `25`. + */ +@property (nonatomic, assign) IBInspectable NSUInteger objectsPerPage; + +/*! + @abstract Whether the table is actively loading new data from the server. + */ +@property (nonatomic, assign, getter=isLoading) BOOL loading; + +///-------------------------------------- +/// @name Responding to Events +///-------------------------------------- + +/*! + Called when objects will loaded from Parse. If you override this method, you must + call [super objectsWillLoad] in your implementation. + */ +- (void)objectsWillLoad; + +/*! + Called when objects have loaded from Parse. If you override this method, you must + call [super objectsDidLoad:] in your implementation. + @param error The Parse error from running the PFQuery, if there was any. + */ +- (void)objectsDidLoad:(PFUI_NULLABLE NSError *)error; + +///-------------------------------------- +/// @name Accessing Results +///-------------------------------------- + +/*! + @abstract The array of instances of that is used as a data source. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, copy, readonly) NSArray *objects; + +/*! + @abstract Returns an object at a particular indexPath. + + @discussion The default impementation returns the object at `indexPath.row`. + If you want to return objects in a different indexPath order, like for sections, override this method. + + @param indexPath The indexPath. + + @returns The object at the specified index + */ +- (PFUI_NULLABLE PFObject *)objectAtIndexPath:(PFUI_NULLABLE NSIndexPath *)indexPath; + +/*! + @abstract Removes an object at the specified index path, animated. + */ +- (void)removeObjectAtIndexPath:(PFUI_NULLABLE NSIndexPath *)indexPath; + +/*! + @abstract Removes an object at the specified index path, with or without animation. + */ +- (void)removeObjectAtIndexPath:(PFUI_NULLABLE NSIndexPath *)indexPath animated:(BOOL)animated; + +/*! + @abstract Removes all objects at the specified index paths, animated. + */ +- (void)removeObjectsAtIndexPaths:(PFUI_NULLABLE NSArray *)indexes; + +/*! + @abstract Removes all objects at the specified index paths, with or without animation. + */ +- (void)removeObjectsAtIndexPaths:(PFUI_NULLABLE NSArray *)indexes animated:(BOOL)animated; + +/*! + @abstract Clears the table of all objects. + */ +- (void)clear; + +/*! + @abstract Clears the table and loads the first page of objects. + + @returns An awaitable task that completes when the reload succeeds + */ +- (BFTask *)loadObjects; + +/*! + @abstract Loads the objects of the className at the specified page and appends it to the + objects already loaded and refreshes the table. + + @param page The page of objects to load. + @param clear Whether to clear the table after receiving the objects + + @returns An awaitable task that completes when the reload succeeds + */ +- (BFTask *)loadObjects:(NSInteger)page clear:(BOOL)clear; + +/*! + @abstract Loads the next page of objects, appends to table, and refreshes. + */ +- (void)loadNextPage; + +///-------------------------------------- +/// @name Querying +///-------------------------------------- + +/*! + Override to construct your own custom PFQuery to get the objects. + @result PFQuery that loadObjects will use to the objects for this table. + */ +- (PFQuery *)queryForTable; + +///-------------------------------------- +/// @name Data Source Methods +///-------------------------------------- + +/*! + @abstract Override this method to customize each cell given a PFObject that is loaded. + + @discussion If you don't override this method, it will use a default style cell and display either + the first data key from the object, or it will display the key as specified with `textKey`, `imageKey`. + + @warning The cell should inherit from which is a subclass of `UITableViewCell`. + + @param tableView The table view object associated with this controller. + @param indexPath The indexPath of the cell. + @param object The PFObject that is associated with the cell. + + @returns The cell that represents this object. + */ +- (PFUI_NULLABLE PFTableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath + object:(PFUI_NULLABLE PFObject *)object; + +/*! + @discussion Override this method to customize the cell that allows the user to load the + next page when pagination is turned on. + + @param tableView The table view object associated with this controller. + @param indexPath The indexPath of the cell. + + @returns The cell that allows the user to paginate. + */ +- (PFUI_NULLABLE PFTableViewCell *)tableView:(UITableView *)tableView + cellForNextPageAtIndexPath:(NSIndexPath *)indexPath; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.m b/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.m new file mode 100644 index 0000000..f9fad49 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.m @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFQueryTableViewController.h" + +#import +#import + +#import + +#import "PFActivityIndicatorTableViewCell.h" +#import "PFImageView.h" +#import "PFLoadingView.h" +#import "PFLocalization.h" +#import "PFTableViewCell.h" + +// Add headers to kill any warnings. +// `initWithStyle:` is a UITableViewController method. +// `initWithCoder:`/`initWithNibName:bundle:` are UIViewController methods and are, for sure, available. +@interface UITableViewController () + +- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithCoder:(NSCoder *)decoder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER; + +@end + +@interface PFQueryTableViewController () { + NSMutableArray *_mutableObjects; + + BOOL _firstLoad; // Whether we have loaded the first set of objects + NSInteger _currentPage; // The last page that was loaded + NSInteger _lastLoadCount; // The count of objects from the last load. + // Set to -1 when objects haven't loaded, or there was an error. + UITableViewCellSeparatorStyle _savedSeparatorStyle; +} + +@property (nonatomic, strong) PFLoadingView *loadingView; + +- (instancetype)initWithCoder:(NSCoder *)decoder NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER; + +@end + +@implementation PFQueryTableViewController + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithCoder:(NSCoder *)decoder { + // initWithCoder is usually a parallel designated initializer, as is the case here + // It's used by storyboard + if (self = [super initWithCoder:decoder]) { + [self _setupWithClassName:nil]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + // This is used by interface builder + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + [self _setupWithClassName:nil]; + } + return self; +} + +- (instancetype)initWithStyle:(UITableViewStyle)style { + return [self initWithStyle:style className:nil]; +} + +- (instancetype)initWithClassName:(NSString *)className { + return [self initWithStyle:UITableViewStylePlain className:className]; +} + +- (instancetype)initWithStyle:(UITableViewStyle)style className:(NSString *)className { + if (self = [super initWithStyle:style]) { + [self _setupWithClassName:className]; + } + return self; +} + +- (void)_setupWithClassName:(NSString *)otherClassName { + _mutableObjects = [NSMutableArray array]; + _firstLoad = YES; + + // Set some reasonable defaults + _objectsPerPage = 25; + _loadingViewEnabled = YES; + _paginationEnabled = YES; + _pullToRefreshEnabled = YES; + _lastLoadCount = -1; + + _parseClassName = [otherClassName copy]; +} + +#pragma mark - +#pragma mark UIViewController + +- (void)loadView { + [super loadView]; + + // Setup the Pull to Refresh UI if needed + if (self.pullToRefreshEnabled) { + UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; + [refreshControl addTarget:self + action:@selector(_refreshControlValueChanged:) + forControlEvents:UIControlEventValueChanged]; + self.refreshControl = refreshControl; + } +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self loadObjects]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + self.loadingView.frame = self.tableView.bounds; +} + +- (void)setEditing:(BOOL)editing animated:(BOOL)animated { + [self.tableView beginUpdates]; + + // If we're currently showing the pagination cell, we need to hide it during editing. + if ([self paginationEnabled] && [self _shouldShowPaginationCell]) { + [self.tableView deleteRowsAtIndexPaths:@[ [self _indexPathForPaginationCell] ] + withRowAnimation:UITableViewRowAnimationAutomatic]; + } + + [super setEditing:editing animated:animated]; + + // Ensure proper re-insertion of the pagination cell. + if ([self paginationEnabled] && [self _shouldShowPaginationCell]) { + [self.tableView insertRowsAtIndexPaths:@[ [self _indexPathForPaginationCell] ] + withRowAnimation:UITableViewRowAnimationAutomatic]; + } + + [self.tableView endUpdates]; +} + +#pragma mark - +#pragma mark Data + +- (void)objectsWillLoad { + if (_firstLoad) { + _savedSeparatorStyle = self.tableView.separatorStyle; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + } + [self _refreshLoadingView]; +} + +- (void)objectsDidLoad:(NSError *)error { + if (_firstLoad) { + _firstLoad = NO; + self.tableView.separatorStyle = _savedSeparatorStyle; + } + [self _refreshLoadingView]; +} + +- (PFQuery *)queryForTable { + if (!self.parseClassName) { + [NSException raise:NSInternalInconsistencyException + format:@"You need to specify a parseClassName for the PFQueryTableViewController.", nil]; + } + + PFQuery *query = [PFQuery queryWithClassName:self.parseClassName]; + + // If no objects are loaded in memory, we look to the cache first to fill the table + // and then subsequently do a query against the network. + if ([self.objects count] == 0 && ![Parse isLocalDatastoreEnabled]) { + query.cachePolicy = kPFCachePolicyCacheThenNetwork; + } + + [query orderByDescending:@"createdAt"]; + + return query; +} + +// Alters a query to add functionality like pagination +- (void)_alterQuery:(PFQuery *)query forLoadingPage:(NSInteger)page { + if (self.paginationEnabled && self.objectsPerPage) { + query.limit = self.objectsPerPage; + query.skip = page * self.objectsPerPage; + } +} + +- (void)clear { + [_mutableObjects removeAllObjects]; + [self.tableView reloadData]; + _currentPage = 0; +} + +- (BFTask *)loadObjects { + return [self loadObjects:0 clear:YES]; +} + +- (BFTask *)loadObjects:(NSInteger)page clear:(BOOL)clear { + self.loading = YES; + [self objectsWillLoad]; + + BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource]; + + PFQuery *query = [self queryForTable]; + [self _alterQuery:query forLoadingPage:page]; + [query findObjectsInBackgroundWithBlock:^(NSArray *foundObjects, NSError *error) { + if (![Parse isLocalDatastoreEnabled] && + query.cachePolicy != kPFCachePolicyCacheOnly && + error.code == kPFErrorCacheMiss) { + // no-op on cache miss + return; + } + + self.loading = NO; + + if (error) { + _lastLoadCount = -1; + [self _refreshPaginationCell]; + } else { + _currentPage = page; + _lastLoadCount = [foundObjects count]; + + if (clear) { + [_mutableObjects removeAllObjects]; + } + + [_mutableObjects addObjectsFromArray:foundObjects]; + [self.tableView reloadData]; + } + + [self objectsDidLoad:error]; + [self.refreshControl endRefreshing]; + + [source setError:error]; + }]; + + return source.task; +} + +- (void)loadNextPage { + if (!self.loading) { + [self loadObjects:(_currentPage + 1) clear:NO]; + [self _refreshPaginationCell]; + } +} + +#pragma mark - +#pragma mark UIScrollViewDelegate + +// scrollViewDidEndDragging:willDecelerate: is called when a user stops dragging the table view. +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { + // If the user lets go and the table view has no momentum, + // scrollViewDidEndDragging:willDecelerate: is called with willDecelerate:NO. + // In this case, we trigger a load for all the PFImageViews + // in our PFTableViewCells through _loadImagesForOnscreenRows. + if (!decelerate) { + [self _loadImagesForOnscreenRows]; + } +} + +// If the user lets go and the table view has momentum, +// scrollViewDidEndDragging:willDecelerate: is called with willDecelerate:YES. +// We will defer loading of images until scrollViewDidEndDecelerating: is called. +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + [self _loadImagesForOnscreenRows]; +} + +#pragma mark - +#pragma mark UITableViewDataSource + +// Return the number of rows in the section. +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + NSInteger count = [self.objects count]; + + if ([self _shouldShowPaginationCell]) { + count += 1; + } + + return count; +} + +// Default implementation that displays a default style cell +- (PFTableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath + object:(PFObject *)object { + static NSString *cellIdentifier = @"PFTableViewCell"; + PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (!cell) { + cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; + } + + [self tableView:tableView configureCell:cell atIndexPath:indexPath object:object]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView + configureCell:(PFTableViewCell *)cell + atIndexPath:(NSIndexPath *)indexPath + object:(PFObject *)object { + // Grab a key to display + NSString *key; + if (self.textKey) { + key = self.textKey; + } else if ([[object allKeys] count] > 0) { + key = [[object allKeys] objectAtIndex:0]; + } + + // Configure the cell + if (key) { + cell.textLabel.text = [NSString stringWithFormat:@"%@", [object objectForKey:key]]; + } + + if (self.placeholderImage) { + cell.imageView.image = self.placeholderImage; + } + + if (self.imageKey) { + cell.imageView.file = object[self.imageKey]; + } +} + +- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath { + return self.objects[indexPath.row]; +} + +- (void)removeObjectAtIndexPath:(NSIndexPath *)indexPath { + [self removeObjectAtIndexPath:indexPath animated:YES]; +} + +- (void)removeObjectAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { + [self removeObjectsAtIndexPaths:@[ indexPath ] animated:animated]; +} + +- (void)removeObjectsAtIndexPaths:(NSArray *)indexPaths { + [self removeObjectsAtIndexPaths:indexPaths animated:YES]; +} + +- (void)removeObjectsAtIndexPaths:(NSArray *)indexPaths animated:(BOOL)animated { + if (indexPaths.count == 0) { + return; + } + + // We need the contents as both an index set and a list of index paths. + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + + for (NSIndexPath *indexPath in indexPaths) { + if (indexPath.section != 0) { + [NSException raise:NSRangeException format:@"Index Path section %lu out of range!", (long)indexPath.section]; + } + + if (indexPath.row >= self.objects.count) { + [NSException raise:NSRangeException format:@"Index Path row %lu out of range!", (long)indexPath.row]; + } + + [indexes addIndex:indexPath.row]; + } + + BFContinuationBlock deletionHandlerBlock = ^id (BFTask *task) { + self.refreshControl.enabled = YES; + if (task.error) { + [self _handleDeletionError:task.error]; + } + + return nil; + }; + + NSMutableArray *allDeletionTasks = [NSMutableArray arrayWithCapacity:indexes.count]; + NSArray *objectsToRemove = [self.objects objectsAtIndexes:indexes]; + + // Remove the contents from our local cache so we can give the user immediate feedback. + [_mutableObjects removeObjectsInArray:objectsToRemove]; + [self.tableView deleteRowsAtIndexPaths:indexPaths + withRowAnimation:animated ? UITableViewRowAnimationAutomatic : UITableViewRowAnimationNone]; + + for (id obj in objectsToRemove) { + [allDeletionTasks addObject:[obj deleteInBackground]]; + } + + [[BFTask taskForCompletionOfAllTasks:allDeletionTasks] + continueWithBlock:deletionHandlerBlock]; +} + +- (PFTableViewCell *)tableView:(UITableView *)otherTableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellIdentifier = @"PFTableViewCellNextPage"; + + PFActivityIndicatorTableViewCell *cell = [otherTableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (cell == nil) { + cell = [[PFActivityIndicatorTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:cellIdentifier]; + cell.textLabel.text = NSLocalizedString(@"Load more...", @"Load more..."); + } + + cell.animating = self.loading; + + return cell; +} + +- (UITableViewCell *)tableView:(UITableView *)otherTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + PFTableViewCell *cell; + if ([self _shouldShowPaginationCell] && [indexPath isEqual:[self _indexPathForPaginationCell]]) { + // Return the pagination cell on the last cell + cell = [self tableView:otherTableView cellForNextPageAtIndexPath:indexPath]; + } else { + cell = [self tableView:otherTableView + cellForRowAtIndexPath:indexPath + object:[self objectAtIndexPath:indexPath]]; + } + + if ([cell isKindOfClass:[PFTableViewCell class]] && + !otherTableView.dragging && + !otherTableView.decelerating) { + // The reason we dispatch to the main queue is that we want to enable subclasses to override + // tableView:cellForRowAtIndexPath:object:, and we still do image loading after they assign + // the remote image file. + dispatch_async(dispatch_get_main_queue(), ^{ + [cell.imageView loadInBackground]; + }); + } + return cell; +} + +#pragma mark - +#pragma mark UITableViewDelegate + +- (void)tableView:(UITableView *)otherTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + // Handle selection of the next page row + if (!_firstLoad && + self.paginationEnabled && + [indexPath isEqual:[self _indexPathForPaginationCell]]) { + [self loadNextPage]; + } +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView + editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { + if ([indexPath isEqual:[self _indexPathForPaginationCell]]) { + return UITableViewCellEditingStyleNone; + } + + return UITableViewCellEditingStyleDelete; +} + +- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { + if ([indexPath isEqual:[self _indexPathForPaginationCell]]) { + return NO; + } + + return YES; +} + +#pragma mark - +#pragma mark Private + +// Whether we need to show the pagination cell +- (BOOL)_shouldShowPaginationCell { + return (self.paginationEnabled && + !self.editing && + [self.objects count] != 0 && + (_lastLoadCount == -1 || _lastLoadCount >= (NSInteger)self.objectsPerPage)); +} + +// Selectively refresh pagination cell +- (void)_refreshPaginationCell { + if ([self _shouldShowPaginationCell]) { + [self.tableView reloadRowsAtIndexPaths:@[ [self _indexPathForPaginationCell] ] + withRowAnimation:UITableViewRowAnimationNone]; + } +} + +// The row of the pagination cell +- (NSIndexPath *)_indexPathForPaginationCell { + return [NSIndexPath indexPathForRow:[self.objects count] inSection:0]; +} + +- (void)_loadImagesForOnscreenRows { + if (self.objects.count > 0) { + NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows]; + for (NSIndexPath *indexPath in visiblePaths) { + [self _loadImageForCellAtIndexPath:indexPath]; + } + } +} + +- (void)_loadImageForCellAtIndexPath:(NSIndexPath *)indexPath { + PFTableViewCell *cell = (PFTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]; + if ([cell isKindOfClass:[PFTableViewCell class]]) { + [cell.imageView loadInBackground]; + } +} + +#pragma mark - +#pragma mark Error handling + +- (void)_handleDeletionError:(NSError *)error { + // Fully reload on error. + [self loadObjects]; + + NSString *errorMessage = [NSString stringWithFormat:@"%@: \"%@\"", + NSLocalizedString(@"Error occurred during deletion", @"Error occurred during deletion"), + error.localizedDescription]; + + if ([UIAlertController class]) { + UIAlertController *errorController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Error", @"Error") + message:errorMessage + preferredStyle:UIAlertControllerStyleAlert]; + + [errorController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleCancel + handler:nil]]; + + [self presentViewController:errorController animated:YES completion:nil]; + } else { + // Cast to `id` is required for building succesfully for app extensions, + // this code actually never runs in App Extensions, since they are iOS 8.0+, so we are good with just a hack + UIAlertView *alertView = [(id)[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"Error") + message:errorMessage + delegate:nil + cancelButtonTitle:NSLocalizedString(@"OK", @"OK") + otherButtonTitles:nil]; + + [alertView show]; + } +} + +#pragma mark - +#pragma mark Actions + +- (void)_refreshControlValueChanged:(UIRefreshControl *)refreshControl { + [self loadObjects]; +} + +#pragma mark - +#pragma mark Accessors + +- (NSArray *)objects { + return _mutableObjects; +} + +#pragma mark - +#pragma mark Loading View + +- (void)_refreshLoadingView { + BOOL showLoadingView = self.loadingViewEnabled && self.loading && _firstLoad; + + if (showLoadingView) { + [self.tableView addSubview:self.loadingView]; + [self.view setNeedsLayout]; + } else { + // Avoid loading `loadingView` - just use an ivar. + if (_loadingView) { + [self.loadingView removeFromSuperview]; + self.loadingView = nil; + } + } +} + +- (PFLoadingView *)loadingView { + if (!_loadingView) { + _loadingView = [[PFLoadingView alloc] initWithFrame:CGRectZero]; + } + return _loadingView; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.h b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.h new file mode 100644 index 0000000..d6a8795 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + `PFSignUpFields` bitmask specifies the sign up elements which are enabled in the view. + + @see PFSignUpViewController + @see PFSignUpView + */ +typedef NS_OPTIONS(NSInteger, PFSignUpFields) { + /*! Username and password fields. */ + PFSignUpFieldsUsernameAndPassword = 0, + /*! Email field. */ + PFSignUpFieldsEmail = 1 << 0, + /*! This field can be used for something else. */ + PFSignUpFieldsAdditional = 1 << 1, + /*! Sign Up Button */ + PFSignUpFieldsSignUpButton = 1 << 2, + /*! Dismiss Button */ + PFSignUpFieldsDismissButton = 1 << 3, + /*! Default value. Combines Username, Password, Email, Sign Up and Dismiss Buttons. */ + PFSignUpFieldsDefault = (PFSignUpFieldsUsernameAndPassword | + PFSignUpFieldsEmail | + PFSignUpFieldsSignUpButton | + PFSignUpFieldsDismissButton) +}; + +@class PFTextField; + +/*! + The `PFSignUpView` class provides a standard sign up interface for authenticating a . + */ +@interface PFSignUpView : UIScrollView + +///-------------------------------------- +/// @name Creating SignUp View +///-------------------------------------- + +/*! + @abstract Initializes the view with the specified sign up elements. + + @param fields A bitmask specifying the sign up elements which are enabled in the view + + @returns An initialized `PFSignUpView` object or `nil` if the object couldn't be created. + + @see PFSignUpFields + */ +- (instancetype)initWithFields:(PFSignUpFields)fields; + +/*! + @abstract The view controller that will present this view. + + @discussion Used to lay out elements correctly when the presenting view controller has translucent elements. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, weak) UIViewController *presentingViewController; + +///-------------------------------------- +/// @name Customizing the Logo +///-------------------------------------- + +/*! + @abstract The logo. By default, it is the Parse logo. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) UIView *logo; + +///-------------------------------------- +/// @name Configure Username Behaviour +///-------------------------------------- + +/*! + @abstract If email should be used to log in, instead of username + + @discussion By default, this is set to `NO`. + */ +@property (nonatomic, assign) BOOL emailAsUsername; + +///-------------------------------------- +/// @name Sign Up Elements +///-------------------------------------- + +/*! + @abstract The bitmask which specifies the enabled sign up elements in the view + */ +@property (nonatomic, assign, readonly) PFSignUpFields fields; + +/*! + @abstract The username text field. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *usernameField; + +/*! + @abstract The password text field. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *passwordField; + +/*! + @abstract The email text field. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *emailField; + +/*! + @abstract The additional text field. It is `nil` if the element is not enabled. + + @discussion This field is intended to be customized. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFTextField *additionalField; + +/*! + @abstract The sign up button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *signUpButton; + +/*! + @abstract The dismiss button. It is `nil` if the element is not enabled. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) UIButton *dismissButton; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.m b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.m new file mode 100644 index 0000000..b171404 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpView.m @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFSignUpView.h" + +#import "PFColor.h" +#import "PFDismissButton.h" +#import "PFImage.h" +#import "PFLocalization.h" +#import "PFPrimaryButton.h" +#import "PFRect.h" +#import "PFTextButton.h" +#import "PFTextField.h" + +static NSString *const PFSignUpViewDefaultLogoImageName = @"parse_logo.png"; + +@implementation PFSignUpView + +#pragma mark - +#pragma mark NSObject + +- (instancetype)initWithFields:(PFSignUpFields)otherFields { + self = [super init]; + if (!self) return nil; + + _fields = otherFields; + + self.opaque = YES; + self.backgroundColor = [PFColor commonBackgroundColor]; + + _logo = [[UIImageView alloc] initWithImage:[PFImage imageNamed:PFSignUpViewDefaultLogoImageName]]; + _logo.contentMode = UIViewContentModeScaleAspectFit; + [self addSubview:_logo]; + + if (_fields & PFSignUpFieldsDismissButton) { + _dismissButton = [[PFDismissButton alloc] initWithFrame:CGRectZero]; + [self addSubview:_dismissButton]; + } + + _usernameField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:(PFTextFieldSeparatorStyleTop | + PFTextFieldSeparatorStyleBottom)]; + _usernameField.autocorrectionType = UITextAutocorrectionTypeNo; + _usernameField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _usernameField.returnKeyType = UIReturnKeyNext; + [self addSubview:_usernameField]; + [self _updateUsernameFieldStyle]; + + _passwordField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:PFTextFieldSeparatorStyleBottom]; + _passwordField.placeholder = NSLocalizedString(@"Password", @"Password"); + _passwordField.secureTextEntry = YES; + _passwordField.autocorrectionType = UITextAutocorrectionTypeNo; + _passwordField.autocapitalizationType = UITextAutocapitalizationTypeNone; + if (!(_fields & PFSignUpFieldsEmail) && !(_fields & PFSignUpFieldsAdditional)) { + _passwordField.returnKeyType = UIReturnKeyDone; + } else { + _passwordField.returnKeyType = UIReturnKeyNext; + } + [self addSubview:_passwordField]; + + if (_fields & PFSignUpFieldsEmail) { + _emailField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:PFTextFieldSeparatorStyleBottom]; + _emailField.autocorrectionType = UITextAutocorrectionTypeNo; + _emailField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _emailField.keyboardType = UIKeyboardTypeEmailAddress; + _emailField.placeholder = NSLocalizedString(@"Email", @"Email"); + if (!(_fields & PFSignUpFieldsAdditional)) { + _emailField.returnKeyType = UIReturnKeyDone; + } else { + _emailField.returnKeyType = UIReturnKeyNext; + } + [self addSubview:_emailField]; + } + + if (_fields & PFSignUpFieldsAdditional) { + _additionalField = [[PFTextField alloc] initWithFrame:CGRectZero + separatorStyle:PFTextFieldSeparatorStyleBottom]; + _additionalField.autocorrectionType = UITextAutocorrectionTypeNo; + _additionalField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _additionalField.placeholder = NSLocalizedString(@"Additional", @"Additional"); + _additionalField.returnKeyType = UIReturnKeyDone; + [self addSubview:_additionalField]; + } + + if (_fields & PFSignUpFieldsSignUpButton) { + _signUpButton = [[PFPrimaryButton alloc] initWithBackgroundImageColor:[PFColor signupButtonBackgroundColor]]; + [_signUpButton setTitle:NSLocalizedString(@"Sign Up", @"Sign Up") forState:UIControlStateNormal]; + [self addSubview:_signUpButton]; + } + + return self; +} + +#pragma mark - +#pragma mark UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGRect bounds = self.bounds; + + if (_dismissButton) { + CGPoint origin = CGPointMake(16.0f, 16.0f); + + // In iOS 7+, if the view controller that contains this view + // is presented modally, it's edges extend under the status bar. + // This lets us move down the dismiss button a bit so that it's not covered by the status bar. + if ([self.presentingViewController respondsToSelector:@selector(topLayoutGuide)]) { + origin.y += self.presentingViewController.topLayoutGuide.length; + } + + CGRect frame = PFRectMakeWithOriginSize(origin, [_dismissButton sizeThatFits:bounds.size]); + _dismissButton.frame = frame; + } + + const CGRect contentRect = PFRectMakeWithSizeCenteredInRect([self _contentSizeThatFits:bounds.size], + PFRectMakeWithSize(bounds.size)); + const CGSize contentSize = contentRect.size; + const CGSize contentSizeScale = [self _contentSizeScaleForContentSize:bounds.size]; + + CGFloat currentY = CGRectGetMinY(contentRect); + if (_logo) { + CGFloat logoTopInset = floorf(48.0f * contentSizeScale.height); + CGFloat logoBottomInset = floorf(36.0f * contentSizeScale.height); + + CGFloat logoAvailableHeight = floorf(68.0f * contentSizeScale.height); + + CGSize logoSize = [_logo sizeThatFits:CGSizeMake(contentSize.width, logoAvailableHeight)]; + logoSize.width = MIN(contentSize.width, logoSize.width); + logoSize.height = MIN(logoAvailableHeight, logoSize.height); + + CGRect frame = PFRectMakeWithSizeCenteredInRect(logoSize, contentRect); + frame.origin.y = CGRectGetMinY(contentRect) + logoTopInset; + _logo.frame = CGRectIntegral(frame); + + currentY = floorf(CGRectGetMaxY(frame) + logoBottomInset); + } + + if (_usernameField) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_usernameField sizeThatFits:contentSize], contentRect); + frame.origin.y = currentY; + _usernameField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_passwordField) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_passwordField sizeThatFits:contentSize], contentRect); + frame.origin.y = currentY; + _passwordField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_emailField && !_emailAsUsername) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_emailField sizeThatFits:contentSize], contentRect); + frame.origin.y = currentY; + _emailField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_additionalField) { + CGRect frame = PFRectMakeWithSizeCenteredInRect([_additionalField sizeThatFits:contentSize], contentRect); + frame.origin.y = currentY; + _additionalField.frame = frame; + + currentY = CGRectGetMaxY(frame); + } + + if (_signUpButton) { + CGFloat loginButtonTopInset = floorf(24.0f * contentSizeScale.height); + + CGRect frame = PFRectMakeWithSizeCenteredInRect([_signUpButton sizeThatFits:contentSize], contentRect);; + frame.origin.y = currentY + loginButtonTopInset; + _signUpButton.frame = frame; + + currentY = CGRectGetMaxY(frame); + } +} + +- (CGSize)_contentSizeThatFits:(CGSize)boundingSize { + CGSize maxContentSize = [self _maxContentSize]; + CGSize contentSizeScale = [self _contentSizeScaleForContentSize:boundingSize]; + + CGSize size = PFSizeMin(maxContentSize, boundingSize); + size.height = 0.0f; + if (_logo) { + CGFloat logoTopInset = floorf(36.0f * contentSizeScale.height); + CGFloat logoBottomInset = floorf(36.0f * contentSizeScale.height); + + CGFloat logoAvailableHeight = floorf(68.0f * contentSizeScale.height); + + CGFloat scale = MAX(contentSizeScale.width, contentSizeScale.height); + + CGSize logoSize = [_logo sizeThatFits:CGSizeMake(boundingSize.width, logoAvailableHeight)]; + logoSize.height *= scale; + logoSize.width *= scale; + + size.height += logoSize.height + logoTopInset + logoBottomInset; + } + if (_usernameField) { + CGSize fieldSize = [_usernameField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_passwordField) { + CGSize fieldSize = [_passwordField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_emailField && !_emailAsUsername) { + CGSize fieldSize = [_emailField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_additionalField) { + CGSize fieldSize = [_additionalField sizeThatFits:boundingSize]; + size.height += fieldSize.height; + } + if (_signUpButton) { + CGFloat buttonTopInset = floorf(24.0f * contentSizeScale.height); + + CGSize buttonSize = [_signUpButton sizeThatFits:boundingSize]; + + size.height += buttonSize.height + buttonTopInset; + } + + size.width = floorf(size.width); + size.height = floorf(size.height); + + return size; +} + +- (CGSize)_maxContentSize { + return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? + CGSizeMake(420.0f, 500.0f) : + CGSizeMake(500.0f, 800.0f)); +} + +- (CGSize)_contentSizeScaleForContentSize:(CGSize)contentSize { + CGSize maxContentSize = [self _maxContentSize]; + if (maxContentSize.width < contentSize.width && + maxContentSize.height < contentSize.height) { + return CGSizeMake(1.0f, 1.0f); + } + + CGSize contentSizeScale = CGSizeMake(contentSize.width / maxContentSize.width, + contentSize.height / maxContentSize.height); + return contentSizeScale; +} + +#pragma mark - +#pragma mark PFSignUpView + +- (void)setLogo:(UIView *)logo { + if (self.logo != logo) { + [_logo removeFromSuperview]; + _logo = logo; + [self addSubview:_logo]; + + [self setNeedsLayout]; + } +} + +- (void)setEmailAsUsername:(BOOL)otherEmailAsUsername { + if (_emailAsUsername != otherEmailAsUsername) { + _emailAsUsername = otherEmailAsUsername; + + if (_emailAsUsername) { + if (_emailField.superview) { + [_emailField removeFromSuperview]; + } + } else { + if (_emailField.superview == nil) { + [self addSubview:_emailField]; + } + [self setNeedsLayout]; + } + [self _updateUsernameFieldStyle]; + } +} + +#pragma mark - +#pragma mark Private + +- (void)_updateUsernameFieldStyle { + UIKeyboardType keyboardType = UIKeyboardTypeDefault; + NSString *placeholder = nil; + if (!_emailAsUsername) { + placeholder = NSLocalizedString(@"Username", @"Username"); + keyboardType = UIKeyboardTypeDefault; + } else { + placeholder = NSLocalizedString(@"Email", @"Email"); + keyboardType = UIKeyboardTypeEmailAddress; + } + + _usernameField.placeholder = placeholder; + _usernameField.keyboardType = keyboardType; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.h b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.h new file mode 100644 index 0000000..88b2332 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import +#import + +@class PFUser; +@protocol PFSignUpViewControllerDelegate; + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + The `PFSignUpViewController` class that presents and manages + a standard authentication interface for signing up a . + */ +@interface PFSignUpViewController : UIViewController + +///-------------------------------------- +/// @name Configuring Sign Up Elements +///-------------------------------------- + +/*! + @abstract A bitmask specifying the log in elements which are enabled in the view. + + @see PFSignUpFields + */ +@property (nonatomic, assign) PFSignUpFields fields; + +/*! + @abstract The sign up view. It contains all the enabled log in elements. + + @see PFSignUpView + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong, readonly) PFSignUpView *signUpView; + +///-------------------------------------- +/// @name Configuring Sign Up Behaviors +///-------------------------------------- + +/*! + @abstract The delegate that responds to the control events of `PFSignUpViewController`. + + @see PFSignUpViewControllerDelegate + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, weak) id delegate; + +/*! + @abstract Minimum required password length for user signups, defaults to `0`. + */ +@property (nonatomic, assign) NSUInteger minPasswordLength; + +/*! + @abstract Whether to use the email as username on the attached . + + @discussion If set to `YES`, we'll hide the email field, prompt for the email in + the username field, and save the email into both username and email + fields on the new object. By default, this is set to `NO`. + */ +@property (nonatomic, assign) BOOL emailAsUsername; + +@end + +///-------------------------------------- +/// @name Notifications +///-------------------------------------- + +/*! + @abstract The notification is posted immediately after the sign up succeeds. + */ +extern NSString *const PFSignUpSuccessNotification; + +/*! + @abstract The notification is posted immediately after the sign up fails. + + @discussion If the delegate prevents the sign up to start, the notification is not sent. + */ +extern NSString *const PFSignUpFailureNotification; + +/*! + @abstract The notification is posted immediately after the user cancels sign up. + */ +extern NSString *const PFSignUpCancelNotification; + +///-------------------------------------- +/// @name PFSignUpViewControllerDelegate +///-------------------------------------- + +/*! + The `PFLogInViewControllerDelegate` protocol defines methods a delegate of a should implement. + All methods of this protocol are optional. + */ +@protocol PFSignUpViewControllerDelegate + +@optional + +///-------------------------------------- +/// @name Customizing Behavior +///-------------------------------------- + +/*! + @abstract Sent to the delegate to determine whether the sign up request should be submitted to the server. + + @param signUpController The signup view controller that is requesting the data. + @param info An `NSDictionary` instance which contains all sign up information that the user entered. + + @returns A `BOOL` indicating whether the sign up should proceed. + */ +- (BOOL)signUpViewController:(PFSignUpViewController *)signUpController shouldBeginSignUp:(NSDictionary *)info; + +///-------------------------------------- +/// @name Responding to Actions +///-------------------------------------- + +/*! + @abstract Sent to the delegate when a is signed up. + + @param signUpController The signup view controller where signup finished. + @param user object that is a result of the sign up. + */ +- (void)signUpViewController:(PFSignUpViewController *)signUpController didSignUpUser:(PFUser *)user; + +/*! + @abstract Sent to the delegate when the sign up attempt fails. + + @param signUpController The signup view controller where signup failed. + @param error `NSError` object representing the error that occured. + */ +- (void)signUpViewController:(PFSignUpViewController *)signUpController + didFailToSignUpWithError:(PFUI_NULLABLE NSError *)error; + +/*! + @abstract Sent to the delegate when the sign up screen is cancelled. + + @param signUpController The signup view controller where signup was cancelled. + */ +- (void)signUpViewControllerDidCancelSignUp:(PFSignUpViewController *)signUpController; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.m b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.m new file mode 100644 index 0000000..257b5fb --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/SignUpViewController/PFSignUpViewController.m @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFSignUpViewController.h" + +#import +#import + +#import "PFUIAlertView.h" +#import "PFLocalization.h" +#import "PFPrimaryButton.h" +#import "PFTextField.h" + +NSString *const PFSignUpSuccessNotification = @"com.parse.ui.signup.success"; +NSString *const PFSignUpFailureNotification = @"com.parse.ui.signup.failure"; +NSString *const PFSignUpCancelNotification = @"com.parse.ui.signup.cancel"; + +// Keys that are used to pass information to the delegate on `signUpViewController:shouldBeginSignUp`. +static NSString *const PFSignUpViewControllerDelegateInfoUsernameKey = @"username"; +static NSString *const PFSignUpViewControllerDelegateInfoPasswordKey = @"password"; +static NSString *const PFSignUpViewControllerDelegateInfoEmailKey = @"email"; +static NSString *const PFSignUpViewControllerDelegateInfoAdditionalKey = @"additional"; + +@interface PFSignUpViewController () { + struct { + BOOL shouldSignUp : YES; + BOOL didSignUp : YES; + BOOL didFailToSignUp : YES; + BOOL didCancelSignUp : YES; + } _delegateExistingMethods; +} + +@property (nonatomic, strong, readwrite) PFSignUpView *signUpView; + +@property (nonatomic, assign) BOOL loading; + +@property (nonatomic, assign) CGFloat visibleKeyboardHeight; + +@end + +@implementation PFSignUpViewController + +#pragma mark - +#pragma mark Init + +- (instancetype)init { + if (self = [super init]) { + [self _commonInit]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { + [self _commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super initWithCoder:decoder]) { + [self _commonInit]; + } + return self; +} + +- (void)_commonInit { + self.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + self.modalPresentationStyle = UIModalPresentationFormSheet; + _fields = PFSignUpFieldsDefault; + + if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { + self.automaticallyAdjustsScrollViewInsets = NO; + } +} + +#pragma mark - +#pragma mark Dealloc + +- (void)dealloc { + // Unregister from all notifications + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#pragma mark - +#pragma mark UIViewController + +- (void)loadView { + _signUpView = [[PFSignUpView alloc] initWithFields:_fields]; + _signUpView.presentingViewController = self; + self.view = _signUpView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self _setupHandlers]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self _registerForKeyboardNotifications]; + if (self.navigationController && + self.fields & PFSignUpFieldsDismissButton) { + self.fields = self.fields & ~PFSignUpFieldsDismissButton; + + [_signUpView.dismissButton removeFromSuperview]; + } +} + +#pragma mark - +#pragma mark Rotation + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + return UIInterfaceOrientationMaskAll; + } + + return UIInterfaceOrientationMaskPortrait; +} + +#pragma mark - +#pragma mark PFSignUpViewController + +- (PFSignUpView *)signUpView { + return (PFSignUpView *)self.view; // self.view will call loadView if the view is nil +} + +- (void)setDelegate:(id)delegate { + if (self.delegate != delegate) { + _delegate = delegate; + + _delegateExistingMethods.shouldSignUp = [delegate respondsToSelector:@selector(signUpViewController: + shouldBeginSignUp:)]; + _delegateExistingMethods.didSignUp = [delegate respondsToSelector:@selector(signUpViewController: + didSignUpUser:)]; + _delegateExistingMethods.didFailToSignUp = [delegate respondsToSelector:@selector(signUpViewController: + didFailToSignUpWithError:)]; + _delegateExistingMethods.didCancelSignUp = [delegate + respondsToSelector:@selector(signUpViewControllerDidCancelSignUp:)]; + } +} + +- (void)setEmailAsUsername:(BOOL)otherEmailAsUsername { + self.signUpView.emailAsUsername = otherEmailAsUsername; +} + +- (BOOL)emailAsUsername { + return self.signUpView.emailAsUsername; +} + +#pragma mark - +#pragma mark UITextFieldDelegate + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + [self _updateSignUpViewContentOffsetAnimated:YES]; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + if (textField == _signUpView.usernameField) { + [_signUpView.passwordField becomeFirstResponder]; + return YES; + } + + if (textField == _signUpView.passwordField) { + if (_signUpView.emailField) { + [_signUpView.emailField becomeFirstResponder]; + return YES; + } else if (_signUpView.additionalField) { + [_signUpView.additionalField becomeFirstResponder]; + return YES; + } + } + + if (textField == _signUpView.emailField) { + if (_signUpView.additionalField) { + [_signUpView.additionalField becomeFirstResponder]; + return YES; + } + } + + [self _signUpAction]; + + return YES; +} + +#pragma mark - +#pragma mark Private + +- (void)_setupHandlers { + _signUpView.delegate = self; // UIScrollViewDelegate + [_signUpView.dismissButton addTarget:self + action:@selector(_dismissAction) + forControlEvents:UIControlEventTouchUpInside]; + _signUpView.usernameField.delegate = self; + _signUpView.passwordField.delegate = self; + _signUpView.emailField.delegate = self; + _signUpView.additionalField.delegate = self; + [_signUpView.signUpButton addTarget:self + action:@selector(_signUpAction) + forControlEvents:UIControlEventTouchUpInside]; + + UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(_dismissKeyboard)]; + gestureRecognizer.cancelsTouchesInView = NO; + [_signUpView addGestureRecognizer:gestureRecognizer]; +} + +- (void)_dismissAction { + [self _cancelSignUp]; + + // Normally the role of dismissing a modal controller lies on the presenting controller. + // Here we violate the principle so that the presenting modal log in controller is especially easy. + // Cons of this design is that it makes working with non-modally presented log in controller hard; + // but this concern is mitigated by the fact that navigationally presented controller should not have + // dismiss button. + + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)_signUpAction { + if (self.loading) { + return; + } + + [self _dismissKeyboard]; + + NSString *username = _signUpView.usernameField.text ?: @""; + NSString *password = _signUpView.passwordField.text ?: @""; + NSString *email = (self.emailAsUsername ? username : _signUpView.emailField.text); + email = [email stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + NSString *additional = _signUpView.additionalField.text; + + NSMutableDictionary *dictionary = [@{ PFSignUpViewControllerDelegateInfoUsernameKey : username, + PFSignUpViewControllerDelegateInfoPasswordKey : password } mutableCopy]; + + if (email) { + dictionary[PFSignUpViewControllerDelegateInfoEmailKey] = email; + } + if (additional) { + dictionary[PFSignUpViewControllerDelegateInfoAdditionalKey] = additional; + } + + if (_delegateExistingMethods.shouldSignUp) { + if (![_delegate signUpViewController:self shouldBeginSignUp:dictionary]) { + return; + } + } + + if ([password length] < _minPasswordLength) { + NSString *errorMessage = NSLocalizedString(@"Password must be at least %d characters.", + @"Password too short error message in PFSignUpViewController"); + errorMessage = [NSString stringWithFormat:errorMessage, (unsigned long)_minPasswordLength]; + NSError *error = [NSError errorWithDomain:PFParseErrorDomain + code:0 + userInfo:@{ NSLocalizedDescriptionKey : errorMessage }]; + [self _signUpDidFailWithError:error]; + [_signUpView.passwordField becomeFirstResponder]; + + return; + } + + PFUser *user = [PFUser user]; + user.username = username; + user.password = password; + + if (email) { + user.email = email; + } + if (additional) { + user[PFSignUpViewControllerDelegateInfoAdditionalKey] = additional; + } + + self.loading = YES; + if ([_signUpView.signUpButton isKindOfClass:[PFPrimaryButton class]]) { + [(PFPrimaryButton *)_signUpView.signUpButton setLoading:YES]; + } + [user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { + self.loading = NO; + if ([_signUpView.signUpButton isKindOfClass:[PFPrimaryButton class]]) { + [(PFPrimaryButton *)_signUpView.signUpButton setLoading:NO]; + } + + if (succeeded) { + [self _signUpDidSuceedWithUser:user]; + } + else { + [self _signUpDidFailWithError:error]; + } + }]; +} + +- (void)_signUpDidSuceedWithUser:(PFUser *)user { + if (_delegateExistingMethods.didSignUp) { + [_delegate signUpViewController:self didSignUpUser:user]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFSignUpSuccessNotification object:self]; +} + +- (void)_signUpDidFailWithError:(NSError *)error { + if (_delegateExistingMethods.didFailToSignUp) { + [_delegate signUpViewController:self didFailToSignUpWithError:error]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFSignUpFailureNotification object:self]; + + NSString *title = NSLocalizedString(@"Sign Up Error", @"Sign Up Error"); + + if ([[error domain] isEqualToString:PFParseErrorDomain]) { + NSInteger errorCode = [error code]; + NSString *message = nil; + UIResponder *responder = nil; + + if (errorCode == kPFErrorInvalidEmailAddress) { + message = NSLocalizedString(@"The email address is invalid. Please enter a valid email.", + @"Invalid email address error message in PFSignUpViewControllers"); + responder = _signUpView.emailField ?: _signUpView.usernameField; + } else if (errorCode == kPFErrorUsernameMissing) { + message = NSLocalizedString(@"Please enter a username.", + @"Username missing error message in PFSignUpViewController"); + responder = _signUpView.usernameField; + } else if (errorCode == kPFErrorUserPasswordMissing) { + message = NSLocalizedString(@"Please enter a password.", + @"Password missing error message in PFSignUpViewController"); + responder = _signUpView.passwordField; + } else if (errorCode == kPFErrorUsernameTaken) { + NSString *format = NSLocalizedString(@"The username '%@' is taken. Please try choosing a different username.", + @"Username taken error format in PFSignUpViewController"); + message = [NSString stringWithFormat:format, _signUpView.usernameField.text]; + responder = _signUpView.usernameField; + } else if (error.code == kPFErrorUserEmailTaken) { + NSString *format = NSLocalizedString(@"The email '%@' is taken. Please try using a different email.", + @"Email is taken error format in PFSignUpViewController."); + UITextField *textField = self.emailAsUsername ? _signUpView.usernameField : _signUpView.emailField; + + message = [NSString stringWithFormat:format, textField.text]; + responder = textField; + } else if (error.code == kPFErrorUserEmailMissing) { + message = NSLocalizedString(@"Please enter an email.", + @"Email missing error message in PFSignUpViewController"); + responder = _signUpView.emailField; + } + + if (message != nil) { + [PFUIAlertView showAlertViewWithTitle:title message:message]; + [responder becomeFirstResponder]; + + return; + } + } + + // Show the generic error alert, as no custom cases matched before + [PFUIAlertView showAlertViewWithTitle:title error:error]; +} + +- (void)_cancelSignUp { + if (_delegateExistingMethods.didCancelSignUp) { + [_delegate signUpViewControllerDidCancelSignUp:self]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:PFSignUpCancelNotification object:self]; +} + +- (UIView *)_currentFirstResponder { + if ([_signUpView.usernameField isFirstResponder]) { + return _signUpView.usernameField; + } + if ([_signUpView.passwordField isFirstResponder]) { + return _signUpView.passwordField; + } + if ([_signUpView.emailField isFirstResponder]) { + return _signUpView.emailField; + } + if ([_signUpView.additionalField isFirstResponder]) { + return _signUpView.additionalField; + } + + return nil; +} + +#pragma mark Keyboard + +- (void)_dismissKeyboard { + [self.view endEditing:YES]; +} + +- (void)_registerForKeyboardNotifications { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardWillShow:) + name:UIKeyboardWillShowNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardWillHide:) + name:UIKeyboardWillHideNotification object:nil]; +} + +- (void)_keyboardWillShow:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; + CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; + + CGRect keyboardFrame = [self.view convertRect:endFrame fromView:self.view.window]; + CGFloat visibleKeyboardHeight = CGRectGetMaxY(self.view.bounds) - CGRectGetMinY(keyboardFrame); + + [self setVisibleKeyboardHeight:visibleKeyboardHeight + animationDuration:duration + animationOptions:curve << 16]; +} + +- (void)_keyboardWillHide:(NSNotification *)notification { + NSDictionary *userInfo = [notification userInfo]; + CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; + [self setVisibleKeyboardHeight:0.0 + animationDuration:duration + animationOptions:curve << 16]; +} + +- (void)setVisibleKeyboardHeight:(CGFloat)visibleKeyboardHeight + animationDuration:(NSTimeInterval)animationDuration + animationOptions:(UIViewAnimationOptions)animationOptions { + + dispatch_block_t animationsBlock = ^{ + self.visibleKeyboardHeight = visibleKeyboardHeight; + }; + + if (animationDuration == 0.0) { + animationsBlock(); + } else { + [UIView animateWithDuration:animationDuration + delay:0.0 + options:animationOptions | UIViewAnimationOptionBeginFromCurrentState + animations:animationsBlock + completion:nil]; + } +} + +- (void)setVisibleKeyboardHeight:(CGFloat)visibleKeyboardHeight { + if (self.visibleKeyboardHeight != visibleKeyboardHeight) { + _visibleKeyboardHeight = visibleKeyboardHeight; + [self _updateSignUpViewContentOffsetAnimated:NO]; + } +} + +- (void)_updateSignUpViewContentOffsetAnimated:(BOOL)animated { + CGPoint contentOffset = CGPointZero; + if (self.visibleKeyboardHeight > 0.0f) { + // Scroll the view to keep fields visible + CGFloat offsetForScrollingTextFieldToTop = CGRectGetMinY([self _currentFirstResponder].frame); + + UIView *lowestView; + if (_signUpView.signUpButton) { + lowestView = _signUpView.signUpButton; + } else if (_signUpView.additionalField) { + lowestView = _signUpView.additionalField; + } else if (_signUpView.emailField) { + lowestView = _signUpView.emailField; + } else { + lowestView = _signUpView.passwordField; + } + + CGFloat offsetForScrollingLowestViewToBottom = 0.0f; + offsetForScrollingLowestViewToBottom += self.visibleKeyboardHeight; + offsetForScrollingLowestViewToBottom += CGRectGetMaxY(lowestView.frame); + offsetForScrollingLowestViewToBottom -= CGRectGetHeight(_signUpView.bounds); + + if (offsetForScrollingLowestViewToBottom < 0) { + return; // No scrolling required + } + + contentOffset = CGPointMake(0.0f, MIN(offsetForScrollingTextFieldToTop, + offsetForScrollingLowestViewToBottom)); + } + + [_signUpView setContentOffset:contentOffset animated:animated]; +} + +#pragma mark - +#pragma mark Accessors + +- (void)setLoading:(BOOL)loading { + if (self.loading != loading) { + _loading = loading; + + _signUpView.usernameField.enabled = !self.loading; + _signUpView.passwordField.enabled = !self.loading; + _signUpView.emailField.enabled = !self.loading; + _signUpView.additionalField.enabled = !self.loading; + _signUpView.dismissButton.enabled = !self.loading; + } +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.h b/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.h new file mode 100644 index 0000000..368b3d6 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +typedef void(^PFImageViewImageResultBlock)(UIImage *PFUI_NULLABLE_S image, NSError *PFUI_NULLABLE_S error); + +@class BFTask; +@class PFFile; + +/*! + An image view that downloads and displays remote image stored on Parse's server. + */ +@interface PFImageView : UIImageView + +/*! + @abstract The remote file on Parse's server that stores the image. + + @warning Note that the download does not start until is called. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) PFFile *file; + +/*! + @abstract Initiate downloading of the remote image. + + @discussion Once the download completes, the remote image will be displayed. + + @returns The task, that encapsulates the work being done. + */ +- (BFTask *)loadInBackground; + +/*! + @abstract Initiate downloading of the remote image. + + @discussion Once the download completes, the remote image will be displayed. + + @param completion the completion block. + */ +- (void)loadInBackground:(PFUI_NULLABLE PFImageViewImageResultBlock)completion; + +/*! + @abstract Initiate downloading of the remote image. + + @discussion Once the download completes, the remote image will be displayed. + + @param completion the completion block. + @param progressBlock called with the download progress as the image is being downloaded. + Will be called with a value of 100 before the completion block is called. + */ +- (void)loadInBackground:(PFUI_NULLABLE PFImageViewImageResultBlock)completion + progressBlock:(PFUI_NULLABLE void (^)(int percentDone))progressBlock; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.m b/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.m new file mode 100644 index 0000000..fe44076 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Views/PFImageView.m @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFImageView.h" + +#import + +#import + +#import "PFImageCache.h" + +@implementation PFImageView + +#pragma mark - +#pragma mark Accessors + +- (void)setFile:(PFFile *)otherFile { + // Here we don't check (file != otherFile) + // because self.image needs to be updated regardless. + // setFile: could have altered self.image + _file = otherFile; + NSURL *url = [NSURL URLWithString:self.file.url]; + UIImage *cachedImage = [[PFImageCache sharedCache] imageForURL:url]; + if (cachedImage) { + self.image = cachedImage; + } +} + +#pragma mark - +#pragma mark Load + +- (BFTask *)loadInBackground { + BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource]; + [self loadInBackground:^(UIImage *image, NSError *error) { + if (error) { + [source trySetError:error]; + } else { + [source trySetResult:image]; + } + }]; + return source.task; +} + + +- (void)loadInBackground:(void (^)(UIImage *, NSError *))completion { + [self loadInBackground:completion progressBlock:nil]; +} + +- (void)loadInBackground:(void (^)(UIImage *, NSError *))completion progressBlock:(PFProgressBlock)progressBlock { + if (!self.file) { + // When there is nothing to load, the user just wants to display + // the placeholder image. I think the better design decision is + // to return with no error, to simplify caller logic. (arguable) + if (completion) { + completion(nil, nil); + } + return; + } + + if (!self.file.url) { + // The file has not been saved. + if (completion) { + NSError *error = [NSError errorWithDomain:PFParseErrorDomain code:kPFErrorUnsavedFile userInfo:nil]; + completion(nil, error); + } + return; + } + + NSURL *url = [NSURL URLWithString:self.file.url]; + if (url) { + UIImage *cachedImage = [[PFImageCache sharedCache] imageForURL:url]; + if (cachedImage) { + self.image = cachedImage; + + if (progressBlock) { + progressBlock(100); + } + if (completion) { + completion(cachedImage, nil); + } + return; + } + } + + + PFFile *file = _file; + [_file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) { + if (error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, error); + }); + } + return; + } + + // We dispatch to a background queue to offload the work to decode data into image + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + UIImage *image = [UIImage imageWithData:data]; + if (!image) { + if (completion) { + NSError *invalidDataError = [NSError errorWithDomain:PFParseErrorDomain + code:kPFErrorInvalidImageData + userInfo:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, invalidDataError); + }); + } + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + // check if a latter issued loadInBackground has not replaced the file being loaded + if (file == _file) { + self.image = image; + } + + if (completion) { + completion(image, nil); + } + }); + + if (url) { + // We always want to store the image in the cache. + // In previous checks we've verified neither key nor value is nil. + [[PFImageCache sharedCache] setImage:image forURL:url]; + } + }); + } progressBlock:progressBlock]; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.h b/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.h new file mode 100644 index 0000000..5bfb168 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import + +#import + +PFUI_ASSUME_NONNULL_BEGIN + +/*! + `PFTextFieldSeparatorStyle` bitmask specifies the style of the separators, + that should be used for a given `PFTextField`. + + @see PFTextField + */ +typedef NS_OPTIONS(uint8_t, PFTextFieldSeparatorStyle){ + /*! No separators are visible. */ + PFTextFieldSeparatorStyleNone = 0, + /*! Separator on top of the text field. */ + PFTextFieldSeparatorStyleTop = 1 << 0, + /*! Separator at the bottom of the text field. */ + PFTextFieldSeparatorStyleBottom = 1 << 1 +}; + +/*! + `PFTextField` class serves as a stylable subclass of `UITextField`. + It includes styles that are specific to `ParseUI` framework and allows advanced customization. + */ +@interface PFTextField : UITextField + +/*! + @abstract Separator style bitmask that should be applied to this textfield. + + @discussion Default: + + @see PFTextFieldSeparatorStyle + */ +@property (nonatomic, assign) PFTextFieldSeparatorStyle separatorStyle; + +/*! + @abstract Color that should be used for the separators, if they are visible. + + @discussion Default: `227,227,227,1.0`. + */ +@property (PFUI_NULLABLE_PROPERTY nonatomic, strong) UIColor *separatorColor UI_APPEARANCE_SELECTOR; + +/*! + This method is a convenience initializer that sets both `frame` and `separatorStyle` for an instance of `PFTextField.` + + @param frame The frame rectangle for the view, measured in points. + @param separatorStyle Initial separator style to use. + + @return An initialized instance of `PFTextField` or `nil` if it couldn't be created. + */ +- (instancetype)initWithFrame:(CGRect)frame separatorStyle:(PFTextFieldSeparatorStyle)separatorStyle; + +@end + +PFUI_ASSUME_NONNULL_END diff --git a/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.m b/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.m new file mode 100644 index 0000000..7372cd5 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Classes/Views/PFTextField.m @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import "PFTextField.h" + +#import "PFColor.h" + +@implementation PFTextField + +#pragma mark - +#pragma mark Init + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (!self) return nil; + + self.backgroundColor = [PFColor textFieldBackgroundColor]; + self.textColor = [PFColor textFieldTextColor]; + + self.font = [UIFont systemFontOfSize:17.0f]; + + self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; + + _separatorColor = [PFColor textFieldSeparatorColor]; + + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame separatorStyle:(PFTextFieldSeparatorStyle)separatorStyle { + self = [self initWithFrame:frame]; + if (!self) return nil; + + _separatorStyle = separatorStyle; + + return self; +} + +#pragma mark - +#pragma mark Accessors + +- (void)setPlaceholder:(NSString *)placeholder { + NSDictionary *attributes = @{ NSForegroundColorAttributeName : [PFColor textFieldPlaceholderColor] }; + self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholder attributes:attributes]; +} + +- (void)setSeparatorStyle:(PFTextFieldSeparatorStyle)separatorStyle { + if (self.separatorStyle != separatorStyle) { + _separatorStyle = separatorStyle; + [self setNeedsDisplay]; + } +} + +#pragma mark - +#pragma mark Drawing + +- (void)drawRect:(CGRect)rect { + [super drawRect:rect]; + + const CGRect bounds = self.bounds; + CGContextRef context = UIGraphicsGetCurrentContext(); + + if (self.separatorStyle != PFTextFieldSeparatorStyleNone) { + [self.separatorColor setFill]; + } + + if (self.separatorStyle & PFTextFieldSeparatorStyleTop) { + CGRect borderRect = CGRectMake(0.0f, 0.0f, CGRectGetWidth(bounds), 1.0f); + CGContextFillRect(context, borderRect); + } + + if (self.separatorStyle & PFTextFieldSeparatorStyleBottom) { + CGRect borderRect = CGRectMake(0.0f, CGRectGetMaxY(bounds) - 1.0f, CGRectGetWidth(bounds), 1.0f); + CGContextFillRect(context, borderRect); + } +} + +#pragma mark - +#pragma mark Frame + +- (CGRect)textRectForBounds:(CGRect)bounds { + return CGRectMake(20.0f, 0.0f, CGRectGetWidth(bounds) - 30.0f, CGRectGetHeight(bounds)); +} + +- (CGRect)placeholderRectForBounds:(CGRect)bounds { + return [self textRectForBounds:bounds]; +} + +- (CGRect)editingRectForBounds:(CGRect)bounds { + return [self textRectForBounds:bounds]; +} + +#pragma mark - +#pragma mark Sizing + +- (CGSize)sizeThatFits:(CGSize)boundingSize { + CGSize size = CGSizeZero; + size.width = boundingSize.width; + size.height = MIN(44.0f, boundingSize.height); + return size; +} + +@end diff --git a/Pods/ParseUI/ParseUI/Generated/PFResources.h b/Pods/ParseUI/ParseUI/Generated/PFResources.h new file mode 100644 index 0000000..9ac07eb --- /dev/null +++ b/Pods/ParseUI/ParseUI/Generated/PFResources.h @@ -0,0 +1,13 @@ +// This is an auto-generated file. +#import +@interface PFResources : NSObject ++ (NSData *)facebook_icon_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)facebook_icon2x_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)facebook_icon3x_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)parse_logo_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)parse_logo2x_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)parse_logo3x_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)twitter_icon_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)twitter_icon2x_png;//modified:2015-10-10 17:26:23 -0400 ++ (NSData *)twitter_icon3x_png;//modified:2015-10-10 17:26:23 -0400 +@end diff --git a/Pods/ParseUI/ParseUI/Generated/PFResources.m b/Pods/ParseUI/ParseUI/Generated/PFResources.m new file mode 100644 index 0000000..5475765 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Generated/PFResources.m @@ -0,0 +1,76 @@ +// This is an auto-generated file. +#import "PFResources.h" +@implementation PFResources + const unsigned char facebook_icon_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x16, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xb4, 0x6c, 0x3b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x46, 0x34, 0x42, 0x46, 0x44, 0x38, 0x36, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x46, 0x34, 0x42, 0x46, 0x44, 0x38, 0x37, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x46, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x38, 0x30, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x98, 0x50, 0xa6, 0xc2, 0x0, 0x0, 0x1, 0x78, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0xd8, 0xbc, 0x79, 0xf3, 0x9a, 0xbf, 0x7f, 0xff, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xf, 0x6a, 0xe0, 0x7f, 0xff, 0xfe, 0x7d, 0xdf, 0xb5, 0x6b, 0xd7, 0x26, 0x46, 0x20, 0xe7, 0x2f, 0x3, 0x3, 0x3, 0x13, 0x3, 0x95, 0x1, 0xc8, 0xc0, 0x3f, 0xc, 0xd4, 0x7, 0x7f, 0x59, 0xc8, 0xd1, 0xf5, 0xf9, 0xf3, 0xe7, 0xf, 0x6b, 0xd7, 0xae, 0x5d, 0x75, 0xf6, 0xec, 0xd9, 0x73, 0xbf, 0x7f, 0xff, 0xfe, 0xc9, 0xc1, 0xc1, 0xc1, 0xe5, 0xe1, 0xe1, 0xe1, 0xe, 0xc4, 0x7e, 0x70, 0x45, 0xc0, 0xa0, 0xf8, 0xf9, 0x9f, 0x4, 0x70, 0xef, 0xde, 0xbd, 0x5b, 0x3a, 0x3a, 0x3a, 0x1a, 0xe8, 0x96, 0x65, 0x65, 0x65, 0x25, 0x21, 0x29, 0xfb, 0x43, 0x72, 0xd8, 0xe6, 0xe4, 0xe4, 0x64, 0x5d, 0xb9, 0x72, 0xe5, 0x6, 0x16, 0x29, 0x46, 0x64, 0xe, 0x49, 0x41, 0xf1, 0xe8, 0xd1, 0xa3, 0x7b, 0x7b, 0xf6, 0xec, 0x39, 0x0, 0xe3, 0xeb, 0xeb, 0xeb, 0x6b, 0x45, 0x46, 0x46, 0x46, 0x70, 0x73, 0x73, 0x73, 0x18, 0x1a, 0x1a, 0x1a, 0xa1, 0x28, 0x26, 0x25, 0x28, 0x4e, 0x9e, 0x3c, 0x79, 0x18, 0x59, 0xef, 0x91, 0x23, 0x47, 0xf6, 0xe1, 0x50, 0x4a, 0x5a, 0x50, 0x30, 0x32, 0xa2, 0xf8, 0x96, 0x41, 0x0, 0x8, 0xf0, 0x25, 0x37, 0xa2, 0x1, 0x33, 0x33, 0x33, 0xba, 0xfa, 0xff, 0xb8, 0xd4, 0x12, 0xc, 0xe3, 0x97, 0x2f, 0x5f, 0x3e, 0xf, 0xb, 0xb, 0xb, 0xf9, 0xf9, 0xf3, 0xe7, 0xef, 0x2f, 0x5f, 0xbe, 0x7c, 0x46, 0x96, 0x3, 0x86, 0x6f, 0x2c, 0x17, 0x17, 0x17, 0x37, 0x88, 0x5d, 0x55, 0x55, 0x55, 0xee, 0xe7, 0xe7, 0x17, 0x4c, 0xb4, 0xc1, 0x40, 0x3, 0x7f, 0x1c, 0x3a, 0x74, 0xe8, 0x18, 0x36, 0xb9, 0xcb, 0x97, 0x2f, 0x5f, 0xc3, 0xe5, 0x1b, 0x82, 0x41, 0x1, 0x8a, 0x9, 0x62, 0x82, 0x49, 0x5e, 0x5e, 0x5e, 0x81, 0xa4, 0xa0, 0x10, 0x14, 0x14, 0x14, 0xea, 0xe8, 0xe8, 0x68, 0xfd, 0x3, 0x4, 0x4f, 0x9e, 0x3c, 0x79, 0x38, 0x63, 0xc6, 0x8c, 0x79, 0x48, 0x69, 0x3a, 0x4d, 0x42, 0x42, 0x42, 0x9a, 0x8d, 0x8d, 0x8d, 0x55, 0x51, 0x51, 0x51, 0x99, 0xec, 0xe4, 0x76, 0xed, 0xda, 0xb5, 0x73, 0xc8, 0x7a, 0xef, 0xde, 0xbd, 0x7b, 0x9d, 0x2a, 0xc9, 0xd, 0x18, 0x79, 0x5f, 0xd1, 0xf8, 0x5f, 0xa8, 0x92, 0xdc, 0x48, 0x2d, 0x36, 0x47, 0xd, 0x86, 0x1b, 0x4c, 0x74, 0x9, 0x47, 0x64, 0x92, 0x6, 0xe7, 0x17, 0xa6, 0xdd, 0xbb, 0x77, 0x6f, 0x5, 0x6a, 0xf8, 0x5, 0xad, 0xa2, 0x88, 0xc1, 0xc8, 0x16, 0x61, 0xc8, 0x3, 0xc5, 0x7e, 0x1f, 0x3c, 0x78, 0x70, 0x17, 0x40, 0x80, 0x1, 0x0, 0x86, 0x7f, 0xa9, 0xd0, 0x39, 0x35, 0xd1, 0x78, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)facebook_icon_png { + return [NSData dataWithBytes:facebook_icon_png length:sizeof(facebook_icon_png)]; + } + + const unsigned char facebook_icon2x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1e, 0x84, 0x5a, 0x1, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x44, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x45, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x42, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x43, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x64, 0xec, 0x39, 0x7f, 0x0, 0x0, 0x2, 0x9a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xcf, 0x8f, 0xd2, 0x40, 0x14, 0xc7, 0x99, 0x96, 0xd2, 0xf2, 0x63, 0xbb, 0x18, 0x10, 0x83, 0x42, 0x14, 0x12, 0x4f, 0x12, 0xaf, 0x60, 0xbc, 0x60, 0xf8, 0x2f, 0x8c, 0x81, 0x4, 0x88, 0x5c, 0xbc, 0x7a, 0xf2, 0x60, 0xb8, 0xb0, 0x7f, 0x0, 0xc6, 0x13, 0x7, 0xc2, 0xdd, 0x83, 0x37, 0xc2, 0xca, 0x9, 0x3, 0x44, 0x8d, 0x82, 0x31, 0x21, 0x1c, 0x48, 0x5c, 0x12, 0x48, 0x38, 0xd8, 0x8, 0xba, 0x14, 0x97, 0xa5, 0xf8, 0x6a, 0x62, 0xb2, 0x36, 0x9d, 0x2e, 0x34, 0xed, 0xb2, 0x8d, 0x7d, 0xc9, 0x24, 0x4d, 0xdf, 0xcc, 0x9b, 0x4f, 0xa7, 0x6f, 0xde, 0x7c, 0x7, 0x5, 0x2, 0x1, 0x5f, 0xa9, 0x54, 0x7a, 0x19, 0x8f, 0xc7, 0x1f, 0xd0, 0x34, 0x4d, 0x5a, 0x2e, 0xa1, 0x2d, 0x97, 0x4b, 0xe1, 0x1d, 0x58, 0x36, 0x9b, 0xcd, 0x59, 0xaa, 0xd5, 0xea, 0xab, 0xb5, 0x41, 0xac, 0xdd, 0x6e, 0x1f, 0x22, 0x41, 0x10, 0x7e, 0x20, 0x84, 0x5c, 0x16, 0x63, 0xd8, 0x9, 0x2, 0xf0, 0x29, 0x3c, 0xb0, 0x6, 0x1, 0x5e, 0x88, 0xc0, 0xdf, 0xe1, 0x61, 0xdf, 0x20, 0xc0, 0xbc, 0x9, 0xbc, 0xd1, 0x7f, 0x5d, 0x2c, 0x66, 0xc3, 0xe1, 0xf0, 0x2b, 0xc7, 0x71, 0xdf, 0x4e, 0xc1, 0xec, 0x76, 0x3b, 0x45, 0x92, 0x24, 0xe3, 0x72, 0xb9, 0x9c, 0x2c, 0xcb, 0xee, 0x7b, 0xbd, 0xde, 0xeb, 0x4, 0x41, 0x58, 0xe5, 0x80, 0x2d, 0x22, 0xf0, 0x45, 0xed, 0xf2, 0x6e, 0xb7, 0xdb, 0xcc, 0xe5, 0x72, 0x8f, 0xc2, 0xe1, 0xf0, 0xd, 0x8a, 0xa2, 0x90, 0x94, 0xc6, 0x66, 0xb3, 0x21, 0x8f, 0xc7, 0xe3, 0x18, 0xc, 0x6, 0x9f, 0x31, 0x21, 0xe6, 0x17, 0x6, 0x5c, 0x28, 0x14, 0x9e, 0x31, 0xc, 0x43, 0x6c, 0xf2, 0x7, 0xfa, 0xfd, 0xfe, 0xa7, 0x9d, 0x2, 0xe7, 0xf3, 0xf9, 0xa7, 0x9b, 0xa6, 0x8b, 0xd5, 0x6a, 0xdd, 0x2d, 0x30, 0x14, 0xfb, 0x37, 0xdb, 0xe4, 0xf7, 0x79, 0xc0, 0x84, 0xde, 0x1b, 0xec, 0x0, 0x6c, 0x9b, 0xfe, 0xb0, 0x7, 0xc5, 0x45, 0x5c, 0x63, 0x3f, 0x48, 0x4f, 0xd8, 0xc9, 0x64, 0x72, 0x54, 0xaf, 0xd7, 0xdf, 0x2a, 0xf5, 0xf1, 0xf9, 0x7c, 0x62, 0x55, 0x70, 0x43, 0x95, 0x40, 0x7f, 0x57, 0x18, 0x72, 0x9d, 0xc6, 0xe, 0xd0, 0x33, 0x25, 0x6a, 0xb5, 0xda, 0x6b, 0xdc, 0xbc, 0x4e, 0xa7, 0x93, 0x2e, 0x97, 0xcb, 0x45, 0x28, 0x6d, 0xe3, 0xd5, 0x6a, 0x35, 0x87, 0xee, 0xfc, 0x99, 0x26, 0xe0, 0x52, 0x42, 0xd7, 0x15, 0x1e, 0x8d, 0x46, 0x43, 0x9c, 0x2f, 0x93, 0xc9, 0x3c, 0x4c, 0xa5, 0x52, 0x4f, 0xb6, 0x8d, 0xa9, 0x6b, 0xe, 0x43, 0x3e, 0x2e, 0x70, 0xbe, 0x58, 0x2c, 0x76, 0x4f, 0x4d, 0x4c, 0x5d, 0x81, 0x41, 0x5, 0x62, 0x7d, 0xa0, 0xbd, 0x6d, 0x97, 0xe, 0x58, 0xc9, 0x40, 0xd6, 0xae, 0xd, 0x5, 0xac, 0xd6, 0x74, 0x5, 0x6, 0x51, 0xc3, 0xe0, 0x7c, 0xe, 0x87, 0xc3, 0xae, 0x2a, 0xcd, 0xb4, 0x50, 0x6b, 0xe3, 0xf1, 0xf8, 0xe8, 0x39, 0x98, 0x34, 0x36, 0x9c, 0x58, 0x5f, 0x1a, 0x8d, 0xc6, 0x7, 0xb9, 0x31, 0x89, 0x44, 0xe2, 0x7e, 0x28, 0x14, 0xba, 0x2d, 0x56, 0xd6, 0xb3, 0xef, 0xe1, 0x8e, 0x79, 0xd, 0x42, 0x1d, 0x60, 0x16, 0x53, 0x1b, 0xb5, 0x6, 0x2a, 0xec, 0xbd, 0x56, 0x7f, 0x25, 0x1a, 0x8d, 0xde, 0x55, 0x98, 0x4a, 0x9b, 0xa3, 0x19, 0xb4, 0x2b, 0xa1, 0x54, 0x11, 0xb6, 0xb1, 0x48, 0x24, 0x72, 0xc7, 0x50, 0x9b, 0xe, 0xd2, 0xe4, 0x96, 0x9, 0x2c, 0xa3, 0x47, 0x94, 0x4, 0xd6, 0x56, 0x16, 0xc, 0x6, 0x6f, 0x2a, 0xca, 0x4f, 0x2d, 0x26, 0x81, 0xab, 0xd, 0xe5, 0xf7, 0xfb, 0xaf, 0x4a, 0xa1, 0x79, 0x9e, 0x9f, 0x4f, 0xa7, 0xd3, 0x63, 0xb9, 0x31, 0x6e, 0xb7, 0x7b, 0xf, 0x54, 0xd9, 0x3f, 0x65, 0x4f, 0xbc, 0x36, 0x9d, 0x7, 0xac, 0x49, 0x95, 0x0, 0xb5, 0x75, 0xa, 0x60, 0x9c, 0xb4, 0x15, 0x8b, 0xc5, 0x3c, 0x6e, 0xde, 0x4a, 0xa5, 0xf2, 0x42, 0xda, 0x7f, 0x36, 0x9b, 0x71, 0xa, 0x4a, 0x4d, 0x3b, 0xb5, 0x6, 0x45, 0x82, 0x84, 0xdb, 0xee, 0x15, 0xe9, 0x7b, 0xb8, 0x5, 0xef, 0xe1, 0xc6, 0x80, 0x8f, 0x95, 0x1b, 0xb3, 0xd3, 0x93, 0xe, 0x56, 0x7e, 0xa5, 0xc6, 0x67, 0x6a, 0x9, 0x13, 0xd8, 0x4, 0x36, 0x81, 0x4d, 0x60, 0x13, 0xf8, 0xff, 0x6, 0x46, 0x6, 0xe2, 0x45, 0x22, 0x30, 0x69, 0x20, 0x60, 0x92, 0x68, 0xb5, 0x5a, 0x4d, 0x3d, 0x57, 0x44, 0xa5, 0x4f, 0xd6, 0x7a, 0xbd, 0xde, 0x47, 0x22, 0x9d, 0x4e, 0x3f, 0x6, 0xe8, 0x43, 0x10, 0x4f, 0x3f, 0xff, 0x5c, 0xa3, 0xb5, 0x6d, 0x27, 0xa, 0xf3, 0xff, 0xda, 0x34, 0x8e, 0x20, 0x8, 0xc7, 0x9d, 0x4e, 0xa7, 0x99, 0x4c, 0x26, 0xd3, 0xbf, 0x5, 0x18, 0x0, 0x98, 0xef, 0x3, 0xa7, 0xc, 0x73, 0xce, 0xd3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)facebook_icon2x_png { + return [NSData dataWithBytes:facebook_icon2x_png length:sizeof(facebook_icon2x_png)]; + } + + const unsigned char facebook_icon3x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x42, 0x8, 0x6, 0x0, 0x0, 0x0, 0xe3, 0x54, 0x0, 0xe8, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x39, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x41, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x37, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x32, 0x42, 0x32, 0x44, 0x31, 0x37, 0x38, 0x33, 0x44, 0x43, 0x39, 0x31, 0x31, 0x45, 0x34, 0x38, 0x33, 0x35, 0x43, 0x42, 0x42, 0x36, 0x30, 0x39, 0x42, 0x42, 0x31, 0x39, 0x35, 0x32, 0x44, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x90, 0x6f, 0x2b, 0x47, 0x0, 0x0, 0x3, 0x4d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x9c, 0xdf, 0x4b, 0x53, 0x61, 0x18, 0xc7, 0xcf, 0x39, 0x9b, 0x4e, 0xdc, 0x28, 0x6d, 0xb3, 0xd4, 0xd9, 0x36, 0x1c, 0x4a, 0x2c, 0x49, 0xa2, 0x1b, 0x17, 0x24, 0xde, 0xc8, 0x48, 0x85, 0xa3, 0x5e, 0xf4, 0x17, 0x94, 0xd2, 0xa5, 0x5e, 0x8c, 0x6, 0x42, 0x41, 0x46, 0x30, 0x6, 0x61, 0x74, 0x57, 0xd7, 0x43, 0x2f, 0x92, 0xae, 0x84, 0x89, 0x45, 0x9b, 0x3f, 0x82, 0x5, 0xf9, 0xa3, 0x99, 0x76, 0xe3, 0x61, 0x3, 0x65, 0x84, 0xc2, 0xf4, 0xa0, 0xec, 0x6c, 0x3b, 0x3b, 0x3d, 0xcb, 0x6e, 0xba, 0x48, 0x5f, 0x73, 0x9c, 0xce, 0xf6, 0x3e, 0xf, 0x1c, 0x36, 0xb6, 0x67, 0xe7, 0x3d, 0xfb, 0xec, 0xf9, 0xf1, 0x7d, 0xde, 0x3, 0x63, 0x19, 0x30, 0x97, 0xcb, 0x65, 0xb, 0x4, 0x2, 0xcf, 0xdc, 0x6e, 0xf7, 0x9d, 0x9a, 0x9a, 0x1a, 0x3, 0xbc, 0xa4, 0x30, 0xe5, 0x6d, 0xec, 0xe1, 0xe1, 0x61, 0xf6, 0xb, 0xd8, 0xd8, 0xd8, 0xd8, 0xe3, 0x48, 0x24, 0xb2, 0x5a, 0x80, 0x60, 0xdf, 0xda, 0xda, 0x8a, 0x29, 0x94, 0x5a, 0x2a, 0x95, 0xda, 0xe9, 0xea, 0xea, 0xba, 0xc9, 0x84, 0x42, 0xa1, 0xa0, 0x42, 0xb9, 0xad, 0xac, 0xac, 0x7c, 0x60, 0x45, 0x51, 0xdc, 0x36, 0x99, 0x4c, 0x8d, 0xc, 0xc5, 0x96, 0xcf, 0xe7, 0xf, 0x38, 0x80, 0xc0, 0x31, 0x94, 0x1b, 0xc7, 0x71, 0x2c, 0x47, 0x41, 0x61, 0x24, 0x31, 0x85, 0x43, 0x6, 0xbf, 0xa3, 0x2, 0x11, 0x20, 0x88, 0xb2, 0x2, 0x91, 0x83, 0xee, 0x97, 0x85, 0x47, 0xf9, 0xbc, 0x27, 0xd2, 0x97, 0xd0, 0x97, 0xce, 0x80, 0x10, 0x5c, 0x9c, 0x9f, 0x9f, 0xff, 0xb8, 0xbc, 0xbc, 0xbc, 0x9a, 0x48, 0x24, 0xb6, 0x41, 0x1d, 0x8a, 0x70, 0xe4, 0xcd, 0x66, 0xb3, 0xe, 0xde, 0xaf, 0x80, 0xc3, 0x0, 0xcf, 0x8d, 0xd5, 0xd5, 0xd5, 0x17, 0xea, 0xea, 0xea, 0xcc, 0x9d, 0x9d, 0x9d, 0xb7, 0x7, 0x6, 0x6, 0x1e, 0x90, 0x95, 0x4b, 0x45, 0xd9, 0xd1, 0xb8, 0xde, 0x91, 0x82, 0xc1, 0xe0, 0xcb, 0x8e, 0x8e, 0xe, 0xd7, 0x59, 0xc9, 0xf5, 0xf5, 0xf5, 0xb9, 0x9, 0xd7, 0xd8, 0xd7, 0x74, 0x44, 0x8, 0x82, 0xf0, 0x75, 0x68, 0x68, 0xe8, 0xfe, 0xdc, 0xdc, 0x5c, 0xf4, 0x5f, 0x3e, 0xf, 0x91, 0x61, 0x2c, 0xf9, 0xd4, 0x88, 0xc5, 0x62, 0x8b, 0xf0, 0x8b, 0xf2, 0xf1, 0x78, 0x7c, 0x8f, 0xda, 0x62, 0x99, 0x4c, 0x26, 0x5, 0x9e, 0xe7, 0x7, 0xd5, 0x82, 0xa0, 0x59, 0x10, 0xa3, 0xa3, 0xa3, 0xf, 0x61, 0x22, 0xfe, 0x41, 0x75, 0xfb, 0x8c, 0x46, 0xa3, 0x33, 0x93, 0x93, 0x93, 0x21, 0xea, 0x75, 0xc4, 0xc4, 0xc4, 0xc4, 0x2b, 0xa8, 0xe2, 0x74, 0xb, 0xaa, 0x54, 0x2a, 0xb5, 0x3d, 0x3b, 0x3b, 0x1b, 0x2e, 0x9a, 0xf0, 0xc8, 0x64, 0x8e, 0x4a, 0xb2, 0x6b, 0x80, 0x60, 0xfa, 0xbc, 0xbb, 0xbb, 0x4b, 0x7c, 0xf1, 0x6d, 0x6d, 0x6d, 0xf6, 0xfe, 0xfe, 0xfe, 0xbb, 0x2d, 0x2d, 0x2d, 0x2e, 0x10, 0x50, 0x26, 0x96, 0x65, 0xff, 0x90, 0x48, 0xf5, 0xf5, 0xf5, 0xd6, 0x92, 0x4, 0xb1, 0xbe, 0xbe, 0xbe, 0x46, 0xea, 0x3b, 0x3c, 0x3c, 0x3c, 0x8, 0x69, 0xf4, 0xc6, 0x60, 0x30, 0xd4, 0x96, 0x5d, 0x6a, 0x80, 0x6c, 0x4e, 0x90, 0xf8, 0xd9, 0xed, 0x76, 0xb, 0x40, 0x78, 0x5d, 0x2c, 0x8, 0x9a, 0x3, 0x1, 0x73, 0xc3, 0x3e, 0x89, 0x1f, 0xcc, 0x10, 0x6e, 0x80, 0x70, 0xa9, 0x6c, 0x8b, 0x25, 0xe4, 0x38, 0xd1, 0x14, 0xd9, 0xdc, 0xdc, 0x6c, 0xc3, 0x31, 0xbc, 0x50, 0xd8, 0xf4, 0xfa, 0xa, 0x4, 0x71, 0x3c, 0x31, 0x2b, 0x8, 0x82, 0xa6, 0x59, 0x3, 0x41, 0x20, 0x88, 0x63, 0xab, 0xac, 0xac, 0x24, 0x2a, 0x82, 0x55, 0x55, 0x55, 0x86, 0xa2, 0x17, 0x60, 0x15, 0xf7, 0x18, 0xe2, 0xd3, 0xd3, 0xd3, 0x6f, 0x4f, 0xea, 0x9e, 0x20, 0xb1, 0x37, 0x48, 0xce, 0x15, 0x89, 0x44, 0x3e, 0x19, 0x8d, 0xc6, 0x17, 0xcc, 0x29, 0x37, 0xa7, 0x7a, 0x7a, 0x7a, 0x3c, 0xe, 0x87, 0xe3, 0x3a, 0x69, 0x5, 0x56, 0x65, 0xcf, 0x32, 0x1c, 0xe, 0xcf, 0xa8, 0x1d, 0x61, 0xb0, 0xe6, 0x3b, 0xd2, 0x3d, 0x4b, 0xd5, 0x52, 0x43, 0xa7, 0xd3, 0xa9, 0x3a, 0xd7, 0x80, 0xf2, 0x64, 0x6d, 0x36, 0x9b, 0x93, 0xfa, 0x62, 0x69, 0xb1, 0x58, 0x2e, 0xc2, 0x44, 0x7a, 0x85, 0x7a, 0x10, 0xd, 0xd, 0xd, 0x97, 0xa1, 0x8e, 0xd4, 0x52, 0xf, 0xc2, 0x6a, 0xb5, 0x36, 0x9e, 0xa5, 0x19, 0x94, 0x2d, 0x8, 0xe8, 0x16, 0x76, 0x14, 0x54, 0xc7, 0x13, 0xaa, 0x3, 0x41, 0x30, 0xbf, 0x36, 0x6f, 0xb4, 0x9, 0x42, 0x96, 0xe5, 0x9c, 0x9a, 0x20, 0x9a, 0x9a, 0x9a, 0xae, 0x6a, 0x52, 0x59, 0x3a, 0x9d, 0xce, 0x6b, 0x81, 0x40, 0xe0, 0xe9, 0x49, 0xca, 0x12, 0x94, 0x67, 0x70, 0x69, 0x69, 0xe9, 0xdb, 0x69, 0xe7, 0xf2, 0x78, 0x3c, 0xee, 0xee, 0xee, 0xee, 0xde, 0xbf, 0x29, 0x4b, 0xd0, 0x2c, 0x6c, 0x6b, 0x6b, 0xeb, 0x8d, 0xb3, 0xce, 0xf6, 0x9a, 0xb9, 0x1b, 0x3e, 0x32, 0x32, 0x72, 0x8f, 0xe4, 0x9a, 0xfd, 0x7e, 0xbf, 0xb7, 0xc8, 0x4b, 0xef, 0x6b, 0xaa, 0x46, 0x48, 0x92, 0x94, 0x25, 0xf1, 0x4b, 0xa7, 0xd3, 0x12, 0x8e, 0xe1, 0xb8, 0x1f, 0x81, 0x20, 0x10, 0x4, 0x82, 0x40, 0x10, 0x8, 0x2, 0x41, 0x20, 0x8, 0x4, 0x81, 0x20, 0x10, 0x4, 0x82, 0x40, 0x10, 0x8, 0x2, 0x41, 0x20, 0x8, 0x4, 0x51, 0x7e, 0x20, 0x58, 0xc4, 0xc0, 0xb0, 0x9c, 0x24, 0x49, 0x88, 0xa1, 0x10, 0x11, 0x85, 0xbf, 0xe, 0xa0, 0x1d, 0x82, 0x20, 0x8, 0xdf, 0x39, 0xaf, 0xd7, 0xeb, 0x13, 0x45, 0x31, 0xa9, 0x95, 0x10, 0x2d, 0xb2, 0xdf, 0xa9, 0x26, 0xcb, 0xf2, 0x91, 0xcf, 0xe7, 0x7b, 0xa4, 0x5f, 0x58, 0x58, 0x58, 0xe3, 0x79, 0xbe, 0xd7, 0xef, 0xf7, 0x3f, 0x6f, 0x6f, 0x6f, 0xbf, 0x55, 0x1, 0xf6, 0x1f, 0x41, 0x64, 0x9, 0xfd, 0xa, 0xf9, 0x7c, 0x70, 0x9e, 0x85, 0x72, 0xb9, 0x9c, 0xbc, 0xb9, 0xb9, 0xb9, 0x31, 0x3e, 0x3e, 0xfe, 0x64, 0x6a, 0x6a, 0xea, 0xfd, 0x4f, 0x1, 0x6, 0x0, 0xec, 0x28, 0x31, 0x86, 0x11, 0x87, 0xbb, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)facebook_icon3x_png { + return [NSData dataWithBytes:facebook_icon3x_png length:sizeof(facebook_icon3x_png)]; + } + + const unsigned char parse_logo_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xde, 0x0, 0x0, 0x0, 0x44, 0x8, 0x6, 0x0, 0x0, 0x0, 0xee, 0x9a, 0x8b, 0x92, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x44, 0x43, 0x37, 0x45, 0x43, 0x42, 0x35, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x44, 0x43, 0x37, 0x45, 0x43, 0x42, 0x36, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x31, 0x30, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x44, 0x43, 0x37, 0x45, 0x43, 0x42, 0x34, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0xf6, 0xc4, 0x10, 0x9d, 0x0, 0x0, 0x11, 0x8e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x5d, 0x9, 0x98, 0x54, 0xc5, 0x11, 0xae, 0xdd, 0x9d, 0x5, 0x59, 0x90, 0x9b, 0x5, 0xb9, 0x5, 0x11, 0xf0, 0x2, 0x14, 0x54, 0x54, 0x22, 0x6e, 0x44, 0x3c, 0x50, 0xbc, 0x95, 0xc3, 0x88, 0x9, 0x41, 0x3c, 0x21, 0x1e, 0x68, 0x30, 0x11, 0x8f, 0x18, 0x3c, 0x51, 0x14, 0x13, 0x94, 0x28, 0x26, 0x9e, 0x78, 0x44, 0x12, 0x15, 0x25, 0xe0, 0x89, 0x8a, 0x28, 0x88, 0xa0, 0xe2, 0xe2, 0x2, 0x72, 0xca, 0xd, 0xcb, 0xd, 0x7b, 0xb0, 0xe9, 0xdf, 0xf7, 0xf7, 0x37, 0x9d, 0x9, 0xc8, 0xcc, 0xce, 0x3b, 0xe6, 0x3d, 0xba, 0xbe, 0xaf, 0xbe, 0xe9, 0xd9, 0x9d, 0x79, 0xd3, 0xaf, 0xbb, 0xff, 0xea, 0xaa, 0xea, 0xaa, 0x7a, 0x59, 0xfd, 0xae, 0xbd, 0xe5, 0x55, 0x11, 0x69, 0xa3, 0xb8, 0x5c, 0xfc, 0xa1, 0x2c, 0xc5, 0x6b, 0x15, 0x17, 0x2a, 0xfe, 0x54, 0xf1, 0xc7, 0x8a, 0x97, 0x8b, 0x25, 0x4b, 0x11, 0xa5, 0x8a, 0x8a, 0xa, 0xc5, 0x22, 0xf7, 0xe, 0x1f, 0x2a, 0xcd, 0x9b, 0x1c, 0xf4, 0xd3, 0xdf, 0x62, 0x8a, 0x4f, 0x52, 0xdc, 0x30, 0x80, 0xfe, 0xf4, 0x50, 0x7c, 0x9d, 0xe2, 0x95, 0x8a, 0xa7, 0x29, 0xfe, 0x87, 0xe2, 0xc9, 0x8a, 0xcb, 0xec, 0x54, 0x59, 0x8a, 0x3a, 0x1, 0x78, 0xbb, 0xd9, 0xde, 0xaa, 0x78, 0x1, 0x77, 0x24, 0xcf, 0xc0, 0xaf, 0xb8, 0x8a, 0xe2, 0x3a, 0x8a, 0x1b, 0xf0, 0xf7, 0x21, 0x2, 0x2e, 0x26, 0xcf, 0x54, 0x7c, 0xa7, 0xe2, 0x37, 0xed, 0xd4, 0x58, 0x8a, 0x3a, 0xf0, 0x34, 0xcd, 0x50, 0x7c, 0xaa, 0x4f, 0xbf, 0x59, 0x53, 0x71, 0x63, 0xc5, 0xc7, 0x2a, 0x3e, 0x9b, 0xbb, 0x5f, 0x75, 0xc5, 0x9d, 0x15, 0xbf, 0xa1, 0x78, 0x82, 0xe2, 0x6b, 0x15, 0xaf, 0xb3, 0x53, 0x64, 0x29, 0x8a, 0x94, 0x9d, 0x60, 0x7b, 0xf9, 0x41, 0x50, 0x25, 0x37, 0x28, 0xfe, 0x46, 0xf1, 0xd3, 0x8a, 0xcf, 0x53, 0xdc, 0x41, 0xf1, 0x63, 0x8a, 0x77, 0xf2, 0x33, 0x97, 0xd0, 0xfe, 0xeb, 0x64, 0xa7, 0xc8, 0x52, 0xd4, 0x81, 0x17, 0x24, 0x2d, 0x54, 0x7c, 0xbd, 0xe2, 0xe3, 0x15, 0x4f, 0xe7, 0xdf, 0xe0, 0xf0, 0x79, 0x57, 0x71, 0x81, 0x9d, 0x26, 0x4b, 0x16, 0x78, 0xde, 0xd2, 0x1c, 0xc5, 0xa7, 0x28, 0xfe, 0x2b, 0xdf, 0xc3, 0x16, 0x7c, 0x4d, 0xf1, 0x71, 0x76, 0xaa, 0x2c, 0x59, 0xe0, 0x79, 0x4b, 0xbb, 0x14, 0x5f, 0xad, 0xf8, 0x76, 0xbe, 0xaf, 0xad, 0xf8, 0x45, 0xc5, 0xcd, 0xec, 0x74, 0x59, 0xb2, 0xc0, 0xf3, 0x9e, 0xee, 0x56, 0xfc, 0x0, 0xdb, 0x7, 0x2b, 0xfe, 0x8b, 0x8f, 0x76, 0xa8, 0x25, 0x4b, 0xfb, 0x2d, 0xf0, 0x40, 0xb7, 0x88, 0xe3, 0xe5, 0x4, 0xf5, 0x52, 0x7c, 0xa5, 0x9d, 0x32, 0x4b, 0x16, 0x78, 0xde, 0x13, 0xce, 0xfd, 0xe0, 0x74, 0x59, 0xc5, 0xf7, 0xb7, 0x29, 0xce, 0xb7, 0xd3, 0x66, 0xc9, 0x2, 0xcf, 0x7b, 0x5a, 0xac, 0x78, 0x4, 0xdb, 0x4d, 0xc5, 0x89, 0x76, 0xb1, 0x64, 0xc9, 0x2, 0xcf, 0x7, 0x7a, 0x46, 0xf1, 0x6c, 0xb6, 0x2f, 0x57, 0x5c, 0xd7, 0x4e, 0x9d, 0x25, 0xb, 0x3c, 0xef, 0xa9, 0x44, 0xf1, 0xe3, 0x6c, 0xc3, 0xbb, 0xd9, 0xdb, 0x4e, 0x9d, 0x25, 0xb, 0x3c, 0x7f, 0x68, 0xa2, 0x38, 0x1, 0xd5, 0xa0, 0xf3, 0xec, 0xd4, 0x59, 0xb2, 0xc0, 0xf3, 0x87, 0xd6, 0x2b, 0x7e, 0x8f, 0x6d, 0x44, 0xb8, 0x34, 0xb0, 0xd3, 0x67, 0xc9, 0x2, 0xcf, 0x1f, 0xd2, 0xc0, 0x3, 0xe8, 0xe, 0xb7, 0xd3, 0x67, 0x29, 0xac, 0x14, 0xb, 0x59, 0x7f, 0xbf, 0x34, 0xda, 0x0, 0xde, 0x7, 0x2e, 0x5f, 0x1f, 0x7, 0xf4, 0x4d, 0x79, 0xed, 0x76, 0x8a, 0x9b, 0x28, 0xce, 0x13, 0xe7, 0x58, 0x3, 0xb4, 0x51, 0xf1, 0xf, 0x8a, 0xe7, 0x8b, 0x93, 0xc8, 0xbb, 0xde, 0xa7, 0xfb, 0x46, 0xbf, 0xaa, 0x89, 0x93, 0xac, 0xbc, 0xcb, 0xa5, 0x6b, 0xe6, 0x48, 0xfa, 0xc9, 0xcf, 0xb8, 0x46, 0x73, 0xc5, 0x6d, 0xc5, 0x9, 0x72, 0x40, 0xc6, 0x49, 0x9d, 0x84, 0xcf, 0x14, 0x8b, 0x73, 0x1c, 0xb4, 0x48, 0x9c, 0xb4, 0xb3, 0x25, 0x2e, 0xde, 0x83, 0x9b, 0x73, 0xde, 0x8e, 0xf7, 0x1, 0x1f, 0x42, 0xd, 0xce, 0x39, 0xfe, 0xb7, 0x59, 0x9c, 0x44, 0xed, 0x22, 0xc5, 0xf3, 0x14, 0xaf, 0x30, 0xd6, 0xc3, 0x7e, 0x3, 0xbc, 0x95, 0x5c, 0xec, 0xf5, 0x14, 0xb7, 0x76, 0x71, 0xd7, 0xef, 0x48, 0xbb, 0xb1, 0xa7, 0xe2, 0xf6, 0x1c, 0xf8, 0x7d, 0x11, 0xb2, 0xe8, 0x91, 0x41, 0xf1, 0x8a, 0x38, 0x87, 0xfc, 0x9b, 0x3d, 0xbc, 0xef, 0x2b, 0x14, 0xdf, 0x27, 0xce, 0xd1, 0xca, 0x99, 0xfc, 0xed, 0x64, 0xa9, 0x85, 0xe2, 0x13, 0x15, 0x1f, 0xa3, 0xb8, 0x15, 0xb5, 0x85, 0xfa, 0x4, 0xc1, 0xf9, 0xe2, 0x38, 0xae, 0x52, 0x5, 0xdb, 0x9, 0x8a, 0x2f, 0x54, 0xdc, 0x5d, 0x9c, 0x60, 0xf6, 0x6a, 0x49, 0x7e, 0xb7, 0x84, 0xc0, 0xfb, 0x94, 0x36, 0xfb, 0x54, 0x71, 0xf2, 0x40, 0x83, 0xa0, 0xc3, 0x14, 0x5f, 0xa4, 0xf8, 0x74, 0xc5, 0x47, 0x24, 0x39, 0xe7, 0x5b, 0x8, 0xbe, 0x77, 0x14, 0xa3, 0x72, 0xc3, 0x37, 0xfb, 0xb, 0xf0, 0x70, 0xe3, 0x1b, 0x8, 0xbc, 0xfa, 0x69, 0x5e, 0xb, 0x3, 0x8d, 0xe4, 0xdb, 0xdf, 0x8a, 0x13, 0x84, 0x9d, 0x18, 0x8e, 0xb6, 0x9b, 0xd2, 0x7a, 0x13, 0x77, 0x6, 0xec, 0x7c, 0xb5, 0xf9, 0xaa, 0xd5, 0xdd, 0xde, 0x64, 0x2c, 0xa6, 0x87, 0x14, 0x8f, 0x93, 0x78, 0x6a, 0x93, 0x9b, 0xd4, 0x8a, 0xf7, 0x8b, 0x9c, 0xc5, 0x9a, 0x49, 0x0, 0xf, 0xf3, 0x8a, 0x3c, 0xc7, 0x41, 0x8a, 0xbb, 0xf1, 0x7b, 0x89, 0x54, 0x93, 0x80, 0x29, 0x49, 0x41, 0x40, 0x61, 0xbc, 0x6e, 0x56, 0x7c, 0xf4, 0x1e, 0xfe, 0x5f, 0xc6, 0xb1, 0xda, 0xc6, 0x6b, 0x66, 0xf1, 0x77, 0x6b, 0x19, 0xc0, 0xac, 0x42, 0xa0, 0x82, 0x2f, 0xe7, 0xb8, 0x3d, 0x25, 0x4e, 0x38, 0xa0, 0x5f, 0xda, 0x43, 0x77, 0xde, 0xc3, 0xa9, 0xec, 0x8f, 0x49, 0x15, 0x9c, 0x73, 0x8, 0xd1, 0x52, 0xc5, 0xb9, 0x8a, 0xf, 0xe4, 0x3d, 0xe4, 0xb0, 0x7d, 0x1c, 0xf9, 0x36, 0x9a, 0x3e, 0xf, 0x13, 0x88, 0x91, 0x6, 0xde, 0xe, 0x4e, 0xac, 0x96, 0xbc, 0x95, 0x5, 0xdc, 0x60, 0x71, 0x12, 0x6d, 0x5b, 0x24, 0x0, 0xed, 0x3b, 0xc5, 0x1f, 0x2a, 0xfe, 0x84, 0xd2, 0x6c, 0xd, 0x25, 0x72, 0x39, 0x17, 0x4f, 0x4d, 0xaa, 0x56, 0x5d, 0xc4, 0x49, 0xde, 0xfd, 0x5, 0xff, 0x8e, 0xeb, 0x3c, 0xca, 0x9d, 0x69, 0x60, 0x82, 0x4a, 0xec, 0x6, 0x95, 0x1b, 0xb, 0x63, 0x5f, 0x6a, 0xe, 0x42, 0xeb, 0xee, 0x92, 0x3d, 0xe7, 0x32, 0x6e, 0xa5, 0xd6, 0xa0, 0xf3, 0x21, 0x93, 0x15, 0x12, 0x87, 0x2a, 0x1e, 0x2b, 0x4e, 0xe6, 0x88, 0x79, 0xad, 0x4f, 0xa9, 0xee, 0xcf, 0xe6, 0x6e, 0xac, 0x81, 0x57, 0x4a, 0xe0, 0xe5, 0x71, 0xb1, 0xe6, 0x73, 0x87, 0x41, 0xa2, 0xf3, 0x49, 0x6c, 0xc7, 0x38, 0x6e, 0xe8, 0x2b, 0x42, 0x1, 0x6f, 0x55, 0xfc, 0x9c, 0x87, 0x6b, 0x7, 0xf7, 0x70, 0xbf, 0xfc, 0xff, 0x51, 0xd4, 0x7c, 0x2, 0x8, 0x73, 0xfe, 0xad, 0x31, 0xe7, 0xa5, 0xec, 0x63, 0xd, 0xa, 0x7a, 0x7c, 0xbf, 0xab, 0x38, 0x69, 0x6a, 0x1d, 0xf8, 0xbf, 0xd3, 0xc8, 0x28, 0x59, 0x82, 0xf0, 0xc6, 0x39, 0x51, 0x5, 0x5e, 0x2e, 0x59, 0x24, 0xf5, 0x80, 0x69, 0x48, 0xec, 0x3e, 0xe2, 0x94, 0x96, 0x30, 0xd5, 0xd4, 0xd5, 0x8a, 0x5f, 0xe6, 0xa4, 0x7f, 0x29, 0x7b, 0xaf, 0xf9, 0xb2, 0x83, 0xb, 0x16, 0xb, 0xec, 0x23, 0xee, 0x70, 0x87, 0x28, 0xbe, 0x8a, 0xbb, 0xe6, 0x81, 0x5c, 0xec, 0xd3, 0x8, 0xbe, 0x17, 0x7d, 0x1e, 0x9b, 0x7c, 0x4a, 0xdf, 0xbe, 0x9, 0x7f, 0x2f, 0xa2, 0x2a, 0x3c, 0x85, 0x6a, 0xd2, 0xaa, 0x14, 0xd5, 0xcb, 0xd3, 0x39, 0x36, 0xf5, 0xf8, 0x7e, 0xa9, 0xe2, 0x31, 0x8a, 0x5f, 0x52, 0xbc, 0x6c, 0x1f, 0xdf, 0xdd, 0x2e, 0x4e, 0x15, 0x1, 0xd8, 0xc5, 0xa8, 0x70, 0x30, 0x9e, 0x2, 0xf3, 0x8, 0xf6, 0x73, 0x0, 0xfb, 0xd, 0x5b, 0xfa, 0x59, 0xaa, 0xb0, 0x43, 0xb8, 0xe8, 0xdd, 0xa4, 0x6b, 0x14, 0x8f, 0xe4, 0x1c, 0x9, 0x6d, 0xcc, 0x7f, 0x29, 0x7e, 0x42, 0x9c, 0x62, 0x5b, 0x25, 0xfb, 0xd0, 0xb2, 0x56, 0x52, 0x50, 0xfd, 0x93, 0xeb, 0xa8, 0x13, 0x85, 0x6c, 0x7f, 0xee, 0x86, 0x3d, 0xb9, 0x93, 0xde, 0x45, 0x93, 0xa0, 0x3c, 0x99, 0xc5, 0x18, 0x26, 0xca, 0xe3, 0xae, 0x23, 0x29, 0xda, 0x6, 0x47, 0x71, 0xe1, 0x3d, 0x67, 0x80, 0xe, 0x8b, 0x61, 0x28, 0xa5, 0x2f, 0xe2, 0x41, 0x3f, 0x97, 0xd4, 0xb, 0x2d, 0xc1, 0x4e, 0xba, 0x91, 0x92, 0x7c, 0xaa, 0xd1, 0x47, 0x2c, 0xa2, 0x5f, 0xf9, 0x38, 0x2e, 0x28, 0xa1, 0x31, 0x3d, 0x1, 0x74, 0xef, 0x2b, 0x3e, 0x47, 0xf1, 0x91, 0xec, 0xe3, 0x3b, 0x4, 0x4d, 0x2a, 0xa0, 0xc3, 0xae, 0xfe, 0xba, 0x1, 0xba, 0xd1, 0x1c, 0xcb, 0x7, 0x92, 0x0, 0xdd, 0xcf, 0xed, 0xde, 0x73, 0xb8, 0x43, 0x1c, 0xce, 0x5d, 0x48, 0xef, 0xbc, 0x57, 0x11, 0x9c, 0x6e, 0xd1, 0x1, 0xbc, 0xde, 0x18, 0x3, 0x74, 0x93, 0x38, 0x5e, 0x97, 0x70, 0xa7, 0x4b, 0xd5, 0xc6, 0x85, 0x66, 0x34, 0x8b, 0x1a, 0x13, 0xc6, 0xe2, 0x6f, 0xbc, 0xa7, 0xaa, 0x8a, 0xef, 0x21, 0xa0, 0xeb, 0x45, 0xd, 0x78, 0xb5, 0xc, 0xdb, 0x6e, 0x65, 0x92, 0xdf, 0x19, 0x46, 0x69, 0x5b, 0x60, 0x78, 0x26, 0x87, 0x53, 0x5d, 0x18, 0xcd, 0x5d, 0x2c, 0x5d, 0xfa, 0x9e, 0x52, 0xef, 0x21, 0x43, 0xd, 0x1e, 0xc7, 0x85, 0xeb, 0x35, 0x9d, 0x45, 0xd0, 0xb7, 0xe2, 0xfb, 0xc5, 0x74, 0x1a, 0x14, 0x70, 0xa7, 0xab, 0xac, 0x7, 0xb1, 0xa5, 0x38, 0xa1, 0x7a, 0x7, 0x70, 0x61, 0x5d, 0x41, 0x41, 0xb5, 0xc9, 0xc5, 0xbe, 0xaf, 0x23, 0x0, 0x7b, 0x48, 0xbc, 0xc4, 0x63, 0x3f, 0x89, 0xe7, 0x62, 0xa6, 0x43, 0x75, 0x79, 0xff, 0x3, 0xf8, 0xbe, 0x98, 0xf7, 0x80, 0xf1, 0x9a, 0xeb, 0x52, 0xff, 0x97, 0x52, 0xdb, 0x39, 0x8d, 0x42, 0x58, 0xcf, 0x7, 0x2a, 0x27, 0x34, 0x8b, 0x12, 0xf0, 0xe, 0x31, 0xc, 0xf5, 0xc2, 0x24, 0xbf, 0x53, 0xc0, 0xc5, 0x23, 0x54, 0x8f, 0x3a, 0x50, 0xed, 0xd8, 0xe2, 0x72, 0xdf, 0x20, 0x9, 0x6f, 0x12, 0x27, 0x8f, 0x50, 0x3b, 0x12, 0x9e, 0x10, 0x6f, 0xb3, 0x29, 0xe0, 0xe1, 0x7c, 0xcd, 0x90, 0xe6, 0x13, 0xe8, 0xbd, 0x7c, 0xd5, 0x85, 0x6b, 0x63, 0x8c, 0x1a, 0x1b, 0x3b, 0xd1, 0x33, 0x1e, 0xde, 0xc7, 0xc7, 0x54, 0x69, 0x7f, 0xe4, 0xfb, 0x6b, 0xd3, 0x1c, 0xb7, 0xea, 0x1c, 0x17, 0x5d, 0xbc, 0xeb, 0x3b, 0xda, 0xe3, 0x5e, 0xdd, 0x3, 0x76, 0xce, 0xe3, 0xd, 0x27, 0x4b, 0x7, 0xee, 0xac, 0xcd, 0xa2, 0x2, 0xbc, 0x2e, 0x86, 0x93, 0x21, 0x59, 0xe0, 0x5d, 0x47, 0xd5, 0xa0, 0x2f, 0x6d, 0xbc, 0x65, 0x1e, 0xf7, 0x11, 0xd2, 0xfa, 0x79, 0xb6, 0xf, 0xa6, 0xde, 0xef, 0x5, 0x75, 0xa5, 0x1d, 0x59, 0x95, 0xef, 0x1, 0xf8, 0x4b, 0x5d, 0xda, 0xc1, 0xbb, 0xf2, 0x5a, 0xa0, 0x17, 0xb8, 0x7b, 0x7b, 0x4d, 0xdf, 0xd2, 0x16, 0x9b, 0xc4, 0xdf, 0x5e, 0x53, 0xc9, 0xeb, 0xc0, 0xf6, 0xff, 0x3b, 0x6d, 0x2e, 0xe1, 0xdc, 0x43, 0xf8, 0x7e, 0xed, 0x71, 0xff, 0xd7, 0xd3, 0x71, 0xf3, 0x2c, 0xdf, 0x1f, 0x41, 0xb5, 0xb3, 0x6e, 0x14, 0x80, 0x57, 0x60, 0xa8, 0x53, 0xf3, 0x93, 0xfc, 0x4e, 0x11, 0x8d, 0x76, 0x3f, 0x9d, 0x1d, 0x37, 0xb0, 0x8f, 0x42, 0xf5, 0xe6, 0x28, 0x17, 0xaf, 0xd, 0x9b, 0xa4, 0xe, 0x17, 0x97, 0xb6, 0x77, 0x87, 0xbb, 0xa4, 0x9e, 0x69, 0xea, 0x6f, 0x38, 0x21, 0xee, 0xf5, 0x71, 0xdc, 0x26, 0x52, 0x55, 0x7b, 0x2f, 0x8d, 0x6b, 0xdc, 0xa1, 0xf8, 0x2, 0xb6, 0xe1, 0x10, 0xe9, 0x25, 0xf1, 0x7c, 0x4e, 0xaf, 0x9, 0x73, 0xf3, 0x6b, 0xee, 0xb6, 0x42, 0x27, 0xcc, 0x38, 0xd9, 0x83, 0x23, 0x30, 0x4c, 0xc0, 0x6b, 0xc5, 0xed, 0x5c, 0xab, 0x26, 0xdb, 0x53, 0x1c, 0x10, 0x3f, 0x69, 0x4d, 0x82, 0xca, 0x39, 0xd8, 0x45, 0x75, 0x16, 0x93, 0x88, 0xa3, 0x8b, 0x36, 0xfc, 0xdb, 0xa3, 0x54, 0xb, 0xdd, 0xa2, 0xaa, 0x86, 0x8a, 0xf6, 0xae, 0xf, 0x3b, 0x85, 0x9b, 0x84, 0x7e, 0xff, 0x91, 0xed, 0xb5, 0xdc, 0x39, 0x57, 0xf9, 0xdc, 0x87, 0x32, 0xa, 0xdb, 0xcf, 0xf9, 0xfe, 0x7c, 0x9a, 0x20, 0xa1, 0x5, 0xde, 0x25, 0x86, 0x2d, 0xf3, 0x6a, 0x8, 0xfa, 0xfb, 0x3c, 0xd5, 0x27, 0x10, 0xa2, 0x62, 0xdc, 0x8, 0xea, 0xde, 0x4e, 0x67, 0x41, 0x7f, 0x3, 0x18, 0x37, 0xb9, 0xdc, 0xef, 0x36, 0x74, 0xac, 0x80, 0xa6, 0x84, 0x68, 0x7d, 0xc0, 0xae, 0x7b, 0xd0, 0xd8, 0x5d, 0xae, 0x37, 0xc6, 0xdf, 0x6f, 0xda, 0x42, 0xa7, 0x4b, 0x31, 0xdf, 0x23, 0x91, 0xfb, 0xc8, 0x30, 0x2, 0xaf, 0x16, 0x6f, 0x44, 0x1b, 0xca, 0x53, 0x43, 0xd0, 0x67, 0xa8, 0x69, 0xfa, 0x40, 0xb8, 0x11, 0x8d, 0xfb, 0x74, 0xa9, 0xb6, 0x1, 0xb4, 0x62, 0xda, 0xaf, 0x6e, 0x9f, 0x79, 0x35, 0x93, 0x78, 0x44, 0x47, 0x61, 0x88, 0x80, 0x77, 0x3d, 0x9d, 0x1a, 0x42, 0x35, 0xfc, 0xa5, 0x80, 0xfb, 0x33, 0x57, 0xe2, 0x95, 0x13, 0xaa, 0x1b, 0x1a, 0x50, 0xa8, 0x80, 0x37, 0x88, 0x8e, 0xa, 0xa1, 0xce, 0xbc, 0x3d, 0x24, 0xfd, 0x7e, 0xc3, 0x0, 0x86, 0x1b, 0xe5, 0xf1, 0xab, 0x18, 0x76, 0xdd, 0x83, 0x14, 0x42, 0x6e, 0x53, 0xbe, 0xa1, 0xd6, 0xae, 0x9, 0xc9, 0x38, 0xe7, 0xd3, 0x31, 0x3, 0xc2, 0x11, 0xc5, 0x9d, 0x19, 0xd2, 0x2f, 0xd4, 0x87, 0x9d, 0x9e, 0x95, 0x95, 0x25, 0x25, 0xa5, 0x25, 0xbd, 0x67, 0xcd, 0x9d, 0xd7, 0x23, 0x4c, 0xc0, 0x83, 0xea, 0x73, 0x2b, 0xdb, 0x70, 0xa8, 0x3c, 0x1d, 0x22, 0x29, 0x5c, 0x28, 0xf1, 0x40, 0x5a, 0x37, 0xcb, 0xd1, 0xc3, 0x61, 0x34, 0xc6, 0xa3, 0x3e, 0xe7, 0x26, 0xd8, 0x94, 0x61, 0x20, 0xa8, 0xdf, 0x4d, 0xd8, 0x7e, 0x52, 0x9c, 0xe0, 0x88, 0x4c, 0x20, 0x8, 0xdd, 0x3f, 0xa1, 0x11, 0x8b, 0xc5, 0x64, 0xf2, 0x87, 0x9f, 0xdc, 0xb0, 0x79, 0xeb, 0xb6, 0x50, 0x0, 0x2f, 0xc6, 0x5, 0xa6, 0x5d, 0xb2, 0x77, 0x88, 0xbb, 0x7, 0xb8, 0x5e, 0x53, 0xb9, 0x1, 0x3c, 0x44, 0xcc, 0xd4, 0x71, 0xe9, 0xba, 0xe3, 0x3c, 0x1c, 0x87, 0x5d, 0x86, 0x50, 0xae, 0x1f, 0x82, 0x31, 0xc6, 0x19, 0xad, 0x8e, 0x12, 0x82, 0x4b, 0x7f, 0x6c, 0x86, 0xf5, 0xef, 0x6d, 0xc5, 0x1f, 0xc7, 0x72, 0x72, 0x64, 0xed, 0xfa, 0x8d, 0xa7, 0x7d, 0x53, 0x58, 0x74, 0x6c, 0x18, 0x80, 0x37, 0x4a, 0x9c, 0xa8, 0x0, 0xd0, 0xb, 0x19, 0xa0, 0xb7, 0x57, 0x86, 0xb4, 0x81, 0xf, 0xc7, 0x90, 0x1b, 0xcf, 0x21, 0x5c, 0xc7, 0xb1, 0xf0, 0x8a, 0x4c, 0x2f, 0x60, 0xdb, 0x10, 0x8c, 0x2f, 0x82, 0xae, 0x75, 0x52, 0x34, 0xdc, 0xf8, 0xcb, 0x32, 0xac, 0x7f, 0x38, 0x73, 0x46, 0x58, 0x99, 0x28, 0x95, 0x33, 0x7b, 0xea, 0xb4, 0xe9, 0x7d, 0x32, 0x1d, 0x78, 0xa3, 0x24, 0x5e, 0xca, 0xf, 0xbb, 0xc6, 0x10, 0x9, 0x27, 0xad, 0xe0, 0x2b, 0xdc, 0xf4, 0xf5, 0x5c, 0xb8, 0xde, 0x5b, 0xc6, 0x35, 0xbd, 0x20, 0x84, 0xbf, 0xe9, 0x38, 0xd8, 0x30, 0x14, 0x95, 0x3a, 0xc7, 0x68, 0x4f, 0xc8, 0xd0, 0x3e, 0x22, 0x28, 0x60, 0x75, 0x95, 0xdc, 0x5c, 0x59, 0xb8, 0x64, 0xf9, 0x99, 0xab, 0xd6, 0xac, 0xab, 0x96, 0x89, 0xc0, 0x83, 0xf3, 0x0, 0xae, 0xf8, 0xdf, 0xf1, 0x3d, 0x62, 0x32, 0x2f, 0x95, 0xf0, 0x3e, 0x2b, 0xaf, 0x38, 0xc1, 0x39, 0x92, 0x2e, 0xbd, 0xee, 0x71, 0x7f, 0x91, 0x23, 0x37, 0x83, 0x6d, 0x38, 0x3, 0x2e, 0xc8, 0xe0, 0xb1, 0xc5, 0xfa, 0x3d, 0x99, 0x6d, 0xc4, 0x4a, 0x7e, 0x96, 0xa1, 0xfd, 0xc4, 0x99, 0xe2, 0xfb, 0xd9, 0xd9, 0x59, 0xb2, 0x7d, 0xc7, 0xce, 0x43, 0x7f, 0x58, 0xbe, 0xa2, 0x63, 0xa6, 0x1, 0xf, 0x51, 0xb, 0x38, 0x78, 0xec, 0x6b, 0x2c, 0x82, 0xb3, 0x24, 0xb8, 0xf3, 0x18, 0xb7, 0x54, 0xd, 0x31, 0xd4, 0xcd, 0x74, 0x68, 0x87, 0xa4, 0x91, 0xf5, 0x9c, 0x2, 0x99, 0x8e, 0x1b, 0xa8, 0x49, 0xa7, 0x66, 0xe8, 0xd8, 0x1e, 0x4a, 0x6, 0x21, 0x9f, 0x2e, 0x93, 0xbd, 0xdd, 0x3f, 0x1d, 0x81, 0xed, 0x2e, 0x2f, 0x97, 0xa2, 0x45, 0x4b, 0x4e, 0x34, 0x81, 0x17, 0x94, 0x7, 0xb, 0x6, 0x3c, 0xe, 0xc7, 0x91, 0xc6, 0xf2, 0xa6, 0x61, 0x57, 0x20, 0xaf, 0xd, 0x67, 0x5f, 0xb3, 0x25, 0xdc, 0x94, 0xe5, 0x32, 0x88, 0xfd, 0x98, 0xa7, 0x89, 0xda, 0x2e, 0x11, 0xe7, 0xec, 0x10, 0xf3, 0x32, 0x52, 0x32, 0xcf, 0xd9, 0xd2, 0x56, 0xe2, 0x1, 0xf0, 0x9f, 0x64, 0xf8, 0x3a, 0xc0, 0x6e, 0x5c, 0x1e, 0xcb, 0x8d, 0xc9, 0xf7, 0xb, 0x17, 0x77, 0x36, 0x13, 0x61, 0x71, 0x70, 0x8a, 0x4c, 0xe0, 0x1c, 0xf1, 0xfe, 0x39, 0xe8, 0x31, 0xfe, 0x1e, 0xe, 0x3c, 0x3b, 0x25, 0x4c, 0x28, 0xec, 0x8b, 0xfb, 0xc9, 0x41, 0x14, 0xc5, 0xc1, 0xfd, 0xd7, 0xa2, 0xca, 0x8b, 0x7e, 0x9a, 0x45, 0x86, 0xf0, 0xa, 0x7f, 0x30, 0x22, 0x13, 0x76, 0x46, 0x0, 0xc8, 0x3f, 0x47, 0xd7, 0xf1, 0xde, 0xfb, 0xd1, 0x3e, 0xc5, 0x91, 0xe, 0x12, 0x7c, 0x91, 0x34, 0x8c, 0xb8, 0xd7, 0x2f, 0x24, 0xf8, 0xa2, 0x45, 0x87, 0x19, 0xed, 0x4c, 0xf, 0x6d, 0x5b, 0xac, 0x78, 0x69, 0x4e, 0x76, 0xf6, 0xc1, 0xc5, 0x9b, 0xb7, 0xb4, 0x8f, 0x25, 0x48, 0x8f, 0x20, 0x5d, 0xb1, 0xc8, 0x93, 0x7b, 0x89, 0x4e, 0x95, 0x5, 0x3e, 0xfe, 0x2e, 0x4a, 0x39, 0x20, 0x6, 0x14, 0xe9, 0x34, 0x8, 0xeb, 0x69, 0x41, 0x29, 0xaf, 0x81, 0x57, 0x95, 0xbb, 0x4c, 0x49, 0x2, 0xf0, 0x60, 0xbb, 0x21, 0x87, 0xc, 0x67, 0x46, 0x85, 0x64, 0x9c, 0xaf, 0xad, 0x12, 0x17, 0xaa, 0x50, 0x65, 0x0, 0x41, 0xb0, 0xf4, 0xa7, 0x26, 0x82, 0xb3, 0xa8, 0x46, 0x14, 0x90, 0x57, 0x93, 0x8b, 0xf8, 0xbf, 0xa9, 0x4, 0xe1, 0xd2, 0x0, 0xb4, 0xa6, 0xa6, 0x7c, 0x45, 0x8d, 0x94, 0x65, 0x19, 0x3e, 0x9e, 0x58, 0x37, 0x4b, 0xb3, 0x15, 0xf0, 0x36, 0x14, 0x6f, 0x6a, 0x10, 0x54, 0xe9, 0x87, 0x32, 0xe, 0x16, 0xca, 0x2e, 0x7c, 0x25, 0x4e, 0x1e, 0xd3, 0x64, 0xbe, 0xf7, 0x83, 0xb0, 0x88, 0x2e, 0xa4, 0xe3, 0x0, 0xe7, 0x2a, 0x79, 0x49, 0xec, 0x82, 0x3a, 0xf, 0x10, 0x35, 0x38, 0xf4, 0xb1, 0x40, 0xe7, 0x84, 0xcf, 0x6d, 0xe2, 0x82, 0x9c, 0xce, 0x45, 0xf9, 0x1f, 0x9, 0xcf, 0x21, 0xf4, 0xde, 0xe8, 0x29, 0xaa, 0x9e, 0x38, 0x2b, 0x1b, 0x40, 0xe1, 0x84, 0x5d, 0x57, 0x17, 0x2d, 0x1a, 0x44, 0xdb, 0xea, 0x3b, 0xaa, 0x53, 0xa8, 0xc3, 0x32, 0x93, 0x2, 0xa9, 0xd4, 0xe3, 0xbe, 0x69, 0x2f, 0xb1, 0xae, 0x3e, 0x97, 0xe9, 0x84, 0x0, 0x90, 0x93, 0x15, 0xf8, 0x6a, 0x99, 0xc0, 0x83, 0x53, 0xe3, 0xa, 0x4a, 0x79, 0x2f, 0xd5, 0x99, 0x2c, 0x4a, 0x53, 0xec, 0x18, 0xeb, 0x24, 0xf5, 0x72, 0xb, 0xe9, 0x4a, 0xc8, 0x61, 0x94, 0xe4, 0x7b, 0xaa, 0xff, 0x8, 0x75, 0x60, 0x11, 0xa5, 0xe7, 0x7a, 0xee, 0xc2, 0x15, 0x46, 0xbf, 0xb5, 0xa, 0xa, 0xc9, 0x7f, 0x10, 0xd5, 0xe5, 0x46, 0x5c, 0x0, 0xfa, 0xff, 0x9d, 0xc9, 0x50, 0xd5, 0x10, 0x72, 0xf5, 0x63, 0x4, 0x76, 0x3f, 0x8c, 0x5, 0xea, 0xb9, 0x3c, 0x46, 0xcd, 0x0, 0xa9, 0x36, 0x3d, 0x8, 0xc2, 0x3c, 0xf2, 0x31, 0xe4, 0x6b, 0xa8, 0x1d, 0x2c, 0x22, 0x0, 0x3f, 0xa2, 0x20, 0x2a, 0xf2, 0x40, 0x35, 0xad, 0xcd, 0x57, 0x8c, 0xfb, 0xef, 0x25, 0x9e, 0xbd, 0x91, 0xa9, 0x1a, 0x84, 0x3e, 0x6f, 0xcc, 0x33, 0x81, 0xa7, 0x6b, 0x6, 0x46, 0x91, 0x30, 0x19, 0x57, 0x51, 0x65, 0x32, 0x1, 0x57, 0x48, 0xc7, 0xc1, 0x14, 0xda, 0x8, 0xab, 0x53, 0xdc, 0xa1, 0x72, 0x8, 0xba, 0xe6, 0x5c, 0x84, 0xd8, 0x3d, 0x91, 0xfb, 0x87, 0xe2, 0xa8, 0x38, 0x3a, 0xc8, 0x97, 0x68, 0x3d, 0xcf, 0xf, 0x42, 0x72, 0x6, 0x79, 0x4, 0x5, 0xf, 0x92, 0x66, 0x4f, 0xa4, 0xba, 0xde, 0x8e, 0x1a, 0x41, 0x15, 0xb6, 0xdb, 0x51, 0xc8, 0xe1, 0x7b, 0xb, 0xe9, 0x0, 0x41, 0x24, 0x7, 0x2a, 0xb9, 0xad, 0x75, 0xb1, 0x5f, 0x10, 0x7e, 0xb7, 0x67, 0xfa, 0xe0, 0x55, 0x54, 0xc4, 0x2d, 0x90, 0x58, 0x0, 0x46, 0xbb, 0xdf, 0x4, 0xa9, 0x88, 0xf8, 0x4e, 0xf3, 0x41, 0x27, 0x38, 0xd0, 0x1c, 0x4d, 0x75, 0x30, 0x1d, 0x75, 0xa8, 0x9c, 0xbb, 0xda, 0x1a, 0x4a, 0x77, 0x5d, 0x45, 0xb, 0x2a, 0x18, 0x5c, 0xf0, 0xe7, 0x8a, 0x13, 0x59, 0x51, 0x35, 0x82, 0xe3, 0xa, 0x1, 0xb5, 0x84, 0xc, 0xdb, 0x1c, 0x1e, 0x72, 0xc4, 0x4b, 0x76, 0x24, 0x18, 0x1, 0xc4, 0xa3, 0x28, 0x98, 0x62, 0xf4, 0x21, 0x80, 0x91, 0x28, 0xba, 0x81, 0x63, 0xff, 0x22, 0x81, 0xb8, 0x3d, 0x8d, 0xf1, 0x17, 0xc3, 0xf9, 0x95, 0x15, 0x2, 0xe0, 0xa1, 0x8f, 0xa5, 0x31, 0x89, 0x36, 0x21, 0x7, 0xe, 0x19, 0x2, 0xc7, 0xf1, 0x3d, 0xd4, 0x9d, 0x21, 0x9c, 0x6c, 0xaf, 0xa8, 0xdc, 0x70, 0xb6, 0x8c, 0xa1, 0x5d, 0x34, 0x5e, 0xa2, 0x4f, 0xbb, 0xa9, 0xa2, 0x2f, 0x93, 0xf8, 0xe3, 0xb3, 0xeb, 0x13, 0x88, 0xa8, 0xc7, 0xd9, 0x9d, 0x6d, 0xa8, 0xa5, 0x75, 0x69, 0x5f, 0x5f, 0x40, 0x95, 0x14, 0x51, 0xfc, 0x4f, 0x48, 0xea, 0x75, 0x70, 0x36, 0xf2, 0x15, 0x26, 0x2, 0xa, 0xf8, 0xee, 0x90, 0xca, 0xd7, 0x5b, 0xf5, 0x7c, 0xa7, 0x1b, 0x3a, 0xf0, 0x32, 0x69, 0xd6, 0xb8, 0x51, 0x56, 0x69, 0x59, 0x59, 0xa4, 0x81, 0x57, 0x95, 0x12, 0x55, 0x83, 0xe, 0x35, 0x11, 0x7f, 0x23, 0xff, 0x1b, 0x49, 0xe2, 0x7, 0xad, 0x95, 0xfd, 0x97, 0x60, 0xc3, 0x4f, 0x95, 0x78, 0xfe, 0x24, 0x52, 0xbb, 0x7a, 0x12, 0x70, 0xdd, 0x38, 0x47, 0xa8, 0x2c, 0x80, 0x72, 0x81, 0x57, 0x52, 0x28, 0x4e, 0xaa, 0xc4, 0xd8, 0xd6, 0xe2, 0x2e, 0xba, 0x3a, 0x53, 0x7, 0x2, 0xd8, 0x6b, 0x94, 0x5f, 0x5f, 0xe, 0x6a, 0xe8, 0xe4, 0x43, 0x67, 0x47, 0x78, 0xd2, 0x61, 0x83, 0xfc, 0x92, 0x6d, 0xa8, 0x42, 0x17, 0x5, 0x0, 0xba, 0x28, 0xab, 0xf0, 0x95, 0x21, 0x78, 0x3a, 0xc7, 0xd2, 0x31, 0x83, 0x33, 0xdc, 0x51, 0xc6, 0x9c, 0xa0, 0x82, 0x1c, 0xe2, 0x50, 0xff, 0x90, 0xe2, 0xf5, 0xf4, 0xce, 0xda, 0x38, 0x4c, 0x3, 0x11, 0x55, 0xe0, 0xc1, 0xb6, 0x18, 0xca, 0x36, 0xce, 0x98, 0x6, 0x4a, 0xf8, 0xdd, 0xfa, 0x51, 0x23, 0xb8, 0xd6, 0x6f, 0xe4, 0x5c, 0x99, 0xa5, 0xdb, 0x91, 0xa9, 0x3d, 0x2c, 0xc9, 0x6b, 0x98, 0x89, 0xc0, 0x9d, 0x2c, 0xf0, 0x82, 0x27, 0xd4, 0x65, 0xc4, 0xb9, 0x5b, 0x5, 0x27, 0x71, 0x9b, 0x5d, 0xe7, 0x19, 0x4b, 0xb0, 0x9, 0x2f, 0x93, 0x78, 0x26, 0x8a, 0x6, 0xdf, 0xb1, 0x49, 0x7c, 0xb7, 0xd0, 0xd8, 0x31, 0x4f, 0xb2, 0xc0, 0xb, 0x96, 0x60, 0xb8, 0x9f, 0xc5, 0x36, 0xe, 0xe5, 0x3f, 0xb0, 0x6b, 0x3b, 0x14, 0x4, 0x47, 0x94, 0xae, 0x34, 0x80, 0xe3, 0x88, 0x5b, 0x92, 0x4, 0xed, 0x3c, 0x3, 0x78, 0x79, 0x16, 0x78, 0xc1, 0xaa, 0x99, 0x5a, 0xdf, 0x9f, 0x68, 0xd7, 0x73, 0xa8, 0x8, 0x25, 0xf0, 0x75, 0x59, 0x3c, 0xd8, 0x81, 0x2d, 0x93, 0xf8, 0xce, 0xbb, 0x7c, 0xc5, 0x11, 0x4e, 0x57, 0xb, 0xbc, 0xe0, 0xc8, 0xcc, 0x9a, 0x9e, 0x69, 0xd7, 0x72, 0xa8, 0x8, 0x7, 0xed, 0xcf, 0xb0, 0x8d, 0x14, 0xaa, 0x2e, 0x49, 0x7c, 0xe7, 0xd, 0x89, 0x47, 0x17, 0xf5, 0xb1, 0xc0, 0xb, 0x8e, 0x74, 0x1c, 0x25, 0xb2, 0x1c, 0x36, 0xda, 0xb5, 0x1c, 0x3a, 0x9a, 0x61, 0x0, 0xa9, 0x55, 0x12, 0x9f, 0x87, 0xf3, 0x4c, 0xa7, 0x4, 0x21, 0x48, 0xa2, 0xb9, 0x5, 0x5e, 0x30, 0x94, 0x69, 0xee, 0xfb, 0xa, 0x8b, 0xa5, 0x94, 0x68, 0xa5, 0x21, 0x30, 0x93, 0xb5, 0xd9, 0xc6, 0x1b, 0xf6, 0xfd, 0x95, 0x16, 0x78, 0xc1, 0x90, 0x2e, 0x11, 0x51, 0x4d, 0xd2, 0xcf, 0xf8, 0x76, 0x83, 0x1a, 0x5b, 0x2c, 0xa5, 0x44, 0x8, 0xe1, 0x2b, 0x4f, 0xf1, 0x3b, 0xc8, 0x11, 0xd4, 0x4e, 0x16, 0xc4, 0xe4, 0xb6, 0xb6, 0xc0, 0xf3, 0x9f, 0x16, 0xf3, 0x35, 0x27, 0x49, 0x55, 0xc5, 0x6b, 0xea, 0x65, 0xb1, 0x94, 0x12, 0x21, 0xb6, 0xb6, 0xba, 0x61, 0x2e, 0x24, 0x43, 0xf8, 0xdc, 0x68, 0xb6, 0x11, 0x4, 0x7f, 0x77, 0x86, 0xdc, 0x4b, 0x4d, 0x89, 0x3f, 0xc5, 0xa9, 0x6e, 0xd4, 0x81, 0x87, 0x43, 0xd5, 0x1d, 0x6c, 0x17, 0x4, 0xdc, 0x97, 0x6e, 0x16, 0x78, 0x29, 0x53, 0x2b, 0x43, 0xc5, 0x4c, 0x25, 0x21, 0x7a, 0xbc, 0x61, 0xeb, 0xc1, 0xc9, 0xd2, 0x2f, 0x3, 0xee, 0x5, 0xd1, 0x52, 0x88, 0xc4, 0x41, 0xe8, 0xe2, 0xc5, 0x51, 0x7, 0x1e, 0x82, 0x6e, 0x75, 0x9d, 0x96, 0x33, 0x2, 0x54, 0x37, 0x21, 0xb5, 0x1f, 0x92, 0x68, 0x87, 0xe5, 0x79, 0x41, 0xba, 0xb0, 0x12, 0x84, 0xe7, 0x9c, 0x14, 0x55, 0x54, 0x4, 0x4b, 0xe8, 0x27, 0x43, 0x3d, 0x22, 0xf1, 0xfc, 0xb7, 0xa0, 0x4c, 0xc, 0xfd, 0xe4, 0x22, 0xe4, 0x33, 0x4e, 0x8a, 0x3a, 0xf0, 0x40, 0xfa, 0xf9, 0x64, 0xad, 0x29, 0x75, 0x82, 0x20, 0x80, 0xae, 0x4b, 0xc8, 0xc6, 0xd, 0xc5, 0x83, 0x7, 0x5, 0xf8, 0xfb, 0x8, 0x76, 0xd6, 0xe5, 0x4, 0xbf, 0xa0, 0x10, 0x4d, 0x85, 0x90, 0xfd, 0xae, 0x1f, 0x14, 0x82, 0xf8, 0x4d, 0x14, 0xfe, 0x6d, 0x18, 0xd0, 0xbd, 0xe0, 0xf1, 0x69, 0x2d, 0xd8, 0xc6, 0x73, 0x2e, 0x96, 0xee, 0xf, 0xc0, 0xc3, 0x80, 0xeb, 0x8a, 0xc8, 0xb7, 0xca, 0x5e, 0x9e, 0xca, 0xe9, 0x21, 0x41, 0xaf, 0xd7, 0xde, 0x35, 0x84, 0xab, 0x85, 0x21, 0x4e, 0x14, 0xc5, 0x6b, 0x91, 0x14, 0x8c, 0xf4, 0x9c, 0x9b, 0x2, 0xea, 0xc3, 0x10, 0xc3, 0x2e, 0xaf, 0x6c, 0x2a, 0x15, 0x1e, 0xa4, 0xa9, 0x2b, 0x8e, 0x23, 0x98, 0x62, 0x2, 0x1, 0xed, 0xb7, 0xd0, 0xd5, 0x2, 0x4, 0xcf, 0x72, 0x1c, 0x95, 0xf8, 0x81, 0xa8, 0x2, 0xf, 0xa0, 0x7b, 0x98, 0xed, 0x36, 0x46, 0xdb, 0x6b, 0x42, 0x9a, 0xcb, 0x38, 0x89, 0x47, 0xd8, 0x7f, 0xc5, 0x76, 0x18, 0xc6, 0x19, 0x65, 0xf2, 0xf4, 0x3, 0x4b, 0x90, 0xa6, 0x83, 0x10, 0xae, 0x2a, 0x3e, 0xfe, 0x7e, 0x77, 0x71, 0xca, 0x37, 0x80, 0xbe, 0x94, 0xf4, 0x9e, 0xe0, 0x8b, 0x47, 0xba, 0x7d, 0xc8, 0x36, 0xa, 0xde, 0xa2, 0xc2, 0x80, 0x5f, 0xe7, 0x7b, 0x0, 0xd9, 0xd, 0x6c, 0xa3, 0xec, 0xc7, 0x40, 0xd9, 0xc3, 0x83, 0x51, 0xa3, 0x6c, 0x7f, 0x3c, 0x42, 0x69, 0x3, 0x42, 0xa1, 0x9e, 0xfb, 0x3c, 0xfe, 0x3d, 0x44, 0xc7, 0x4f, 0xe3, 0x40, 0x83, 0xe0, 0xe4, 0x39, 0x53, 0xc2, 0x53, 0x8c, 0x17, 0x3b, 0xc3, 0x0, 0x89, 0x67, 0xe4, 0xa3, 0x76, 0xa, 0xc2, 0xb1, 0xda, 0xfb, 0xf0, 0xdb, 0x28, 0x1d, 0xf1, 0xa, 0xc1, 0x5f, 0x46, 0x5b, 0x2d, 0x9d, 0xfa, 0x2c, 0x5b, 0xb9, 0xe3, 0xe8, 0xf9, 0xef, 0xc2, 0xb9, 0x39, 0xc5, 0xc3, 0x7b, 0x40, 0xa2, 0x1d, 0xaa, 0x7c, 0xeb, 0xa, 0xe8, 0xa8, 0x4a, 0x80, 0xa7, 0xc1, 0xee, 0xf1, 0x91, 0xe1, 0x51, 0x6, 0x1e, 0xa4, 0xc, 0x12, 0x5f, 0xf5, 0x73, 0x6, 0x30, 0x99, 0x8f, 0x8b, 0xfb, 0x65, 0x18, 0x1a, 0x11, 0xe4, 0xd3, 0xd, 0x9b, 0x6e, 0x32, 0x25, 0xf8, 0x4a, 0x89, 0x17, 0x5c, 0xd, 0x3, 0xe1, 0x81, 0x8e, 0x88, 0xfe, 0xd0, 0x7, 0xd8, 0x8, 0x3c, 0xfe, 0x8c, 0xea, 0x7a, 0x35, 0x8f, 0x7e, 0x13, 0xa5, 0x20, 0xde, 0x96, 0x78, 0x6d, 0xd5, 0x9b, 0x25, 0x1e, 0x7f, 0x99, 0xe, 0xc1, 0xa1, 0xd1, 0x8b, 0xea, 0xb3, 0x70, 0xc7, 0x9b, 0xc2, 0xdd, 0xdc, 0x6d, 0xd5, 0xf3, 0x22, 0xda, 0xa4, 0xe7, 0xf2, 0x3d, 0xd6, 0x1c, 0x32, 0xe2, 0x67, 0xec, 0xed, 0xb, 0x51, 0xf7, 0xb8, 0xe1, 0x1, 0x1c, 0x28, 0xe3, 0xa7, 0x33, 0x95, 0x51, 0xf, 0x12, 0x2e, 0xe7, 0x13, 0xd2, 0xbc, 0x2e, 0xa2, 0x63, 0x8e, 0x16, 0xa7, 0xea, 0xd6, 0x3c, 0xda, 0x26, 0x0, 0x34, 0x2a, 0x49, 0xd, 0xe7, 0x84, 0xeb, 0x87, 0x3a, 0x9a, 0x55, 0xd4, 0xaa, 0x84, 0x60, 0xcc, 0xde, 0x22, 0xe0, 0x74, 0xb0, 0x32, 0xce, 0xa2, 0x46, 0x52, 0x6d, 0x86, 0xdd, 0x5a, 0xdb, 0x85, 0xdf, 0xc0, 0xf8, 0xa1, 0x4a, 0x38, 0x32, 0xd3, 0x51, 0x3e, 0xf0, 0x40, 0x43, 0x38, 0x3e, 0xe2, 0xe2, 0xbd, 0x6c, 0x22, 0x18, 0xa0, 0xed, 0x20, 0x82, 0x28, 0x87, 0xf6, 0x2b, 0xee, 0x65, 0xb0, 0xc4, 0x1f, 0xf2, 0x59, 0x19, 0xc2, 0xb5, 0x90, 0x4d, 0x8f, 0xda, 0x31, 0x2f, 0x1b, 0x8e, 0x94, 0xf7, 0xb9, 0xbe, 0x3e, 0xff, 0xb9, 0x2f, 0xef, 0xf, 0xae, 0x6e, 0x48, 0x6c, 0x64, 0xa2, 0xeb, 0x67, 0xe, 0x1c, 0x43, 0xfd, 0x7f, 0x22, 0x55, 0xc1, 0x1a, 0x29, 0xa8, 0x12, 0x50, 0x55, 0xee, 0xa0, 0x24, 0xc3, 0xc0, 0x22, 0xef, 0x4f, 0x57, 0x2d, 0x7b, 0x93, 0x3b, 0xde, 0xc8, 0x4, 0xb0, 0xa1, 0xce, 0x8b, 0x3e, 0x57, 0xbc, 0x91, 0xbf, 0x9f, 0xe9, 0x34, 0x8f, 0xc0, 0x18, 0x21, 0xf1, 0x43, 0x6c, 0x3c, 0xa3, 0x60, 0x2c, 0x55, 0x68, 0xd4, 0x48, 0x41, 0xea, 0x55, 0x43, 0x49, 0x3e, 0x44, 0x2f, 0x8f, 0xea, 0xf8, 0x30, 0x8e, 0xdf, 0x7, 0x12, 0xaf, 0x10, 0xb0, 0x96, 0x2, 0xf2, 0x1, 0xf, 0xee, 0xa5, 0x9c, 0x3b, 0xf6, 0x19, 0x9c, 0xb, 0x50, 0x4b, 0xde, 0xc3, 0xb7, 0xb4, 0xff, 0xbb, 0x25, 0xb9, 0xe, 0xe, 0xe0, 0x3d, 0xc0, 0x16, 0x9d, 0x25, 0x4e, 0x3d, 0xd8, 0xee, 0xfc, 0xdf, 0x66, 0xee, 0xd6, 0x0, 0xe3, 0xd2, 0x7d, 0x5d, 0xc8, 0xeb, 0x1a, 0x9a, 0x99, 0x42, 0x5f, 0x53, 0x8a, 0xff, 0x59, 0x1c, 0x77, 0x39, 0xee, 0xbb, 0x37, 0x79, 0x5, 0x8d, 0xf9, 0x79, 0x34, 0x86, 0x35, 0x68, 0x70, 0xe, 0x87, 0xd2, 0x7c, 0x28, 0x61, 0xd7, 0x9a, 0x93, 0x95, 0xf8, 0xec, 0x80, 0x52, 0xaa, 0x95, 0xf, 0x1a, 0xc6, 0x7c, 0x22, 0x2d, 0x20, 0x28, 0xa1, 0x8e, 0xa0, 0xf2, 0xd6, 0x5d, 0x12, 0xcf, 0x17, 0x4c, 0x65, 0x87, 0xf0, 0x9b, 0x76, 0xb1, 0xaf, 0x78, 0x72, 0x13, 0x72, 0xe3, 0xfa, 0x72, 0x4c, 0x1a, 0x71, 0xb7, 0x18, 0x4c, 0x95, 0x74, 0x21, 0xed, 0x98, 0x65, 0x4, 0xd0, 0x4e, 0xf6, 0x17, 0x42, 0xbd, 0x2e, 0x3f, 0xdf, 0x9a, 0xc0, 0x6d, 0x96, 0x20, 0xec, 0xf1, 0x1b, 0xcf, 0x52, 0x98, 0xad, 0xf0, 0xf8, 0x7e, 0x26, 0x4b, 0xbc, 0xee, 0xe7, 0x50, 0xa, 0x8d, 0xa6, 0x6c, 0x83, 0x97, 0x13, 0x88, 0xf3, 0xd9, 0xd6, 0xc9, 0xd3, 0x50, 0xb1, 0x51, 0x3d, 0xd, 0x4e, 0xba, 0xc3, 0xb8, 0xb3, 0x99, 0x5, 0x95, 0xb6, 0x73, 0x8c, 0xee, 0x11, 0xa7, 0xe2, 0x5a, 0x52, 0x14, 0x33, 0x16, 0x5a, 0x79, 0xc4, 0xc1, 0xb7, 0x89, 0x83, 0x3e, 0x8e, 0x52, 0xf7, 0x1c, 0x2e, 0xa4, 0x26, 0xe4, 0xb3, 0x53, 0x58, 0x90, 0xd8, 0x3d, 0xff, 0x4d, 0x87, 0x40, 0x32, 0xcf, 0x21, 0x47, 0x76, 0x35, 0xca, 0xdc, 0x15, 0x48, 0xe5, 0x82, 0xa6, 0x4b, 0x8c, 0x39, 0x2a, 0xf3, 0x79, 0xdc, 0x16, 0x52, 0x58, 0x41, 0x68, 0xf5, 0xe1, 0xce, 0x84, 0x1a, 0xa2, 0xb9, 0xdc, 0xed, 0x75, 0x1, 0xdf, 0x54, 0x68, 0x1, 0x1d, 0x11, 0x4f, 0xed, 0xcd, 0xf9, 0xe0, 0x11, 0xa1, 0x8a, 0xd9, 0xbd, 0x5c, 0x3, 0xfd, 0xe8, 0x74, 0xeb, 0x48, 0x20, 0x35, 0x25, 0xf7, 0x4c, 0xe2, 0x3a, 0x15, 0x14, 0xd4, 0x13, 0x28, 0x38, 0x16, 0xa7, 0xda, 0x91, 0x18, 0x75, 0xe0, 0x6, 0xc9, 0x6c, 0x8f, 0x11, 0xa1, 0xaf, 0x28, 0xbd, 0x5b, 0x52, 0xfd, 0x0, 0x18, 0xe, 0xe7, 0xee, 0x56, 0x9d, 0xea, 0x44, 0x29, 0x25, 0xf7, 0x36, 0x2, 0x56, 0x67, 0x3a, 0xcf, 0xa4, 0x9a, 0x84, 0x85, 0x93, 0xca, 0xd9, 0xdc, 0x6a, 0xaa, 0xb5, 0x9d, 0xa5, 0x72, 0xa9, 0x4a, 0x4f, 0xd2, 0x78, 0xdf, 0x44, 0x69, 0x1c, 0x4, 0x2d, 0xa6, 0x1a, 0x7d, 0x3f, 0x77, 0xaf, 0x6e, 0xdc, 0x41, 0xda, 0x53, 0x70, 0xd5, 0xe4, 0xee, 0x50, 0x8d, 0xbb, 0xda, 0x4e, 0xa, 0x29, 0xf4, 0x79, 0x1d, 0xed, 0xed, 0xd9, 0xb4, 0xb1, 0x67, 0x49, 0x70, 0xf, 0x7d, 0xd1, 0x8e, 0x97, 0x47, 0xe9, 0x6c, 0x3b, 0x92, 0x26, 0x4, 0xec, 0xb2, 0xb6, 0xc4, 0x42, 0xd, 0xae, 0x85, 0xdd, 0xdc, 0xd1, 0xb6, 0x72, 0x37, 0x9f, 0xcf, 0xf9, 0x87, 0x76, 0x33, 0x57, 0xf6, 0x70, 0x4c, 0x90, 0x2c, 0xfd, 0x57, 0x80, 0x1, 0x0, 0xc5, 0xb1, 0xf0, 0x59, 0xcd, 0x2e, 0xe2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)parse_logo_png { + return [NSData dataWithBytes:parse_logo_png length:sizeof(parse_logo_png)]; + } + + const unsigned char parse_logo2x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0xbb, 0x0, 0x0, 0x0, 0x88, 0x8, 0x6, 0x0, 0x0, 0x0, 0x7e, 0xf0, 0x45, 0xa9, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x45, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x46, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x43, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x44, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0xe8, 0x51, 0xff, 0x90, 0x0, 0x0, 0x26, 0xd1, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x5d, 0x7, 0x98, 0x14, 0x55, 0xd6, 0xbd, 0xd, 0x33, 0xcc, 0x90, 0x54, 0x24, 0x88, 0xa2, 0x2, 0x8a, 0x28, 0x20, 0x62, 0x40, 0x51, 0x5c, 0x57, 0x10, 0x51, 0x44, 0xd7, 0x35, 0xff, 0xbb, 0x46, 0x5c, 0x31, 0xc3, 0x9a, 0x45, 0xd4, 0x55, 0x74, 0xd7, 0xac, 0xb0, 0x86, 0x5d, 0x73, 0x5e, 0x75, 0x4d, 0x6b, 0x56, 0x14, 0x11, 0xcc, 0x1, 0x41, 0xc9, 0x51, 0x82, 0x8, 0x8, 0x22, 0x82, 0x48, 0x9a, 0xf8, 0xbf, 0x63, 0x9d, 0x96, 0xa6, 0xe6, 0x55, 0x4f, 0xcf, 0x4c, 0x55, 0x77, 0x55, 0xf5, 0x3d, 0xdf, 0x77, 0x3f, 0xe0, 0x55, 0x33, 0x53, 0xfd, 0xea, 0xd5, 0x3b, 0x37, 0xbf, 0xc4, 0x49, 0x83, 0x86, 0xb4, 0x13, 0x91, 0x53, 0x8c, 0x34, 0x96, 0xf8, 0xa2, 0xbe, 0x91, 0xa9, 0x46, 0x66, 0x19, 0x99, 0x61, 0x64, 0xb5, 0x91, 0x52, 0x51, 0x28, 0x14, 0xa, 0x45, 0x6c, 0xb0, 0x66, 0xed, 0x3a, 0x19, 0x3a, 0x68, 0xa0, 0xec, 0xb3, 0x47, 0xd7, 0x2a, 0xd7, 0xa, 0x8c, 0xdc, 0x62, 0xa4, 0xb7, 0x91, 0xe2, 0x18, 0xcf, 0x41, 0x82, 0xe4, 0x56, 0x66, 0xa4, 0xc4, 0xc8, 0x14, 0x92, 0xdf, 0x24, 0xfe, 0x1d, 0xb2, 0x41, 0x97, 0x8a, 0x42, 0xa1, 0x50, 0xc4, 0x13, 0x20, 0xbb, 0xed, 0x8c, 0x34, 0xe3, 0xdf, 0xf3, 0x5, 0x6d, 0x8c, 0x1c, 0xca, 0xbf, 0xff, 0x42, 0xd2, 0xfb, 0xd0, 0xc8, 0x33, 0xfc, 0x7b, 0xa5, 0x2e, 0xd, 0x85, 0x42, 0xa1, 0x88, 0xf, 0xea, 0x19, 0x29, 0x37, 0x52, 0x91, 0xc7, 0x73, 0xd0, 0xc4, 0x48, 0x4f, 0x23, 0x43, 0x8c, 0x8c, 0x33, 0x32, 0xd2, 0xc8, 0x31, 0x46, 0xa, 0x75, 0x79, 0x28, 0x14, 0xa, 0x45, 0x7c, 0xc8, 0x4e, 0xb1, 0x11, 0x20, 0xb8, 0x43, 0x8c, 0xbc, 0x68, 0x64, 0xb4, 0x91, 0x63, 0x75, 0x8e, 0x14, 0xa, 0x85, 0x22, 0xfa, 0xf0, 0x72, 0x5d, 0x22, 0x89, 0x63, 0x6a, 0x8c, 0x8, 0xbd, 0x95, 0x91, 0x46, 0xfc, 0x73, 0xeb, 0xc, 0x9, 0xec, 0x0, 0xca, 0x28, 0x23, 0x7f, 0x17, 0xc7, 0xcd, 0xa9, 0x50, 0x28, 0x14, 0x8a, 0x18, 0x91, 0xdd, 0x93, 0x46, 0x6e, 0x8c, 0xd1, 0xf7, 0x6c, 0x40, 0xb2, 0xdb, 0xca, 0x48, 0x6b, 0x23, 0x9d, 0x53, 0xc8, 0xc, 0xf1, 0xbb, 0x44, 0x9a, 0xff, 0xdb, 0x97, 0x9f, 0xbb, 0xd7, 0xc8, 0x75, 0x46, 0x56, 0xe9, 0xb2, 0x51, 0x28, 0x14, 0x8a, 0xe8, 0x59, 0x3d, 0x36, 0x34, 0x8c, 0xd9, 0xf7, 0x44, 0x6, 0xe6, 0x4a, 0x23, 0x33, 0x8d, 0xbc, 0x4f, 0xe2, 0x3a, 0xd1, 0x48, 0x27, 0x23, 0xfd, 0x8d, 0x3c, 0x6a, 0xe4, 0xbb, 0x34, 0xff, 0x1f, 0x99, 0xaa, 0x17, 0x19, 0x79, 0xdb, 0x48, 0x77, 0x5d, 0x36, 0xa, 0x85, 0x42, 0x11, 0xf, 0xb2, 0xcb, 0x17, 0x20, 0x13, 0x13, 0x9, 0x29, 0x7f, 0x31, 0x72, 0x90, 0x91, 0x2b, 0x8d, 0xcc, 0x4d, 0xf3, 0xf9, 0x1e, 0x46, 0xde, 0x32, 0x32, 0x40, 0x97, 0x8e, 0x42, 0xa1, 0x50, 0x28, 0xd9, 0x45, 0x11, 0xb3, 0x8d, 0xdc, 0x64, 0x64, 0x2f, 0x71, 0xdc, 0x95, 0xcb, 0x3c, 0x3e, 0xd7, 0x82, 0x96, 0x20, 0xdc, 0xbc, 0x9, 0x9d, 0x36, 0x85, 0x42, 0xa1, 0x50, 0xb2, 0x8b, 0x22, 0xe0, 0xee, 0x1c, 0x66, 0xa4, 0x8f, 0x91, 0x17, 0xd2, 0x7c, 0x6e, 0xa8, 0x38, 0xee, 0xd0, 0x22, 0x9d, 0x32, 0x85, 0x42, 0xa1, 0x50, 0xb2, 0x8b, 0x2a, 0xd0, 0x55, 0xe5, 0x78, 0x23, 0x3, 0x8d, 0xac, 0xf0, 0xf8, 0xcc, 0xd9, 0x46, 0x1e, 0x96, 0xf8, 0xc5, 0x38, 0x15, 0xa, 0x85, 0x42, 0xc9, 0x2e, 0xcf, 0x0, 0x32, 0x43, 0xed, 0xdd, 0xd7, 0x1e, 0xd7, 0x4f, 0xa2, 0x85, 0xa7, 0x45, 0xe8, 0xa, 0x85, 0x42, 0xa1, 0x64, 0x17, 0x69, 0x8c, 0x17, 0xa7, 0x4, 0xe1, 0x4d, 0x8f, 0xeb, 0xa7, 0x89, 0xe3, 0xfa, 0x54, 0x28, 0x14, 0xa, 0x85, 0x92, 0x5d, 0xa4, 0xb1, 0xdc, 0xc8, 0x9f, 0xc4, 0x49, 0x4e, 0xb1, 0x1, 0x99, 0x9c, 0x67, 0xea, 0x34, 0x29, 0x14, 0xa, 0x85, 0x92, 0x5d, 0xd4, 0x81, 0xa3, 0x81, 0x10, 0xc3, 0x7b, 0xc8, 0xe3, 0xfa, 0x8, 0x23, 0xbd, 0x74, 0x9a, 0x14, 0xa, 0x85, 0x42, 0xc9, 0x2e, 0xea, 0x40, 0xd3, 0xec, 0xc1, 0xe2, 0xf4, 0xcf, 0x74, 0x3, 0x67, 0x2, 0x22, 0x7e, 0xd7, 0x42, 0xa7, 0x49, 0xa1, 0x50, 0x28, 0x94, 0xec, 0xa2, 0x8e, 0xf5, 0xb4, 0xf0, 0x3e, 0xb6, 0x5c, 0xdb, 0x45, 0x9c, 0x33, 0x2, 0xb, 0x74, 0x9a, 0x14, 0xa, 0x85, 0x42, 0xc9, 0x2e, 0xea, 0x40, 0x3d, 0xde, 0x0, 0x23, 0xdf, 0x5a, 0xae, 0x9d, 0x6a, 0xe4, 0x70, 0x9d, 0x22, 0x85, 0x42, 0xa1, 0x50, 0xb2, 0x8b, 0x3, 0xe6, 0x18, 0x19, 0x24, 0xce, 0x29, 0xe8, 0xa9, 0x48, 0x9e, 0x0, 0xbf, 0x85, 0x4e, 0x91, 0x42, 0xa1, 0x50, 0x28, 0xd9, 0xc5, 0x1, 0xaf, 0x19, 0xb9, 0xdb, 0x32, 0xbe, 0xb3, 0x91, 0x8b, 0x75, 0x7a, 0x14, 0xa, 0x85, 0x42, 0xc9, 0x2e, 0x2e, 0xb8, 0xd9, 0xc8, 0x44, 0xcb, 0xf8, 0x19, 0x46, 0x76, 0xd2, 0xe9, 0x51, 0x28, 0x14, 0xa, 0x25, 0xbb, 0x38, 0xe0, 0x7, 0x23, 0x97, 0x59, 0xc6, 0xb7, 0x11, 0xe7, 0x68, 0x20, 0x85, 0x42, 0xa1, 0x50, 0x28, 0xd9, 0xc5, 0x2, 0x38, 0xd1, 0xfc, 0x19, 0xcb, 0x38, 0x92, 0x55, 0xda, 0xeb, 0xf4, 0x28, 0x14, 0xa, 0x85, 0x92, 0x5d, 0x5c, 0x80, 0xa2, 0xf2, 0x9f, 0x5d, 0x63, 0xa8, 0xbd, 0xfb, 0x8b, 0x4e, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0xd9, 0xc5, 0x5, 0xe3, 0x8c, 0xbc, 0x6a, 0x19, 0x3f, 0xc1, 0x48, 0x4b, 0x9d, 0x1e, 0x85, 0x42, 0xa1, 0x50, 0xb2, 0x8b, 0x93, 0x75, 0xe7, 0x46, 0x47, 0xd1, 0x36, 0x62, 0xa, 0x85, 0x42, 0xa1, 0x64, 0x17, 0x23, 0x7c, 0x65, 0xe4, 0xd, 0xcb, 0xf8, 0x19, 0x3a, 0x35, 0xa, 0x85, 0x42, 0xa1, 0x64, 0x17, 0x17, 0x54, 0x1a, 0x79, 0xce, 0x32, 0xde, 0xd3, 0x48, 0x3b, 0x9d, 0x1e, 0x85, 0x42, 0xa1, 0x50, 0xb2, 0x8b, 0xb, 0xde, 0x36, 0x32, 0xd7, 0x35, 0xd6, 0x54, 0x9c, 0x33, 0xf1, 0x14, 0xa, 0x85, 0x42, 0xa1, 0x64, 0x17, 0xb, 0x2c, 0x35, 0xf2, 0xa5, 0x65, 0xfc, 0x77, 0x3a, 0x35, 0xa, 0x85, 0x42, 0xa1, 0x64, 0x17, 0x27, 0xd8, 0xb2, 0x32, 0xf7, 0x36, 0xd2, 0x5a, 0xa7, 0x46, 0xa1, 0x50, 0x28, 0x94, 0xec, 0xe2, 0x82, 0xb1, 0x46, 0x4a, 0x5c, 0x63, 0x68, 0x1d, 0xb6, 0x9d, 0x4e, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0xd9, 0xc5, 0x5, 0x3f, 0x1a, 0x99, 0xea, 0x1a, 0x2b, 0x10, 0xed, 0x95, 0xa9, 0x50, 0x28, 0x14, 0x59, 0x87, 0x1e, 0x30, 0x1a, 0x1c, 0x70, 0xc0, 0xeb, 0x27, 0x46, 0xf6, 0x70, 0x8d, 0xef, 0x66, 0xe4, 0xe9, 0x8, 0x7f, 0xaf, 0x42, 0x23, 0xd, 0x8c, 0x34, 0xf1, 0xb0, 0x52, 0x13, 0x46, 0xd6, 0x19, 0x59, 0x48, 0xcb, 0x76, 0x83, 0x38, 0xa7, 0xbb, 0x2b, 0x14, 0x99, 0xee, 0x49, 0xc9, 0x35, 0x86, 0xf5, 0xd5, 0x50, 0x9c, 0xc, 0x67, 0x9b, 0xa2, 0xe, 0x85, 0xf2, 0x7b, 0xae, 0x2f, 0xac, 0xb5, 0x72, 0x9d, 0x3e, 0x5f, 0x90, 0xe0, 0xfc, 0xe3, 0x59, 0x6c, 0x6d, 0x64, 0x4b, 0xcb, 0x3b, 0x8c, 0xf9, 0xc7, 0x99, 0x9e, 0x8b, 0x8c, 0x94, 0x89, 0x73, 0xcc, 0x59, 0x45, 0xd8, 0x17, 0x96, 0x22, 0x38, 0xcc, 0xb0, 0x8c, 0xed, 0x1c, 0xb1, 0x8d, 0x7, 0x27, 0xaf, 0x77, 0x30, 0xd2, 0xc9, 0xc8, 0x5e, 0x46, 0xda, 0xf2, 0xdf, 0x45, 0xdc, 0x94, 0x2a, 0x2d, 0x2f, 0x4a, 0x5, 0x17, 0xff, 0x4f, 0xe2, 0x9c, 0xf9, 0x87, 0x79, 0x98, 0x69, 0x64, 0x16, 0xff, 0xfc, 0x26, 0xa6, 0xcf, 0xbb, 0x1e, 0x37, 0x9, 0xe1, 0xc6, 0x5b, 0xaa, 0xaf, 0x40, 0x5a, 0x6c, 0x6b, 0x64, 0x47, 0xa, 0xd6, 0x19, 0x9a, 0x2f, 0xa0, 0x79, 0xfa, 0xf6, 0x54, 0xa6, 0xa, 0x39, 0xa7, 0x95, 0x1e, 0x1b, 0x72, 0x39, 0x37, 0x5a, 0x6c, 0xba, 0xf3, 0xc5, 0x39, 0x48, 0x79, 0x26, 0xff, 0x3e, 0xdb, 0xc8, 0x2, 0x6e, 0xc6, 0x8a, 0xf4, 0x68, 0xcd, 0xf7, 0xbb, 0x3, 0xf7, 0xa7, 0x2e, 0x46, 0x76, 0x30, 0xd2, 0x86, 0x7b, 0x40, 0x7d, 0x8f, 0xf7, 0x3c, 0x39, 0xff, 0xdf, 0x73, 0xee, 0x67, 0xf0, 0x1d, 0x9f, 0xc2, 0x77, 0x7c, 0x9e, 0x92, 0x5d, 0xfe, 0x60, 0x11, 0x17, 0x49, 0x22, 0x65, 0xac, 0x63, 0xc8, 0xef, 0x19, 0xbd, 0x3c, 0xf, 0x31, 0x72, 0xb0, 0x91, 0xae, 0x5c, 0xfc, 0xad, 0x6a, 0xf1, 0x73, 0x1a, 0xf0, 0x67, 0x61, 0x43, 0xeb, 0x95, 0x32, 0x8e, 0xd, 0x68, 0xba, 0x91, 0x31, 0x46, 0xfe, 0x27, 0x4e, 0x89, 0x46, 0x1c, 0x2c, 0x3f, 0x28, 0x2, 0xb7, 0x53, 0x9, 0x10, 0x6e, 0x2, 0x77, 0x18, 0x79, 0x25, 0x8b, 0xf7, 0xd0, 0x90, 0xd6, 0xd0, 0xce, 0x54, 0x4a, 0x5a, 0xf3, 0x7e, 0x92, 0x8d, 0xc8, 0x8b, 0x68, 0x1, 0x5d, 0x60, 0xe4, 0xbb, 0x1c, 0xcc, 0x11, 0x7e, 0x7f, 0x37, 0x23, 0x87, 0x89, 0x93, 0xac, 0xb5, 0x13, 0x89, 0xae, 0x7e, 0x1d, 0xf6, 0xaf, 0x22, 0xae, 0x33, 0x6c, 0xcc, 0xfb, 0xbb, 0x3c, 0x2b, 0xdf, 0x52, 0xd9, 0x1a, 0x2f, 0xce, 0xb9, 0x93, 0x93, 0xe8, 0x69, 0xc8, 0x77, 0xd4, 0xe7, 0x3e, 0x84, 0xf7, 0xbc, 0x37, 0x9f, 0x43, 0xe7, 0x3a, 0xcc, 0x7f, 0x52, 0x61, 0xe9, 0x9d, 0x72, 0x6d, 0x1e, 0x15, 0x8e, 0x64, 0xb, 0xc5, 0xaf, 0xa5, 0x6a, 0xe, 0x83, 0x92, 0x5d, 0x8c, 0xb0, 0xd8, 0xc8, 0x2f, 0xe2, 0xd4, 0xd8, 0x25, 0xd1, 0x82, 0xda, 0x6a, 0x98, 0x36, 0x78, 0xb8, 0x29, 0x50, 0xf4, 0x7e, 0x84, 0x91, 0xc3, 0x49, 0x50, 0x41, 0xa1, 0x2d, 0xa5, 0x9f, 0x91, 0xeb, 0x8d, 0x7c, 0x64, 0xe4, 0x29, 0x23, 0x6f, 0x8a, 0x53, 0xb2, 0x11, 0x55, 0x1c, 0x21, 0x55, 0x5b, 0xc2, 0x2d, 0xa, 0x98, 0xec, 0xb0, 0x69, 0x75, 0x37, 0xb2, 0x9f, 0x91, 0x3, 0x8c, 0xec, 0x23, 0x4e, 0xf, 0xd6, 0xa2, 0x6a, 0xfe, 0xdf, 0xf3, 0x46, 0xfe, 0x9b, 0xc5, 0xb9, 0x1, 0xb1, 0xf5, 0x31, 0x72, 0x1a, 0xad, 0x87, 0x6c, 0xec, 0x3b, 0xc5, 0xdc, 0xd0, 0x21, 0xfd, 0x8d, 0xc, 0xe5, 0x6, 0x8c, 0x86, 0xf, 0x23, 0xb9, 0xee, 0xf2, 0xd, 0xed, 0xa9, 0xc4, 0x9e, 0xc8, 0xf7, 0xbd, 0x41, 0xc0, 0xbf, 0xab, 0x3d, 0x9, 0x75, 0x8, 0x95, 0x8e, 0xff, 0x70, 0xee, 0xc7, 0x2b, 0xd9, 0xc5, 0xf, 0x38, 0x1, 0x61, 0x8d, 0x8b, 0xec, 0x8a, 0xa8, 0x7d, 0x2f, 0x8, 0xc1, 0xfd, 0x61, 0x23, 0x38, 0x95, 0x1b, 0x75, 0xb7, 0x1c, 0x69, 0xfa, 0x7d, 0x28, 0xd3, 0x8c, 0x3c, 0x6e, 0xe4, 0x3e, 0xa9, 0x7a, 0x72, 0x44, 0x14, 0x60, 0x73, 0xb5, 0x95, 0x7, 0xb8, 0x91, 0x9c, 0xcc, 0x79, 0xeb, 0x4e, 0xcb, 0xa6, 0x26, 0x48, 0x64, 0x61, 0x3e, 0x12, 0x24, 0x19, 0x9c, 0xf8, 0xd1, 0xd7, 0xf5, 0xe, 0xe4, 0x6a, 0xaf, 0x83, 0x5, 0x73, 0x95, 0x38, 0x67, 0x4f, 0xc2, 0xb3, 0x80, 0x23, 0xb9, 0x9e, 0xa5, 0x15, 0x18, 0x67, 0xec, 0x6e, 0xe4, 0x1c, 0x23, 0x87, 0x4a, 0x6e, 0xba, 0x38, 0x25, 0xc3, 0x21, 0xff, 0xe0, 0xdc, 0x7f, 0xc0, 0xf7, 0xfc, 0x1d, 0x7a, 0x40, 0x94, 0xec, 0x62, 0x80, 0xd5, 0x46, 0xd6, 0x5a, 0xb4, 0xf1, 0x46, 0x39, 0xbe, 0xaf, 0x83, 0xb8, 0x9, 0x1d, 0x23, 0x8e, 0xeb, 0xab, 0xa6, 0xc0, 0x26, 0xe, 0x3f, 0x3d, 0x12, 0x51, 0x56, 0x88, 0x13, 0x9b, 0x4a, 0x70, 0xc3, 0x87, 0x65, 0x81, 0x58, 0x4b, 0x73, 0x23, 0x9b, 0xd5, 0xe0, 0x67, 0xc2, 0x8d, 0x72, 0x8b, 0x91, 0xb3, 0xc4, 0x39, 0xf9, 0xfd, 0x71, 0x89, 0x56, 0xcc, 0xab, 0x32, 0xc3, 0xb1, 0xba, 0xa0, 0x27, 0x37, 0xad, 0xa3, 0x42, 0x40, 0x1e, 0xe9, 0x80, 0xe6, 0x9, 0x57, 0x53, 0xab, 0xaf, 0xd, 0xb1, 0xae, 0xa4, 0x60, 0xfe, 0x96, 0x71, 0x43, 0x4c, 0xfe, 0x1c, 0x78, 0x44, 0xb6, 0x20, 0xc1, 0x37, 0xa2, 0x57, 0xa2, 0xb0, 0x86, 0x3f, 0xbf, 0x1, 0x37, 0x7e, 0xc8, 0xa5, 0x46, 0x6e, 0x23, 0xe9, 0xc5, 0xcd, 0xc5, 0x9, 0x4b, 0xff, 0x5c, 0x5a, 0x72, 0xb5, 0xb1, 0xe2, 0xa0, 0x74, 0x22, 0xee, 0xe, 0xf7, 0xe3, 0xf2, 0x94, 0xf5, 0x5c, 0xc9, 0xf5, 0xd7, 0x94, 0xcf, 0x21, 0xe9, 0xad, 0xca, 0x4, 0x9b, 0x1b, 0xf9, 0x3, 0x15, 0x6c, 0x58, 0x78, 0x77, 0xd0, 0xd3, 0x10, 0x78, 0x72, 0x91, 0x92, 0x5d, 0xb0, 0xc0, 0x42, 0xf9, 0xc5, 0xb2, 0x1, 0xe6, 0xc2, 0x85, 0x59, 0x9f, 0x9b, 0xe4, 0x59, 0xb4, 0x8, 0x32, 0x8d, 0x93, 0x94, 0x93, 0xd0, 0x3e, 0x17, 0xa7, 0xc9, 0x35, 0x82, 0xcf, 0x70, 0x37, 0x2e, 0xa1, 0x46, 0x9c, 0x4a, 0x76, 0x15, 0x29, 0x64, 0x87, 0x17, 0xa0, 0x15, 0xad, 0x10, 0x4, 0xbc, 0x7f, 0x4f, 0xcd, 0xba, 0xba, 0x97, 0xe, 0xbe, 0xff, 0x7, 0x8d, 0xfc, 0xd9, 0xc8, 0xe5, 0xb9, 0x72, 0x79, 0x84, 0xc, 0x3d, 0x8c, 0x5c, 0xc8, 0xe7, 0x57, 0x1c, 0xe2, 0xfb, 0x44, 0x72, 0xc9, 0x35, 0x46, 0x4e, 0xcf, 0x70, 0x73, 0xc5, 0xba, 0x41, 0xec, 0x10, 0xb1, 0x34, 0xc4, 0x76, 0xe6, 0x51, 0x89, 0x5a, 0xc9, 0x77, 0x47, 0xaa, 0x21, 0xbb, 0x86, 0x54, 0xaa, 0x40, 0x78, 0xad, 0xb9, 0xce, 0xba, 0xd3, 0x8a, 0xd8, 0x3c, 0xc3, 0xd, 0x78, 0x57, 0x2a, 0x56, 0x3, 0x68, 0x79, 0xbc, 0x17, 0x83, 0xf5, 0x2, 0xeb, 0xed, 0x62, 0x2a, 0xb4, 0x99, 0x5a, 0xfd, 0x78, 0x97, 0x67, 0xf1, 0x3d, 0x1f, 0xc7, 0xe7, 0xb0, 0x84, 0xef, 0x77, 0x2a, 0xd9, 0x25, 0x95, 0xda, 0x26, 0x54, 0x66, 0x93, 0x64, 0xb7, 0xd, 0xdf, 0xef, 0xae, 0x54, 0x76, 0x9a, 0x57, 0xc3, 0x2f, 0x9, 0x3e, 0x2b, 0x58, 0xd7, 0x3, 0x8d, 0xdc, 0x4a, 0x4b, 0x4f, 0xc9, 0x2e, 0xa2, 0xa8, 0x97, 0x25, 0x97, 0x51, 0x75, 0xe8, 0x4f, 0xf7, 0x41, 0xaf, 0x1a, 0x68, 0xd6, 0xe3, 0xb9, 0xf0, 0xb1, 0x0, 0x27, 0xd0, 0x4a, 0xcd, 0x4, 0xc9, 0xec, 0xb7, 0xf9, 0x16, 0x6d, 0x1a, 0x19, 0x5e, 0x87, 0x51, 0xa3, 0x3e, 0xa0, 0x1a, 0xb, 0x17, 0xd6, 0xe7, 0x28, 0x5a, 0x8, 0xff, 0xce, 0xd3, 0xf5, 0x83, 0xac, 0xc4, 0x21, 0x54, 0x50, 0x6a, 0xfa, 0xae, 0x42, 0x2b, 0x5f, 0x98, 0x42, 0x1e, 0x73, 0xb9, 0x69, 0x81, 0x5c, 0x3e, 0xe, 0xe0, 0x5e, 0xf1, 0x5c, 0xef, 0x12, 0x27, 0x26, 0x97, 0xe, 0x3f, 0x70, 0x33, 0x7d, 0x5f, 0x9c, 0xf8, 0xcd, 0x2c, 0xa9, 0xb9, 0x1b, 0x71, 0x59, 0x35, 0x7b, 0x1a, 0x48, 0x10, 0x9, 0x43, 0x7, 0x8a, 0x93, 0xb4, 0xd2, 0x85, 0x9b, 0x71, 0x3a, 0xf4, 0xe6, 0x9a, 0xbc, 0xdb, 0xc8, 0x30, 0x89, 0xa6, 0x2b, 0x1d, 0xdf, 0xfd, 0x4c, 0x2a, 0x1c, 0x99, 0x74, 0x6a, 0x42, 0x1c, 0xed, 0xb, 0x71, 0x7a, 0xf9, 0x7e, 0xc8, 0x77, 0x37, 0xd3, 0x24, 0x12, 0x58, 0xc1, 0x3f, 0x7a, 0x5c, 0xc3, 0xfc, 0xef, 0xce, 0xf7, 0x1c, 0x4a, 0xee, 0xde, 0x24, 0x47, 0x2f, 0xf4, 0xe1, 0xfb, 0xfe, 0xb0, 0x91, 0x9b, 0xa4, 0x6a, 0x5f, 0x61, 0x25, 0x3b, 0x45, 0xb5, 0xc0, 0xb, 0x7f, 0xa5, 0x91, 0x23, 0x33, 0x78, 0xd6, 0x15, 0xd4, 0xb0, 0x9f, 0xe0, 0x46, 0x34, 0xc1, 0xe7, 0x7b, 0xc1, 0x4b, 0x34, 0x83, 0x82, 0x33, 0xff, 0xf6, 0x35, 0x72, 0x34, 0x37, 0xf2, 0x2d, 0x3c, 0xfe, 0x4f, 0x33, 0x23, 0xff, 0xa2, 0xa6, 0x8e, 0x4d, 0x7f, 0x5d, 0x1e, 0x3d, 0xbb, 0x53, 0xb8, 0x69, 0x75, 0xc8, 0xf0, 0xf3, 0x20, 0x8c, 0x89, 0xdc, 0xb8, 0xbe, 0x22, 0xa9, 0x2d, 0x94, 0xec, 0x24, 0xfd, 0xc0, 0xea, 0x84, 0xeb, 0x39, 0x5d, 0x62, 0xc, 0xb2, 0xf1, 0x1e, 0xa5, 0xe5, 0x34, 0x25, 0xc0, 0x7b, 0x29, 0xa3, 0x62, 0x36, 0x96, 0x22, 0x5c, 0x3f, 0x70, 0x1, 0xff, 0x89, 0xa, 0x5f, 0x61, 0x9a, 0xfd, 0xf0, 0x22, 0xbe, 0x37, 0x58, 0x97, 0x33, 0x23, 0xb4, 0x5e, 0x92, 0x61, 0x80, 0x23, 0x32, 0x98, 0x9f, 0xd1, 0xe2, 0x24, 0x8b, 0x20, 0x76, 0xf6, 0x6d, 0x0, 0xf7, 0x82, 0xf7, 0xf4, 0x53, 0x4a, 0x82, 0x64, 0x77, 0x38, 0xd7, 0x74, 0xfb, 0x34, 0x96, 0x1e, 0x2c, 0xbc, 0xbe, 0x5c, 0xf7, 0x4f, 0x28, 0xd9, 0x45, 0xb, 0x30, 0xe5, 0x9b, 0x7a, 0x3c, 0xd8, 0xa0, 0x2d, 0x2, 0xb8, 0x31, 0xce, 0x91, 0xea, 0x33, 0xf3, 0x16, 0xd3, 0x82, 0x7a, 0x82, 0xda, 0x5d, 0xb6, 0xe2, 0x64, 0x9f, 0x51, 0x86, 0xf3, 0x5e, 0xe1, 0xfa, 0xf2, 0x3a, 0xcd, 0x7d, 0x30, 0x5d, 0x24, 0x27, 0xd1, 0xad, 0x12, 0x67, 0x40, 0x23, 0xbf, 0x9d, 0xdf, 0x35, 0x13, 0x5, 0x2, 0x1b, 0xfa, 0x5b, 0x46, 0x5e, 0x26, 0xc1, 0x95, 0x65, 0xf1, 0x5e, 0xb, 0xf9, 0xfc, 0x6, 0xa5, 0xf9, 0xcc, 0x47, 0xb4, 0x96, 0x5e, 0x92, 0xdc, 0xc5, 0x60, 0x93, 0x4a, 0xd6, 0x63, 0xb4, 0x38, 0xce, 0x33, 0x72, 0x6c, 0x1a, 0x25, 0xb, 0xd6, 0x8, 0xd2, 0xe5, 0x7, 0x70, 0xc3, 0x8e, 0x82, 0x62, 0x84, 0xe7, 0xd0, 0x22, 0xcd, 0x67, 0xf0, 0xde, 0xa0, 0xd4, 0xe7, 0x11, 0xbe, 0x77, 0x95, 0x59, 0xba, 0xb7, 0x4a, 0x5a, 0x8f, 0x10, 0xb8, 0x2a, 0x8f, 0xe1, 0xbe, 0xd4, 0xd3, 0xe3, 0xf3, 0xc8, 0xd4, 0x7e, 0x9c, 0x4a, 0xc9, 0x10, 0x7a, 0x2, 0x7c, 0x81, 0xb6, 0xb, 0xb, 0x16, 0x70, 0xd3, 0x15, 0x5b, 0x2c, 0xa8, 0xa0, 0xb2, 0xbf, 0xea, 0x91, 0x34, 0xa0, 0x3d, 0x5f, 0x50, 0xd, 0xd1, 0x2d, 0xa4, 0xbb, 0xe6, 0x20, 0xbe, 0xd4, 0xef, 0xe5, 0x68, 0x33, 0x5a, 0xca, 0x45, 0xdd, 0x87, 0x1b, 0xa2, 0x17, 0xfa, 0x51, 0x1b, 0x6d, 0x16, 0xe3, 0xf5, 0x82, 0xd, 0xe0, 0x9d, 0xc, 0x88, 0x6e, 0x39, 0xad, 0x63, 0x3c, 0x3b, 0xb8, 0x8a, 0xfe, 0x29, 0x8e, 0xdb, 0xb8, 0x2c, 0xcb, 0xf7, 0x3b, 0x22, 0xd, 0xd1, 0x2d, 0xe2, 0xa6, 0x86, 0x24, 0x95, 0xe7, 0x24, 0x1c, 0xc9, 0x46, 0x15, 0xf4, 0x58, 0xc, 0xe4, 0x7a, 0x43, 0x46, 0xa0, 0x57, 0x62, 0x44, 0x47, 0x12, 0xde, 0x81, 0x21, 0x5e, 0x2f, 0x5, 0x24, 0x90, 0xc7, 0xd3, 0x10, 0x5d, 0x5, 0x49, 0x1e, 0xdf, 0xf7, 0x4c, 0x92, 0x77, 0x65, 0x8e, 0xee, 0x17, 0x99, 0xe9, 0x4f, 0xf2, 0x5e, 0xb0, 0xc6, 0xa7, 0xa6, 0xf9, 0xec, 0xe9, 0x54, 0xe2, 0x7a, 0x28, 0xd9, 0x45, 0x3, 0x4d, 0xa5, 0x6a, 0x5c, 0x6a, 0x43, 0x40, 0xae, 0x3, 0x68, 0xac, 0x23, 0xa9, 0xb9, 0xed, 0x98, 0xe6, 0x73, 0x88, 0x91, 0xc0, 0xb5, 0x89, 0xb6, 0x65, 0xd7, 0x85, 0xc8, 0x55, 0x33, 0x99, 0x5a, 0xdf, 0x5f, 0x65, 0x63, 0x72, 0x82, 0x1b, 0x88, 0xb, 0x3d, 0x95, 0x46, 0x23, 0x8f, 0x32, 0x4e, 0xe4, 0xf3, 0xeb, 0x9a, 0xe6, 0x33, 0x8b, 0xa8, 0xa0, 0xec, 0x46, 0x6b, 0xf8, 0xe3, 0x1c, 0xde, 0x2f, 0xea, 0xd6, 0xce, 0xf7, 0xb8, 0xf6, 0x16, 0x35, 0xf3, 0xfb, 0x25, 0xbc, 0xae, 0x67, 0x90, 0xde, 0xb9, 0x54, 0x16, 0x3e, 0xf4, 0xf8, 0x4c, 0xb, 0x2a, 0x58, 0x5d, 0x42, 0x78, 0xff, 0xf0, 0x82, 0x20, 0x83, 0x14, 0xb1, 0x78, 0x2f, 0x4f, 0xd1, 0xe7, 0xfc, 0x7e, 0x20, 0x8e, 0xaf, 0x43, 0x74, 0xef, 0x50, 0xf6, 0x9f, 0x26, 0x91, 0xd, 0x4d, 0x63, 0xbd, 0xc1, 0x9d, 0xfc, 0x6, 0xf7, 0x5, 0x25, 0xbb, 0x90, 0x3, 0xd9, 0x88, 0xee, 0xf4, 0xfb, 0x1f, 0xc4, 0xdf, 0x6c, 0x4c, 0x68, 0x77, 0x97, 0x88, 0xe3, 0x8a, 0xec, 0x5b, 0x8d, 0x1b, 0xe3, 0x6, 0x5a, 0x3, 0x8, 0x2, 0xaf, 0xc, 0xe9, 0x9c, 0xc1, 0xe5, 0x75, 0x38, 0x49, 0xd9, 0x8b, 0xf0, 0x86, 0x4b, 0x38, 0x12, 0x7f, 0xfc, 0xc2, 0x60, 0x6a, 0xdf, 0x4d, 0xd3, 0x68, 0xe7, 0xf, 0x50, 0x23, 0x86, 0x82, 0xb2, 0x24, 0xc7, 0xf7, 0x8b, 0x58, 0xeb, 0xdf, 0x3d, 0xae, 0xdd, 0xc1, 0xeb, 0x73, 0x22, 0x32, 0xf7, 0x88, 0x5f, 0xf5, 0xa7, 0x75, 0x6c, 0xc3, 0xb6, 0xb4, 0x9c, 0xb6, 0xc, 0xd1, 0x3d, 0xb7, 0xa5, 0x17, 0xc4, 0x8b, 0x4, 0xa0, 0x50, 0x27, 0x4b, 0x3f, 0xde, 0xd, 0xf1, 0xdc, 0xc3, 0xd2, 0xbb, 0x99, 0x7b, 0xd2, 0x48, 0x8f, 0xcf, 0x34, 0xe7, 0xfc, 0x5f, 0xa0, 0x64, 0x17, 0x6e, 0x6c, 0x6f, 0x19, 0x9b, 0xef, 0xb3, 0xe5, 0x8, 0x17, 0xd1, 0xed, 0x69, 0xdc, 0x18, 0x58, 0xf8, 0x88, 0xc7, 0xed, 0xc3, 0x17, 0x20, 0xa, 0xbd, 0x2, 0x3f, 0xe5, 0x6, 0xf4, 0x59, 0x1a, 0x17, 0xc7, 0xc5, 0x31, 0x58, 0x1f, 0x50, 0x54, 0xae, 0x15, 0x27, 0x8b, 0xd1, 0x2b, 0x69, 0x2, 0xc9, 0x26, 0x70, 0xe1, 0x9e, 0x1d, 0x12, 0x2b, 0x1c, 0xb1, 0xd3, 0x3b, 0xc5, 0x5e, 0xba, 0x72, 0xa3, 0x38, 0x75, 0x6b, 0x51, 0xab, 0x57, 0x43, 0x79, 0x10, 0x12, 0x53, 0x4e, 0x13, 0x7b, 0x88, 0x61, 0x2f, 0x3e, 0xa7, 0x30, 0x0, 0x56, 0x26, 0xba, 0xd, 0xed, 0xef, 0x71, 0x7d, 0x1a, 0xdf, 0x1d, 0x28, 0xb6, 0x51, 0xc9, 0x28, 0x9d, 0x42, 0x5, 0x17, 0x56, 0x9e, 0x2d, 0xeb, 0xbb, 0x9, 0x95, 0x91, 0xcb, 0x94, 0xec, 0xc2, 0xb, 0x5b, 0x26, 0x9d, 0x9f, 0x1a, 0x2f, 0x5c, 0x19, 0x7, 0xa4, 0xb9, 0xfe, 0x39, 0x17, 0x11, 0x5e, 0xe2, 0xa8, 0x35, 0x5f, 0xfe, 0x86, 0x16, 0xc2, 0x27, 0x1e, 0xd7, 0x61, 0x59, 0xfc, 0x3e, 0xe2, 0xeb, 0x3, 0x75, 0x5d, 0xc3, 0xaa, 0xb1, 0x72, 0xfb, 0xd2, 0x6a, 0xf, 0xb, 0xb0, 0xe9, 0xdb, 0x4e, 0xbb, 0xb8, 0x85, 0xca, 0x54, 0x94, 0x1, 0xa5, 0x10, 0xc9, 0x2b, 0xb6, 0xf8, 0xe2, 0xb9, 0x54, 0x3a, 0x72, 0x89, 0x4e, 0xb4, 0xe8, 0xbc, 0xfa, 0x58, 0xbe, 0x40, 0x6b, 0x2e, 0x8a, 0xb5, 0x82, 0x15, 0xb4, 0xf2, 0x8e, 0x13, 0xef, 0xee, 0x52, 0x37, 0x53, 0x29, 0x51, 0xb2, 0xb, 0x19, 0xa0, 0xa9, 0xef, 0x6a, 0x19, 0x9f, 0xec, 0xe3, 0xef, 0x98, 0x27, 0x76, 0x77, 0x12, 0x12, 0x18, 0xae, 0x20, 0x19, 0x8c, 0x8e, 0xf0, 0x1c, 0x7e, 0xcf, 0xc5, 0x6f, 0x4b, 0x55, 0x47, 0x2d, 0xcf, 0x8, 0x9, 0x77, 0x27, 0x91, 0x74, 0x80, 0x5b, 0x66, 0x88, 0xc7, 0x35, 0x3c, 0x3f, 0xb4, 0x3, 0x43, 0xfc, 0xf2, 0xc7, 0x10, 0xdd, 0x33, 0x14, 0xa7, 0x13, 0x2c, 0xe3, 0x2f, 0x92, 0xe8, 0x2a, 0x63, 0xf0, 0xde, 0xa2, 0x3c, 0xe2, 0x7c, 0xcb, 0x77, 0xc1, 0xfb, 0xdc, 0x26, 0x87, 0xf7, 0x5, 0xc5, 0xf6, 0x71, 0xf1, 0x3e, 0xf, 0x13, 0xef, 0x2, 0x4a, 0x2b, 0xa2, 0x7e, 0xca, 0xc3, 0x3b, 0x5c, 0x67, 0x5f, 0x7a, 0xf0, 0xd5, 0x70, 0x7a, 0xf, 0x94, 0xec, 0x42, 0x4, 0xc4, 0xea, 0xf6, 0xb3, 0x8c, 0xcf, 0xf2, 0xf1, 0x77, 0xe0, 0x85, 0xbc, 0x47, 0x36, 0x3d, 0x1f, 0x6f, 0x24, 0xad, 0x1, 0x68, 0xda, 0x25, 0x31, 0x98, 0xc7, 0x25, 0xb4, 0x4c, 0x6d, 0x41, 0xec, 0x3d, 0xeb, 0xea, 0xda, 0xc8, 0x11, 0x50, 0xc3, 0x75, 0x87, 0xc7, 0x35, 0x14, 0xd4, 0xa2, 0x9d, 0xd2, 0x53, 0x21, 0xbb, 0x67, 0x64, 0x15, 0x5f, 0x2c, 0x55, 0xdd, 0xad, 0x8b, 0x39, 0x5e, 0x16, 0xa3, 0x77, 0x17, 0x1d, 0x7c, 0x1e, 0x4a, 0xf9, 0x37, 0xdc, 0xe9, 0xe8, 0x21, 0xfb, 0x4c, 0x8e, 0xee, 0x7, 0xa, 0x1d, 0x5a, 0x6a, 0xed, 0xed, 0x71, 0xfd, 0x1a, 0x3e, 0x83, 0xb8, 0x9c, 0xe7, 0x87, 0x2c, 0xcd, 0x3f, 0xca, 0xc6, 0x3a, 0x49, 0x9b, 0x17, 0xe1, 0x7c, 0x25, 0xbb, 0xf0, 0x0, 0x8d, 0x95, 0xdd, 0xad, 0x7a, 0x16, 0x5, 0xa0, 0x79, 0x55, 0x70, 0xa1, 0x63, 0x61, 0xdc, 0xc4, 0x45, 0xf2, 0x75, 0xcc, 0xe6, 0x72, 0x2, 0xdd, 0x17, 0xb6, 0xc4, 0x1e, 0x8c, 0xef, 0x16, 0xa1, 0xef, 0x2, 0x57, 0x98, 0x57, 0xcc, 0xb, 0x5d, 0x6b, 0xe, 0x16, 0xef, 0x58, 0x65, 0x2e, 0x81, 0x98, 0x6f, 0x6f, 0xcb, 0xf8, 0xdf, 0x24, 0x98, 0xec, 0xe2, 0x5c, 0x3, 0x1e, 0x13, 0x64, 0x36, 0xa3, 0x1e, 0xf, 0xa1, 0x2, 0xa4, 0xcc, 0xaf, 0xcd, 0xc1, 0x7d, 0x60, 0x9d, 0x20, 0xa6, 0xeb, 0x55, 0x2, 0x71, 0x95, 0x78, 0x27, 0xb, 0x45, 0x19, 0x8b, 0xa9, 0xf4, 0xbd, 0xe2, 0xc1, 0x5b, 0xe8, 0x67, 0xda, 0x5f, 0xc9, 0x2e, 0x1c, 0x38, 0xd2, 0x43, 0x63, 0x59, 0x18, 0xc0, 0xef, 0x5a, 0xca, 0xdf, 0x77, 0x65, 0x4c, 0xac, 0x39, 0x1b, 0x60, 0xe9, 0x3c, 0x6a, 0x19, 0x47, 0xf0, 0x7a, 0x50, 0x44, 0xbe, 0x3, 0x6a, 0xb7, 0xee, 0x13, 0x7b, 0x7f, 0xcb, 0x2f, 0xf9, 0xc, 0xe7, 0x85, 0xf4, 0xde, 0x61, 0xd9, 0xb8, 0x33, 0x60, 0xe1, 0x92, 0x7f, 0x2e, 0xa6, 0xeb, 0xd, 0xef, 0xe9, 0x19, 0xe2, 0x14, 0x62, 0xe7, 0xd2, 0x6a, 0x1d, 0x4c, 0xcf, 0x86, 0x97, 0x45, 0x77, 0x63, 0x8c, 0xf7, 0x50, 0x24, 0xe, 0xd, 0xf0, 0x50, 0xfe, 0x10, 0xc6, 0x40, 0x86, 0x72, 0xa6, 0x1d, 0x86, 0x94, 0xec, 0x2, 0x2, 0xfa, 0x40, 0xda, 0x3a, 0x4, 0xa0, 0x8b, 0x40, 0x50, 0xc5, 0xb5, 0xab, 0xf3, 0x60, 0x5e, 0x87, 0x79, 0x90, 0x1, 0x62, 0x15, 0x5d, 0x43, 0x7e, 0xef, 0x28, 0xf0, 0x47, 0xdd, 0x59, 0x5b, 0xcb, 0xb5, 0xc9, 0xb4, 0x20, 0x16, 0x87, 0xf4, 0xde, 0xe1, 0x92, 0x3f, 0xd4, 0xe2, 0x51, 0xb8, 0x47, 0xaa, 0x36, 0x3a, 0x57, 0xf8, 0x7, 0xa4, 0xe4, 0xdf, 0x22, 0xf6, 0x32, 0x9b, 0xbb, 0x62, 0x6a, 0xd1, 0xb9, 0x81, 0x12, 0x29, 0xc4, 0xed, 0x6d, 0xde, 0xaa, 0x36, 0xb4, 0xb8, 0x33, 0x3a, 0x5d, 0x45, 0xc9, 0x2e, 0x18, 0xa0, 0xc0, 0x7b, 0x67, 0xd7, 0x18, 0xfc, 0xe9, 0x63, 0x74, 0x6a, 0xea, 0x84, 0xef, 0xf8, 0xf2, 0xbb, 0x81, 0x98, 0xc6, 0xd9, 0x21, 0xbf, 0x77, 0xd4, 0xc7, 0xf5, 0xf2, 0xb0, 0x20, 0x50, 0x50, 0x1e, 0x66, 0x57, 0x20, 0x36, 0xdd, 0xad, 0x5d, 0x63, 0x70, 0xc7, 0xbf, 0xac, 0x4b, 0x32, 0x30, 0xa0, 0x6d, 0xdc, 0xdd, 0x62, 0x3f, 0x3d, 0x2, 0x9d, 0x5d, 0x2e, 0xcb, 0xa3, 0xb9, 0xc0, 0x5a, 0x3b, 0xd9, 0xe3, 0x1d, 0x41, 0x8f, 0xdd, 0x8c, 0xca, 0x42, 0x94, 0xec, 0x82, 0xc1, 0x11, 0x16, 0x6d, 0x3, 0xe9, 0xb4, 0x1f, 0xeb, 0xd4, 0xd4, 0x19, 0xe8, 0x68, 0x31, 0xc9, 0x32, 0x7e, 0xbc, 0x64, 0xd6, 0xe9, 0x3d, 0x17, 0x40, 0x31, 0xf8, 0x85, 0x96, 0x71, 0xc4, 0x80, 0x90, 0xac, 0x32, 0x25, 0xe4, 0x73, 0xe, 0x2f, 0x85, 0x3b, 0xc6, 0x88, 0x92, 0x90, 0x65, 0xba, 0x1c, 0x3, 0x3, 0x36, 0x70, 0x5b, 0x89, 0x1, 0x9a, 0x7d, 0xa3, 0xc, 0xa2, 0x24, 0xcf, 0xe6, 0x63, 0x2a, 0xdf, 0x21, 0x9b, 0x67, 0xc, 0x39, 0xb, 0x47, 0x29, 0xd9, 0x65, 0x1f, 0x68, 0xf, 0xf6, 0x7f, 0x96, 0x71, 0xd4, 0xc7, 0x6c, 0xd0, 0xe9, 0xa9, 0x33, 0xd0, 0x75, 0xe1, 0x1e, 0xcb, 0x78, 0xcb, 0x4c, 0x16, 0x7c, 0x96, 0x51, 0x42, 0xab, 0x13, 0xd6, 0xa8, 0xad, 0x4f, 0x29, 0xd2, 0xf5, 0x47, 0x86, 0x7c, 0xbe, 0x11, 0x5f, 0xb4, 0x9d, 0x62, 0xff, 0xb6, 0x2e, 0xc5, 0xc0, 0xd0, 0x97, 0x4a, 0x90, 0x1b, 0xd8, 0x3f, 0xd0, 0x6f, 0x74, 0x71, 0x9e, 0xce, 0xb, 0xf6, 0x50, 0xaf, 0x2c, 0x66, 0xd4, 0xac, 0xb6, 0x50, 0xb2, 0xcb, 0x2e, 0x50, 0x8, 0xdd, 0xd1, 0x35, 0x6, 0x6d, 0xe4, 0x39, 0x9d, 0x1a, 0xdf, 0x80, 0x14, 0xf0, 0x19, 0xae, 0xb1, 0x4, 0xc9, 0xae, 0x28, 0x44, 0xf7, 0x9, 0xb2, 0x43, 0x3d, 0xdd, 0x5e, 0x96, 0x6b, 0x48, 0x7c, 0x18, 0x11, 0x81, 0xb9, 0x46, 0x1f, 0x52, 0xdb, 0xb1, 0x2c, 0x9f, 0xeb, 0x32, 0xc, 0x4, 0x50, 0x8e, 0x6e, 0xb7, 0xec, 0xcd, 0x28, 0x33, 0x42, 0xe6, 0xe5, 0x67, 0x79, 0x3e, 0x3f, 0xc8, 0x38, 0xff, 0xc0, 0x32, 0x8e, 0xce, 0x32, 0x43, 0x94, 0xec, 0xb2, 0x87, 0x22, 0xba, 0x18, 0xdc, 0x80, 0xcb, 0x67, 0x92, 0x4e, 0x8f, 0x6f, 0x40, 0x52, 0xc4, 0xf3, 0x96, 0x71, 0x9c, 0x90, 0xbc, 0x7d, 0x88, 0xee, 0x13, 0x75, 0x51, 0x36, 0xf7, 0xe5, 0x3c, 0x8f, 0xf1, 0xb0, 0x92, 0x9d, 0x7b, 0x4e, 0xe1, 0xbe, 0x5c, 0xa1, 0xcb, 0x30, 0x10, 0x60, 0xff, 0xb0, 0x95, 0xd2, 0x8c, 0x15, 0xe7, 0x6c, 0xc7, 0x7c, 0x7, 0x5a, 0xa0, 0x9d, 0x27, 0xf6, 0xc4, 0xa8, 0xf3, 0x3c, 0x14, 0x4b, 0x25, 0xbb, 0x0, 0x80, 0xd4, 0x71, 0x5b, 0xcf, 0x3a, 0xa4, 0xcd, 0xaf, 0xd7, 0xe9, 0xf1, 0x15, 0xb0, 0x94, 0xdd, 0x1d, 0xf5, 0x51, 0xd7, 0xd8, 0x33, 0x44, 0xf7, 0x88, 0x17, 0xaf, 0xb9, 0x6b, 0xc, 0x59, 0x8c, 0xc3, 0x24, 0x98, 0x12, 0x94, 0x20, 0xd0, 0xc4, 0x62, 0x2d, 0xa3, 0x31, 0xc2, 0x6a, 0x5d, 0x82, 0xbe, 0x3, 0xa7, 0xa9, 0xdb, 0xba, 0x83, 0x60, 0x9d, 0x5f, 0xa4, 0x7b, 0xc8, 0x6f, 0x98, 0x4a, 0xeb, 0xd7, 0x8d, 0x46, 0x95, 0x95, 0x95, 0xd7, 0x16, 0x35, 0x68, 0xd0, 0x40, 0xc9, 0x2e, 0x58, 0xa0, 0xb3, 0x84, 0xad, 0x33, 0xf7, 0x7c, 0x51, 0x17, 0x66, 0x10, 0x80, 0x1b, 0xd3, 0x76, 0x34, 0x4b, 0xbf, 0x90, 0xdf, 0x37, 0xda, 0xb7, 0x3d, 0x1d, 0xa1, 0x79, 0xb6, 0x95, 0x4a, 0x20, 0x76, 0x54, 0xa6, 0x4b, 0xd0, 0x77, 0xa0, 0x2b, 0x88, 0xed, 0x0, 0x63, 0xc4, 0xa8, 0x27, 0xea, 0xf4, 0x6c, 0x2, 0x34, 0x86, 0x9e, 0xe6, 0x1e, 0x34, 0x44, 0xd7, 0xef, 0x8d, 0xd1, 0x1f, 0xf4, 0x56, 0xb2, 0xb, 0x16, 0xa7, 0x79, 0x58, 0x75, 0x88, 0xcb, 0xac, 0xd2, 0xe9, 0xf1, 0x1d, 0x65, 0x1e, 0x64, 0xb7, 0xaf, 0x84, 0x2b, 0x6e, 0xe7, 0x26, 0x89, 0x2b, 0x23, 0x46, 0x14, 0x4d, 0x2c, 0x63, 0x68, 0xdd, 0x56, 0xaa, 0x4b, 0xd0, 0x57, 0x20, 0x2e, 0x3a, 0xc0, 0x32, 0x3e, 0x4f, 0xa2, 0x11, 0xdb, 0xcd, 0x36, 0xb0, 0xa7, 0x5e, 0xef, 0x1e, 0x2c, 0x28, 0x28, 0x28, 0x9c, 0x38, 0x6d, 0xe6, 0x25, 0xd3, 0x67, 0xcf, 0x4d, 0x28, 0xd9, 0x5, 0x3, 0xd4, 0x20, 0x5d, 0x61, 0x19, 0x9f, 0x2d, 0xb9, 0xeb, 0xa7, 0x97, 0xf, 0x40, 0x92, 0x84, 0xdb, 0xb5, 0x83, 0x73, 0xc7, 0x3a, 0x87, 0xf4, 0x7e, 0xd1, 0xc8, 0x77, 0x42, 0xc4, 0xe6, 0xd8, 0xd6, 0xa2, 0x6d, 0xb5, 0xf8, 0x7b, 0x26, 0xa3, 0xc2, 0x51, 0x96, 0xb7, 0xb1, 0x8c, 0xa3, 0x65, 0xd9, 0x12, 0x9d, 0x1e, 0x2b, 0x70, 0xca, 0xc3, 0x26, 0x8d, 0xee, 0x13, 0x86, 0xe2, 0x4a, 0x4a, 0x4b, 0xfb, 0xbe, 0xfc, 0xf6, 0x7b, 0x87, 0x54, 0x54, 0x54, 0x28, 0xd9, 0xf9, 0xc, 0x68, 0x10, 0xe8, 0x64, 0x60, 0x3b, 0x1d, 0xfc, 0x1f, 0xe2, 0x7d, 0xa, 0xaf, 0xa2, 0xee, 0x98, 0x60, 0xd9, 0x8, 0x90, 0xcd, 0xb6, 0x4b, 0x8, 0xef, 0x15, 0xa7, 0x17, 0xdc, 0x11, 0x13, 0x92, 0x80, 0x15, 0x52, 0xac, 0xcb, 0xcf, 0x37, 0x6c, 0xee, 0x61, 0xd5, 0xa1, 0x98, 0xfa, 0x5e, 0x9d, 0x1e, 0x4f, 0xa0, 0x51, 0xc7, 0xad, 0x6e, 0x2f, 0x43, 0xe3, 0x46, 0xd, 0x65, 0xfc, 0xa4, 0xa9, 0x83, 0xbf, 0x9e, 0x3a, 0xa3, 0xbe, 0x92, 0x9d, 0xbf, 0x40, 0xab, 0xaa, 0xd3, 0x2d, 0xe3, 0xef, 0x1b, 0x79, 0x56, 0xa7, 0x27, 0x70, 0x2, 0x59, 0x60, 0x51, 0x3e, 0xc2, 0x48, 0x76, 0x6f, 0x89, 0xbf, 0x27, 0x5e, 0x64, 0x53, 0x99, 0x73, 0xa3, 0xbe, 0x2e, 0x3d, 0x5f, 0x81, 0x63, 0x93, 0x6c, 0xb1, 0xd1, 0x7f, 0x4b, 0xb8, 0x8e, 0x78, 0xa, 0x23, 0x50, 0x86, 0x30, 0xae, 0xa, 0xb, 0x96, 0x57, 0xf4, 0x5a, 0xb7, 0x7e, 0x43, 0x77, 0x25, 0x3b, 0xff, 0x80, 0xb6, 0x60, 0x23, 0x2c, 0xf3, 0x88, 0xf4, 0xd8, 0x28, 0x9e, 0xd8, 0x1c, 0x45, 0x4c, 0xb3, 0x8c, 0x6d, 0x1b, 0xb2, 0x7b, 0x44, 0xbd, 0xdd, 0xdd, 0x11, 0x9d, 0x5f, 0x9b, 0x67, 0x2, 0x87, 0xb7, 0x36, 0xd6, 0xa5, 0xe7, 0xb, 0x10, 0x5f, 0x3e, 0xd6, 0x32, 0x8e, 0xc2, 0xf1, 0xa7, 0x74, 0x7a, 0xaa, 0x5, 0xc2, 0x18, 0x55, 0x4a, 0x32, 0x8a, 0x8b, 0x8b, 0x1a, 0xff, 0xe7, 0xc5, 0xd7, 0x8f, 0x57, 0xb2, 0xf3, 0x7, 0x88, 0xd, 0x3d, 0x6c, 0x64, 0x2b, 0xcb, 0x35, 0xb8, 0xab, 0xbe, 0xd4, 0x29, 0xca, 0xa, 0x6c, 0xf5, 0x8b, 0x88, 0xa1, 0x16, 0x86, 0xe8, 0x1e, 0xdf, 0x95, 0xe8, 0xc5, 0xea, 0x92, 0xb0, 0xc5, 0x8b, 0x60, 0x85, 0x34, 0xd5, 0xa5, 0xe7, 0xb, 0xe0, 0x85, 0xe8, 0x65, 0x19, 0x7f, 0x43, 0xbc, 0x4f, 0xec, 0x56, 0x6c, 0x8a, 0x57, 0xdc, 0x73, 0x55, 0x2f, 0x91, 0x90, 0x1f, 0x57, 0xae, 0x3a, 0xe9, 0xb5, 0x51, 0x63, 0x37, 0x53, 0xb2, 0xab, 0x1b, 0x50, 0x3b, 0x5, 0x17, 0xe5, 0x9e, 0x96, 0x6b, 0x1f, 0x89, 0x73, 0x7c, 0xbc, 0x22, 0x3b, 0xb0, 0x6d, 0x8, 0xcd, 0xc4, 0x69, 0xdb, 0x16, 0x6, 0x20, 0xae, 0xf0, 0x5f, 0x89, 0x6e, 0xaa, 0x3e, 0xba, 0xce, 0x2f, 0x75, 0x8d, 0x41, 0x91, 0xe8, 0xa9, 0x4b, 0xcf, 0x17, 0xf4, 0x11, 0x7b, 0xf6, 0xf0, 0x23, 0x3a, 0x35, 0x19, 0x3, 0x2d, 0x4, 0x1f, 0x4b, 0x1d, 0x48, 0x18, 0xb2, 0x2b, 0x29, 0x29, 0x6d, 0x3d, 0x67, 0xfe, 0xb7, 0x7d, 0x95, 0xec, 0x6a, 0x8f, 0x2d, 0xb8, 0x79, 0x1d, 0x6c, 0xb9, 0x86, 0xae, 0xfc, 0xe8, 0xbe, 0x5f, 0xa2, 0xd3, 0x94, 0x35, 0xd8, 0x3a, 0xa1, 0xb7, 0xe, 0x91, 0xe5, 0x1, 0xcb, 0xe8, 0xf5, 0x8, 0xcf, 0xef, 0x4f, 0x1e, 0x73, 0xfc, 0x47, 0x5d, 0x7a, 0xbe, 0xe0, 0x78, 0xcb, 0xd8, 0x78, 0x8a, 0x22, 0x73, 0xe0, 0x1d, 0xdb, 0xa4, 0xd1, 0x41, 0xc3, 0xe2, 0x6, 0x32, 0x7d, 0xf6, 0xdc, 0xa3, 0xd, 0xe1, 0x29, 0xd9, 0xd5, 0x2, 0xb0, 0x18, 0x9e, 0xf7, 0x20, 0x3a, 0x10, 0xdc, 0x99, 0x62, 0x8f, 0x21, 0x29, 0x82, 0xc3, 0x5a, 0x8b, 0x72, 0x1, 0xa2, 0x6b, 0x10, 0x92, 0xfb, 0x1b, 0x49, 0xc2, 0x88, 0x2a, 0x56, 0x79, 0x6c, 0xbc, 0xb0, 0x48, 0x3a, 0xea, 0xf2, 0xab, 0x13, 0x76, 0x12, 0xfb, 0x39, 0x8c, 0x50, 0xa6, 0xb5, 0x8e, 0xb1, 0x66, 0xc0, 0xc9, 0x21, 0x9b, 0x84, 0xa, 0xa, 0xa, 0xa, 0x64, 0xc9, 0xd2, 0xe5, 0xfb, 0x2d, 0x5f, 0xf1, 0x53, 0x1b, 0x25, 0xbb, 0x9a, 0x1, 0xbe, 0xf5, 0xb7, 0x3c, 0x88, 0xe, 0x18, 0x2c, 0xe1, 0xef, 0x60, 0x1f, 0x47, 0xa0, 0x95, 0x92, 0x3b, 0x89, 0x2, 0xae, 0xc3, 0xb0, 0xa4, 0xf8, 0xc7, 0x21, 0x23, 0x77, 0xb4, 0x65, 0x3e, 0xe1, 0xca, 0xbf, 0x5c, 0x97, 0x5f, 0x9d, 0x80, 0x73, 0x2, 0xdd, 0xee, 0x76, 0xf4, 0x7c, 0xd4, 0xa3, 0xc0, 0x6a, 0xe, 0x24, 0xaa, 0x8c, 0x4a, 0x1d, 0xa8, 0xac, 0xac, 0x34, 0x84, 0x57, 0xbf, 0x5d, 0x79, 0x45, 0xc5, 0xee, 0x4a, 0x76, 0x35, 0x73, 0x35, 0xe0, 0x85, 0xef, 0xe1, 0x71, 0x1d, 0x7, 0x73, 0x3e, 0xa0, 0xd3, 0x94, 0x33, 0x94, 0xbb, 0xfe, 0x8d, 0x1a, 0xb0, 0x30, 0x74, 0x51, 0x81, 0xff, 0x64, 0x72, 0xc, 0xe6, 0xf7, 0x1d, 0x23, 0x73, 0x2d, 0xe3, 0x67, 0x88, 0x93, 0x36, 0xaf, 0xa8, 0x1d, 0xd0, 0x71, 0xc9, 0x5d, 0xda, 0x31, 0x4f, 0xc2, 0x7f, 0xbe, 0x61, 0x58, 0xf1, 0x86, 0x5b, 0x29, 0x6b, 0x58, 0x5c, 0x54, 0xef, 0xc9, 0x17, 0x5e, 0xdb, 0x7f, 0x43, 0x49, 0x89, 0x92, 0x5d, 0x35, 0xc0, 0xf9, 0x48, 0x48, 0x19, 0x47, 0x2f, 0xc3, 0x6d, 0x3c, 0x3e, 0x73, 0x83, 0x38, 0x8d, 0x7d, 0x15, 0xb9, 0x83, 0x7b, 0xc3, 0x40, 0x6, 0x56, 0x93, 0x10, 0xdc, 0xd7, 0xa7, 0x12, 0x8f, 0x3, 0x4e, 0x51, 0x4a, 0x73, 0xbf, 0xc7, 0xb5, 0xfb, 0x8c, 0x1c, 0xa6, 0x4b, 0xb0, 0xc6, 0x40, 0x48, 0xa4, 0x8b, 0x65, 0x1c, 0x56, 0x9d, 0x36, 0xd9, 0xae, 0x1d, 0x10, 0x42, 0x9a, 0xed, 0x1e, 0x2c, 0x2d, 0x2b, 0x3b, 0xa0, 0x41, 0x61, 0x61, 0x42, 0xc9, 0xce, 0xe, 0x6c, 0x94, 0x28, 0x14, 0x47, 0x3b, 0xaa, 0x41, 0x46, 0xa, 0x2c, 0x9f, 0x41, 0x76, 0x1d, 0x8e, 0xe3, 0xb8, 0x5a, 0xa7, 0x2b, 0x94, 0xa8, 0xc, 0xc1, 0x3d, 0x6c, 0x8, 0xc9, 0x7d, 0xf8, 0x81, 0xc7, 0xc4, 0x1e, 0x8f, 0xc6, 0xa6, 0x8d, 0x18, 0xd3, 0xa9, 0xba, 0xe4, 0x6a, 0x84, 0x76, 0x46, 0x3a, 0x59, 0xc6, 0xc7, 0xe8, 0xd4, 0xd4, 0x1a, 0x25, 0xee, 0xf9, 0xfb, 0x35, 0x2b, 0xb3, 0xb4, 0xb4, 0xfb, 0xec, 0x79, 0xb, 0x5a, 0x28, 0xd9, 0x6d, 0x8a, 0x86, 0xe2, 0xb4, 0xed, 0x81, 0xcb, 0x12, 0xa9, 0xbf, 0x3b, 0x78, 0x7c, 0xe, 0xda, 0xfa, 0x49, 0xd4, 0x6a, 0x15, 0xa, 0x2f, 0xc4, 0xe9, 0xfd, 0x5a, 0x2e, 0xce, 0xe1, 0x98, 0xb6, 0x12, 0x8a, 0xcd, 0xf8, 0xbe, 0xa0, 0x13, 0x7d, 0x2b, 0x7d, 0xec, 0x19, 0x61, 0x3b, 0xee, 0x37, 0x6e, 0x5, 0x4d, 0xeb, 0x73, 0xeb, 0x86, 0x8f, 0xdd, 0x64, 0xf7, 0xcb, 0x2f, 0x6b, 0x8b, 0x47, 0x8e, 0xf9, 0x68, 0x8f, 0x7a, 0x21, 0xd6, 0x8a, 0xb3, 0x5, 0xbc, 0xa8, 0xbd, 0xc4, 0xe9, 0x63, 0x9, 0x5f, 0xf9, 0xa3, 0x46, 0xf6, 0x49, 0xf3, 0xf9, 0x2f, 0x8c, 0x1c, 0x22, 0x7a, 0x6c, 0x8f, 0x22, 0xff, 0x80, 0xf4, 0xee, 0x6b, 0x3d, 0xae, 0xa1, 0x85, 0xd8, 0x5, 0xdc, 0xac, 0x71, 0x54, 0x4d, 0x6b, 0x9d, 0xae, 0xb4, 0xb0, 0x35, 0x2b, 0x47, 0x7c, 0x57, 0x9b, 0x3e, 0xd7, 0xd, 0xd3, 0xc5, 0x75, 0xb0, 0x2b, 0x8, 0xaf, 0xb8, 0xa8, 0xa8, 0x73, 0x41, 0x9a, 0x85, 0x8b, 0x6b, 0x89, 0x18, 0x4d, 0x42, 0x25, 0xbf, 0x53, 0x63, 0xba, 0xf, 0xba, 0x89, 0x73, 0x1c, 0xc, 0xfe, 0xbe, 0x67, 0x6, 0xdf, 0x15, 0x49, 0x10, 0x77, 0x8a, 0xd3, 0xf4, 0x79, 0xa5, 0xae, 0x29, 0x45, 0x9e, 0x2, 0xd, 0x13, 0xd0, 0x35, 0xe8, 0xaf, 0x69, 0x2c, 0x96, 0x7b, 0x48, 0x78, 0xaf, 0x89, 0xe3, 0xfe, 0x9c, 0x91, 0x67, 0xa, 0x74, 0x26, 0xb0, 0x95, 0x1c, 0xe0, 0x50, 0x52, 0x3d, 0xa0, 0xb5, 0x6e, 0x40, 0xad, 0xf3, 0x42, 0x71, 0xb9, 0x88, 0xcb, 0xca, 0xcb, 0x3d, 0xc9, 0xe, 0xf1, 0x2a, 0xa4, 0xd8, 0xc7, 0xc9, 0xd, 0x53, 0xc9, 0x97, 0x14, 0x49, 0x27, 0x45, 0x52, 0xb3, 0x66, 0xb6, 0x63, 0xc5, 0xc9, 0xb8, 0x1c, 0x9b, 0xa7, 0xb, 0x8, 0xf3, 0x85, 0x62, 0xfa, 0x86, 0x29, 0xf3, 0x57, 0xc9, 0xf5, 0xd1, 0x82, 0x73, 0x89, 0xf8, 0xd4, 0xa, 0xd7, 0x7c, 0xe3, 0xdf, 0xf0, 0xa3, 0xaf, 0xa1, 0x82, 0xa0, 0xc5, 0xf6, 0xd1, 0x47, 0x5, 0x2d, 0xb8, 0xf9, 0x7c, 0x27, 0xbc, 0x8a, 0xf7, 0x3b, 0x51, 0x40, 0x8a, 0xef, 0x19, 0x79, 0xc9, 0xc8, 0x67, 0xa2, 0x99, 0x86, 0x49, 0xd8, 0x1a, 0x3f, 0xcf, 0x54, 0xa5, 0xa0, 0xce, 0x58, 0x4a, 0xc2, 0xfb, 0x8d, 0xec, 0x1a, 0x35, 0x2c, 0x96, 0xcf, 0x27, 0x4c, 0xda, 0xda, 0x8b, 0xec, 0xb6, 0xa6, 0xe4, 0x3b, 0x92, 0xc7, 0xbf, 0x3f, 0x9d, 0x27, 0x1b, 0x35, 0xda, 0x40, 0xc1, 0xad, 0xbb, 0x87, 0x38, 0x6e, 0x96, 0x5d, 0xb8, 0xe, 0x70, 0x7a, 0x72, 0x33, 0x17, 0xd9, 0x55, 0x90, 0xec, 0x52, 0x8b, 0xb7, 0x93, 0x5a, 0x69, 0xc2, 0x45, 0x76, 0x70, 0x2b, 0xa0, 0x38, 0x19, 0x59, 0x7d, 0xc8, 0x34, 0x43, 0x8b, 0xaf, 0x6f, 0x29, 0xd8, 0x34, 0xe7, 0xf0, 0x73, 0x7a, 0xfa, 0x75, 0x74, 0x80, 0x6, 0xe8, 0xe3, 0xf8, 0x7e, 0xf4, 0x48, 0xf3, 0x39, 0x94, 0x81, 0xf4, 0xa7, 0x2c, 0xe3, 0xff, 0x41, 0x5c, 0xe5, 0x4d, 0x5a, 0x7c, 0xf9, 0xda, 0x2c, 0x7d, 0x7b, 0xf, 0xb2, 0x53, 0xd4, 0x1d, 0xdf, 0x18, 0xe9, 0xeb, 0xb2, 0xec, 0x5a, 0x16, 0xe8, 0xbc, 0x54, 0x1, 0x36, 0x5d, 0xf4, 0xb7, 0x7c, 0x88, 0xda, 0x68, 0xdc, 0xdd, 0xa, 0x70, 0xa7, 0xec, 0x26, 0x4e, 0xaf, 0x43, 0xd4, 0xfd, 0x20, 0x29, 0xa7, 0x71, 0x2d, 0xad, 0x7a, 0xf7, 0x19, 0x67, 0xdb, 0x64, 0xf0, 0x7f, 0x4a, 0x49, 0x80, 0x58, 0xa0, 0x13, 0xc5, 0xa9, 0xe7, 0x9a, 0x4c, 0x57, 0xc4, 0x7c, 0xd1, 0x53, 0xde, 0xc3, 0xc, 0xbc, 0x27, 0xbd, 0x8d, 0x5c, 0x22, 0x4e, 0xf7, 0xa0, 0xed, 0xab, 0xf9, 0x3c, 0x92, 0x57, 0xe, 0xa7, 0x5c, 0xc7, 0xe7, 0x8b, 0x42, 0xe0, 0x4f, 0x48, 0x7c, 0x33, 0xe8, 0x5, 0x88, 0x3b, 0xa0, 0x30, 0x36, 0xb1, 0xec, 0x3b, 0x7a, 0xf6, 0xa5, 0x3f, 0xa8, 0x52, 0x13, 0x9a, 0x48, 0x24, 0x36, 0x57, 0xb2, 0xdb, 0xb8, 0xd0, 0x90, 0x56, 0x8d, 0x93, 0x6f, 0xdf, 0xa7, 0xe6, 0x19, 0x57, 0x77, 0x2, 0x48, 0xac, 0x83, 0x91, 0xa3, 0x8d, 0x1c, 0x6a, 0x64, 0x57, 0x5a, 0x6e, 0xb9, 0xb4, 0x26, 0xb7, 0xa4, 0xec, 0x9d, 0x32, 0xe, 0xab, 0x70, 0x1e, 0x5, 0xee, 0xaf, 0xb1, 0x7c, 0x46, 0xeb, 0x74, 0xb9, 0x86, 0xa, 0x78, 0x1e, 0x48, 0xee, 0x42, 0xf9, 0x1, 0xce, 0x76, 0x1c, 0x28, 0x76, 0x17, 0x9d, 0xed, 0xb9, 0xef, 0x44, 0x39, 0x8f, 0x4a, 0xcd, 0xc, 0x3e, 0x63, 0xbc, 0x7f, 0x1f, 0x50, 0xe1, 0x89, 0xa3, 0xb2, 0x89, 0xee, 0x33, 0xee, 0xbd, 0x17, 0xdf, 0x7f, 0xb9, 0x2e, 0x27, 0x5f, 0x30, 0xdf, 0x32, 0xb6, 0x59, 0xbe, 0x91, 0x1d, 0x5c, 0x26, 0x70, 0xa5, 0xe1, 0x40, 0xc4, 0xa5, 0xdc, 0x48, 0x91, 0x3d, 0x86, 0xda, 0x8c, 0xd9, 0x12, 0xef, 0x7e, 0x74, 0xdb, 0xd0, 0xb4, 0x3f, 0xc5, 0xc8, 0xef, 0x24, 0x1c, 0x1d, 0x46, 0xd2, 0x21, 0x49, 0x80, 0x7b, 0x19, 0x39, 0x8e, 0xcf, 0x6, 0x9a, 0xef, 0x48, 0x12, 0xdf, 0x24, 0x5a, 0x82, 0x3f, 0x8b, 0xc6, 0x39, 0xc2, 0x80, 0x39, 0x24, 0xbd, 0xbb, 0x8c, 0x1c, 0x43, 0xe2, 0x43, 0x56, 0x73, 0xb3, 0xc, 0xff, 0x3f, 0x4e, 0xeb, 0xee, 0x41, 0x39, 0x9d, 0xef, 0xea, 0x12, 0x3e, 0xeb, 0xf, 0x69, 0xf9, 0xcf, 0x90, 0xaa, 0x27, 0x30, 0x44, 0x11, 0xb0, 0xea, 0xdc, 0x39, 0x3, 0x3f, 0xc7, 0xe4, 0xbb, 0x85, 0x1, 0xf3, 0xb8, 0x5f, 0xa4, 0x1e, 0xf3, 0xd5, 0xc8, 0x8b, 0xec, 0x5e, 0xa0, 0xa6, 0x16, 0x27, 0xac, 0xe5, 0x82, 0x4a, 0x92, 0xdd, 0xf7, 0x12, 0x9e, 0xfe, 0x89, 0x41, 0x2, 0xae, 0xa5, 0xb3, 0xb9, 0x1, 0xd5, 0xe5, 0x4, 0xef, 0x52, 0x4a, 0x32, 0x16, 0xb7, 0x58, 0xec, 0x49, 0x3e, 0xb8, 0x8e, 0x7e, 0x7f, 0xdb, 0x52, 0x7b, 0xad, 0xc7, 0x3f, 0xeb, 0x4b, 0xdd, 0x12, 0x9e, 0xa, 0x49, 0xd8, 0x7f, 0xa1, 0xac, 0xa0, 0xc5, 0x87, 0x26, 0xc5, 0xd, 0xf5, 0xfd, 0xe, 0xd, 0xf0, 0x7e, 0x3d, 0x46, 0x41, 0xec, 0xb7, 0x27, 0xd7, 0xde, 0x7e, 0x35, 0x7c, 0x4e, 0x50, 0xc6, 0xda, 0x89, 0x53, 0xf7, 0xa, 0x41, 0x36, 0xf4, 0x4c, 0x92, 0xea, 0x44, 0x92, 0xe0, 0x4, 0x5a, 0x44, 0x51, 0x53, 0x76, 0x90, 0xb1, 0x5a, 0x6c, 0xb1, 0x90, 0xb1, 0x1f, 0xe9, 0xa1, 0xb8, 0x75, 0xc7, 0x1a, 0xae, 0x97, 0x54, 0xb2, 0xdb, 0xc2, 0x8b, 0xec, 0xbe, 0x36, 0xf2, 0xa2, 0xce, 0x59, 0xa4, 0x81, 0xc4, 0x12, 0xc4, 0x51, 0x2e, 0x12, 0x27, 0x93, 0x32, 0x53, 0x20, 0x6b, 0x72, 0x1, 0x5d, 0x1, 0xb, 0x68, 0xf1, 0x22, 0x91, 0x64, 0x91, 0x38, 0x6e, 0xa5, 0xe4, 0x42, 0x42, 0x32, 0x49, 0xc2, 0x83, 0xec, 0xea, 0xa5, 0x2c, 0xb4, 0x26, 0x24, 0x3e, 0x64, 0xed, 0xa1, 0xfb, 0xf8, 0x56, 0xdc, 0xc4, 0xf0, 0xc2, 0xa3, 0x16, 0xab, 0x25, 0xc7, 0x8b, 0x6b, 0x61, 0xf9, 0x25, 0x13, 0x1f, 0x14, 0xe1, 0xc4, 0x57, 0x94, 0x7b, 0xb9, 0x1e, 0xf1, 0xac, 0x10, 0xe3, 0xeb, 0x4a, 0xc5, 0xab, 0x26, 0x9e, 0x25, 0x28, 0x4b, 0x9d, 0x29, 0x47, 0x1a, 0xb9, 0x4a, 0x9c, 0xd3, 0x24, 0xd0, 0xe5, 0xe8, 0x7d, 0x12, 0xdf, 0x2c, 0xb1, 0x1f, 0x47, 0x14, 0x36, 0x94, 0x5b, 0x8, 0x1a, 0xdf, 0x6b, 0xae, 0x2e, 0x19, 0x5f, 0x50, 0x29, 0x16, 0xcf, 0x95, 0xd7, 0x62, 0x2b, 0xd6, 0xf9, 0x8a, 0x34, 0xe0, 0xaa, 0x1c, 0x2a, 0xf6, 0x76, 0x44, 0x36, 0x8b, 0xd, 0xee, 0x21, 0xb8, 0x72, 0x3f, 0xe0, 0xb, 0xb7, 0x40, 0x36, 0x2d, 0x23, 0xa8, 0xd, 0x4a, 0x52, 0xb4, 0x2c, 0x2f, 0xf7, 0x4c, 0xa3, 0x14, 0xb2, 0x83, 0xd5, 0x86, 0x44, 0x19, 0x74, 0x28, 0xef, 0x2e, 0x4e, 0x10, 0xbf, 0x50, 0x1f, 0x65, 0x2c, 0x50, 0x41, 0x65, 0xe9, 0x41, 0x4a, 0x4b, 0xae, 0x4d, 0x3c, 0x6f, 0x34, 0x68, 0x40, 0xbd, 0x6b, 0xb3, 0x1a, 0x92, 0x1f, 0x14, 0xaa, 0xe6, 0x2e, 0x85, 0x7, 0xae, 0x4e, 0x14, 0x15, 0x23, 0xe6, 0x87, 0xd3, 0xe1, 0x91, 0xe8, 0x14, 0x95, 0x6c, 0xcf, 0x7a, 0x6a, 0xd5, 0x5, 0xb, 0x4d, 0x50, 0x89, 0x17, 0x60, 0x2d, 0xdd, 0x40, 0xb2, 0xab, 0x6e, 0xf3, 0x41, 0x6, 0x1c, 0x62, 0x21, 0x38, 0x9f, 0x6f, 0xaa, 0xe4, 0xa6, 0xb4, 0x62, 0x2d, 0x89, 0x35, 0x79, 0xda, 0xf8, 0xb, 0xfc, 0x13, 0xee, 0x2e, 0x24, 0x2e, 0xf4, 0x12, 0xc7, 0xd, 0x6, 0xad, 0xb7, 0x8b, 0xe8, 0x29, 0x1d, 0x71, 0xc1, 0xf, 0x14, 0x28, 0x57, 0xf7, 0xd0, 0xea, 0x7, 0xf9, 0xfd, 0x5e, 0x1c, 0x77, 0x27, 0xce, 0xc9, 0xdb, 0x41, 0x6a, 0x7e, 0xda, 0xfc, 0x8e, 0x94, 0x23, 0x8c, 0x5c, 0x2f, 0x4e, 0xec, 0xe6, 0x15, 0x92, 0xdf, 0x67, 0xa2, 0x31, 0x31, 0x25, 0x3b, 0x45, 0x2c, 0x80, 0xce, 0xf3, 0x48, 0xe, 0xe8, 0x50, 0xd, 0xb9, 0x3c, 0x45, 0xf9, 0x44, 0xc2, 0x9b, 0x90, 0x83, 0xf8, 0xc5, 0x24, 0xca, 0x5d, 0xb4, 0x4, 0x40, 0x76, 0xfb, 0xf3, 0x7b, 0x22, 0x6b, 0xb3, 0x81, 0x3e, 0xf2, 0xd8, 0x0, 0xa5, 0x27, 0x5f, 0x50, 0x80, 0x56, 0x24, 0xbb, 0xce, 0xb4, 0xfa, 0x90, 0xe8, 0x2, 0xb7, 0x67, 0x4d, 0x92, 0xaa, 0xa, 0x49, 0x9a, 0x97, 0x51, 0x90, 0xe5, 0x39, 0x8e, 0xa, 0xd5, 0x58, 0x71, 0xb5, 0x94, 0xca, 0x81, 0x15, 0x97, 0xd0, 0xc7, 0xae, 0x64, 0xa7, 0xa8, 0x39, 0xd0, 0xa5, 0xe2, 0x16, 0xf1, 0x76, 0x3f, 0x23, 0xa5, 0x19, 0x8d, 0x7a, 0x11, 0x3b, 0x99, 0x1f, 0x51, 0x4b, 0x60, 0x2c, 0xe5, 0x66, 0x6e, 0x62, 0x28, 0x9d, 0xe8, 0xc7, 0xcd, 0x50, 0xdd, 0x9d, 0xf1, 0xc2, 0x32, 0xca, 0x67, 0x5c, 0xb7, 0x78, 0xbe, 0x6d, 0xa9, 0xe4, 0xe0, 0x79, 0xef, 0x46, 0x4b, 0x70, 0xab, 0x1a, 0xfc, 0xcc, 0x64, 0xbc, 0xef, 0x34, 0x7a, 0x12, 0x90, 0x80, 0xf7, 0x32, 0x7f, 0x47, 0x2e, 0x3c, 0x1a, 0xe5, 0x2e, 0x4f, 0x5, 0xe2, 0x4c, 0x1b, 0xf4, 0xd1, 0xd7, 0xd, 0xe8, 0x83, 0xb9, 0x6e, 0xfd, 0x6, 0x29, 0x2f, 0xdf, 0xe4, 0x88, 0x4b, 0xc4, 0x7b, 0x57, 0x2a, 0xd9, 0x45, 0x1f, 0x68, 0xcc, 0x3b, 0x2c, 0xcd, 0x75, 0x58, 0x71, 0xb7, 0xd2, 0x4a, 0x8a, 0x3, 0xb0, 0x8a, 0xa7, 0x53, 0xf0, 0xbd, 0x10, 0xaf, 0x79, 0x58, 0x9c, 0x18, 0x9f, 0x22, 0x9e, 0x80, 0x7, 0x62, 0xe, 0xe5, 0x19, 0x92, 0xc4, 0x2e, 0x54, 0x7a, 0x50, 0xaa, 0x70, 0x8, 0x2d, 0xff, 0x4c, 0x2d, 0x3f, 0x10, 0x27, 0x4e, 0x70, 0x18, 0x2c, 0x4e, 0x6c, 0xf, 0xeb, 0xe7, 0xd, 0xa9, 0x7a, 0x8, 0x70, 0x50, 0x40, 0xfc, 0x72, 0xbd, 0x4b, 0x49, 0x83, 0xcb, 0xf5, 0x6c, 0x12, 0xa1, 0xa2, 0x96, 0x58, 0x6f, 0x88, 0xee, 0x98, 0xc3, 0xfa, 0x48, 0x87, 0xf6, 0x6d, 0xdd, 0x96, 0xb4, 0x92, 0x5d, 0xc4, 0x81, 0xb8, 0xc4, 0xdf, 0x3c, 0xae, 0xc1, 0x82, 0xbb, 0x9a, 0x64, 0x17, 0x57, 0x20, 0x23, 0x14, 0x7d, 0x17, 0xd7, 0x28, 0xd9, 0xe5, 0x15, 0x10, 0x73, 0x9e, 0x46, 0x81, 0x75, 0x76, 0x8d, 0x38, 0x25, 0x36, 0x7, 0x18, 0xe9, 0x43, 0xcb, 0x6f, 0xb7, 0xc, 0x7e, 0xe, 0x62, 0x82, 0x47, 0x52, 0xe0, 0x35, 0x18, 0x2e, 0x4e, 0xf3, 0xea, 0xc0, 0xf7, 0x64, 0xa9, 0x9a, 0x8d, 0x9, 0xeb, 0x3, 0x9, 0x35, 0x1a, 0x57, 0xac, 0x8b, 0x56, 0x54, 0x56, 0x26, 0x5d, 0x3a, 0x76, 0x90, 0x6e, 0x5d, 0x76, 0xae, 0x72, 0x4d, 0x3, 0xfe, 0xd1, 0xc5, 0xe0, 0x34, 0x44, 0x7, 0x2d, 0xf5, 0xa0, 0x98, 0x13, 0x5d, 0x12, 0x8d, 0x75, 0x1d, 0xeb, 0x1e, 0x27, 0x4e, 0x26, 0xe6, 0x63, 0xe2, 0x24, 0x67, 0x61, 0xed, 0xa3, 0x81, 0x2, 0x8a, 0xdc, 0x67, 0x65, 0x68, 0xb1, 0xf5, 0x12, 0x27, 0x99, 0xe5, 0x79, 0x5a, 0x8c, 0x41, 0x2, 0x8d, 0x8a, 0xdd, 0x2e, 0x4b, 0x94, 0xd2, 0xb4, 0xd7, 0x47, 0x59, 0x37, 0xc0, 0x8d, 0xb9, 0xa1, 0xc4, 0x9e, 0x6b, 0xa7, 0x9b, 0x44, 0x34, 0x81, 0x6c, 0xb3, 0x5b, 0x3c, 0xae, 0xa1, 0xa7, 0xe7, 0x51, 0xe2, 0xb8, 0x45, 0x14, 0x8a, 0x7c, 0x4, 0x9a, 0x46, 0xbc, 0x4b, 0x65, 0x10, 0x47, 0x79, 0x1d, 0x28, 0x4e, 0xa2, 0xd3, 0xe4, 0xea, 0xf6, 0x4a, 0x71, 0xba, 0xf5, 0x20, 0x4b, 0xf9, 0x1c, 0x9, 0x2e, 0x89, 0x4, 0xbb, 0xf1, 0x32, 0xd7, 0x18, 0xea, 0x51, 0xf5, 0xc, 0xc0, 0x0, 0xa1, 0x64, 0x17, 0x3d, 0x20, 0x28, 0x8f, 0x74, 0x6d, 0x5b, 0x37, 0xa, 0x74, 0xa0, 0x3f, 0x57, 0xf4, 0xf4, 0x0, 0x85, 0x22, 0x9, 0xb8, 0xc, 0x51, 0x7a, 0x70, 0x1, 0x2d, 0x3e, 0xb4, 0x31, 0x7b, 0xb3, 0x1a, 0x6b, 0xf, 0xd9, 0xa0, 0xf7, 0x52, 0x82, 0x6a, 0xab, 0xb7, 0xc0, 0x42, 0xb4, 0x3b, 0xe8, 0xe3, 0x52, 0xb2, 0x53, 0x6c, 0x7c, 0x5e, 0xb0, 0xe8, 0x6c, 0x8d, 0x76, 0x9f, 0x14, 0x27, 0xc5, 0x5a, 0x89, 0x4e, 0xa1, 0xb0, 0x3, 0x59, 0xc9, 0xcf, 0x8a, 0xe3, 0x19, 0x41, 0x6c, 0xef, 0x25, 0x49, 0x5f, 0x5f, 0x7a, 0x36, 0x3f, 0x13, 0x44, 0xa3, 0xf4, 0x19, 0x96, 0xb1, 0x1d, 0xf5, 0x11, 0x29, 0xd9, 0x29, 0x1c, 0xa0, 0xd5, 0xd2, 0x9f, 0x2d, 0xe3, 0x68, 0x97, 0x74, 0x8e, 0x4e, 0x8f, 0x42, 0x91, 0x11, 0x2a, 0xf9, 0xce, 0xa0, 0x67, 0x27, 0xea, 0x36, 0xdf, 0x4b, 0xf3, 0x59, 0x5c, 0x7f, 0x24, 0x0, 0xb, 0xcf, 0x46, 0x76, 0x28, 0xa8, 0xd7, 0xee, 0x55, 0x4a, 0x76, 0x79, 0xf, 0xb8, 0x39, 0x2e, 0x97, 0xaa, 0xc5, 0xd4, 0x88, 0x4f, 0xe0, 0x88, 0x14, 0x4d, 0x59, 0x56, 0x28, 0x6a, 0xe, 0x10, 0x1d, 0x8e, 0xba, 0xba, 0x50, 0x9c, 0x5e, 0x9b, 0x36, 0xc0, 0x12, 0xbc, 0x55, 0xfc, 0x8d, 0xe1, 0x81, 0xec, 0xdc, 0xae, 0x54, 0x94, 0x4f, 0x68, 0x56, 0xb1, 0x92, 0x5d, 0xde, 0x3, 0xf1, 0x86, 0x43, 0x2c, 0xe3, 0xc8, 0x38, 0x9b, 0xa6, 0xd3, 0xa3, 0x50, 0xd4, 0x1a, 0x70, 0xfd, 0xdf, 0x69, 0xe4, 0xf, 0xe2, 0x7d, 0x5a, 0x38, 0x14, 0xca, 0xa3, 0x7c, 0xfc, 0x9d, 0x68, 0xaa, 0xbe, 0xd8, 0x35, 0x6, 0x45, 0xb6, 0x87, 0x3e, 0xe, 0x25, 0xbb, 0x7c, 0xc7, 0xf9, 0x96, 0x31, 0xf4, 0xb4, 0x7c, 0x5c, 0xa7, 0x46, 0xa1, 0xf0, 0x5, 0x48, 0x64, 0x39, 0xc6, 0x83, 0xf0, 0xa, 0xa8, 0x58, 0x36, 0xf5, 0xe9, 0x77, 0x2d, 0xf2, 0x50, 0x52, 0x7b, 0xeb, 0x63, 0x50, 0xb2, 0xcb, 0x67, 0xa0, 0x29, 0xf2, 0xfe, 0x96, 0x71, 0x94, 0x19, 0xfc, 0xa4, 0xd3, 0xa3, 0x50, 0xf8, 0x6, 0x10, 0x10, 0x8a, 0xcc, 0x97, 0x58, 0xae, 0xa1, 0xdd, 0xd8, 0xa9, 0x3e, 0xfd, 0x1e, 0xd4, 0x6, 0x7e, 0x65, 0x19, 0x47, 0x1f, 0x50, 0x8d, 0xdb, 0x29, 0xd9, 0xe5, 0x2d, 0x10, 0xb8, 0x6e, 0xe5, 0x1a, 0x43, 0xbf, 0xc8, 0x67, 0x74, 0x6a, 0x14, 0xa, 0xdf, 0x81, 0x42, 0xf4, 0x81, 0x62, 0x8f, 0x83, 0x23, 0x11, 0xcc, 0xaf, 0xc3, 0x82, 0xd1, 0x8c, 0xdd, 0x7d, 0x80, 0x34, 0x7a, 0x7e, 0xee, 0xae, 0x8f, 0x40, 0xc9, 0x2e, 0x5f, 0xb1, 0x97, 0x65, 0xc, 0xd9, 0x64, 0xda, 0x5a, 0x48, 0xa1, 0x8, 0x6, 0xa8, 0xc5, 0x7b, 0xc9, 0x32, 0x8e, 0xee, 0x2a, 0x7, 0xf8, 0xf4, 0x3b, 0xf0, 0xe, 0xbb, 0x3d, 0x33, 0x4d, 0x7c, 0xfc, 0xf9, 0xa, 0x25, 0xbb, 0x48, 0x1, 0x2e, 0x8d, 0xee, 0x96, 0xf1, 0x51, 0x3a, 0x35, 0xa, 0x45, 0xa0, 0xb8, 0x4d, 0x9c, 0xbe, 0xab, 0xa9, 0x40, 0x12, 0x49, 0x2f, 0x9f, 0x7e, 0xfe, 0xcf, 0x46, 0x46, 0x5b, 0xc6, 0x4f, 0x11, 0x3d, 0xc2, 0x4a, 0xc9, 0x2e, 0xf, 0x81, 0xde, 0x8f, 0xee, 0x33, 0xea, 0x90, 0xb2, 0xfc, 0xb5, 0x4e, 0x8d, 0x42, 0x11, 0x28, 0x26, 0x8a, 0xd3, 0x3a, 0xcc, 0x8d, 0x1e, 0x3e, 0x92, 0xd1, 0x2b, 0x96, 0xb1, 0xae, 0xe2, 0x1c, 0x5a, 0xac, 0x50, 0xb2, 0xcb, 0x3b, 0xcb, 0xce, 0x1d, 0xaf, 0x43, 0x5f, 0xbd, 0xe5, 0x3a, 0x35, 0xa, 0x45, 0xe0, 0x78, 0xce, 0x32, 0x86, 0x98, 0xda, 0x66, 0x3e, 0xfd, 0xfc, 0x31, 0x46, 0xe6, 0x5a, 0xc6, 0x4f, 0xd7, 0xa9, 0x57, 0xb2, 0xcb, 0x37, 0xd8, 0x5a, 0x83, 0x81, 0xec, 0x56, 0xeb, 0xd4, 0x28, 0x14, 0x81, 0x3, 0x19, 0x93, 0xa5, 0xae, 0x31, 0x9c, 0x50, 0xb0, 0xb9, 0x4f, 0x3f, 0x1f, 0x59, 0x9f, 0xef, 0x58, 0xc6, 0x51, 0xe8, 0xae, 0xed, 0xc3, 0x94, 0xec, 0xf2, 0xa, 0x8d, 0x3d, 0x9e, 0x5b, 0x42, 0xa7, 0x46, 0xa1, 0x8, 0x1c, 0xe8, 0x50, 0xb4, 0xd0, 0x32, 0xde, 0xce, 0xc7, 0xdf, 0x81, 0xa3, 0xb8, 0xdc, 0x59, 0x99, 0x68, 0xf8, 0xfe, 0x27, 0x9d, 0x7e, 0x25, 0xbb, 0x7c, 0x42, 0xa5, 0x4e, 0x81, 0x42, 0x91, 0x33, 0xac, 0x12, 0x7b, 0xd6, 0xb3, 0x9f, 0xca, 0x26, 0x4a, 0x10, 0x6c, 0xb1, 0x41, 0x9c, 0x60, 0xd2, 0x5c, 0x1f, 0x81, 0x92, 0x5d, 0xbe, 0xa0, 0xc4, 0x83, 0x0, 0x95, 0x4, 0x37, 0xce, 0x45, 0x85, 0x4e, 0x83, 0x22, 0x20, 0xfc, 0x62, 0x64, 0x45, 0xc0, 0xbf, 0x3, 0xeb, 0xf7, 0x7e, 0xcb, 0x78, 0x1b, 0x12, 0x9e, 0x42, 0xc9, 0x2e, 0x2f, 0xb0, 0xc0, 0x32, 0x6, 0xd7, 0x66, 0x91, 0x4e, 0xcd, 0xaf, 0x40, 0x81, 0x6f, 0x2b, 0x9d, 0x6, 0x45, 0xc0, 0xa, 0x55, 0xd0, 0x78, 0xd5, 0xc8, 0x78, 0xcb, 0xf8, 0x59, 0x46, 0xb6, 0xd7, 0x47, 0xa0, 0x64, 0x97, 0xf, 0x40, 0x17, 0x7, 0x77, 0xad, 0xcf, 0xd6, 0xe2, 0x4, 0xc9, 0x15, 0x4e, 0x9a, 0x76, 0x43, 0x9d, 0x6, 0x45, 0x40, 0x48, 0x64, 0x69, 0x9f, 0xc4, 0x3b, 0x7e, 0xa7, 0x65, 0x7c, 0x3b, 0x23, 0x97, 0xea, 0x63, 0x50, 0xb2, 0xcb, 0x7, 0xac, 0x33, 0xf2, 0x8d, 0x6b, 0xac, 0x91, 0x5a, 0x33, 0xbf, 0xe1, 0x8f, 0x3a, 0x5, 0x8a, 0x0, 0x81, 0xc6, 0xcf, 0xb6, 0xb8, 0x59, 0x10, 0xae, 0x73, 0x94, 0x39, 0x4c, 0xb4, 0x8c, 0xe3, 0x10, 0x59, 0x3d, 0xd, 0xa1, 0x2a, 0xba, 0x89, 0x93, 0xad, 0x9e, 0x11, 0x8f, 0x29, 0xd9, 0x45, 0x83, 0xec, 0xe6, 0x58, 0xc6, 0xf7, 0xd4, 0xa9, 0x91, 0xd6, 0xe2, 0x1c, 0xae, 0xa9, 0x50, 0x4, 0x85, 0x26, 0x16, 0xb2, 0x83, 0x5b, 0x73, 0x56, 0x0, 0xbf, 0x6b, 0x83, 0x91, 0x9b, 0xa5, 0xea, 0x39, 0x77, 0x28, 0x60, 0xbf, 0x4b, 0xfc, 0xab, 0xed, 0x8b, 0xcb, 0xbb, 0xff, 0x82, 0x38, 0xe7, 0x2, 0xbe, 0x6b, 0xe4, 0xa, 0xb1, 0x77, 0x9a, 0x52, 0xb2, 0x8b, 0x10, 0xb0, 0xf0, 0xa7, 0x5a, 0xc6, 0xf, 0xd5, 0xa9, 0xf9, 0x35, 0x9e, 0xd1, 0x46, 0xa7, 0x41, 0x11, 0xf0, 0xa6, 0xda, 0xde, 0x35, 0x86, 0xe3, 0x79, 0xd6, 0x4, 0xf4, 0xfb, 0x5e, 0x34, 0x32, 0xd2, 0x32, 0x8e, 0xd3, 0x10, 0x2e, 0xd6, 0xc7, 0xf1, 0x1b, 0xfe, 0x4f, 0x9c, 0xce, 0x52, 0x68, 0xba, 0x81, 0x63, 0x91, 0x6e, 0xe2, 0xbc, 0x15, 0x2b, 0xd9, 0x45, 0x1b, 0x68, 0xd, 0x56, 0x61, 0xb1, 0xec, 0xb6, 0xcd, 0xe3, 0x39, 0xc1, 0xa9, 0xce, 0x7f, 0xd5, 0xa5, 0xa1, 0x8, 0x18, 0xbb, 0x18, 0xa9, 0xef, 0x1a, 0x9b, 0x2c, 0x4e, 0x96, 0x66, 0x10, 0x40, 0x1, 0xfb, 0x95, 0x62, 0x6f, 0x1a, 0x31, 0xd4, 0xc8, 0xef, 0xf4, 0x91, 0xfc, 0x5a, 0x83, 0x78, 0xa5, 0x65, 0x1c, 0x46, 0x41, 0x99, 0x92, 0x5d, 0xb4, 0x31, 0x9e, 0xda, 0x64, 0x2a, 0xd0, 0xc1, 0xe1, 0xc8, 0x3c, 0x9d, 0xf, 0x24, 0xd, 0xdc, 0x2a, 0x5a, 0x83, 0xa4, 0x8, 0x1e, 0xfd, 0x3c, 0x94, 0xcf, 0xd2, 0x0, 0x7f, 0xe7, 0x24, 0x23, 0x77, 0x58, 0xc6, 0xe1, 0xce, 0xbc, 0xcf, 0xc8, 0x36, 0x79, 0xfe, 0x4c, 0xae, 0x15, 0x7b, 0xce, 0xc2, 0xbf, 0x94, 0xec, 0xa2, 0xf, 0x94, 0x1f, 0x7c, 0x62, 0xd9, 0xf0, 0x8f, 0x95, 0xfc, 0xec, 0x8e, 0x7e, 0x89, 0x91, 0xfe, 0xba, 0x2c, 0x14, 0x1, 0x63, 0x4b, 0xb, 0xd9, 0xc1, 0xc3, 0xf2, 0x65, 0x16, 0x7e, 0xf7, 0x70, 0xb1, 0x17, 0x9a, 0xc3, 0xa3, 0x71, 0xaf, 0xe4, 0x6f, 0xe9, 0x11, 0x14, 0xfc, 0xb3, 0x2c, 0xe3, 0x98, 0xab, 0x37, 0xd3, 0xfd, 0x47, 0x25, 0xbb, 0xe8, 0xc0, 0xd6, 0x1d, 0x1d, 0x2e, 0x8d, 0xdf, 0xe7, 0xd9, 0x3c, 0xc0, 0x3f, 0xff, 0x37, 0x5d, 0xe, 0x59, 0xc1, 0xbe, 0x92, 0xdf, 0x49, 0x11, 0xc7, 0x19, 0x69, 0xe6, 0x1a, 0x83, 0x87, 0x65, 0x42, 0x16, 0x7e, 0x37, 0xdc, 0x98, 0x70, 0xd3, 0xff, 0xe4, 0xb1, 0xe1, 0xff, 0x23, 0xf, 0x9f, 0x7, 0x2c, 0x5a, 0x94, 0x67, 0xd4, 0xb7, 0x5c, 0xbb, 0x41, 0xaa, 0x71, 0x2d, 0x2b, 0xd9, 0x45, 0x7, 0xaf, 0x4b, 0xd5, 0xee, 0xe8, 0xb0, 0xea, 0x2e, 0xcf, 0xa3, 0x39, 0xc0, 0x29, 0xce, 0xf, 0x8b, 0x66, 0xa5, 0x65, 0x3, 0x97, 0x51, 0x5b, 0x7e, 0x44, 0x9c, 0x8c, 0xc4, 0x7c, 0x3, 0x12, 0x1d, 0x4e, 0xb7, 0xec, 0x91, 0xef, 0x19, 0x99, 0x9f, 0xa5, 0x7b, 0x80, 0xbb, 0x14, 0x59, 0x86, 0x36, 0xd7, 0x1c, 0x6a, 0xef, 0x86, 0xe6, 0xd1, 0xf3, 0x68, 0xcc, 0xb5, 0xd8, 0xce, 0x72, 0xed, 0x49, 0x23, 0x6f, 0x57, 0xf7, 0x3, 0x94, 0xec, 0xa2, 0x83, 0xd5, 0xdc, 0xe8, 0xdd, 0x38, 0x58, 0x1c, 0x77, 0x66, 0x3e, 0x68, 0x75, 0xcf, 0x48, 0xd5, 0xcc, 0x38, 0x85, 0xbf, 0x80, 0x7b, 0xc, 0x87, 0x96, 0x22, 0x26, 0x5a, 0xc0, 0xb5, 0x95, 0x8f, 0x84, 0x77, 0x2a, 0x2d, 0x5b, 0x37, 0x1e, 0xcf, 0xf2, 0x7d, 0x3c, 0x64, 0xe4, 0x41, 0x8f, 0x6b, 0xd7, 0x1b, 0x39, 0x39, 0x4f, 0x9e, 0x7, 0xd6, 0xa4, 0x2d, 0x3, 0x1d, 0x8a, 0x47, 0x46, 0x9e, 0x1e, 0x25, 0xbb, 0x68, 0xe1, 0x9, 0xa9, 0xda, 0x81, 0x1d, 0xb1, 0xbb, 0x1b, 0xc5, 0x49, 0x91, 0x8e, 0x2b, 0x90, 0x62, 0x8c, 0x9a, 0x9a, 0x6e, 0x96, 0x6b, 0x3f, 0x48, 0xf0, 0xbd, 0xb, 0xf3, 0x9, 0x37, 0x4a, 0xd5, 0x8e, 0x1d, 0xc7, 0x8b, 0xe3, 0x46, 0xdf, 0x2a, 0x4f, 0xe6, 0xa0, 0x2d, 0x89, 0xc4, 0x8d, 0xb7, 0xc4, 0x1e, 0x47, 0xb, 0x12, 0x88, 0x11, 0x5e, 0xe8, 0x61, 0xb9, 0x14, 0x90, 0x8, 0x4f, 0x8d, 0xf1, 0xb3, 0xc0, 0x77, 0x1c, 0x21, 0xf6, 0x1e, 0xa1, 0x65, 0xf4, 0x40, 0x2c, 0x50, 0xb2, 0x8b, 0x1f, 0xbe, 0x13, 0x27, 0x1b, 0xcb, 0x8d, 0x8e, 0x62, 0xcf, 0xde, 0x8a, 0x3, 0xd0, 0x39, 0x2, 0x45, 0xa3, 0xfb, 0x59, 0xae, 0x7d, 0x6b, 0x64, 0xa0, 0xe8, 0x41, 0xb6, 0x7e, 0xc2, 0x6b, 0x4f, 0x38, 0x88, 0xcf, 0x61, 0xf7, 0x98, 0x7f, 0x7f, 0xc4, 0x83, 0x86, 0x5b, 0x88, 0x1d, 0xd9, 0x97, 0x77, 0x49, 0x9a, 0x6c, 0xbf, 0x0, 0x81, 0x66, 0xf0, 0x3, 0xc4, 0x39, 0x5b, 0xcf, 0x8d, 0x62, 0xee, 0x9, 0x43, 0x62, 0xfa, 0x3c, 0x6e, 0x25, 0xd9, 0xdb, 0x30, 0x82, 0x4a, 0x70, 0x9d, 0x16, 0xb6, 0x22, 0xbc, 0xf8, 0xa7, 0xd8, 0x5b, 0xa, 0x9d, 0x98, 0x66, 0x51, 0x44, 0x15, 0x28, 0x1c, 0x7d, 0x5d, 0xec, 0x7, 0xd8, 0x7e, 0x6f, 0xe4, 0x4, 0x23, 0xa3, 0x44, 0x7b, 0x63, 0xfa, 0x9, 0x58, 0x34, 0x5e, 0x59, 0x6d, 0xbb, 0xf2, 0x79, 0x1c, 0x17, 0xe3, 0xef, 0x7f, 0x9d, 0x91, 0x63, 0x2c, 0xe3, 0x2f, 0x89, 0xbd, 0xd8, 0x3b, 0x5b, 0xc0, 0x7a, 0x87, 0xcb, 0x72, 0xb6, 0xe5, 0x1a, 0xd6, 0x3f, 0x3a, 0xaf, 0xdc, 0x46, 0x4b, 0x28, 0xe, 0x80, 0xdb, 0xfc, 0x1, 0x23, 0x17, 0x79, 0x5c, 0x47, 0x48, 0xa3, 0x46, 0x31, 0x4b, 0x25, 0xbb, 0xe8, 0x61, 0x2d, 0x1f, 0xf2, 0x7a, 0xcb, 0x35, 0x64, 0x68, 0x9d, 0x12, 0x93, 0x85, 0xe, 0x52, 0xc7, 0xa1, 0x96, 0x2d, 0x2c, 0xd7, 0x91, 0x11, 0x7, 0xd7, 0xda, 0xe7, 0xa2, 0xc9, 0x2a, 0x7e, 0xe3, 0x27, 0x6e, 0xaa, 0x5e, 0x1b, 0x3b, 0x3a, 0xd6, 0xfc, 0x87, 0xa4, 0x10, 0xb7, 0xb2, 0x97, 0xab, 0x8d, 0x5c, 0xe5, 0x31, 0x27, 0x57, 0x87, 0xe0, 0xfe, 0xa6, 0x19, 0x39, 0xda, 0xc8, 0x4c, 0x8f, 0xeb, 0x97, 0xf2, 0xd9, 0xb4, 0x8c, 0xf8, 0x73, 0xc0, 0x29, 0xf, 0xe8, 0x13, 0x7a, 0xa6, 0xc7, 0x75, 0x9c, 0x10, 0x1, 0xb7, 0x66, 0xb9, 0x92, 0x5d, 0xfc, 0x81, 0xd8, 0xc1, 0x70, 0xcb, 0x38, 0x32, 0x96, 0x90, 0xc4, 0x12, 0x65, 0x1f, 0x3e, 0xdc, 0x65, 0x63, 0x8c, 0x5c, 0x20, 0xf6, 0x14, 0xe3, 0xe9, 0xd4, 0xbc, 0x3f, 0xd2, 0x65, 0x10, 0x28, 0xe1, 0xc1, 0x53, 0xe0, 0xe5, 0x22, 0x42, 0x12, 0xcb, 0x35, 0x46, 0xde, 0x31, 0xb2, 0x47, 0xc, 0xbe, 0x2f, 0xd6, 0xd9, 0x30, 0x23, 0x7f, 0xb7, 0x5c, 0xc3, 0x86, 0x3a, 0xd8, 0xc3, 0xa2, 0xca, 0x5, 0xa6, 0xd2, 0xb2, 0x9e, 0x93, 0xc6, 0x1b, 0x32, 0x8a, 0xef, 0x51, 0x14, 0x81, 0xfa, 0xd9, 0xb1, 0xe2, 0xdd, 0xf3, 0xf6, 0x25, 0x2a, 0x63, 0xab, 0x6a, 0xfa, 0x83, 0x95, 0xec, 0xa2, 0xb, 0xd4, 0x95, 0xd8, 0xdc, 0x4d, 0x85, 0xe2, 0x14, 0x9d, 0xe, 0x8c, 0xd8, 0xf7, 0xd9, 0x41, 0x9c, 0x60, 0x3b, 0x2c, 0xa, 0xaf, 0x86, 0xae, 0x78, 0x89, 0xf, 0x31, 0xf2, 0x85, 0x3e, 0xfe, 0xac, 0x59, 0x78, 0xf7, 0xa6, 0xf9, 0xcc, 0x81, 0xe2, 0x24, 0x4e, 0xa0, 0x75, 0x53, 0x71, 0x44, 0xbf, 0x27, 0xa, 0xc7, 0x91, 0x6d, 0x7a, 0xad, 0xc7, 0xf5, 0x11, 0xf4, 0x30, 0x84, 0x9, 0x53, 0x8c, 0x1c, 0x2e, 0x55, 0x1b, 0x4d, 0x24, 0xd1, 0x8d, 0xd6, 0xcf, 0xb5, 0x12, 0x9d, 0x2c, 0x5a, 0x3c, 0x87, 0xdb, 0xa9, 0x60, 0xb5, 0x4f, 0x63, 0xd1, 0xd, 0x10, 0x7b, 0x2b, 0x35, 0x25, 0xbb, 0x18, 0x63, 0x2d, 0x9, 0xcd, 0x16, 0xb4, 0x6e, 0x44, 0xe2, 0x80, 0xf5, 0xd7, 0x34, 0xe4, 0xdf, 0x63, 0x3b, 0x5a, 0x9, 0xe3, 0xf9, 0x7d, 0xa, 0x3d, 0x3e, 0x87, 0x56, 0x40, 0x28, 0xa6, 0xfd, 0x4e, 0x1f, 0x7d, 0xd6, 0x80, 0x2e, 0xfc, 0xe7, 0x89, 0x13, 0xb, 0x5e, 0xef, 0xf1, 0x99, 0x96, 0x54, 0xbc, 0x90, 0xa5, 0xf8, 0x87, 0x88, 0x7d, 0x3f, 0x28, 0x4e, 0xef, 0xa6, 0xf1, 0x84, 0x20, 0xfb, 0x39, 0xac, 0xb5, 0x6c, 0x38, 0x75, 0x1, 0x2e, 0xcd, 0xb7, 0x3c, 0xae, 0x37, 0xa6, 0xb5, 0x8a, 0xba, 0xc0, 0x7e, 0x21, 0x7e, 0x6, 0xc8, 0x26, 0x47, 0x48, 0x2, 0x9e, 0x1a, 0x74, 0x46, 0xf2, 0x8a, 0xbf, 0xa3, 0x4, 0x3, 0x21, 0x9a, 0x9f, 0x6b, 0xfb, 0x8b, 0x94, 0xec, 0xa2, 0x8d, 0x25, 0xd4, 0xbe, 0x67, 0x78, 0x5c, 0x47, 0x70, 0x17, 0xae, 0xa6, 0x7d, 0x43, 0x78, 0xef, 0xd0, 0xde, 0x90, 0x41, 0x3a, 0x5a, 0x9c, 0xf8, 0xcf, 0x16, 0x69, 0xbe, 0xe3, 0x69, 0x46, 0x6, 0xa5, 0xd9, 0x70, 0x13, 0x96, 0xb1, 0x72, 0x5d, 0x1e, 0xbe, 0x1, 0x5d, 0x2b, 0xe0, 0xd6, 0xfc, 0x36, 0xcd, 0x67, 0x60, 0x8d, 0xc3, 0xc5, 0xf4, 0x9a, 0x84, 0xdf, 0x85, 0xb6, 0x13, 0xad, 0x39, 0x24, 0xdb, 0x78, 0xb9, 0x61, 0x51, 0xa8, 0x7c, 0x8e, 0xe4, 0x26, 0xfb, 0x32, 0x53, 0x2c, 0x13, 0xa7, 0xe, 0xf2, 0x9f, 0x69, 0x3e, 0xb3, 0x37, 0xbf, 0xe7, 0xb3, 0x46, 0x76, 0xb, 0x19, 0xc9, 0xf5, 0x25, 0x59, 0x23, 0x3e, 0xd7, 0xc9, 0xe3, 0x73, 0xc8, 0x44, 0x45, 0x1c, 0xf5, 0xec, 0xba, 0x10, 0x9d, 0x92, 0x5d, 0x3c, 0x80, 0xa0, 0x35, 0x7c, 0xf8, 0x5e, 0x41, 0xeb, 0x7d, 0x49, 0x28, 0xc8, 0xd4, 0xca, 0xf5, 0x29, 0x9, 0x2d, 0x68, 0x9d, 0xe1, 0xc5, 0x43, 0xb3, 0xdb, 0x8b, 0xb9, 0xf1, 0x78, 0xe1, 0x79, 0x71, 0x5a, 0xa2, 0x3d, 0x91, 0xe6, 0x33, 0x78, 0x19, 0x56, 0x5a, 0xc6, 0x5b, 0xeb, 0xd2, 0xf0, 0x15, 0x20, 0x32, 0x34, 0x30, 0x78, 0x35, 0xcd, 0x67, 0x10, 0xfb, 0x3a, 0x42, 0x1c, 0x77, 0x33, 0x48, 0xef, 0xa8, 0x34, 0x4a, 0x4c, 0x2e, 0xb0, 0x97, 0x38, 0x47, 0xc1, 0x7c, 0x26, 0x4e, 0x77, 0x14, 0x2f, 0x2f, 0xc2, 0xfd, 0x46, 0xce, 0x10, 0xe7, 0x2c, 0xc9, 0xb0, 0x63, 0x1d, 0x95, 0x5a, 0x28, 0xbd, 0xdf, 0xa7, 0x79, 0x2e, 0x27, 0xf0, 0x7b, 0xa3, 0x4c, 0x21, 0x97, 0x2d, 0x6, 0x1b, 0x73, 0xf, 0x0, 0x1, 0x23, 0x64, 0x71, 0x68, 0x35, 0xd6, 0x2b, 0xbc, 0x5, 0xa8, 0xfd, 0xac, 0xf3, 0x61, 0xb9, 0x4a, 0x76, 0xf1, 0x0, 0x82, 0xd6, 0x8, 0xec, 0xbe, 0xeb, 0x71, 0x1d, 0x6e, 0xcd, 0x4b, 0xe9, 0xd2, 0x80, 0x6b, 0x63, 0xfb, 0x2c, 0xde, 0x5b, 0x23, 0x2e, 0xe8, 0xbb, 0x49, 0xba, 0xaf, 0xf0, 0xc5, 0x6b, 0x52, 0xcd, 0x22, 0x3f, 0x85, 0x9f, 0x9b, 0x5b, 0xcd, 0xcf, 0x5f, 0xe5, 0x61, 0xd9, 0x9e, 0x2f, 0xd1, 0x8d, 0x23, 0x85, 0x15, 0x48, 0xd2, 0x80, 0xcb, 0x9, 0x6e, 0xcd, 0xe5, 0xd5, 0xec, 0x2b, 0x47, 0x90, 0x20, 0xf1, 0xcc, 0x6f, 0xa1, 0xe6, 0x5e, 0x98, 0x83, 0x7b, 0xde, 0x92, 0x44, 0xf0, 0x3c, 0xd7, 0xff, 0x15, 0x1c, 0xb3, 0x1, 0xb5, 0x74, 0x70, 0xa5, 0x9d, 0x2b, 0xc1, 0x9e, 0x6a, 0x10, 0x4, 0x10, 0x57, 0xec, 0x43, 0x12, 0xf1, 0x42, 0x43, 0x5a, 0x48, 0xa3, 0x68, 0x51, 0xd, 0xc8, 0x92, 0x32, 0x2, 0x2b, 0x6e, 0x47, 0x71, 0xc2, 0x15, 0xc9, 0x3d, 0xa0, 0x7f, 0x35, 0xfc, 0xf3, 0x0, 0x3d, 0x4, 0xef, 0xf8, 0x75, 0x13, 0x5, 0xfa, 0xfe, 0xc6, 0x6, 0x20, 0x85, 0xa3, 0xb9, 0xb1, 0x9c, 0xe7, 0xf1, 0x19, 0x58, 0x51, 0x8, 0x5a, 0x23, 0xd3, 0xf1, 0x65, 0x71, 0x82, 0xc1, 0xe8, 0xbf, 0xb7, 0xc8, 0xc7, 0xfb, 0xd8, 0x8e, 0xb, 0xbb, 0x2b, 0x35, 0xc8, 0x9e, 0x46, 0xb6, 0x16, 0xbb, 0xab, 0xd1, 0xd, 0xb8, 0xc9, 0x1e, 0x13, 0xc7, 0xbd, 0x99, 0xa9, 0xcb, 0xa2, 0x82, 0x1a, 0xab, 0xbb, 0xf6, 0xb, 0x1a, 0xe1, 0x4, 0x6e, 0x2, 0xb7, 0xd1, 0x2, 0x54, 0xd4, 0x1d, 0x98, 0xc7, 0x3b, 0xb9, 0x9, 0x5d, 0x4d, 0x85, 0x24, 0xdd, 0x3e, 0xb2, 0x27, 0x5, 0x4d, 0x8d, 0x91, 0x58, 0x81, 0xa4, 0xaa, 0x4f, 0xc5, 0xc9, 0xaa, 0x5d, 0xe8, 0x87, 0xc6, 0xee, 0x2, 0x36, 0xef, 0x9d, 0x8d, 0x74, 0xe6, 0xe6, 0x7f, 0xa8, 0xd8, 0xcb, 0x57, 0xdc, 0x98, 0x44, 0x12, 0x1f, 0x13, 0x71, 0x2f, 0xcf, 0x91, 0x24, 0xeb, 0x2b, 0xf8, 0x2e, 0xda, 0x80, 0x92, 0x91, 0x7e, 0x94, 0xdb, 0x68, 0x85, 0xc3, 0xca, 0x9a, 0x4c, 0xc5, 0xb1, 0xd2, 0x87, 0x7b, 0x69, 0xce, 0xe7, 0x0, 0xcf, 0x12, 0x62, 0xa3, 0x7, 0x66, 0xa8, 0x7c, 0x8e, 0x13, 0x27, 0x6, 0xfc, 0x8a, 0xdf, 0x93, 0xa3, 0x64, 0x17, 0x2f, 0xfc, 0x42, 0x8b, 0x6, 0xc9, 0x1e, 0x28, 0x32, 0x6d, 0x99, 0x66, 0x43, 0x18, 0x40, 0x99, 0xc5, 0x17, 0x7d, 0xa, 0x17, 0xda, 0x54, 0x6a, 0xed, 0xa5, 0xdc, 0x88, 0x52, 0x49, 0xaa, 0x92, 0x6b, 0xa6, 0x3e, 0xb5, 0x63, 0xf4, 0xab, 0x44, 0xc1, 0x37, 0x32, 0x29, 0xd1, 0xc5, 0x5, 0x35, 0x58, 0xed, 0x49, 0x76, 0x35, 0xf1, 0x1a, 0xfc, 0x40, 0x4d, 0xe, 0x44, 0x37, 0xa7, 0x16, 0xdf, 0x1b, 0xc4, 0x3d, 0x54, 0xaa, 0x9e, 0x6f, 0x7, 0x6b, 0x62, 0x8, 0x7f, 0xee, 0x22, 0x5d, 0x1e, 0xbe, 0x2, 0x64, 0x75, 0x12, 0xe7, 0x16, 0x73, 0xdf, 0xbb, 0x9a, 0xcf, 0x63, 0xa3, 0xeb, 0x2e, 0x1b, 0x33, 0x6d, 0xe7, 0x92, 0xec, 0xe6, 0x71, 0xed, 0xc1, 0xd, 0xbf, 0x98, 0xe3, 0xa5, 0x24, 0xd5, 0x4a, 0xcb, 0xfa, 0x4b, 0x70, 0xd, 0xd6, 0xe3, 0xfa, 0x83, 0x22, 0xd5, 0x81, 0x8a, 0x5c, 0x27, 0x7a, 0x2d, 0x76, 0xa6, 0x47, 0x21, 0x13, 0xac, 0xa6, 0x72, 0xf5, 0x6f, 0xae, 0xc3, 0xa8, 0xa3, 0x92, 0xdf, 0x5, 0xd6, 0xdb, 0x20, 0x12, 0x5f, 0x3a, 0x8b, 0x1a, 0x8a, 0xc0, 0xe9, 0x94, 0xef, 0xf9, 0x5c, 0xe7, 0x90, 0xf8, 0xb0, 0x17, 0x7c, 0x93, 0x66, 0x3f, 0x10, 0xfe, 0xec, 0x62, 0xce, 0x3f, 0xf6, 0x82, 0x3d, 0x38, 0xff, 0x3b, 0x72, 0x4f, 0xc8, 0xd4, 0xbb, 0xf2, 0x1d, 0x15, 0xf5, 0x27, 0xa5, 0x16, 0x65, 0x5, 0x4a, 0x76, 0xf9, 0x8b, 0x47, 0xe8, 0xb2, 0xb9, 0x96, 0x1b, 0x52, 0xba, 0xc5, 0xde, 0x91, 0x72, 0x1c, 0x5f, 0x94, 0xd, 0xdc, 0x68, 0xd0, 0x6f, 0x6e, 0xbd, 0x6b, 0x71, 0x57, 0x90, 0x40, 0x5b, 0x91, 0xf0, 0xa, 0xea, 0xb8, 0x86, 0xd0, 0x9, 0xe6, 0x7f, 0xe2, 0xd4, 0x6, 0xd6, 0x85, 0x8c, 0xf0, 0x42, 0xc2, 0x4d, 0x3a, 0xcc, 0x72, 0x6d, 0xad, 0xd8, 0xeb, 0xf5, 0x14, 0xfe, 0x0, 0x9b, 0xea, 0x68, 0x5a, 0xd2, 0x83, 0xa8, 0xc1, 0x67, 0xe2, 0xae, 0xdc, 0x81, 0x72, 0x60, 0xca, 0x58, 0x69, 0x8a, 0x5, 0x3e, 0x87, 0x7f, 0xb7, 0x79, 0x4, 0x10, 0x7b, 0x6e, 0x9a, 0xb2, 0xfe, 0x6a, 0xf3, 0x7c, 0x57, 0x71, 0xed, 0xdd, 0x29, 0xf6, 0x8e, 0x44, 0x51, 0xc7, 0x6c, 0x7a, 0x70, 0x1e, 0x15, 0x27, 0x36, 0x7e, 0x94, 0x54, 0x9f, 0x99, 0xdd, 0x9a, 0xd2, 0x3b, 0xe5, 0x7d, 0xc7, 0x33, 0x28, 0xe7, 0x7e, 0xb0, 0xc6, 0xf2, 0x3c, 0xda, 0x51, 0xb1, 0x68, 0x50, 0xcb, 0xbd, 0x60, 0x3a, 0x9f, 0x3, 0xde, 0xdf, 0xa5, 0x41, 0x4e, 0x88, 0xd7, 0x42, 0xd1, 0x58, 0x5e, 0xf4, 0x31, 0x9f, 0xda, 0x1a, 0x88, 0xef, 0x12, 0x6e, 0x46, 0xd5, 0x3d, 0xd7, 0x4, 0x35, 0x31, 0x48, 0xd7, 0x80, 0xee, 0xb, 0x2f, 0xc, 0xe2, 0xa, 0x88, 0xa1, 0x8c, 0x35, 0xf2, 0xa3, 0x4f, 0x3f, 0x17, 0x41, 0xec, 0x5d, 0xa5, 0xaa, 0x3b, 0x33, 0x91, 0xa5, 0xf9, 0xae, 0x9f, 0xe1, 0x58, 0x1c, 0x81, 0x4d, 0x11, 0x6e, 0x27, 0x24, 0xaf, 0x1c, 0x60, 0xe4, 0xcf, 0xe2, 0xc4, 0xf6, 0x6a, 0x7a, 0x92, 0x7c, 0x61, 0xa, 0x51, 0x76, 0xb, 0xe8, 0x5e, 0x57, 0x90, 0x0, 0x9e, 0xa1, 0x7, 0x24, 0xee, 0x40, 0x98, 0x2, 0xa5, 0x15, 0x7b, 0xf3, 0xb9, 0x9c, 0x26, 0xde, 0x31, 0x4b, 0x1b, 0xf, 0x24, 0x2d, 0xb3, 0xce, 0x3e, 0xde, 0x13, 0xc8, 0xf3, 0xb, 0x7a, 0x5, 0x5e, 0x15, 0xef, 0xc4, 0x1a, 0xdf, 0xc9, 0x6e, 0x83, 0x54, 0x6d, 0xfb, 0x53, 0x2a, 0x8a, 0xb8, 0xe0, 0x43, 0xa, 0xb4, 0xb5, 0xf3, 0xb8, 0x19, 0x65, 0xbb, 0x7b, 0x3d, 0x5c, 0x53, 0x53, 0x48, 0x72, 0x6f, 0x4a, 0x30, 0x2e, 0x45, 0xac, 0xd9, 0x93, 0xa9, 0x29, 0x9e, 0x23, 0x1b, 0x5d, 0xb8, 0xd, 0x24, 0x3b, 0xf1, 0x3a, 0x5b, 0x46, 0xe8, 0xea, 0x3c, 0x5b, 0x6b, 0xf0, 0xc, 0x7c, 0x40, 0x41, 0x3c, 0xf, 0x9d, 0x6e, 0x8e, 0xa4, 0xe2, 0xd4, 0x36, 0x87, 0xf7, 0x5, 0x8b, 0xe1, 0x4b, 0xae, 0x3d, 0xa4, 0xb9, 0xe7, 0x63, 0xe3, 0xf0, 0x71, 0x94, 0x9b, 0xf9, 0x9e, 0x20, 0x81, 0xa8, 0xbb, 0x64, 0xb7, 0xe, 0x17, 0x7b, 0xc0, 0xc7, 0x46, 0x9e, 0x16, 0xa7, 0x20, 0x3e, 0xab, 0x65, 0x1d, 0x5, 0x34, 0x71, 0xfb, 0xa4, 0x30, 0x78, 0x5, 0x6f, 0x46, 0x11, 0x2f, 0x8c, 0xa1, 0x40, 0x43, 0xeb, 0xcb, 0x4d, 0x68, 0x1f, 0xf1, 0xbf, 0xc3, 0x42, 0x19, 0x37, 0xf9, 0x9, 0xb4, 0xdc, 0xf0, 0xe7, 0x34, 0xc9, 0xce, 0x81, 0x97, 0x50, 0xdc, 0xae, 0xe1, 0x86, 0x96, 0x4c, 0x8c, 0x81, 0x3b, 0x67, 0x59, 0x16, 0x7e, 0xf7, 0x53, 0xd4, 0x84, 0x1b, 0xf3, 0xdf, 0x20, 0xd8, 0x17, 0xf3, 0x78, 0xbd, 0xc1, 0x62, 0x7f, 0x90, 0x82, 0xf8, 0x4d, 0x17, 0x71, 0x12, 0x96, 0xb0, 0xd7, 0x20, 0xbe, 0xd3, 0x28, 0x40, 0xab, 0x7b, 0x3d, 0x15, 0x2a, 0x28, 0x79, 0xa3, 0x53, 0xd6, 0xa0, 0xc2, 0x79, 0x17, 0x86, 0x53, 0x60, 0xed, 0xe1, 0x34, 0x11, 0x94, 0x94, 0xa0, 0xc4, 0x67, 0x73, 0xf1, 0xd7, 0xab, 0xb7, 0x8e, 0x96, 0x25, 0xba, 0xec, 0x7c, 0xca, 0xbf, 0x2f, 0xcb, 0xd5, 0x17, 0x4f, 0x9c, 0x34, 0x68, 0x88, 0x3e, 0xfe, 0xfc, 0x44, 0x3d, 0x5a, 0x3f, 0xfb, 0x8b, 0x73, 0x8c, 0xe, 0x12, 0x4b, 0x5a, 0xd3, 0xc5, 0xd1, 0x9c, 0x1b, 0x11, 0x12, 0x59, 0x8a, 0x52, 0x88, 0xe4, 0x27, 0x8e, 0x63, 0x33, 0x41, 0xcc, 0x63, 0xd, 0xc7, 0x20, 0xf3, 0x68, 0xc1, 0x21, 0xb0, 0x3d, 0x93, 0xd7, 0x2a, 0x74, 0x9a, 0x15, 0x2e, 0x14, 0x72, 0x7d, 0x81, 0xfc, 0x76, 0x11, 0x27, 0x99, 0xa1, 0xd, 0xd7, 0x5d, 0x33, 0x71, 0xd2, 0xe3, 0x37, 0xe7, 0xda, 0x81, 0xe2, 0x90, 0xda, 0xe8, 0xbb, 0x84, 0x6b, 0x2d, 0xf9, 0xf7, 0x1f, 0xb9, 0xce, 0xf0, 0xe7, 0xf, 0x5c, 0x7f, 0xd3, 0x69, 0xc5, 0x2d, 0xe3, 0x9a, 0x55, 0x64, 0x6, 0xcc, 0x39, 0x92, 0x4b, 0xe0, 0x3e, 0xee, 0x44, 0x45, 0xb1, 0x15, 0xc7, 0x93, 0xe5, 0x9, 0x5b, 0xc8, 0xc6, 0xe, 0x27, 0x6b, 0x52, 0x3c, 0x17, 0x2b, 0xb9, 0x1f, 0x2c, 0xa3, 0x15, 0x8d, 0x78, 0x2b, 0xe2, 0xa0, 0x70, 0x13, 0xc3, 0x6d, 0x9c, 0xb5, 0x6, 0xf, 0x6b, 0xd6, 0xae, 0x93, 0xa1, 0x83, 0x6, 0xca, 0x3e, 0x7b, 0x54, 0x8d, 0xc2, 0xfc, 0xbf, 0x0, 0x3, 0x0, 0x73, 0x78, 0xf7, 0xf2, 0x7e, 0x74, 0x3a, 0x48, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)parse_logo2x_png { + return [NSData dataWithBytes:parse_logo2x_png length:sizeof(parse_logo2x_png)]; + } + + const unsigned char parse_logo3x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x2, 0x99, 0x0, 0x0, 0x0, 0xcc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x38, 0x73, 0xb8, 0x3f, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x0, 0x0, 0x3, 0x23, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x31, 0x34, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x31, 0x34, 0x38, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x33, 0x2f, 0x30, 0x33, 0x2f, 0x31, 0x33, 0x2d, 0x31, 0x32, 0x3a, 0x30, 0x39, 0x3a, 0x31, 0x35, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x29, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x41, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x42, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x38, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x34, 0x31, 0x30, 0x31, 0x39, 0x34, 0x30, 0x39, 0x34, 0x30, 0x30, 0x43, 0x31, 0x31, 0x45, 0x34, 0x41, 0x33, 0x35, 0x31, 0x39, 0x31, 0x30, 0x33, 0x32, 0x36, 0x42, 0x31, 0x39, 0x32, 0x31, 0x42, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, 0x72, 0x22, 0x3f, 0x3e, 0x66, 0x5f, 0x10, 0x71, 0x0, 0x0, 0x22, 0xe8, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x9d, 0x4d, 0x72, 0x1b, 0xbd, 0x11, 0x86, 0x3b, 0x29, 0xef, 0xa3, 0xef, 0x4, 0xdf, 0xf8, 0x4, 0xa6, 0x4f, 0xa0, 0xd1, 0x9, 0x4c, 0xaf, 0x93, 0x2a, 0x53, 0xfb, 0xa4, 0x2c, 0xad, 0x92, 0x9d, 0xc8, 0x5d, 0x76, 0x92, 0x2b, 0x7, 0x10, 0x5d, 0x95, 0xbd, 0xe8, 0x13, 0x68, 0x74, 0x2, 0x53, 0x27, 0xf0, 0xf8, 0x4, 0x56, 0x6e, 0x10, 0x21, 0x2, 0x22, 0x98, 0xa6, 0x8, 0x60, 0x7e, 0x31, 0xc0, 0xf3, 0x54, 0x4d, 0x49, 0x32, 0x29, 0x99, 0x83, 0x1, 0xba, 0xdf, 0x6e, 0x0, 0x8d, 0x3f, 0xfc, 0xf9, 0xaf, 0x7f, 0x3f, 0x12, 0x91, 0x8f, 0x2, 0x7d, 0x70, 0xa7, 0xbf, 0x6e, 0x1f, 0xaf, 0x7, 0x9a, 0x3, 0x0, 0x0, 0x0, 0x52, 0xe0, 0xdf, 0xff, 0xfa, 0xa7, 0xf3, 0x3d, 0xaf, 0x1e, 0xaf, 0xeb, 0xc7, 0x6b, 0x4e, 0x73, 0xd, 0x42, 0xa5, 0x5, 0xe7, 0xbd, 0xfe, 0xba, 0xa5, 0x49, 0x0, 0x0, 0x0, 0x20, 0x45, 0x94, 0xc8, 0x3c, 0xa2, 0x19, 0x6, 0xa3, 0xd4, 0x97, 0x41, 0x65, 0x37, 0x37, 0xf2, 0x94, 0xf1, 0xdc, 0x8, 0xd9, 0x4e, 0x0, 0x0, 0x0, 0x48, 0x84, 0x3f, 0xd2, 0x4, 0xa3, 0xa2, 0x4, 0xfe, 0x42, 0x9e, 0xb2, 0xc9, 0x3f, 0x1e, 0xaf, 0x1b, 0xfd, 0x33, 0x0, 0x0, 0x0, 0x0, 0x22, 0x13, 0x3a, 0x63, 0x6e, 0x9, 0xce, 0xcb, 0xc7, 0xab, 0xa0, 0x49, 0x0, 0x0, 0x0, 0x0, 0x91, 0x9, 0x5d, 0xa1, 0x32, 0x9c, 0x67, 0x8f, 0xd7, 0x37, 0x2d, 0x3a, 0x11, 0x9b, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x4e, 0x59, 0x20, 0x36, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0x7d, 0x8b, 0x4d, 0x35, 0x8d, 0xce, 0x66, 0x2d, 0x0, 0x0, 0x0, 0x88, 0x9a, 0x57, 0x8e, 0xd7, 0xab, 0xc7, 0xeb, 0x84, 0x66, 0xa, 0xa6, 0xd4, 0x5f, 0x8b, 0xc7, 0xeb, 0xf7, 0xc7, 0x6b, 0xa6, 0xbf, 0x9f, 0x75, 0xf0, 0xb7, 0xcf, 0xb4, 0xe0, 0x3c, 0x7f, 0xbc, 0xd6, 0x34, 0x35, 0x0, 0x0, 0x0, 0x4c, 0x51, 0x64, 0x42, 0x33, 0xaa, 0x17, 0xfe, 0xfd, 0x48, 0xb, 0xd0, 0x63, 0xfd, 0xb5, 0xa9, 0xe8, 0x54, 0x7f, 0x47, 0x4d, 0x9f, 0x7f, 0x78, 0xbc, 0x4e, 0x1f, 0xaf, 0x9a, 0x26, 0x7, 0x0, 0x0, 0x80, 0x98, 0x60, 0xba, 0x7c, 0x58, 0x4c, 0x5d, 0x4c, 0x95, 0x85, 0x7c, 0xfb, 0x78, 0xbd, 0xd6, 0xdf, 0x37, 0x2d, 0xca, 0xae, 0x84, 0xea, 0x57, 0xa1, 0x98, 0x3e, 0x0, 0x0, 0x0, 0x20, 0x32, 0xc1, 0xa2, 0x7e, 0xbc, 0xae, 0xb4, 0xe0, 0x54, 0xd7, 0xba, 0xc1, 0xdf, 0x50, 0x59, 0x4d, 0x55, 0x5f, 0xf3, 0x92, 0xe6, 0x4, 0x0, 0x0, 0x0, 0x44, 0x26, 0xec, 0xa2, 0xb2, 0x99, 0x6a, 0xea, 0x5b, 0x65, 0x37, 0x97, 0x12, 0x7e, 0xfa, 0x8f, 0x5a, 0xab, 0xf9, 0x55, 0xd8, 0x14, 0x4, 0x0, 0x0, 0x0, 0x88, 0x4c, 0xd8, 0x43, 0xfd, 0x78, 0xad, 0xb4, 0xd8, 0xbc, 0xa, 0xfc, 0x5d, 0xb5, 0xc6, 0xf3, 0x9b, 0x74, 0xb3, 0xc1, 0x8, 0x0, 0x0, 0x0, 0x0, 0x91, 0x99, 0x20, 0x2a, 0x93, 0x79, 0xae, 0xc5, 0x66, 0x15, 0xf0, 0x7b, 0x2a, 0x93, 0x79, 0x2b, 0x3f, 0x9f, 0x91, 0xe, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x9f, 0xa8, 0xe5, 0xa9, 0x8c, 0xd4, 0x7b, 0xf1, 0x9f, 0x42, 0x37, 0x42, 0x73, 0x41, 0xf3, 0x1, 0x0, 0x0, 0x0, 0x22, 0x13, 0xe, 0xa1, 0x76, 0xa5, 0xbf, 0xd6, 0x5f, 0x7d, 0xb9, 0x46, 0x68, 0x2, 0x0, 0x0, 0x0, 0x22, 0x13, 0x5c, 0xa8, 0x4c, 0xa6, 0xca, 0x68, 0x9e, 0x23, 0x34, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0x5d, 0x63, 0xca, 0x1e, 0xf9, 0x4e, 0x9f, 0x23, 0x34, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0x5e, 0xa8, 0x92, 0x47, 0xaf, 0xc5, 0xbf, 0x90, 0xbb, 0xaa, 0xa3, 0xc9, 0xae, 0x73, 0x0, 0x0, 0x0, 0x40, 0x64, 0x82, 0x13, 0x95, 0xc9, 0x54, 0x9b, 0x82, 0x7c, 0xd6, 0x69, 0x9a, 0xcd, 0x40, 0x5, 0xcd, 0x6, 0x0, 0x0, 0x0, 0x88, 0x4c, 0xf0, 0x11, 0x9a, 0x6a, 0x9d, 0xe6, 0xda, 0x53, 0x68, 0xde, 0x8, 0x5, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0x9e, 0x9c, 0x7a, 0xa, 0x4d, 0x35, 0x65, 0x7e, 0x4d, 0x73, 0x1, 0x0, 0x0, 0x0, 0x22, 0x13, 0xba, 0x16, 0x9a, 0x73, 0x79, 0x3a, 0x86, 0x12, 0x0, 0x0, 0x0, 0x0, 0x91, 0x9, 0xde, 0x42, 0xb3, 0xf2, 0x78, 0x1f, 0x1b, 0x81, 0x0, 0x0, 0x0, 0x0, 0x91, 0x9, 0x41, 0xa8, 0x35, 0x9a, 0x3e, 0xbb, 0xce, 0xd5, 0xb4, 0x39, 0xeb, 0x33, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0x5e, 0x98, 0xcd, 0x40, 0xae, 0x3a, 0x9a, 0x2a, 0x93, 0x79, 0x41, 0x73, 0x1, 0x0, 0x0, 0x0, 0x22, 0x13, 0x7c, 0xa9, 0xb5, 0xd0, 0x74, 0x71, 0x26, 0x4c, 0x9b, 0x3, 0x0, 0x0, 0x0, 0x22, 0x13, 0x2, 0xa8, 0x1e, 0xaf, 0xa5, 0xc7, 0xfb, 0xd8, 0x6d, 0xe, 0x0, 0x0, 0x0, 0x88, 0x4c, 0x8, 0x62, 0x25, 0xee, 0x8d, 0x40, 0x2a, 0x93, 0xc9, 0x6e, 0x73, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0x10, 0x6a, 0xc7, 0xb9, 0x6b, 0x7d, 0xa6, 0x5a, 0x9b, 0xc9, 0x26, 0x20, 0x0, 0x0, 0x0, 0x40, 0x64, 0x82, 0x37, 0xb5, 0x3c, 0x65, 0x34, 0xf, 0xa1, 0x4, 0xe6, 0x47, 0x9a, 0xa, 0x0, 0x0, 0x0, 0x10, 0x99, 0x10, 0xc2, 0x95, 0xb8, 0xa7, 0xcd, 0x97, 0xc2, 0xd9, 0xe6, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x40, 0x4e, 0x3d, 0xde, 0x43, 0x49, 0x23, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0x10, 0xb5, 0x3c, 0x65, 0x34, 0xf, 0xb1, 0x10, 0xb2, 0x99, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x40, 0xd4, 0xda, 0x4c, 0xd7, 0x26, 0xa0, 0xf, 0x34, 0x13, 0x0, 0x0, 0x0, 0x20, 0x32, 0x21, 0x4, 0x25, 0x30, 0x5d, 0xd9, 0x4c, 0x55, 0xce, 0x88, 0x9d, 0xe6, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x20, 0x3e, 0xc9, 0xe1, 0x6c, 0xa6, 0x12, 0x98, 0x73, 0x9a, 0x9, 0x0, 0x0, 0x0, 0x10, 0x99, 0x10, 0x82, 0x4f, 0x36, 0x93, 0x72, 0x46, 0x0, 0x0, 0x0, 0x80, 0xc8, 0x84, 0x60, 0x3e, 0x39, 0x5e, 0x9f, 0x9, 0x67, 0x9a, 0x3, 0x0, 0x0, 0x0, 0x22, 0x13, 0x2, 0x51, 0xd9, 0xcc, 0xb5, 0xe3, 0x3d, 0x64, 0x33, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0xc1, 0xb8, 0xb2, 0x99, 0xac, 0xcb, 0x4, 0x0, 0x0, 0x0, 0x44, 0x26, 0x4, 0xb3, 0x95, 0xa7, 0xda, 0x99, 0x2f, 0xc1, 0x6, 0x20, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0x23, 0x5c, 0xd9, 0xcc, 0x63, 0x9a, 0x8, 0x0, 0x0, 0x0, 0x10, 0x99, 0x10, 0xca, 0xc6, 0xf1, 0x3a, 0x99, 0x4c, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0x30, 0xb5, 0x3c, 0x4d, 0x9b, 0xbf, 0x44, 0x21, 0x1c, 0x33, 0x9, 0x0, 0x0, 0x0, 0x88, 0x4c, 0x68, 0x0, 0xd9, 0x4c, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0xe7, 0x7c, 0x71, 0xbc, 0xce, 0xba, 0x4c, 0x0, 0x0, 0x0, 0x40, 0x64, 0x42, 0x30, 0x6a, 0xba, 0xfc, 0xd0, 0x31, 0x93, 0x14, 0x65, 0x7, 0x0, 0x0, 0x0, 0x44, 0x26, 0x34, 0x16, 0x9a, 0x2f, 0x51, 0x8, 0xeb, 0x32, 0x1, 0x0, 0x0, 0x0, 0x91, 0x9, 0xd, 0xa8, 0x1c, 0xaf, 0x23, 0x32, 0x1, 0x0, 0x0, 0xc0, 0x9b, 0x57, 0x34, 0x1, 0x68, 0xee, 0x1c, 0xaf, 0x1f, 0x7b, 0x8, 0x51, 0x78, 0x42, 0x15, 0xb1, 0xf7, 0x5d, 0x62, 0x50, 0xcb, 0xe1, 0x82, 0xf8, 0x0, 0xd0, 0x9e, 0xa2, 0x41, 0xa0, 0xec, 0x5a, 0x46, 0x4, 0xf4, 0x23, 0x6c, 0x3b, 0x22, 0x13, 0x2, 0xc, 0xaa, 0x6b, 0x70, 0xc1, 0xaf, 0x42, 0xf2, 0xd8, 0x32, 0x3c, 0x33, 0xfd, 0xef, 0x4d, 0xa9, 0xb4, 0x53, 0x53, 0xcf, 0xe2, 0x5e, 0xdc, 0xe5, 0xa5, 0x0, 0xe0, 0x89, 0x99, 0x1e, 0x83, 0x6f, 0xac, 0xf1, 0x18, 0x12, 0xec, 0xf9, 0xa, 0x4e, 0xf3, 0xf5, 0xce, 0xfa, 0x19, 0xd2, 0xed, 0x4b, 0xc6, 0xa6, 0x97, 0x2d, 0xff, 0x66, 0x65, 0x7d, 0xfd, 0xae, 0x6d, 0x7b, 0x85, 0xc8, 0x84, 0x9c, 0x70, 0x45, 0xec, 0xb9, 0x8b, 0xcc, 0x42, 0x1b, 0x9a, 0x63, 0xfd, 0xb5, 0x8f, 0xf6, 0x30, 0x86, 0x6c, 0xbe, 0xc7, 0x40, 0x55, 0xda, 0xb1, 0x55, 0x74, 0xd5, 0x5e, 0x32, 0x11, 0x29, 0x67, 0xad, 0x8c, 0xe0, 0x4a, 0x45, 0x10, 0x1d, 0x59, 0x63, 0x71, 0xd6, 0x81, 0x0, 0xf0, 0x15, 0x1e, 0xf2, 0xc2, 0xff, 0x55, 0x59, 0x81, 0x61, 0x25, 0x64, 0xaf, 0xa6, 0x86, 0x6d, 0xd7, 0xcb, 0x1e, 0xff, 0x8f, 0x7d, 0xfd, 0xc7, 0xf4, 0x9d, 0x3b, 0x2b, 0xc9, 0x80, 0xc8, 0x84, 0x64, 0xa9, 0xe, 0xc, 0xb2, 0x1c, 0x77, 0x98, 0x2b, 0xb1, 0xf7, 0xae, 0x47, 0x51, 0x19, 0x62, 0xa0, 0x4a, 0x2b, 0x18, 0x50, 0x75, 0x4d, 0xbf, 0x88, 0xbb, 0xbe, 0x29, 0xfc, 0xda, 0x87, 0x6f, 0x65, 0x7f, 0xb6, 0x59, 0x19, 0xfa, 0x93, 0x9, 0x19, 0x79, 0x3b, 0xdb, 0xb2, 0x9b, 0xb1, 0x7b, 0x69, 0xc, 0x9f, 0x3e, 0x5e, 0xeb, 0x9, 0xb, 0x1, 0x33, 0x16, 0x67, 0x11, 0x7e, 0x36, 0xbb, 0xcd, 0x6b, 0x6d, 0x4b, 0x19, 0xa3, 0xf1, 0x6, 0x5d, 0x73, 0x2d, 0x2c, 0xe7, 0x91, 0xf4, 0x9d, 0x33, 0xcb, 0xe, 0x19, 0xfb, 0x9e, 0x44, 0x50, 0x88, 0xc8, 0x4, 0x9b, 0x7, 0x47, 0xf6, 0x20, 0x27, 0x61, 0x39, 0x8f, 0xf4, 0x9e, 0xd5, 0x67, 0x5a, 0xe8, 0xcb, 0x8, 0xce, 0xcf, 0x42, 0x86, 0xd3, 0x87, 0x77, 0x7, 0x9e, 0xe9, 0x4c, 0x5f, 0x55, 0xa4, 0xcf, 0xbc, 0x94, 0xf6, 0x99, 0xbb, 0xf, 0x13, 0x13, 0x99, 0xea, 0x5e, 0x3f, 0x46, 0x3c, 0x16, 0xf, 0x89, 0x18, 0x33, 0x46, 0xc5, 0x12, 0xd, 0x6b, 0x86, 0xe0, 0xa8, 0x63, 0x68, 0xae, 0xfb, 0xd3, 0x2c, 0xf2, 0x3e, 0xaf, 0xae, 0xa5, 0xe, 0x56, 0x54, 0xdf, 0xf9, 0x24, 0x13, 0xce, 0x8e, 0xb3, 0xbb, 0x1c, 0x6c, 0xb6, 0x1e, 0x3, 0x35, 0x45, 0xd4, 0xa0, 0xbe, 0x7e, 0xbc, 0x7e, 0x3c, 0x5e, 0x37, 0xda, 0x39, 0x4c, 0xe1, 0x5e, 0x8d, 0xe0, 0x54, 0xd9, 0xb9, 0xaf, 0x96, 0x53, 0x83, 0x34, 0x9c, 0xe2, 0x42, 0xf7, 0x47, 0xd3, 0x2f, 0xcf, 0x64, 0x98, 0xa9, 0xe1, 0xb1, 0xef, 0x5b, 0xdd, 0xe7, 0x37, 0xab, 0x4f, 0x4f, 0xdd, 0xee, 0xcc, 0x2d, 0xfb, 0x72, 0x2d, 0xd4, 0x1d, 0x1e, 0x92, 0x72, 0xc2, 0x6d, 0x5f, 0x58, 0x63, 0xe1, 0x76, 0xaa, 0xf6, 0x1d, 0x91, 0x9, 0xa1, 0x62, 0x2c, 0x35, 0x27, 0xfe, 0x35, 0x11, 0x67, 0x66, 0x84, 0xf2, 0x37, 0xc4, 0xe6, 0xe4, 0x5, 0xc9, 0x8d, 0xe5, 0x14, 0x73, 0x39, 0xd2, 0xb5, 0xb0, 0xfa, 0xef, 0xa5, 0xa4, 0xb9, 0x6, 0xdc, 0xb6, 0x39, 0xb7, 0x8c, 0xd3, 0x5e, 0x49, 0xad, 0x9d, 0x6d, 0xb1, 0x7c, 0x31, 0x25, 0x5f, 0x85, 0xc8, 0x4, 0x9b, 0xff, 0x64, 0xe2, 0xcc, 0x2e, 0xb5, 0x33, 0x4b, 0x31, 0xab, 0x60, 0x3b, 0x6b, 0xce, 0x9c, 0x9f, 0x8e, 0xf8, 0xb8, 0xd0, 0xcf, 0xec, 0x26, 0xb3, 0xe7, 0x76, 0xb4, 0x13, 0x1c, 0xe5, 0xb2, 0x2c, 0xa7, 0x24, 0x28, 0xec, 0x4d, 0x5c, 0xa6, 0x6a, 0xdb, 0xcd, 0x78, 0x59, 0x5a, 0x41, 0x68, 0xf4, 0xc1, 0x18, 0x6b, 0x32, 0xc1, 0x26, 0xe5, 0x72, 0x1c, 0x66, 0x7d, 0xd7, 0xa2, 0xe7, 0xf6, 0x53, 0xeb, 0x24, 0x2b, 0x4b, 0xb4, 0xbf, 0xd4, 0xa6, 0xc7, 0x3b, 0xe, 0xa7, 0xcb, 0x72, 0x2b, 0x46, 0x6c, 0xde, 0xe8, 0xcf, 0xa2, 0x36, 0x7c, 0xd4, 0x74, 0xef, 0x28, 0x1d, 0x86, 0xea, 0x93, 0x67, 0x19, 0x89, 0x2b, 0x9b, 0x8b, 0x1e, 0xef, 0xdd, 0x8c, 0xc5, 0x7a, 0xa7, 0xef, 0xfb, 0xd4, 0x3, 0xb6, 0x6d, 0xc6, 0x91, 0xf4, 0x7b, 0xe2, 0x99, 0x9, 0xa, 0x55, 0x3f, 0x38, 0x17, 0xd6, 0x56, 0xb7, 0x11, 0x97, 0x17, 0x3d, 0x3e, 0xa7, 0x4a, 0x7e, 0x2e, 0x59, 0x75, 0xc8, 0xb6, 0xab, 0x3e, 0xf3, 0xc6, 0xfa, 0x7e, 0xd6, 0x83, 0x7d, 0x37, 0xf7, 0xac, 0xae, 0xab, 0xc7, 0x6b, 0x25, 0x91, 0x6e, 0x5a, 0x44, 0x64, 0x42, 0xe, 0x19, 0x83, 0xb, 0xe9, 0x7e, 0x2d, 0xdb, 0x56, 0x1b, 0x9e, 0x7b, 0xfd, 0x7d, 0xa8, 0x40, 0xb7, 0x9d, 0xc9, 0x6a, 0xc7, 0xb1, 0x15, 0xf2, 0x5c, 0x52, 0x63, 0xd6, 0xc1, 0xfd, 0x7f, 0xd3, 0xd1, 0xef, 0x8a, 0xee, 0x80, 0xb8, 0x8c, 0x64, 0x4c, 0x76, 0x95, 0x85, 0xa9, 0xad, 0xf1, 0x77, 0x27, 0xed, 0xb, 0x60, 0x57, 0xe, 0x41, 0x68, 0xc6, 0xe6, 0xcc, 0x1a, 0xab, 0x5d, 0x5, 0xc1, 0x6a, 0x6a, 0x77, 0xa3, 0xc5, 0x26, 0x41, 0xe1, 0x38, 0xf6, 0xdd, 0x24, 0x9, 0x4c, 0x7f, 0x6a, 0x5a, 0xda, 0x6c, 0x73, 0xe0, 0x39, 0x17, 0x5a, 0x84, 0x96, 0x1d, 0x7d, 0xee, 0xb3, 0x1d, 0xb1, 0x89, 0xc8, 0x4, 0x98, 0xa0, 0xf1, 0xa9, 0xb5, 0xe1, 0xe8, 0xbb, 0x9e, 0x99, 0x71, 0x98, 0x1b, 0x4b, 0x90, 0xd8, 0xe5, 0x36, 0x9a, 0x8a, 0x92, 0xa5, 0x6e, 0xb, 0xb2, 0x9a, 0xe3, 0xa2, 0x9c, 0xc1, 0xe5, 0x40, 0xe2, 0xb2, 0xda, 0xe9, 0x57, 0xf7, 0x11, 0xdc, 0xff, 0xa5, 0x3c, 0x97, 0x6b, 0x69, 0x23, 0x2, 0xbe, 0xc8, 0xf0, 0x35, 0x29, 0x8d, 0x80, 0xad, 0x76, 0x2, 0x86, 0xb2, 0xc3, 0xa0, 0x70, 0xae, 0xff, 0xce, 0x4a, 0x8b, 0x6, 0x78, 0x39, 0x50, 0xbb, 0x94, 0x6e, 0x66, 0xa6, 0x6c, 0xbb, 0xde, 0xf7, 0x6c, 0x9e, 0x6d, 0xdf, 0x57, 0x96, 0xaf, 0x32, 0xf6, 0x7d, 0xd6, 0xa2, 0x3d, 0x96, 0xba, 0x3d, 0x4e, 0x25, 0xa2, 0x8c, 0x38, 0x22, 0x13, 0x52, 0xa3, 0xd0, 0xe2, 0x72, 0xd1, 0x91, 0x41, 0xf8, 0xac, 0xd, 0xc2, 0x58, 0xc2, 0x4c, 0x39, 0xd5, 0xb5, 0xbe, 0x4e, 0xf5, 0x7d, 0x7d, 0x68, 0x28, 0x9e, 0xd5, 0xef, 0xa8, 0xc5, 0xf0, 0xef, 0x85, 0x69, 0xb9, 0xa1, 0x99, 0x69, 0xa7, 0x58, 0x76, 0xfc, 0x77, 0x6b, 0xcb, 0x71, 0xc5, 0x7c, 0x4a, 0x94, 0x1a, 0x97, 0x37, 0xd, 0x9d, 0x68, 0xcc, 0xb5, 0x61, 0xcd, 0x67, 0xdb, 0x58, 0xf7, 0xa9, 0x9e, 0xb1, 0x29, 0x83, 0xd6, 0x46, 0x40, 0x1d, 0xeb, 0x31, 0xcf, 0xd1, 0x96, 0xbf, 0xa, 0xf1, 0xeb, 0x96, 0x81, 0x9a, 0xdd, 0x9f, 0xc6, 0x6e, 0xdf, 0x4a, 0x5f, 0x2b, 0x79, 0xae, 0xe1, 0xf9, 0xa1, 0xe1, 0x58, 0x51, 0xbf, 0x6f, 0x32, 0xe2, 0x51, 0xf4, 0x1d, 0x36, 0xfe, 0x40, 0x6a, 0x91, 0x6d, 0xdb, 0x85, 0xf4, 0xb5, 0xce, 0x20, 0xbc, 0x7e, 0xbc, 0xde, 0xea, 0xef, 0xeb, 0x88, 0xee, 0x53, 0x89, 0xcd, 0x13, 0xfd, 0xf9, 0xd6, 0xd, 0xdb, 0x49, 0x19, 0xa1, 0x33, 0xba, 0xcc, 0x60, 0x5c, 0x68, 0x71, 0xdf, 0x85, 0xc0, 0x34, 0xfd, 0x53, 0x5, 0xa, 0xbf, 0xe9, 0x7e, 0xf0, 0x5e, 0x3b, 0xa8, 0x4d, 0xa4, 0x2, 0x73, 0xa6, 0xef, 0x3f, 0xd4, 0x69, 0x6e, 0xb5, 0xa3, 0x7c, 0xad, 0xbf, 0x4e, 0xa1, 0xb0, 0x79, 0xad, 0xc7, 0xa5, 0x79, 0x3e, 0xa7, 0x2d, 0x9e, 0xc9, 0xbc, 0x61, 0xbb, 0xa5, 0x6c, 0xe3, 0x6f, 0xf4, 0x75, 0xd4, 0xf0, 0xd9, 0x2c, 0xad, 0x31, 0xb3, 0x8e, 0x50, 0xc0, 0x9b, 0xf1, 0xfd, 0x56, 0x5f, 0x4d, 0x3f, 0xe3, 0x5c, 0x22, 0xd9, 0xfc, 0x89, 0xc8, 0x84, 0xdd, 0x28, 0x68, 0x8a, 0x98, 0x5a, 0x62, 0x6d, 0x84, 0xd3, 0x46, 0x1b, 0x1e, 0x65, 0x80, 0xa6, 0xb0, 0x26, 0xaa, 0xd6, 0xe, 0xec, 0x37, 0x69, 0x36, 0xad, 0x76, 0xa9, 0xb3, 0x1, 0xd0, 0xbf, 0xb8, 0x5a, 0xb6, 0xfc, 0x3b, 0x5b, 0xdd, 0x27, 0x5f, 0x5b, 0xfd, 0x33, 0x86, 0xc, 0x8c, 0xf, 0xb, 0x79, 0xf9, 0x94, 0xa5, 0x97, 0xa8, 0x74, 0x20, 0xd5, 0xc6, 0xc9, 0xc6, 0x80, 0x99, 0x85, 0xb0, 0x5, 0x43, 0x13, 0x9b, 0x7c, 0x2b, 0xe9, 0xd7, 0x47, 0x75, 0x51, 0xb6, 0x10, 0x4d, 0x95, 0x15, 0xac, 0xac, 0x64, 0x3a, 0xcb, 0x85, 0x76, 0x83, 0xac, 0xd0, 0xcf, 0x6d, 0x44, 0x79, 0xdb, 0xac, 0x2f, 0x22, 0x13, 0x3a, 0xe3, 0xf7, 0x9, 0x1a, 0x9e, 0xaf, 0xd2, 0x7c, 0x8d, 0xdb, 0xc3, 0x4e, 0x64, 0x3b, 0xc5, 0x23, 0xe0, 0x1e, 0x2c, 0x1, 0x12, 0xfa, 0xf9, 0x17, 0x8, 0xcd, 0xde, 0xc5, 0xd5, 0xac, 0xc5, 0x73, 0x8d, 0x39, 0xa3, 0x1e, 0xd2, 0xbf, 0x7c, 0xc7, 0xa6, 0x39, 0xda, 0xf3, 0x44, 0xd2, 0x5b, 0xce, 0x61, 0xb, 0x86, 0x50, 0xb1, 0x69, 0x66, 0x1f, 0x16, 0x99, 0x8e, 0xa5, 0x8b, 0x6, 0x81, 0x8a, 0x1d, 0xac, 0x9c, 0xc8, 0xb4, 0x4f, 0x5b, 0x32, 0xc1, 0x4a, 0x53, 0xb1, 0xd9, 0xd6, 0x16, 0x21, 0x32, 0x61, 0xd0, 0xce, 0x1e, 0x3, 0xa6, 0xb6, 0x5e, 0xd3, 0x81, 0x53, 0x5b, 0xc2, 0x6c, 0x4a, 0x91, 0xad, 0xeb, 0x9e, 0xde, 0xeb, 0x2b, 0xe4, 0x7e, 0x10, 0x9a, 0xdd, 0x73, 0xdd, 0x22, 0x7b, 0x60, 0xc4, 0xc8, 0x6f, 0x32, 0xed, 0x5d, 0xc6, 0x21, 0xfd, 0xca, 0x4, 0x4a, 0x6f, 0x25, 0xfd, 0xb5, 0xc2, 0x75, 0xb, 0xb1, 0x79, 0x9d, 0x99, 0xd0, 0x34, 0x99, 0xb8, 0x65, 0x83, 0x36, 0x4e, 0x35, 0x58, 0x31, 0x62, 0xf3, 0x3c, 0xd0, 0x1f, 0xcf, 0xc6, 0xa, 0x54, 0x10, 0x99, 0xb0, 0x3b, 0xa8, 0x5d, 0xe, 0x70, 0x6c, 0xcc, 0xd4, 0x78, 0x93, 0xc1, 0x62, 0x1b, 0xf8, 0x2b, 0x49, 0x73, 0x41, 0xfd, 0x46, 0x3b, 0xeb, 0x90, 0xac, 0x26, 0x42, 0xb3, 0xbb, 0xf1, 0xd3, 0xf4, 0x78, 0xcf, 0x4a, 0x7e, 0x9e, 0x22, 0x9e, 0x32, 0xb3, 0x80, 0xfe, 0xb4, 0x95, 0xe7, 0x4c, 0x6d, 0x4e, 0x18, 0x5b, 0x74, 0x12, 0x68, 0x57, 0x55, 0xbb, 0x96, 0x19, 0xb4, 0x4f, 0xa1, 0x45, 0x51, 0xc8, 0xf4, 0xb8, 0x3d, 0xab, 0x93, 0x7a, 0xb0, 0x72, 0x65, 0xf9, 0xb1, 0x10, 0xfb, 0xa4, 0xfa, 0xcf, 0x25, 0x22, 0x13, 0xc6, 0x74, 0xe, 0xb1, 0x1b, 0x9d, 0x26, 0x53, 0xe3, 0x6d, 0xb2, 0x7, 0x53, 0x44, 0x19, 0xdb, 0xf7, 0xfa, 0x9e, 0x43, 0x84, 0xe6, 0x25, 0x43, 0xa0, 0xd5, 0xd8, 0x69, 0xb2, 0x49, 0xa3, 0x96, 0xb4, 0xb2, 0x2e, 0x66, 0x9c, 0xfa, 0x3a, 0xca, 0xb7, 0x92, 0x77, 0x49, 0xad, 0x4a, 0xb7, 0xc1, 0x32, 0xe0, 0x77, 0x9a, 0xee, 0xd2, 0x4f, 0x79, 0x2c, 0x6d, 0x32, 0xc, 0x56, 0xec, 0x19, 0x80, 0x90, 0x40, 0xe5, 0x4c, 0x6, 0x5c, 0xa7, 0x89, 0xc8, 0x84, 0xdd, 0x48, 0xe7, 0x50, 0xc6, 0x61, 0x2c, 0xce, 0xa4, 0xd9, 0xee, 0x5c, 0xb3, 0xe6, 0x32, 0x85, 0xec, 0x50, 0x13, 0xd6, 0xfa, 0xde, 0x1f, 0x2, 0xda, 0x79, 0xc1, 0x30, 0x68, 0xe4, 0x14, 0x6f, 0x25, 0x6c, 0xe3, 0x5c, 0xaa, 0x59, 0x17, 0xdf, 0x9d, 0xbf, 0xa7, 0xfa, 0xfe, 0xe1, 0x89, 0x95, 0xe, 0x34, 0x7c, 0xc6, 0xea, 0x91, 0x8c, 0xbc, 0x99, 0xa3, 0x47, 0x4a, 0x9, 0x5b, 0x7f, 0x69, 0x2, 0xea, 0xd0, 0x65, 0x42, 0x29, 0xb1, 0x6d, 0x10, 0xa8, 0x2c, 0xa4, 0xd9, 0x3a, 0x57, 0x44, 0x26, 0xb4, 0x76, 0x96, 0x87, 0x6, 0xf3, 0x18, 0xa2, 0xb7, 0x69, 0xf6, 0x72, 0x2d, 0xcf, 0x6b, 0x2e, 0x73, 0xae, 0x33, 0xb7, 0xd, 0x8c, 0x74, 0x53, 0x3d, 0xf3, 0x37, 0x16, 0xa7, 0xa8, 0xd8, 0x48, 0xf8, 0x54, 0xd7, 0x14, 0xb8, 0xf4, 0xec, 0x3b, 0xa7, 0x99, 0x6, 0x7d, 0x2e, 0x2a, 0xdd, 0x2f, 0x7c, 0xc6, 0xaa, 0x6a, 0xe7, 0x8b, 0xc4, 0xee, 0x3f, 0x54, 0xf8, 0x54, 0x12, 0xbe, 0x34, 0x28, 0xf5, 0x40, 0x25, 0x64, 0x66, 0x60, 0x36, 0x84, 0xd0, 0x44, 0x64, 0x82, 0x8f, 0xc0, 0x14, 0x19, 0x27, 0x93, 0xb9, 0x90, 0xf0, 0xec, 0xa5, 0x31, 0x3c, 0x14, 0x31, 0x7e, 0x46, 0x19, 0x9d, 0x90, 0xb5, 0x5f, 0x4d, 0xeb, 0xd0, 0xe5, 0x46, 0xa8, 0x53, 0xb4, 0xb3, 0x2e, 0xa9, 0xf5, 0x4d, 0x35, 0x4e, 0x7d, 0x4a, 0x88, 0x21, 0x30, 0xdd, 0x7d, 0xc4, 0x77, 0xac, 0x9e, 0x49, 0x3a, 0xeb, 0x33, 0x17, 0x12, 0xb6, 0x2e, 0xfc, 0x4a, 0xb7, 0x53, 0x4d, 0x97, 0xd9, 0x9b, 0x54, 0xf0, 0x1d, 0x63, 0xbd, 0xb, 0x4d, 0x44, 0x26, 0x18, 0xa, 0xc7, 0xeb, 0xdf, 0x47, 0x32, 0xb8, 0x21, 0xef, 0x6d, 0xb2, 0x90, 0x1e, 0xe7, 0xb5, 0xbf, 0x2f, 0xb0, 0x11, 0xe8, 0x30, 0xf3, 0xc0, 0x36, 0x52, 0xc1, 0x4f, 0x93, 0x32, 0x53, 0x53, 0xc1, 0x67, 0x3d, 0x2f, 0x2, 0xb3, 0xfb, 0xb1, 0x9a, 0xc2, 0x38, 0xd, 0x11, 0x98, 0x26, 0x50, 0x63, 0xa9, 0x85, 0xdb, 0x17, 0xfa, 0xb6, 0x51, 0xaf, 0x42, 0x13, 0x91, 0x9, 0x86, 0x37, 0x1e, 0x11, 0xd2, 0xd0, 0xac, 0x3d, 0x9d, 0xf2, 0x5a, 0xf2, 0xd9, 0xd4, 0x33, 0x94, 0xf3, 0x9a, 0x4b, 0x4, 0xa7, 0x45, 0x44, 0x4a, 0xc8, 0xee, 0x69, 0xc5, 0x52, 0xfc, 0xd7, 0xdb, 0x4d, 0x91, 0x33, 0x71, 0xcf, 0x84, 0x5c, 0x31, 0x3e, 0x7b, 0x19, 0xab, 0x85, 0x4c, 0x7b, 0xd6, 0x21, 0x64, 0x2c, 0x99, 0x36, 0x61, 0x7a, 0xdc, 0xf, 0x73, 0x32, 0xd8, 0x83, 0xe7, 0x73, 0xe8, 0x45, 0x68, 0x22, 0x32, 0xc1, 0x50, 0x46, 0x28, 0x32, 0x45, 0xe, 0x17, 0x9f, 0xad, 0xb5, 0xd1, 0x61, 0x6a, 0x3c, 0x3c, 0xca, 0xf5, 0x69, 0xaf, 0x54, 0x37, 0x17, 0xb4, 0x75, 0x8a, 0xbe, 0xc6, 0xd8, 0x64, 0x5d, 0x56, 0x9, 0xb7, 0x87, 0x6a, 0x7, 0xd7, 0xda, 0x40, 0x73, 0x62, 0x11, 0x74, 0x3f, 0x56, 0x97, 0x13, 0xb6, 0x7d, 0x66, 0x2c, 0xf9, 0xb0, 0x15, 0xff, 0xf5, 0xaa, 0xf0, 0xcc, 0x26, 0x20, 0xc0, 0xed, 0x45, 0x68, 0x22, 0x32, 0xc1, 0x47, 0x64, 0xd6, 0x23, 0x1a, 0x32, 0xe3, 0xa8, 0xf7, 0x45, 0x69, 0x39, 0x14, 0x6f, 0xee, 0x83, 0xed, 0xb, 0x6d, 0xba, 0x4f, 0x40, 0x50, 0xd6, 0xe8, 0x99, 0x22, 0x50, 0x60, 0xe6, 0x90, 0x75, 0xf9, 0xe8, 0xd1, 0x1e, 0xa7, 0x74, 0x9d, 0x56, 0x63, 0xf5, 0x7c, 0x4f, 0xdf, 0x32, 0x75, 0x12, 0x57, 0x19, 0x8c, 0xa5, 0xad, 0xa4, 0x3d, 0x13, 0x30, 0x44, 0x1f, 0xf2, 0x5d, 0xbf, 0x1a, 0x22, 0xfc, 0x11, 0x99, 0x10, 0x14, 0x51, 0xba, 0x3a, 0x69, 0x2c, 0x86, 0xd6, 0x5e, 0x93, 0x83, 0xd1, 0x69, 0x8e, 0x12, 0xe7, 0x4b, 0x8f, 0xf7, 0x2d, 0x84, 0x73, 0x93, 0x8d, 0xe0, 0xf6, 0xdd, 0x10, 0x95, 0x53, 0xd6, 0x65, 0xe1, 0x78, 0x7d, 0x29, 0x64, 0x9f, 0xda, 0xb2, 0xd6, 0xe3, 0xb5, 0x96, 0xe7, 0x63, 0x70, 0xa7, 0x7c, 0x1a, 0x54, 0xe8, 0x58, 0x42, 0x60, 0x76, 0xe3, 0x43, 0x7d, 0xab, 0x8c, 0x84, 0x2e, 0x7, 0x3a, 0xc8, 0x2b, 0xda, 0x1e, 0x1e, 0x79, 0xe7, 0x78, 0xfd, 0x2e, 0x82, 0xcf, 0x78, 0x65, 0x19, 0x5c, 0xc, 0x4e, 0x37, 0xac, 0xb4, 0x80, 0x74, 0x89, 0xc8, 0xb, 0x21, 0x63, 0xec, 0x5b, 0xda, 0x29, 0x27, 0xa7, 0xa8, 0x4, 0x66, 0x71, 0xe0, 0x75, 0xd5, 0x6, 0x9f, 0x18, 0x66, 0x9d, 0x90, 0x52, 0x45, 0x82, 0x4b, 0xc6, 0xd2, 0x28, 0x98, 0xd9, 0x95, 0xaf, 0xe2, 0xde, 0xe8, 0xab, 0xc6, 0xf6, 0xbd, 0x74, 0x50, 0x66, 0x8d, 0x4c, 0x26, 0x28, 0x5c, 0x1b, 0x3c, 0x62, 0x11, 0x18, 0xa9, 0x1e, 0x5, 0x39, 0x26, 0x3e, 0xeb, 0x33, 0x7d, 0x84, 0x68, 0xca, 0x5c, 0x88, 0xdf, 0x26, 0xa8, 0xdc, 0x9c, 0xe2, 0x3b, 0xc6, 0xeb, 0xa0, 0x2, 0x21, 0x5, 0x7c, 0xf, 0x7c, 0x40, 0x60, 0xf6, 0xd7, 0x8f, 0x7c, 0x3, 0x96, 0xcb, 0x2e, 0xec, 0x3e, 0x22, 0x13, 0x8e, 0xc4, 0x5d, 0x84, 0x9d, 0xe9, 0xae, 0x74, 0xa9, 0xc5, 0x6f, 0x5d, 0xd7, 0x45, 0xa6, 0xed, 0xa3, 0x8c, 0xec, 0x12, 0xa7, 0xb8, 0xd7, 0x6e, 0xcc, 0x1d, 0x76, 0x83, 0x2c, 0x26, 0xec, 0x8e, 0x25, 0x9f, 0x35, 0xde, 0xf, 0x8, 0xcc, 0x5e, 0x9, 0xb1, 0x55, 0x6a, 0x59, 0x43, 0x81, 0xc8, 0x84, 0x36, 0xb8, 0x32, 0x34, 0x94, 0x8b, 0x48, 0x9f, 0x2b, 0x8f, 0x40, 0xa2, 0x94, 0xfc, 0xb2, 0x99, 0x66, 0xed, 0x18, 0x4e, 0x31, 0xdc, 0x6e, 0xac, 0x11, 0x9, 0xb0, 0x33, 0x96, 0xae, 0x19, 0x4b, 0x51, 0x9, 0x4d, 0xdf, 0xcd, 0x9f, 0x37, 0x6d, 0xfe, 0x23, 0x44, 0x26, 0x4c, 0x61, 0x3d, 0x26, 0xf4, 0x8f, 0x4f, 0x89, 0x99, 0x8f, 0x99, 0xb5, 0x89, 0xcf, 0xe6, 0x84, 0x5c, 0x9d, 0xa2, 0xcb, 0x6e, 0x7c, 0x66, 0x48, 0x81, 0x85, 0x12, 0x98, 0x85, 0xc7, 0xfb, 0x4e, 0x85, 0x99, 0xb3, 0xa1, 0xa8, 0xc4, 0xaf, 0xf2, 0x43, 0xab, 0x23, 0x4c, 0x11, 0x99, 0x79, 0xa3, 0x6, 0x3d, 0x99, 0x4c, 0x30, 0x6, 0xa7, 0x72, 0xbc, 0x67, 0x2e, 0x2d, 0xa7, 0x4e, 0x26, 0x84, 0xef, 0x91, 0x7d, 0xb9, 0x3a, 0xc5, 0x43, 0x6d, 0x53, 0x23, 0x14, 0x60, 0xc7, 0x6e, 0xf8, 0xac, 0x69, 0x5e, 0xe2, 0x6f, 0x6, 0x67, 0x2d, 0x7e, 0x87, 0x24, 0x2c, 0xa5, 0xe1, 0x4c, 0x16, 0x22, 0x93, 0xc1, 0xef, 0x12, 0x98, 0x4c, 0x5b, 0xe4, 0x83, 0xcf, 0xda, 0xcc, 0xf, 0x99, 0x4, 0x5f, 0x3e, 0x91, 0x7b, 0xae, 0x4e, 0x51, 0x65, 0x36, 0x8e, 0x8, 0x4c, 0xc1, 0x3, 0xdf, 0x69, 0xf2, 0x8d, 0xa4, 0x7d, 0x68, 0x41, 0xcc, 0x9c, 0x8b, 0xff, 0x11, 0xa6, 0xc1, 0x85, 0xda, 0x11, 0x99, 0x79, 0xe3, 0x9a, 0xfe, 0xfc, 0x42, 0x13, 0x65, 0x45, 0x25, 0xee, 0x6c, 0xe6, 0x22, 0x83, 0x76, 0xf0, 0x31, 0xa6, 0x39, 0x3b, 0xc5, 0xd2, 0xf1, 0x3a, 0x4b, 0x6c, 0xc0, 0x70, 0x29, 0x7e, 0x4b, 0x4e, 0x28, 0xd8, 0x3f, 0x1e, 0xbe, 0xa7, 0xc0, 0x15, 0xd2, 0x60, 0xc9, 0x14, 0x22, 0x33, 0x5f, 0x5c, 0x53, 0x9f, 0xaa, 0xc3, 0xad, 0x69, 0xa6, 0xec, 0x70, 0x9, 0xa7, 0x42, 0xd2, 0xde, 0x0, 0x34, 0xf7, 0xb8, 0xbf, 0xdc, 0x9d, 0xe2, 0xef, 0x1e, 0xc1, 0xa, 0x40, 0xe9, 0x19, 0x94, 0xa6, 0x54, 0x3, 0x74, 0xaa, 0x6c, 0x3d, 0x83, 0xe6, 0xa5, 0xf8, 0xd5, 0x38, 0x45, 0x64, 0x82, 0x33, 0x22, 0x61, 0xca, 0x2b, 0x4f, 0x94, 0x40, 0xa8, 0x1d, 0xef, 0x49, 0x75, 0xca, 0xdc, 0x77, 0x6a, 0x2f, 0x77, 0xa7, 0xe8, 0x2a, 0x79, 0x86, 0x60, 0x0, 0xf1, 0x1c, 0x4b, 0x57, 0x4, 0x25, 0xd1, 0x70, 0xe5, 0xe9, 0xf7, 0x83, 0x8e, 0x1a, 0x46, 0x64, 0xe6, 0x1b, 0x61, 0x96, 0x8e, 0xf7, 0x50, 0xe3, 0x2e, 0x5f, 0x3e, 0x79, 0xf4, 0x9f, 0x54, 0x3, 0x2f, 0xd7, 0xd4, 0x1e, 0x4e, 0xf1, 0xf0, 0xc, 0x8, 0x1b, 0x7e, 0x40, 0x71, 0x26, 0xee, 0x4d, 0x82, 0x2a, 0x98, 0x65, 0x1d, 0x66, 0x5c, 0xf8, 0x1e, 0xce, 0xb1, 0x40, 0x64, 0xc2, 0x21, 0x5c, 0x9b, 0x1a, 0x2a, 0x9c, 0x45, 0xd6, 0xb8, 0xa2, 0x59, 0xe5, 0x3c, 0x66, 0x9, 0xde, 0xb7, 0x4b, 0x3c, 0xe3, 0x14, 0x11, 0x99, 0xe0, 0xe6, 0x48, 0xfc, 0x36, 0xce, 0xf9, 0x8, 0x1a, 0x18, 0x96, 0x7, 0xf1, 0x3f, 0x9c, 0xc3, 0x6b, 0x13, 0x10, 0x22, 0x33, 0x3f, 0x4a, 0xf, 0x67, 0x4a, 0x8d, 0xbb, 0xbc, 0xa9, 0x3d, 0x84, 0xe6, 0xbb, 0xc, 0xdb, 0xe5, 0x1c, 0xa7, 0xe8, 0xb5, 0x89, 0x3, 0xf2, 0xc6, 0x67, 0x46, 0x40, 0xd9, 0x97, 0x8a, 0xa6, 0x8a, 0x12, 0x9f, 0xd9, 0x9a, 0x42, 0x3c, 0x37, 0x1, 0x21, 0x32, 0xf3, 0xc3, 0x15, 0x61, 0x2a, 0x81, 0xb1, 0xa6, 0x99, 0xb2, 0xc7, 0x55, 0x59, 0xa0, 0xcc, 0xac, 0x3d, 0x2a, 0x61, 0x9d, 0xb2, 0x62, 0x46, 0x13, 0x80, 0x23, 0x8, 0x39, 0xf3, 0x8, 0x44, 0xce, 0x69, 0xaa, 0xa8, 0xf1, 0xc9, 0x66, 0x9e, 0xfd, 0xe5, 0x6f, 0xff, 0x70, 0x66, 0x33, 0x11, 0x99, 0x79, 0xe1, 0xb3, 0x73, 0x96, 0xe9, 0x40, 0x10, 0x8f, 0x48, 0x36, 0x37, 0x91, 0x49, 0x89, 0x15, 0x3f, 0xbe, 0xd3, 0x4, 0x59, 0xe3, 0xbb, 0xae, 0xb9, 0xa6, 0xa9, 0xa2, 0xb7, 0xff, 0x6b, 0x8f, 0x80, 0xc2, 0x99, 0xcd, 0x44, 0x64, 0xe6, 0x15, 0x61, 0xba, 0x76, 0x85, 0xd5, 0x42, 0x16, 0x13, 0x9e, 0xfb, 0x82, 0xcf, 0x79, 0xe6, 0x39, 0xb0, 0xc6, 0x29, 0x6, 0xf5, 0x1b, 0xc8, 0xd7, 0xc7, 0xf8, 0x64, 0x31, 0xd9, 0x54, 0x3a, 0xd, 0x3a, 0xc9, 0x66, 0x22, 0x32, 0xf3, 0x8a, 0x30, 0x8b, 0xe, 0x3a, 0x15, 0xe4, 0x15, 0xcd, 0x1e, 0x22, 0x87, 0xa9, 0x53, 0xa6, 0xf6, 0x0, 0xfc, 0x7d, 0x8c, 0x4f, 0x16, 0x93, 0x75, 0xbb, 0xd3, 0x9, 0x18, 0x97, 0x1e, 0x81, 0xc5, 0x47, 0x44, 0x26, 0xcc, 0x3c, 0x3a, 0x8b, 0x12, 0x14, 0x6b, 0x9a, 0xa, 0x2c, 0x5c, 0x27, 0xb7, 0xbc, 0xc9, 0xa0, 0xd, 0x70, 0x8a, 0x61, 0x1c, 0xd3, 0x4, 0xd9, 0xb2, 0xf0, 0x8, 0xd8, 0xc8, 0x62, 0x4e, 0x8b, 0x4f, 0x1e, 0xf6, 0xef, 0x60, 0x36, 0x13, 0x91, 0x99, 0x3e, 0xbe, 0x5, 0xa6, 0xc9, 0x62, 0xc2, 0xbe, 0xc0, 0xc3, 0x15, 0xbc, 0xa4, 0xc, 0x4e, 0x11, 0xc0, 0x5f, 0x60, 0x16, 0x4, 0x6c, 0x49, 0xda, 0xc0, 0xb5, 0x87, 0xc6, 0x98, 0x23, 0x32, 0xf3, 0xe5, 0xc2, 0x43, 0xc, 0xac, 0x85, 0x72, 0x12, 0xb0, 0xdf, 0xc0, 0xd4, 0x19, 0x8b, 0xcc, 0xd, 0x4e, 0x11, 0xc0, 0xb, 0xd7, 0x29, 0x60, 0x4, 0x6c, 0xd3, 0xc5, 0xe7, 0xb9, 0x5d, 0x20, 0x32, 0xf3, 0x8d, 0x2e, 0x29, 0x27, 0x1, 0x6d, 0xa8, 0x1d, 0xaf, 0x17, 0x9, 0xdf, 0x3b, 0xd9, 0xfd, 0xfd, 0xf6, 0x42, 0x32, 0xe, 0x3c, 0x60, 0xbf, 0xd, 0x28, 0x1d, 0xef, 0x59, 0x13, 0xb0, 0x4d, 0xda, 0x7, 0xac, 0x5d, 0x7d, 0xe0, 0x2f, 0x7f, 0xfb, 0x47, 0x89, 0xc8, 0xcc, 0xb, 0x65, 0xec, 0x7d, 0xce, 0x18, 0xe5, 0xd4, 0x5, 0x38, 0x44, 0x95, 0xa9, 0xc8, 0xdc, 0x8, 0x3b, 0xa5, 0xf7, 0xb1, 0xcd, 0xb4, 0x3f, 0xc0, 0xcb, 0xf8, 0x14, 0xe5, 0x26, 0x8b, 0x39, 0x6d, 0x7c, 0x9e, 0xdf, 0x7, 0x44, 0x66, 0x3e, 0xa8, 0x35, 0x12, 0x37, 0xe2, 0x77, 0xea, 0x2, 0x5, 0xa6, 0xe1, 0x10, 0xae, 0xba, 0x87, 0xa9, 0x8a, 0xa, 0x9c, 0x62, 0xf3, 0xe0, 0x16, 0xf2, 0x62, 0x4e, 0xc0, 0x96, 0x45, 0x70, 0xe9, 0xa, 0x30, 0x17, 0xfb, 0x36, 0x0, 0x21, 0x32, 0xd3, 0x14, 0x98, 0xb7, 0x1e, 0xce, 0x5f, 0x65, 0x2f, 0x29, 0x30, 0xd, 0x2e, 0x5c, 0xce, 0xe1, 0xf7, 0x44, 0xef, 0xb9, 0xe2, 0xd1, 0xbf, 0x88, 0xab, 0x6d, 0x4a, 0x9a, 0x28, 0xab, 0xa0, 0xc2, 0xe5, 0x6b, 0x38, 0xa6, 0x38, 0x9f, 0xc0, 0x7b, 0x8e, 0xc8, 0xcc, 0x43, 0x60, 0xfa, 0x64, 0x13, 0xde, 0xb, 0xd3, 0xe4, 0xd0, 0x5e, 0x64, 0xe6, 0x6a, 0x4c, 0xe9, 0x13, 0x2f, 0xf3, 0x8e, 0x26, 0xca, 0x6, 0x9f, 0xd, 0x3f, 0xcc, 0x96, 0xa5, 0x81, 0xcf, 0x46, 0xc8, 0x77, 0x88, 0x4c, 0x4, 0xa6, 0x62, 0x29, 0x64, 0x6a, 0xa0, 0x1b, 0x41, 0x51, 0x26, 0x6a, 0x4c, 0xe1, 0x65, 0xee, 0x1d, 0xaf, 0xcf, 0x69, 0xa2, 0x6c, 0x70, 0x3d, 0xeb, 0x35, 0x4d, 0x94, 0xc, 0x3e, 0x1, 0xc3, 0x7c, 0x77, 0xca, 0x1c, 0x91, 0x99, 0x9f, 0xc0, 0x54, 0x83, 0x9e, 0x5d, 0xb3, 0x0, 0xfb, 0xa9, 0x84, 0xf5, 0x63, 0x3e, 0x6d, 0x74, 0x88, 0x2, 0xa1, 0x99, 0x5, 0x4c, 0x95, 0xe7, 0xc7, 0x97, 0xd0, 0xc0, 0x3, 0x91, 0x99, 0x97, 0xc0, 0x54, 0xb, 0x77, 0x29, 0x57, 0x4, 0xa1, 0x6c, 0x33, 0xba, 0x57, 0x9c, 0xa2, 0x5f, 0x7f, 0x70, 0x9, 0xf1, 0x8f, 0x34, 0x53, 0xf2, 0x94, 0x8e, 0xd7, 0xeb, 0xcc, 0x6c, 0x47, 0xe, 0x4, 0x4f, 0x99, 0x23, 0x32, 0xa7, 0x1f, 0x49, 0x7e, 0xb, 0x10, 0x98, 0x27, 0xc2, 0x3a, 0x4c, 0x8, 0x27, 0xa7, 0x3e, 0xc3, 0x54, 0x79, 0x37, 0xed, 0xa4, 0x4, 0xc8, 0x19, 0xcd, 0x94, 0x34, 0xef, 0x18, 0x4b, 0x8c, 0xfd, 0x3d, 0x90, 0xc9, 0x4c, 0x4, 0x65, 0xc0, 0xbf, 0x8a, 0xbb, 0x4c, 0x91, 0x11, 0x9, 0xd4, 0xc3, 0x4, 0x70, 0x7, 0x62, 0x8c, 0x11, 0x3f, 0x7c, 0x32, 0xbe, 0xea, 0x14, 0x90, 0x82, 0xa6, 0x4a, 0x96, 0xd2, 0xf1, 0xfa, 0x1d, 0x4d, 0x94, 0x24, 0xce, 0x29, 0x73, 0xbb, 0x30, 0x3b, 0x22, 0x73, 0x7a, 0x98, 0x1a, 0x98, 0x97, 0x9e, 0xef, 0x57, 0x4e, 0xf3, 0x44, 0x98, 0xb6, 0x80, 0x71, 0x1c, 0x4d, 0x6a, 0xc2, 0x9, 0x9e, 0x5, 0x79, 0xe5, 0x69, 0xab, 0x8e, 0x68, 0xae, 0x2c, 0xc7, 0x3d, 0x99, 0xcc, 0x34, 0xa9, 0x3c, 0xde, 0x73, 0x8c, 0xc8, 0x9c, 0x26, 0x2a, 0xd, 0xfd, 0x4d, 0xfc, 0x17, 0xd5, 0x23, 0x30, 0x1, 0xc2, 0x84, 0x13, 0xf8, 0xe3, 0xb3, 0x81, 0x50, 0x2d, 0xe5, 0xb9, 0x45, 0x68, 0x26, 0xc7, 0x31, 0x2, 0x33, 0x5b, 0x1e, 0x24, 0xa0, 0x56, 0x2e, 0x22, 0x73, 0x1a, 0x14, 0xda, 0x50, 0x87, 0x64, 0x5, 0x94, 0xc3, 0x7c, 0x8b, 0xe3, 0x4, 0x80, 0x9e, 0xa8, 0xc4, 0x2f, 0xab, 0x61, 0x84, 0x66, 0x41, 0x93, 0x25, 0x43, 0xe9, 0x78, 0x9d, 0xa9, 0xf2, 0xf4, 0xc7, 0x3e, 0x22, 0x33, 0x11, 0x71, 0x79, 0x2d, 0x4f, 0xd9, 0xcb, 0x32, 0xe0, 0xf7, 0xcc, 0x26, 0x9f, 0x9a, 0x26, 0x4, 0x80, 0x1e, 0xf1, 0x5d, 0xeb, 0xad, 0x84, 0xe6, 0x57, 0xe1, 0x34, 0xa0, 0x54, 0x70, 0x6d, 0x36, 0x25, 0xb9, 0x91, 0x36, 0xce, 0x20, 0xc2, 0xac, 0xcb, 0x44, 0x64, 0xc6, 0x2f, 0x2e, 0x17, 0x81, 0xbf, 0xbb, 0x96, 0xa7, 0xc, 0x26, 0x1b, 0x18, 0x0, 0xa0, 0x6f, 0x54, 0x20, 0xeb, 0x5b, 0x77, 0xd7, 0x94, 0x5b, 0xbb, 0x14, 0xa6, 0xcf, 0xa7, 0xee, 0x9f, 0x5c, 0xcf, 0xaf, 0xa2, 0x99, 0x92, 0xc6, 0xe7, 0xf9, 0xce, 0x10, 0x99, 0x71, 0x71, 0xa4, 0x5, 0xe5, 0x6d, 0x43, 0x71, 0x69, 0xb2, 0xa, 0x9c, 0x47, 0xe, 0x0, 0x43, 0x72, 0x25, 0x61, 0x27, 0xbb, 0x9c, 0x69, 0x1b, 0x47, 0x89, 0xa3, 0xe9, 0x8a, 0x4c, 0x4, 0x26, 0xb8, 0x9e, 0xf3, 0x1b, 0x44, 0x66, 0x1c, 0x83, 0x55, 0x89, 0x49, 0xb5, 0xd6, 0xf2, 0x87, 0x3c, 0x65, 0x2f, 0xcb, 0x6, 0x7f, 0xc7, 0xac, 0xbf, 0x5c, 0xd3, 0xa4, 0x0, 0x30, 0x2, 0xa7, 0x81, 0xe2, 0x42, 0x5, 0xd5, 0x97, 0x96, 0xd8, 0x24, 0xb3, 0x39, 0x1d, 0x5c, 0x9b, 0x7e, 0x98, 0x2a, 0xcf, 0x3, 0xd7, 0x73, 0x26, 0x93, 0x39, 0x30, 0x47, 0xf2, 0x5c, 0xa0, 0xd8, 0x4c, 0x85, 0x7f, 0xd3, 0xdf, 0xb7, 0x39, 0x82, 0x4d, 0x65, 0x11, 0xd8, 0x41, 0xe, 0x0, 0x63, 0xf3, 0x5e, 0xc2, 0xb3, 0x58, 0x85, 0x25, 0x36, 0xaf, 0xc5, 0xef, 0x60, 0x9, 0x18, 0x97, 0xc2, 0xf1, 0xfa, 0x3d, 0x4d, 0x94, 0x5, 0xae, 0xe7, 0xfc, 0xbf, 0xb1, 0xfc, 0xca, 0xe3, 0x4d, 0xb7, 0xb4, 0x65, 0x6b, 0x71, 0xd9, 0x87, 0xe1, 0xdc, 0xea, 0xec, 0x1, 0xe2, 0x12, 0x0, 0x62, 0xc0, 0x94, 0x4c, 0x53, 0x62, 0x71, 0xd1, 0xc0, 0x4e, 0x2e, 0xf4, 0xa5, 0x6c, 0x9a, 0xaa, 0x59, 0xaa, 0xca, 0xe0, 0xd4, 0x34, 0xeb, 0xe4, 0x44, 0x26, 0xcf, 0x2c, 0xf, 0x9c, 0xda, 0xe3, 0x2f, 0x7f, 0xfb, 0xc7, 0xec, 0x95, 0xc7, 0xc0, 0x2f, 0x69, 0xcb, 0xa8, 0x50, 0x3, 0x58, 0x2d, 0xb4, 0x5f, 0xd3, 0x14, 0xa3, 0x51, 0x5a, 0x41, 0xd8, 0x9f, 0x76, 0xfe, 0xed, 0x50, 0x80, 0xf1, 0x70, 0x60, 0x60, 0xd6, 0x3b, 0xc6, 0xf9, 0xce, 0xe3, 0x77, 0x0, 0x62, 0xe4, 0x54, 0xf7, 0xdf, 0xa6, 0x1b, 0x7c, 0x66, 0xfa, 0xba, 0x44, 0x70, 0x4e, 0x52, 0x64, 0x56, 0x34, 0x11, 0x22, 0xd3, 0xf8, 0xc0, 0x57, 0xb4, 0xd3, 0xa4, 0xb2, 0x4, 0x6a, 0x6a, 0xfc, 0x93, 0xb0, 0x73, 0x7c, 0x8, 0x8c, 0x40, 0x3c, 0xd6, 0x46, 0xb5, 0xe8, 0x20, 0xe0, 0x6a, 0x1b, 0xb4, 0x6d, 0x2d, 0xd1, 0xa9, 0xbe, 0xde, 0x8b, 0x5f, 0x61, 0x5c, 0x80, 0xa1, 0x59, 0xeb, 0x7e, 0x79, 0xdd, 0xb2, 0xcf, 0xef, 0xa, 0x4e, 0xf5, 0x37, 0xbf, 0xd0, 0xe7, 0xa3, 0x15, 0x99, 0x4, 0x2, 0xf9, 0x9, 0xcd, 0x43, 0x33, 0xb5, 0xc7, 0x88, 0xcc, 0xf8, 0x51, 0x83, 0x76, 0xa5, 0x23, 0x79, 0xc4, 0x65, 0x7f, 0xcc, 0xb4, 0x33, 0x3c, 0xd6, 0xdf, 0x17, 0x91, 0x7e, 0x46, 0x79, 0xc1, 0x69, 0xd7, 0xfa, 0x52, 0xce, 0xf7, 0xbb, 0xf5, 0x3d, 0xc0, 0x98, 0xb6, 0x4b, 0x4d, 0x9f, 0x2f, 0xa4, 0x9b, 0xb2, 0x45, 0x46, 0x70, 0x9a, 0x5d, 0xe9, 0xca, 0x26, 0xde, 0xe9, 0x7e, 0x4e, 0xb6, 0x7f, 0xb8, 0xe0, 0xdb, 0xf5, 0xcc, 0x21, 0x1f, 0x9c, 0x9a, 0x4, 0x91, 0x19, 0x2f, 0xca, 0x80, 0x9a, 0x69, 0x22, 0xe8, 0xc7, 0x58, 0xce, 0xb5, 0xa8, 0x9c, 0xcb, 0xf4, 0x77, 0xb7, 0x16, 0xb2, 0x3f, 0xdb, 0xba, 0xd5, 0xd7, 0x3d, 0xce, 0x18, 0x46, 0x62, 0xad, 0xed, 0xd8, 0x47, 0xe9, 0x76, 0x27, 0xf9, 0x5c, 0x9e, 0x37, 0x4d, 0x9a, 0x8c, 0x3e, 0xa2, 0x73, 0x98, 0x40, 0x17, 0x91, 0x9, 0xa2, 0xc7, 0x5a, 0x79, 0xa8, 0xbf, 0x20, 0x32, 0xe3, 0x13, 0x96, 0x5f, 0x84, 0xac, 0x65, 0xdf, 0xc2, 0xf2, 0x9d, 0xb4, 0xdb, 0xd1, 0x3f, 0x35, 0xa7, 0x30, 0xdb, 0x63, 0x18, 0x6c, 0x87, 0xc, 0xd0, 0x37, 0xca, 0x9e, 0xa9, 0x19, 0x19, 0xb5, 0xdc, 0x67, 0xa1, 0x5, 0x67, 0xd1, 0xc3, 0xd8, 0x46, 0x74, 0x8e, 0xb, 0x22, 0x13, 0x7e, 0x1a, 0x97, 0x88, 0xcc, 0xf1, 0xa3, 0x80, 0xad, 0x36, 0x84, 0x64, 0x2c, 0xfb, 0x43, 0x39, 0x9e, 0xf, 0x19, 0x9, 0x4b, 0x17, 0xe5, 0x4e, 0xf4, 0x59, 0xc9, 0xf3, 0x3a, 0x37, 0x9c, 0x31, 0xf4, 0x2d, 0x36, 0xaf, 0xf4, 0xd5, 0xe7, 0xb8, 0xdc, 0x15, 0x9d, 0x4, 0x57, 0xc3, 0xf0, 0x1f, 0x9a, 0x20, 0x2b, 0x9c, 0xc7, 0x4b, 0x22, 0x32, 0xfb, 0x8f, 0xea, 0x4c, 0x64, 0x67, 0x6f, 0xd6, 0xa8, 0x71, 0xe6, 0xfd, 0x47, 0x50, 0xf2, 0x94, 0x2d, 0x59, 0x48, 0x9c, 0xeb, 0x2b, 0x63, 0x15, 0x9d, 0xb5, 0x25, 0x3a, 0x9, 0x7c, 0xa0, 0x4f, 0x36, 0xfa, 0x2a, 0x2c, 0xc1, 0x39, 0x1b, 0xa8, 0x9f, 0x1b, 0x9b, 0x6c, 0x8b, 0x4e, 0x66, 0x8f, 0xdc, 0x50, 0x88, 0x1d, 0x82, 0xc6, 0x9c, 0x4b, 0x64, 0xaa, 0x81, 0x77, 0x42, 0x3b, 0xc1, 0x84, 0x50, 0xe, 0xeb, 0x42, 0xc6, 0x5b, 0x67, 0x59, 0x35, 0xf8, 0xbc, 0x45, 0x64, 0xed, 0xb7, 0xd0, 0xd7, 0x83, 0x25, 0x2, 0x0, 0xfa, 0xc, 0xc6, 0x4d, 0x76, 0x73, 0x28, 0xc1, 0x29, 0xf2, 0xeb, 0x46, 0xa2, 0x7a, 0x47, 0x74, 0xd6, 0x3c, 0x1a, 0x80, 0x76, 0x41, 0x5, 0x99, 0x4c, 0x48, 0x5, 0x73, 0x4c, 0xdd, 0xa2, 0x47, 0x47, 0x58, 0xcb, 0xaf, 0xe5, 0x83, 0xea, 0x8e, 0x9d, 0x91, 0x5d, 0x5b, 0x53, 0x7d, 0xff, 0xc6, 0x72, 0x88, 0x47, 0xd6, 0xd7, 0xa1, 0xda, 0x74, 0x41, 0xd7, 0x82, 0x91, 0x4, 0xa7, 0xbd, 0x86, 0xba, 0x1c, 0xa0, 0xdf, 0xdb, 0x1, 0x16, 0xa2, 0xb3, 0x19, 0x1c, 0xde, 0x2, 0x88, 0x4c, 0x48, 0xe, 0x95, 0xb9, 0xec, 0x72, 0xd7, 0xaa, 0x71, 0x2e, 0xf7, 0xf2, 0x3c, 0xa5, 0x36, 0x14, 0xbb, 0x75, 0x2f, 0x5f, 0x9a, 0xb2, 0x2e, 0x2d, 0x11, 0x6a, 0x4a, 0x2e, 0x71, 0x24, 0x1f, 0xa4, 0x84, 0x1a, 0xb, 0x6b, 0x79, 0x3e, 0x78, 0x42, 0xf5, 0xf9, 0x63, 0xf9, 0x75, 0xda, 0x1b, 0xd1, 0x9, 0x80, 0xc8, 0x4, 0xe8, 0x1c, 0xe5, 0x68, 0xae, 0xa5, 0xfd, 0x74, 0xae, 0xed, 0x3c, 0xa6, 0xb2, 0xb3, 0xbf, 0x7a, 0x41, 0x84, 0x96, 0xba, 0x3d, 0x4c, 0xbd, 0x4f, 0x84, 0x27, 0xa4, 0x42, 0xa5, 0xaf, 0x95, 0xfe, 0x79, 0x6e, 0x89, 0xce, 0x21, 0xfa, 0xf9, 0xae, 0xe8, 0xdc, 0xee, 0xd8, 0xd, 0x0, 0x40, 0x64, 0x42, 0x2, 0xa8, 0xc, 0x9e, 0xc9, 0x5e, 0xb6, 0x11, 0x96, 0xa6, 0x16, 0x69, 0x4a, 0x8b, 0xd5, 0x8d, 0xf8, 0x5c, 0xef, 0x8, 0xcf, 0x21, 0x33, 0x40, 0x0, 0x43, 0xb0, 0xb1, 0xc4, 0x9d, 0x7d, 0x42, 0xd7, 0x50, 0xfd, 0x7c, 0x5f, 0x71, 0x78, 0x53, 0xa5, 0xa1, 0xe6, 0xf1, 0x0, 0x20, 0x32, 0x61, 0x7a, 0x28, 0xa3, 0x7e, 0x23, 0xcd, 0xb2, 0x97, 0x66, 0xfa, 0x2d, 0x35, 0x61, 0xe9, 0x23, 0x3c, 0xd5, 0xb5, 0xb2, 0x44, 0xa7, 0x59, 0xe7, 0x46, 0xa6, 0x13, 0x52, 0xe0, 0xe1, 0x85, 0x7e, 0x6e, 0x32, 0xfa, 0xa5, 0xf4, 0xbf, 0xa6, 0xd3, 0x2e, 0x99, 0xc4, 0x99, 0xeb, 0x0, 0x88, 0x4c, 0x98, 0x18, 0x2a, 0x63, 0x70, 0xd9, 0xe0, 0xf7, 0x6a, 0xed, 0x78, 0xd6, 0x34, 0xe1, 0x4f, 0xa2, 0x53, 0xe4, 0x79, 0x37, 0xef, 0xb1, 0x50, 0x47, 0x14, 0xd2, 0xed, 0xe7, 0x62, 0x89, 0xcd, 0x37, 0xf2, 0xbc, 0xac, 0xa4, 0xcf, 0x60, 0xd8, 0x3e, 0x73, 0x1d, 0xc1, 0x9, 0x88, 0x4c, 0x80, 0x88, 0x51, 0x6b, 0x2f, 0x17, 0xd, 0x9c, 0xcc, 0x4a, 0x28, 0xbc, 0xec, 0x12, 0xe0, 0xfb, 0x76, 0xf3, 0x22, 0x38, 0x21, 0x35, 0xcc, 0x11, 0xab, 0x62, 0x5, 0x58, 0xb6, 0xe8, 0xec, 0x2b, 0xab, 0x6f, 0xb, 0xce, 0xca, 0x12, 0x9c, 0xd4, 0xe5, 0x4, 0x44, 0x26, 0xc0, 0xc8, 0x28, 0xe1, 0x73, 0x1b, 0xe8, 0x0, 0x94, 0x70, 0x3a, 0x45, 0x5c, 0x6, 0x63, 0xef, 0xe6, 0x35, 0xe5, 0x8b, 0x86, 0xa8, 0x57, 0x8, 0x30, 0x56, 0x80, 0xb5, 0xde, 0xb1, 0x35, 0x4a, 0x6c, 0xf6, 0xb9, 0x99, 0xa8, 0xd4, 0xd7, 0xa5, 0xfe, 0xbf, 0x3f, 0xc9, 0xb4, 0xb2, 0x9b, 0xf7, 0x1e, 0x42, 0x1e, 0xf1, 0xc, 0x86, 0xcf, 0x88, 0x4c, 0x48, 0x49, 0x60, 0x9a, 0xf3, 0x91, 0xaf, 0x68, 0xba, 0x4e, 0x4, 0xa7, 0xc9, 0x70, 0xde, 0xa, 0x1b, 0x86, 0x20, 0x8f, 0x3e, 0xbf, 0xbb, 0x99, 0xc8, 0x88, 0x4e, 0x95, 0xd9, 0x2f, 0x3a, 0xb6, 0x6d, 0x67, 0xfa, 0xda, 0x68, 0xb1, 0x59, 0x4d, 0xa4, 0x8d, 0xe, 0x71, 0x4e, 0x70, 0x9f, 0xf, 0xff, 0xfe, 0xd7, 0x3f, 0x9d, 0xef, 0xf9, 0x23, 0xcd, 0x4, 0x89, 0x8, 0x4c, 0x65, 0xd8, 0xde, 0x22, 0x30, 0x1, 0xa0, 0x63, 0xd1, 0xa9, 0x84, 0xd3, 0x6b, 0x7d, 0xa9, 0x19, 0x92, 0xb5, 0x74, 0x9b, 0xad, 0x9b, 0x6b, 0x5b, 0x97, 0x42, 0x30, 0xc7, 0xac, 0x7, 0x20, 0x32, 0x21, 0x29, 0x81, 0xf9, 0xa0, 0x9d, 0x80, 0x3a, 0xfe, 0xb4, 0xa6, 0xe9, 0x0, 0xa0, 0x27, 0x6a, 0x2d, 0x30, 0x95, 0xd0, 0xfc, 0x4d, 0x7, 0xb5, 0x4b, 0xe9, 0xae, 0x52, 0x45, 0xa9, 0xed, 0x5e, 0xd3, 0xea, 0x19, 0x43, 0xb5, 0xc1, 0x21, 0xfe, 0x44, 0x37, 0x1, 0x44, 0x26, 0xc4, 0xce, 0xb5, 0xa7, 0xc0, 0xac, 0xb5, 0xb8, 0x24, 0x7b, 0x9, 0x0, 0x43, 0xa3, 0xc4, 0xe5, 0x4a, 0x8b, 0xcd, 0xd7, 0x3a, 0xd8, 0xed, 0x42, 0x70, 0xaa, 0xcc, 0xe6, 0x37, 0x79, 0xaa, 0x5, 0x3c, 0x35, 0x91, 0x49, 0x26, 0x13, 0x10, 0x99, 0x10, 0x35, 0x97, 0xe2, 0xb7, 0xb3, 0x79, 0xab, 0x8d, 0xfb, 0x96, 0x26, 0x3, 0x80, 0x8, 0xc4, 0xd7, 0x55, 0xc7, 0x82, 0x73, 0xf9, 0x78, 0x7d, 0x8d, 0x50, 0xb8, 0x1d, 0x5a, 0x2a, 0x70, 0x44, 0x57, 0x0, 0x44, 0x26, 0xc4, 0x8a, 0x12, 0x97, 0x3e, 0xa7, 0xf8, 0xa8, 0x75, 0x52, 0x27, 0xc2, 0x2e, 0x46, 0x0, 0x88, 0x5f, 0x70, 0x5e, 0xb5, 0xb0, 0x55, 0x33, 0x2d, 0x34, 0xcf, 0x22, 0xba, 0xbf, 0x43, 0xe2, 0xb9, 0xe4, 0xf1, 0x3, 0x22, 0x13, 0x62, 0x44, 0x45, 0xc0, 0xd7, 0x9e, 0x2, 0xf3, 0x3d, 0x2, 0x13, 0x0, 0x26, 0x22, 0x38, 0x55, 0x56, 0x53, 0xad, 0xe1, 0x6c, 0x53, 0x56, 0xed, 0xd2, 0xd3, 0x3e, 0xe, 0x81, 0xcb, 0xf6, 0x16, 0x3c, 0x76, 0x40, 0x64, 0x42, 0x6c, 0x5c, 0x8b, 0x7b, 0xaa, 0x65, 0xab, 0xd, 0x35, 0x0, 0xc0, 0xd4, 0x58, 0xcb, 0xd3, 0xc, 0xcc, 0x49, 0x43, 0xb1, 0xb9, 0x90, 0xa7, 0x8d, 0x41, 0x63, 0x4f, 0x49, 0xbb, 0x96, 0x1, 0x20, 0x32, 0x1, 0x91, 0x9, 0x51, 0x61, 0x9f, 0xf9, 0x7b, 0x28, 0x23, 0xc0, 0x14, 0x39, 0x0, 0x4c, 0x9d, 0xaa, 0x85, 0xd8, 0x2c, 0x23, 0x10, 0x9a, 0xae, 0x82, 0xec, 0xc7, 0x3c, 0x62, 0x40, 0x64, 0x42, 0x4c, 0xb8, 0xce, 0x23, 0x57, 0xc2, 0x92, 0x29, 0x72, 0x0, 0x48, 0x51, 0x6c, 0xbe, 0x97, 0xb0, 0xf2, 0x6b, 0x6a, 0x9d, 0xe6, 0x98, 0x53, 0xe7, 0xae, 0xcf, 0x5a, 0xf2, 0x68, 0x1, 0x91, 0x9, 0xb1, 0xb0, 0x10, 0xf7, 0xf4, 0x8a, 0x2a, 0x13, 0xc2, 0x2e, 0x72, 0x0, 0x48, 0x11, 0xb5, 0xce, 0x3c, 0xf4, 0x20, 0x89, 0xb9, 0x47, 0x70, 0xde, 0x17, 0x2e, 0x5b, 0x4c, 0x19, 0x23, 0x40, 0x64, 0x42, 0x34, 0xb8, 0x6a, 0xc1, 0x55, 0x42, 0x1d, 0x4c, 0x0, 0x48, 0x1b, 0xfb, 0x50, 0x9, 0xdf, 0x19, 0x9b, 0x33, 0xf1, 0x2b, 0xf7, 0xd6, 0x7, 0xd5, 0x81, 0xd7, 0x8e, 0x10, 0x9a, 0x80, 0xc8, 0x84, 0x18, 0x58, 0x88, 0x3b, 0x8b, 0xc9, 0x46, 0x1f, 0x0, 0xc8, 0x5, 0x25, 0xde, 0x42, 0xea, 0xff, 0xfa, 0x6c, 0x98, 0xec, 0x3, 0xd7, 0xe7, 0x2b, 0x79, 0x94, 0x80, 0xc8, 0x84, 0xb1, 0xf9, 0xe8, 0x78, 0x7d, 0x2d, 0x1c, 0x15, 0x9, 0x0, 0x79, 0xa1, 0x6c, 0xde, 0x89, 0xa7, 0xd0, 0x54, 0x2, 0x73, 0x8c, 0x93, 0x81, 0xee, 0x1c, 0xaf, 0xb3, 0xf9, 0x7, 0x10, 0x99, 0x30, 0x2a, 0x33, 0x71, 0x4f, 0xa9, 0xac, 0x68, 0x26, 0x0, 0xc8, 0x90, 0x87, 0x0, 0xa1, 0xa9, 0xa6, 0xcd, 0x8b, 0x81, 0x3f, 0x5f, 0xe5, 0x78, 0xbd, 0xe4, 0x11, 0x2, 0x22, 0x13, 0xc6, 0xe4, 0x83, 0xe3, 0xf5, 0xb5, 0x90, 0xc5, 0x4, 0x0, 0x84, 0xa6, 0xcf, 0x1a, 0xcd, 0x8b, 0x11, 0x3e, 0xdb, 0x21, 0x1, 0x7c, 0x84, 0xd0, 0x4, 0x44, 0x26, 0x8c, 0x89, 0xcb, 0x0, 0x7d, 0xa6, 0x89, 0x0, 0x0, 0xa1, 0xf9, 0x3f, 0xa1, 0xe9, 0x62, 0x21, 0xf1, 0x65, 0x33, 0xdf, 0xf1, 0xf8, 0x0, 0x91, 0x9, 0x63, 0xa0, 0x8c, 0xe1, 0xa1, 0xa9, 0xf2, 0x5a, 0x9a, 0x1f, 0xbf, 0x6, 0x0, 0x90, 0x12, 0x2a, 0x63, 0xb8, 0xf4, 0x78, 0xdf, 0xd0, 0x3b, 0xcd, 0xef, 0x22, 0xfb, 0x3c, 0x80, 0xc8, 0x4, 0xf8, 0x1f, 0xae, 0xb5, 0x98, 0x1b, 0x9a, 0x8, 0x0, 0xe0, 0xff, 0x7c, 0x12, 0xf7, 0xb4, 0xf9, 0x87, 0x81, 0x3f, 0x93, 0xcb, 0x4e, 0x17, 0x42, 0x29, 0x23, 0x44, 0x26, 0x4d, 0x0, 0x23, 0xe0, 0xda, 0x79, 0x78, 0x47, 0x13, 0x1, 0x0, 0xfc, 0x1f, 0x25, 0x30, 0x5d, 0x1b, 0x21, 0x67, 0x32, 0xfc, 0x94, 0xb9, 0x4b, 0x68, 0x7e, 0xe4, 0xd1, 0x21, 0x32, 0x1, 0x86, 0xc6, 0x15, 0xdd, 0x56, 0x34, 0x11, 0x0, 0xc0, 0x4f, 0xac, 0xc5, 0x9d, 0xcd, 0x2c, 0x7, 0xfe, 0x4c, 0x5f, 0x1c, 0xaf, 0x33, 0x65, 0x8e, 0xc8, 0x4, 0x18, 0x9c, 0x43, 0xd1, 0xf6, 0x56, 0x38, 0xa3, 0x1c, 0x0, 0x60, 0x17, 0x65, 0x17, 0x5d, 0x99, 0xc3, 0xa1, 0xeb, 0x53, 0xba, 0x3e, 0x8f, 0xda, 0x65, 0xbe, 0xe0, 0xd1, 0x21, 0x32, 0x1, 0x62, 0x11, 0x99, 0x8, 0x4c, 0x0, 0x80, 0xfd, 0xb8, 0x32, 0x87, 0x43, 0xaf, 0x81, 0x54, 0xf6, 0x7a, 0xed, 0x78, 0xcf, 0x7, 0x1e, 0x1b, 0x22, 0x13, 0x60, 0x28, 0x5c, 0x47, 0xa0, 0x55, 0x34, 0x11, 0x0, 0xc0, 0x5e, 0x5c, 0x99, 0xc3, 0x31, 0x36, 0xda, 0xb8, 0x84, 0x6f, 0x29, 0xc3, 0xaf, 0x15, 0x5, 0x44, 0x26, 0x64, 0xa, 0xbb, 0xd, 0x1, 0x0, 0x9a, 0xe3, 0x3a, 0x5, 0x68, 0xe8, 0xb3, 0xcc, 0x95, 0xf0, 0xad, 0x1d, 0xef, 0xb9, 0xe0, 0xb1, 0x21, 0x32, 0x1, 0x0, 0x0, 0x60, 0xda, 0x22, 0x73, 0x8c, 0x40, 0x7e, 0xed, 0x78, 0x7d, 0x3e, 0x82, 0xf8, 0x5, 0x44, 0x26, 0x0, 0x0, 0x0, 0x4, 0x50, 0x47, 0xf8, 0x99, 0x5c, 0x27, 0xb4, 0x29, 0x81, 0x49, 0x39, 0x23, 0x44, 0x26, 0x0, 0x0, 0x0, 0x44, 0xcc, 0xf7, 0x48, 0x85, 0xef, 0xda, 0xf1, 0x9e, 0x33, 0x21, 0x9b, 0x89, 0xc8, 0x4, 0x0, 0x0, 0x80, 0x68, 0xa9, 0x23, 0xfd, 0x5c, 0x64, 0x33, 0x1, 0x91, 0x9, 0x18, 0x48, 0x0, 0x0, 0xe8, 0x9c, 0x4a, 0xdc, 0xd5, 0x41, 0xc8, 0x66, 0x22, 0x32, 0x1, 0x46, 0x15, 0x99, 0x5, 0x4d, 0x14, 0x1d, 0x25, 0x4d, 0x0, 0x0, 0x1e, 0xb8, 0x8e, 0xbe, 0x54, 0x2, 0x93, 0x9d, 0xe6, 0x88, 0x4c, 0x80, 0xd1, 0x40, 0x64, 0xc6, 0x5, 0x25, 0xa7, 0x0, 0xc0, 0x97, 0x4a, 0xfc, 0xb2, 0x99, 0xd8, 0x79, 0x44, 0x26, 0x40, 0xaf, 0x86, 0x8, 0x51, 0x33, 0xd, 0x4a, 0x9a, 0x0, 0x20, 0x2a, 0x62, 0x9f, 0x6e, 0x5e, 0x79, 0xbc, 0xe7, 0x9a, 0xc7, 0x88, 0xc8, 0x4, 0xe8, 0x8b, 0x7, 0x87, 0x1, 0x65, 0xcd, 0x4e, 0x3c, 0x70, 0x24, 0x1c, 0x40, 0x5c, 0xbc, 0x89, 0xfc, 0xf3, 0x55, 0xe2, 0xce, 0x66, 0xaa, 0xe0, 0x75, 0xce, 0xa3, 0x8c, 0x9a, 0x85, 0x74, 0x90, 0x71, 0x46, 0x64, 0xc2, 0x18, 0xc4, 0x58, 0x4c, 0x18, 0xf6, 0x3f, 0x7, 0x9e, 0x5, 0xc0, 0xb4, 0xa8, 0x22, 0xf8, 0xc, 0xbe, 0xd9, 0x4c, 0x12, 0xa, 0xf1, 0xa, 0x4c, 0xf5, 0x7c, 0xbe, 0xe9, 0xeb, 0x52, 0x1a, 0x16, 0xd4, 0x47, 0x64, 0xc2, 0x18, 0xdc, 0x3b, 0x5e, 0x7f, 0x47, 0x13, 0x45, 0x1, 0xe5, 0x46, 0x0, 0xe2, 0xa3, 0x3c, 0xf0, 0xda, 0x43, 0x44, 0x42, 0x77, 0xed, 0x78, 0xcf, 0x91, 0x30, 0x6d, 0x1e, 0x2b, 0xf6, 0xc, 0x56, 0x21, 0x4f, 0xeb, 0x68, 0x6f, 0x1e, 0xaf, 0x1f, 0x5a, 0x70, 0x22, 0x32, 0x21, 0x6a, 0x5c, 0x99, 0xcc, 0x92, 0x26, 0x1a, 0x9d, 0x99, 0x8e, 0x66, 0x1, 0x20, 0xbe, 0xb1, 0xd9, 0xd4, 0xb6, 0xe, 0xc9, 0xca, 0x43, 0xf4, 0xce, 0x85, 0x69, 0xf3, 0xd8, 0x98, 0x3b, 0x7c, 0xf0, 0x19, 0x22, 0x13, 0x62, 0xa7, 0x96, 0xc3, 0xa5, 0x8c, 0x66, 0xc2, 0xee, 0xc3, 0xb1, 0xb9, 0xa4, 0x9, 0x0, 0xa2, 0x43, 0xd9, 0xc5, 0xa3, 0x89, 0x88, 0xcc, 0x5a, 0x98, 0x36, 0x4f, 0xd1, 0xf6, 0x57, 0x88, 0x4c, 0x98, 0x2, 0xae, 0x8e, 0x4a, 0x74, 0x3b, 0x1e, 0x67, 0x42, 0x36, 0x19, 0x20, 0x46, 0x5c, 0xe3, 0xf2, 0x3e, 0xb2, 0xcf, 0x7b, 0xe5, 0x21, 0x7c, 0x95, 0xc0, 0xbc, 0xe1, 0xd1, 0x46, 0x63, 0xfb, 0xb, 0xc7, 0x7b, 0x3e, 0x21, 0x32, 0x61, 0xa, 0x7c, 0x71, 0xbc, 0xce, 0xae, 0xe6, 0x71, 0x50, 0x6, 0x86, 0x62, 0xc9, 0x0, 0x71, 0xe2, 0x5a, 0xaf, 0x5e, 0x45, 0xf8, 0x99, 0x4f, 0x3d, 0xc5, 0x33, 0x76, 0x27, 0x7e, 0xdb, 0x5f, 0x3f, 0x5e, 0x1b, 0x44, 0x26, 0x4c, 0x1, 0xd5, 0x51, 0xf, 0xad, 0xd7, 0x61, 0x67, 0xf3, 0x38, 0xa8, 0x8c, 0x2, 0x53, 0x57, 0x0, 0xf1, 0xa1, 0xc6, 0xe5, 0xdc, 0x21, 0x0, 0xea, 0x8, 0x3f, 0xb7, 0xca, 0x64, 0x2e, 0x3d, 0xde, 0xb7, 0x14, 0x66, 0xb0, 0xc6, 0xc4, 0x67, 0xd9, 0xc2, 0x2a, 0xf4, 0x8f, 0x22, 0x32, 0x61, 0x6c, 0xa1, 0x79, 0x8, 0x76, 0x37, 0xf, 0xcb, 0x5, 0xc2, 0x1e, 0x20, 0x5a, 0x5c, 0x2, 0xac, 0x8a, 0xf8, 0xb3, 0xaf, 0xc4, 0x6f, 0xbd, 0xe8, 0x35, 0x36, 0x68, 0x14, 0x7c, 0x96, 0x48, 0xa9, 0xfe, 0xb5, 0x46, 0x64, 0xc2, 0x94, 0x70, 0x4d, 0x99, 0x2f, 0x84, 0xd, 0x40, 0x43, 0x3a, 0xb0, 0x25, 0xcd, 0x0, 0x2f, 0x70, 0x24, 0x81, 0xbb, 0x4a, 0xa1, 0x73, 0x5c, 0x41, 0xf7, 0x97, 0xc8, 0x3f, 0xff, 0x7b, 0x71, 0xef, 0x36, 0x37, 0xeb, 0x33, 0x99, 0x4d, 0x19, 0xe, 0x25, 0xea, 0x7d, 0x36, 0x7a, 0xae, 0x9a, 0xfc, 0x71, 0x44, 0x26, 0x8c, 0x89, 0xca, 0x64, 0xd6, 0x8e, 0xf7, 0xb0, 0x4e, 0x67, 0x18, 0x23, 0x43, 0xbd, 0x3a, 0x38, 0xd4, 0x3f, 0xbe, 0x6a, 0x47, 0x44, 0x3f, 0x19, 0x87, 0x52, 0xe, 0x67, 0xf8, 0x6a, 0x9, 0x5c, 0x2b, 0x37, 0x2, 0xea, 0x33, 0x9e, 0x7b, 0xbc, 0xaf, 0x78, 0xbc, 0x6e, 0x11, 0x9a, 0x83, 0x5, 0x8f, 0x3e, 0x9b, 0xae, 0xd6, 0xd2, 0x30, 0x53, 0x8e, 0xc8, 0x84, 0xb1, 0x71, 0xed, 0x54, 0x5b, 0x8, 0xd9, 0xcc, 0x3e, 0xc1, 0xa0, 0x83, 0x4b, 0xdc, 0xdc, 0x5a, 0x63, 0x70, 0x81, 0xd0, 0x1c, 0x5, 0x57, 0xb0, 0xbd, 0x99, 0xc8, 0x7d, 0x28, 0xb1, 0x72, 0xe5, 0x19, 0xd8, 0xb0, 0xe3, 0xbc, 0x7f, 0x6e, 0x3d, 0xfc, 0xeb, 0x83, 0x67, 0x70, 0x80, 0xc8, 0x84, 0x68, 0x8d, 0x8e, 0x6b, 0xa, 0x5, 0xa7, 0xd6, 0x6f, 0x14, 0x8b, 0xc0, 0x84, 0x97, 0x2, 0xbc, 0x7d, 0x1, 0x8, 0x42, 0x73, 0xf8, 0xe7, 0x50, 0xb6, 0xc, 0xd6, 0x63, 0x42, 0x9, 0x16, 0x9f, 0xf5, 0x99, 0x25, 0xfd, 0xac, 0x57, 0x7c, 0xd7, 0xbf, 0x9e, 0x4a, 0x8b, 0x93, 0xa4, 0x10, 0x99, 0x30, 0x36, 0xf, 0x1e, 0x91, 0x6d, 0x29, 0x9c, 0x3e, 0xd3, 0x87, 0xc0, 0xbc, 0xf5, 0x34, 0x32, 0xf, 0x34, 0x57, 0x76, 0x9c, 0x39, 0x1c, 0xfc, 0x4b, 0x2, 0x14, 0xba, 0x1f, 0xa7, 0xae, 0xf5, 0x72, 0x2a, 0x50, 0xaf, 0x27, 0x76, 0x5f, 0x27, 0x9e, 0x9f, 0x99, 0x80, 0xa6, 0x3f, 0x81, 0xe9, 0xe3, 0x53, 0x55, 0xdf, 0x6a, 0x95, 0x25, 0x47, 0x64, 0x42, 0xc, 0x7c, 0xf2, 0x10, 0x32, 0xca, 0xd0, 0x16, 0x34, 0x55, 0x27, 0x28, 0x61, 0xf9, 0xcd, 0x53, 0x60, 0xfa, 0x14, 0x53, 0x86, 0xf4, 0x78, 0xe7, 0xf1, 0x9e, 0x12, 0xa1, 0x39, 0x88, 0x18, 0xe8, 0xbc, 0xac, 0x4c, 0x24, 0xc9, 0x5, 0x9f, 0x8d, 0x40, 0xb6, 0xd0, 0xa4, 0x9f, 0xd, 0x2b, 0x30, 0x95, 0xdd, 0x3f, 0x6f, 0xfb, 0x9f, 0x21, 0x32, 0x21, 0x16, 0x83, 0xe3, 0x32, 0x94, 0x9c, 0xa, 0xd1, 0xd, 0x21, 0xc2, 0x60, 0xdd, 0x85, 0x91, 0x81, 0x49, 0xe2, 0xbb, 0x53, 0x39, 0x24, 0x60, 0x81, 0x30, 0x94, 0x10, 0x98, 0x7b, 0x8c, 0xd1, 0x7a, 0xa2, 0xf7, 0xb7, 0xd5, 0x42, 0xd3, 0xb7, 0x2d, 0x8, 0x68, 0x86, 0x13, 0x98, 0xca, 0x27, 0xb7, 0x9a, 0x26, 0x47, 0x64, 0x42, 0x6c, 0xf8, 0x64, 0xcc, 0x7c, 0x4b, 0x2d, 0x40, 0x7b, 0x43, 0xbd, 0x16, 0xbf, 0x93, 0x3a, 0x20, 0xdd, 0xf1, 0xb8, 0xf6, 0x7c, 0xaf, 0x59, 0x7a, 0xb1, 0xa0, 0xd9, 0x3a, 0xc3, 0xa7, 0xe2, 0x83, 0x4f, 0x70, 0x1e, 0x3b, 0x55, 0x80, 0x9d, 0x99, 0x89, 0xdf, 0x46, 0x15, 0xd8, 0x3f, 0x46, 0xaf, 0x3, 0xc6, 0xe8, 0x7b, 0xe9, 0x68, 0x6, 0xb, 0x91, 0x9, 0x31, 0xe1, 0x93, 0x35, 0x3b, 0xc3, 0x99, 0x35, 0x36, 0x30, 0xbe, 0x6b, 0x9b, 0x10, 0x98, 0x20, 0xba, 0xf, 0xac, 0x3, 0xfb, 0x18, 0x41, 0x60, 0x37, 0x2, 0xf3, 0xd6, 0xe3, 0x7d, 0x4a, 0x60, 0xd6, 0x9, 0xdc, 0x6f, 0x88, 0xbd, 0x31, 0xe5, 0xb4, 0xc8, 0x9c, 0x87, 0xd9, 0xff, 0x90, 0x20, 0xf0, 0x54, 0x3a, 0x2c, 0xec, 0x8f, 0xc8, 0x84, 0xd8, 0xa2, 0x5a, 0x9f, 0xf2, 0x16, 0x21, 0x11, 0x19, 0xe, 0xeb, 0xc9, 0x28, 0xfb, 0xb6, 0x17, 0x2, 0x13, 0x9a, 0xa, 0x4d, 0x13, 0x4, 0xaa, 0xfe, 0x56, 0xd0, 0x74, 0xad, 0x4, 0xa6, 0x6b, 0xb6, 0x61, 0xeb, 0x69, 0x2b, 0x53, 0x14, 0x9a, 0x47, 0x81, 0x36, 0xd, 0xfb, 0xef, 0x2f, 0xca, 0x43, 0xc7, 0x3b, 0x22, 0x13, 0x26, 0x87, 0x6f, 0x79, 0xb, 0x84, 0xa6, 0x9b, 0x8b, 0x40, 0x87, 0x7f, 0x85, 0xc0, 0x84, 0xe, 0x1c, 0x8f, 0x71, 0x6c, 0x9c, 0x10, 0xd4, 0x8f, 0xc0, 0x34, 0x9b, 0x66, 0x52, 0x23, 0x34, 0xc0, 0x35, 0xb3, 0x33, 0xac, 0xd3, 0xec, 0x26, 0xe0, 0xeb, 0x5c, 0x60, 0x22, 0x32, 0x21, 0x66, 0xa7, 0xe6, 0xb3, 0xe0, 0x18, 0xa1, 0xb9, 0x9f, 0x52, 0x9e, 0x36, 0x63, 0x2c, 0x3, 0xdb, 0x9c, 0x4d, 0x3e, 0xd0, 0x55, 0xff, 0x30, 0xa5, 0x77, 0x6e, 0x84, 0xac, 0xa6, 0xf, 0x73, 0xf1, 0x5f, 0x2f, 0xad, 0x9e, 0x45, 0x9d, 0x68, 0x3b, 0x84, 0xa, 0xcd, 0x85, 0x16, 0x52, 0x25, 0x5d, 0xe8, 0xa7, 0xb1, 0x77, 0x23, 0x61, 0x4b, 0x57, 0x7a, 0x11, 0x98, 0x88, 0x4c, 0x88, 0x95, 0x6d, 0x80, 0xa1, 0xb9, 0x16, 0x32, 0x26, 0x86, 0x42, 0x1b, 0x97, 0x90, 0xc5, 0xf1, 0x4a, 0xcc, 0x9f, 0xf4, 0x65, 0x60, 0x20, 0x29, 0x9a, 0x64, 0xba, 0xe7, 0x42, 0x56, 0xd3, 0xc5, 0x85, 0xf8, 0x1f, 0x8a, 0xa0, 0x2, 0xc7, 0x4d, 0xe2, 0xed, 0xb1, 0xd6, 0x36, 0xc9, 0x77, 0x67, 0x73, 0xa1, 0x6d, 0xde, 0x85, 0x90, 0xd5, 0x5c, 0xe8, 0x4, 0xc3, 0x3c, 0xe0, 0x77, 0x7a, 0x13, 0x98, 0x88, 0x4c, 0x88, 0x19, 0x65, 0x48, 0x7d, 0x33, 0x27, 0xe6, 0x4c, 0xe5, 0x5c, 0xd, 0x8c, 0xc9, 0x1a, 0x85, 0x1a, 0x17, 0x25, 0xe6, 0xdf, 0x4a, 0x87, 0x8b, 0xbc, 0x21, 0x79, 0xd6, 0xba, 0xcf, 0x3c, 0x34, 0xe8, 0x9f, 0x64, 0x9c, 0xf6, 0x8b, 0xa3, 0x65, 0x40, 0xdb, 0xaf, 0x32, 0x69, 0x9b, 0x2a, 0x50, 0x68, 0x1a, 0x1, 0x9e, 0x6b, 0x1f, 0x33, 0x7d, 0x29, 0xc4, 0xf, 0x3e, 0xf4, 0x2d, 0x30, 0x11, 0x99, 0x10, 0x3b, 0x21, 0x65, 0x54, 0x16, 0xe2, 0x7f, 0x82, 0x4d, 0x4a, 0xe2, 0xf2, 0x42, 0x8b, 0xcb, 0xd0, 0x4c, 0x91, 0xc9, 0x16, 0xd4, 0x74, 0x33, 0x8, 0xc4, 0x4, 0x27, 0xa1, 0x25, 0x4e, 0x66, 0x96, 0x23, 0x2c, 0x32, 0x6f, 0xc3, 0x8b, 0x40, 0x41, 0xb4, 0x96, 0xfc, 0xd6, 0x4b, 0xab, 0xfe, 0xf5, 0x3a, 0xb0, 0x9f, 0x19, 0xb1, 0x95, 0xcb, 0x32, 0xd, 0x3b, 0xc1, 0x10, 0x22, 0xae, 0x7, 0x9b, 0xc1, 0x42, 0x64, 0x42, 0xec, 0x84, 0x44, 0x5a, 0x66, 0xc3, 0xc1, 0x45, 0x6, 0x51, 0xab, 0x72, 0xd4, 0x3f, 0x74, 0xf4, 0x1e, 0x92, 0xc1, 0x35, 0x9b, 0x6, 0x3a, 0x29, 0xb4, 0xb, 0xd9, 0x52, 0xb7, 0x70, 0x52, 0xb, 0xed, 0x14, 0x73, 0x14, 0x9b, 0x73, 0x79, 0x5e, 0x2f, 0xed, 0x3b, 0x6e, 0x73, 0x14, 0x98, 0xb6, 0xbd, 0x7a, 0xdb, 0xa0, 0x9f, 0x99, 0x76, 0xbe, 0x94, 0x34, 0x67, 0xb8, 0xda, 0x24, 0x18, 0xaa, 0x6, 0xe2, 0x1d, 0x91, 0x9, 0x8, 0x4d, 0x8b, 0xa5, 0xa4, 0x39, 0x6d, 0xb2, 0xd0, 0x51, 0xfa, 0x37, 0x69, 0xb6, 0xe1, 0x69, 0xa3, 0x8d, 0x4b, 0xe8, 0x9a, 0x2e, 0x97, 0x18, 0x65, 0x77, 0x67, 0xbe, 0x2, 0xe0, 0xb4, 0x45, 0xc0, 0x62, 0x8b, 0xcd, 0xd4, 0x67, 0x20, 0x4a, 0x69, 0x96, 0x61, 0xcb, 0x59, 0x60, 0xee, 0xfa, 0x80, 0x26, 0xfd, 0xec, 0x4c, 0xf7, 0xb1, 0x8b, 0x44, 0x2, 0x1a, 0x5b, 0x5c, 0x86, 0x26, 0x18, 0x8c, 0x6f, 0x3c, 0x91, 0x1, 0x13, 0xc, 0x88, 0x4c, 0x48, 0x55, 0x68, 0xce, 0x24, 0x8d, 0x69, 0x13, 0x73, 0xf2, 0xc7, 0xf, 0xfd, 0xb5, 0x89, 0x70, 0x36, 0xd9, 0x4b, 0xdf, 0xb3, 0x82, 0x77, 0x71, 0x45, 0xbc, 0x73, 0xba, 0x67, 0xd6, 0xa8, 0x71, 0xd9, 0x66, 0x6d, 0xef, 0x42, 0x7, 0x85, 0xa9, 0x9d, 0x1a, 0x74, 0x64, 0x9, 0xe9, 0xdb, 0x6, 0x63, 0xf7, 0x1c, 0x81, 0xf9, 0x4b, 0x3f, 0x3b, 0x91, 0xf0, 0xc, 0xdc, 0x91, 0x16, 0x57, 0x53, 0xe, 0x68, 0x6c, 0x3f, 0xd0, 0x44, 0x5c, 0xd6, 0xba, 0xed, 0x6, 0x5f, 0xd3, 0x8b, 0xc8, 0x84, 0x29, 0x46, 0xb3, 0x21, 0x98, 0x69, 0x93, 0x1b, 0x99, 0x4e, 0x66, 0x53, 0x7d, 0x4e, 0xb3, 0xce, 0xc6, 0x14, 0x1d, 0x6e, 0x9a, 0x2d, 0x54, 0xeb, 0x5a, 0x9b, 0x64, 0x2f, 0x6d, 0xee, 0x1d, 0xaf, 0xb3, 0xab, 0x13, 0x8c, 0x13, 0x3b, 0x97, 0xe6, 0x59, 0x92, 0x72, 0x27, 0xa0, 0x9a, 0x6a, 0x76, 0x73, 0xae, 0x3f, 0x7f, 0xd3, 0x25, 0x1, 0x66, 0xbd, 0xdc, 0x15, 0xdd, 0x6a, 0x6f, 0xc0, 0xfb, 0x56, 0xc2, 0xca, 0xb3, 0xed, 0xb, 0x68, 0xda, 0xda, 0xd5, 0xa1, 0x82, 0x94, 0xb3, 0x9d, 0xcf, 0xdb, 0xd4, 0x7, 0x8c, 0xb6, 0xc1, 0x13, 0x91, 0x9, 0x53, 0x8d, 0x66, 0x43, 0x1d, 0x99, 0xa9, 0x43, 0x67, 0xca, 0xa9, 0xc4, 0x64, 0x5c, 0x66, 0xfa, 0x33, 0xdd, 0x68, 0x7, 0x7b, 0xab, 0x7f, 0x2e, 0x5a, 0xfc, 0xcd, 0x4a, 0x8b, 0xcb, 0x36, 0x4e, 0xdf, 0xfe, 0x5b, 0x87, 0x28, 0x2c, 0x87, 0x4a, 0x56, 0x33, 0x6f, 0x8c, 0x43, 0x6b, 0x13, 0xd4, 0x1c, 0x59, 0x62, 0xc0, 0xee, 0x57, 0xb1, 0xa, 0x82, 0x23, 0x4b, 0x58, 0xfe, 0xd0, 0xe3, 0xb8, 0xa9, 0x80, 0x31, 0xe3, 0xb6, 0xa2, 0x2b, 0x1d, 0x64, 0x25, 0xcd, 0x36, 0x9f, 0xd9, 0x36, 0xb7, 0x8b, 0xe7, 0xd5, 0x35, 0xc5, 0x8e, 0x2f, 0xb8, 0x6c, 0x11, 0x6c, 0x6d, 0x3b, 0x8, 0xfc, 0x5a, 0xf3, 0x8a, 0xbe, 0xa, 0x13, 0xc4, 0x18, 0xe2, 0x26, 0xd9, 0xc9, 0x99, 0xbe, 0x2e, 0xb5, 0x23, 0xbc, 0xd3, 0x5f, 0xeb, 0x1, 0x1d, 0x92, 0xfa, 0xff, 0x8f, 0xf5, 0xd7, 0xb2, 0x63, 0xe3, 0x56, 0x69, 0x3, 0xdc, 0xa5, 0x93, 0x7a, 0xd0, 0x6d, 0x34, 0xf7, 0x10, 0x6, 0x8b, 0x96, 0xc6, 0x1f, 0xa6, 0x8f, 0x1a, 0x4b, 0xef, 0xe5, 0x39, 0x23, 0xdf, 0x26, 0x23, 0x59, 0x58, 0xfd, 0xca, 0xf4, 0x6f, 0x75, 0xdd, 0xeb, 0x3e, 0x56, 0x8f, 0x70, 0x7f, 0x85, 0x35, 0x86, 0x4b, 0xe9, 0x26, 0xe3, 0xfa, 0xa0, 0xc7, 0x2d, 0xd9, 0xcb, 0x30, 0x11, 0xf5, 0x56, 0x8b, 0xb2, 0x36, 0xb3, 0x29, 0x73, 0x2b, 0x48, 0x30, 0xfd, 0xeb, 0x6e, 0x20, 0xa1, 0x9f, 0x7c, 0x5f, 0x42, 0x64, 0xc2, 0x54, 0x31, 0x53, 0x4a, 0xca, 0xb8, 0x2c, 0x5b, 0x1a, 0x97, 0x4b, 0xed, 0xac, 0xb6, 0xfa, 0xba, 0xd3, 0x3f, 0xd7, 0x2d, 0xc, 0x47, 0xa1, 0x8d, 0xde, 0x1b, 0xeb, 0xe7, 0x59, 0x8f, 0xd1, 0x72, 0x1f, 0xe2, 0xd2, 0xe6, 0x93, 0xf8, 0x67, 0x29, 0x99, 0x3a, 0x7, 0xd3, 0x27, 0xdf, 0x6a, 0x81, 0xd8, 0xd5, 0xc6, 0x8b, 0x72, 0x27, 0xb0, 0x7c, 0xb0, 0xc6, 0xed, 0x77, 0x2b, 0xb8, 0x69, 0x3b, 0xe, 0x8e, 0x2c, 0x87, 0x7f, 0x6c, 0xfd, 0xdc, 0xc7, 0x18, 0x5e, 0xcb, 0xc8, 0xd9, 0xa6, 0x89, 0x63, 0x4a, 0xdd, 0x5d, 0x48, 0xfb, 0xa2, 0xff, 0xbb, 0xfd, 0x6b, 0x6b, 0x5, 0x33, 0x77, 0x56, 0x7f, 0x6b, 0xda, 0x9f, 0xd4, 0x18, 0xf8, 0x5d, 0xff, 0x1f, 0x85, 0x74, 0xbf, 0x5f, 0x60, 0xa9, 0x6d, 0x75, 0x34, 0x7d, 0x9, 0x91, 0x9, 0x53, 0x47, 0x9, 0xab, 0xcf, 0xd2, 0x7c, 0x53, 0xcc, 0xae, 0x30, 0x9c, 0xef, 0x11, 0xb3, 0xdb, 0x80, 0xdf, 0x1f, 0x9a, 0x8d, 0x36, 0x2a, 0x7d, 0x47, 0xdd, 0x26, 0xc2, 0x2f, 0xe9, 0x72, 0xd0, 0x40, 0x44, 0xad, 0x3b, 0x16, 0x9b, 0xb6, 0xf3, 0x2e, 0x1d, 0xfd, 0xb2, 0xa, 0xf8, 0x5b, 0x43, 0xae, 0x3, 0xed, 0x3b, 0x30, 0xcc, 0x2d, 0xe9, 0x70, 0xae, 0x6d, 0xe1, 0x85, 0x74, 0xb7, 0x81, 0x6c, 0x76, 0xa0, 0x4f, 0x6c, 0x3d, 0xc4, 0x5c, 0x9f, 0x89, 0x85, 0xdd, 0x31, 0xb6, 0x92, 0x8, 0xeb, 0x1e, 0x23, 0x32, 0x21, 0x5, 0xd4, 0xc0, 0x3a, 0x91, 0xe7, 0xac, 0x64, 0x1f, 0x4e, 0x2c, 0x36, 0x83, 0xba, 0xd6, 0x6, 0x75, 0x48, 0xa3, 0xa2, 0xa6, 0x40, 0xbf, 0x9, 0x99, 0x4a, 0x68, 0x2f, 0x36, 0x3f, 0xc, 0x38, 0xae, 0x62, 0x1b, 0xbf, 0x88, 0xcb, 0x7e, 0x7d, 0xc1, 0xa9, 0x6e, 0xdf, 0x2e, 0xc5, 0xe6, 0x4b, 0x2, 0x72, 0x6c, 0x3f, 0xa0, 0xb2, 0xb8, 0x9f, 0x25, 0xe2, 0x43, 0x35, 0xd8, 0xf8, 0x3, 0x29, 0x61, 0xea, 0x40, 0x9e, 0x4a, 0x9a, 0x27, 0xd9, 0x6c, 0xb4, 0xd0, 0xfb, 0x4d, 0x47, 0xed, 0x43, 0xdf, 0xa3, 0x59, 0xa2, 0xc0, 0xb4, 0x1e, 0xb4, 0x15, 0x9b, 0xaa, 0x1f, 0x99, 0x22, 0xdb, 0xf, 0x19, 0xdd, 0xf7, 0x5b, 0x7d, 0xef, 0x8, 0xcc, 0x61, 0xc4, 0xa6, 0xf2, 0x7, 0xcb, 0xc4, 0xfa, 0xd8, 0xd6, 0xba, 0xb7, 0x28, 0xb3, 0x97, 0x88, 0x4c, 0xc8, 0xc1, 0x98, 0xa7, 0x22, 0x36, 0x37, 0xfa, 0x3e, 0x7e, 0xd3, 0x2, 0x73, 0x33, 0xf2, 0xe7, 0x31, 0x3b, 0x16, 0xd9, 0xd8, 0x3, 0x5d, 0x39, 0xcb, 0xdf, 0xf4, 0xd7, 0x4d, 0xa2, 0xf7, 0x78, 0x6e, 0xdd, 0x23, 0xe3, 0x66, 0x78, 0xb1, 0xb9, 0xb2, 0xda, 0x7f, 0xaa, 0xe2, 0xfe, 0xc1, 0xa, 0x52, 0x26, 0x15, 0x9c, 0xfd, 0xd1, 0xd1, 0xe9, 0x19, 0x10, 0x90, 0x82, 0xd8, 0x1c, 0xe4, 0x8c, 0xd6, 0xe, 0x8d, 0xe2, 0x5a, 0x9e, 0x33, 0x96, 0xef, 0x23, 0x34, 0x28, 0x66, 0x57, 0xe7, 0x3e, 0x11, 0xff, 0x10, 0xb1, 0xb0, 0xff, 0x72, 0xa0, 0x1d, 0xb7, 0xd8, 0xbb, 0xd1, 0xc7, 0xea, 0x7b, 0x4b, 0xc, 0x4c, 0xc6, 0x89, 0x1e, 0x10, 0x96, 0xaf, 0xf5, 0x38, 0xb9, 0x12, 0xb2, 0xff, 0xb1, 0xf4, 0xb1, 0x13, 0x79, 0x2e, 0xed, 0x16, 0xfb, 0x78, 0x7f, 0xd8, 0x33, 0x2e, 0x26, 0x67, 0xa3, 0xfe, 0xf0, 0xe7, 0xbf, 0xfe, 0x5d, 0x7d, 0x2d, 0x5f, 0x78, 0xbd, 0xa2, 0x5f, 0x42, 0x42, 0x98, 0x7a, 0x76, 0xef, 0xa4, 0xfb, 0xd2, 0x41, 0x6d, 0x44, 0xa5, 0x1a, 0x67, 0xa6, 0x64, 0x46, 0x3d, 0xc1, 0x76, 0x2d, 0xe4, 0x79, 0x1d, 0x6c, 0x1d, 0xf9, 0x3d, 0xd8, 0x9f, 0x75, 0x57, 0x18, 0x20, 0x4, 0xe2, 0x63, 0x66, 0x8d, 0xd7, 0x99, 0xc4, 0xb9, 0x1e, 0xd8, 0x1e, 0xc3, 0x1b, 0xfa, 0xd1, 0xe4, 0x6c, 0x57, 0x19, 0x91, 0x4f, 0xa8, 0xf4, 0xf5, 0x65, 0xa, 0x82, 0xf2, 0xdf, 0xff, 0xfa, 0xa7, 0xb7, 0xc8, 0x4, 0xc8, 0x11, 0x65, 0x54, 0x8e, 0x7, 0x74, 0x60, 0x95, 0x25, 0xc2, 0xee, 0x10, 0x36, 0x0, 0x8d, 0x44, 0xa7, 0xba, 0xfa, 0x2c, 0x3, 0x73, 0x8, 0x53, 0xce, 0x66, 0xcb, 0x18, 0x4e, 0xb6, 0x7f, 0xa9, 0x7e, 0xf5, 0x46, 0xe, 0xef, 0x2c, 0xef, 0xb2, 0x2f, 0x99, 0x7e, 0x54, 0x4d, 0xad, 0xb1, 0x7c, 0x44, 0x26, 0xbb, 0xcb, 0x21, 0x67, 0x4c, 0xd4, 0xb8, 0xb2, 0xa2, 0xda, 0x42, 0xb, 0x4f, 0x23, 0x42, 0x65, 0xe7, 0x35, 0x9b, 0x7d, 0xe5, 0x8d, 0x6c, 0xa7, 0x73, 0xb7, 0xe7, 0xdf, 0x0, 0xa0, 0x9d, 0x63, 0x36, 0x63, 0x6e, 0xb5, 0x13, 0x30, 0x8a, 0x35, 0x76, 0xf7, 0x95, 0x23, 0x7a, 0x29, 0x90, 0x34, 0x81, 0xdf, 0xae, 0x6d, 0x50, 0x7c, 0xb7, 0x5e, 0xaf, 0x69, 0xfe, 0xac, 0xfa, 0xd7, 0x6e, 0xbf, 0x39, 0x7e, 0xa1, 0x1f, 0x95, 0xe, 0xbf, 0x60, 0xff, 0xdc, 0xa6, 0xd6, 0xe6, 0x24, 0xf9, 0xaf, 0x0, 0x3, 0x0, 0x58, 0xbf, 0x46, 0x22, 0x7, 0x65, 0x51, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)parse_logo3x_png { + return [NSData dataWithBytes:parse_logo3x_png length:sizeof(parse_logo3x_png)]; + } + + const unsigned char twitter_icon_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x12, 0x8, 0x6, 0x0, 0x0, 0x0, 0x5f, 0x25, 0x2e, 0x2d, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x1, 0xcb, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x29, 0x2e, 0xcd, 0x3d, 0x0, 0x0, 0x2, 0x34, 0x49, 0x44, 0x41, 0x54, 0x38, 0x11, 0x8d, 0x93, 0xbd, 0x6b, 0x54, 0x41, 0x14, 0x47, 0xdf, 0x6e, 0x36, 0x51, 0x8b, 0xa0, 0x9d, 0x44, 0x8b, 0x10, 0xb4, 0x10, 0xac, 0x4, 0xb1, 0x52, 0x50, 0x10, 0x8b, 0x80, 0x76, 0x36, 0x16, 0xfe, 0x7, 0xda, 0x5a, 0x58, 0x8b, 0xad, 0x88, 0x20, 0x16, 0x82, 0x36, 0x36, 0x12, 0x10, 0x24, 0xda, 0x89, 0x48, 0x10, 0x92, 0x42, 0xc1, 0x2e, 0x5a, 0xaa, 0x85, 0x22, 0x2a, 0x4a, 0xd4, 0xe8, 0xee, 0xbe, 0x9c, 0x33, 0x6f, 0xee, 0x63, 0xb2, 0x1b, 0xd4, 0xb, 0xe7, 0xdd, 0x3b, 0x77, 0x66, 0x7e, 0xef, 0xce, 0x57, 0x55, 0x61, 0x75, 0x5d, 0x77, 0xb3, 0x9f, 0xd0, 0x6f, 0x65, 0x8c, 0x49, 0x7d, 0xf8, 0x39, 0xb8, 0xb, 0xef, 0xe0, 0x3b, 0x2c, 0xc3, 0xf9, 0x98, 0x43, 0x7c, 0x6, 0xe, 0xa4, 0x36, 0xc1, 0x7d, 0x38, 0x6e, 0x3, 0x3f, 0x5, 0xe9, 0x47, 0xa9, 0xb3, 0xc9, 0x85, 0xe8, 0x41, 0xfa, 0xbe, 0xc0, 0x56, 0xf6, 0x80, 0xe4, 0x4b, 0x58, 0x84, 0x6d, 0xa, 0xed, 0x86, 0x3e, 0xfc, 0x84, 0x13, 0x85, 0xd8, 0x24, 0xed, 0x2e, 0x74, 0x8a, 0xdc, 0x12, 0x6d, 0x6d, 0xd, 0xfa, 0x43, 0xc, 0xff, 0x1b, 0xfe, 0x80, 0xe6, 0xa, 0xae, 0xc2, 0x39, 0x85, 0x67, 0x21, 0x55, 0xd1, 0x8c, 0xab, 0x2f, 0xd3, 0xee, 0x15, 0x62, 0x29, 0x26, 0x77, 0x18, 0x14, 0x1a, 0x64, 0x41, 0xe3, 0x1c, 0xa6, 0xfc, 0x2f, 0xda, 0xda, 0x2b, 0x98, 0x4b, 0xf3, 0x9, 0x56, 0xcd, 0x60, 0x56, 0xae, 0xbd, 0x81, 0x8b, 0xb0, 0xb7, 0xf8, 0xc1, 0x31, 0x3b, 0xb0, 0x41, 0x26, 0x9, 0x13, 0xeb, 0xa3, 0x62, 0x75, 0xb6, 0xc7, 0x1c, 0xab, 0x3e, 0xb, 0x9a, 0x3, 0x5c, 0x5a, 0xd8, 0x57, 0x82, 0x27, 0x70, 0x5, 0x6e, 0xe4, 0x64, 0x2b, 0x98, 0x57, 0x68, 0x3a, 0xe6, 0x3c, 0x56, 0x94, 0x76, 0xaf, 0xc7, 0xc7, 0xaa, 0x76, 0xc0, 0x23, 0x98, 0x87, 0x1, 0xf4, 0xa1, 0x86, 0x9d, 0xe0, 0xbe, 0xa7, 0xbd, 0x57, 0xa1, 0xd3, 0x69, 0xb7, 0xbc, 0x22, 0x76, 0x4c, 0x69, 0x6b, 0xb9, 0x31, 0x74, 0xff, 0xd6, 0xe1, 0x16, 0x78, 0x13, 0xdc, 0x8a, 0x5e, 0x31, 0x61, 0x48, 0xce, 0x1f, 0x69, 0x5d, 0xf2, 0x9b, 0x6e, 0x8b, 0xc9, 0xe6, 0x5f, 0xed, 0xf, 0x56, 0xcd, 0x61, 0x13, 0xe, 0xfe, 0x44, 0x70, 0x1b, 0xa6, 0x60, 0x58, 0x88, 0xd2, 0xac, 0x3a, 0x4c, 0x9c, 0xc4, 0x5b, 0xc0, 0x98, 0x68, 0x1a, 0xd0, 0x54, 0x1d, 0x7d, 0xcb, 0xe6, 0xb0, 0x66, 0x25, 0x4c, 0x9e, 0x86, 0xa7, 0x10, 0xe6, 0x1, 0xb5, 0x7b, 0xf9, 0x8f, 0x38, 0xe, 0xfc, 0x3d, 0xe3, 0x76, 0x25, 0x55, 0xdf, 0x1, 0x8d, 0xb8, 0xfc, 0x33, 0xc4, 0x37, 0xc1, 0x3b, 0xaa, 0xfd, 0xaf, 0xf8, 0x7a, 0x33, 0xbc, 0xbe, 0x96, 0x45, 0x9b, 0xd7, 0x4b, 0x32, 0x84, 0x4f, 0x12, 0x7f, 0x0, 0x85, 0xbd, 0x1d, 0x9b, 0x84, 0xf3, 0xd, 0x18, 0x5d, 0x45, 0x54, 0xfb, 0x8d, 0xf1, 0xb3, 0x6d, 0xb5, 0x6, 0x1a, 0xc9, 0x74, 0xd4, 0xf8, 0x3b, 0xd0, 0x5a, 0xf9, 0x10, 0x48, 0x8e, 0x8a, 0xda, 0x8e, 0x6a, 0x2f, 0x64, 0x9d, 0xf6, 0x61, 0x25, 0x51, 0x6, 0xb4, 0x9, 0xe2, 0x4b, 0xf0, 0x2, 0x3e, 0x43, 0x3c, 0xad, 0x52, 0xd4, 0xb4, 0xab, 0x89, 0x97, 0xb6, 0x90, 0xaa, 0x2b, 0xa, 0x8c, 0xf6, 0x98, 0x67, 0x92, 0xcf, 0xf7, 0x35, 0x68, 0xb1, 0x5c, 0xc5, 0x15, 0xf4, 0x31, 0x44, 0xee, 0x21, 0x71, 0x6c, 0x65, 0xdc, 0x8c, 0x46, 0x8f, 0x8e, 0x3d, 0xb0, 0xf, 0xf6, 0xc3, 0x69, 0xb8, 0x7, 0x3f, 0x40, 0x73, 0xaf, 0x15, 0x51, 0x4c, 0x14, 0xf, 0xbb, 0x1e, 0x15, 0x91, 0x68, 0xe, 0x2c, 0x12, 0x7a, 0x92, 0x87, 0x60, 0x1, 0xa2, 0xa, 0xc2, 0xbf, 0xda, 0x33, 0x7a, 0x4f, 0x85, 0x6, 0xf1, 0xb8, 0x28, 0x9d, 0x71, 0x68, 0xfa, 0xa3, 0xe0, 0x84, 0x23, 0xe0, 0x9, 0xcf, 0x80, 0xaf, 0xee, 0x23, 0xbc, 0x85, 0x15, 0x58, 0xe4, 0x1, 0x3d, 0xc7, 0x5b, 0x90, 0x4b, 0xf7, 0x89, 0x8f, 0x3e, 0x6b, 0xbb, 0xab, 0xd, 0x23, 0x5c, 0xa6, 0xb2, 0xce, 0x6d, 0xd4, 0xea, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)twitter_icon_png { + return [NSData dataWithBytes:twitter_icon_png length:sizeof(twitter_icon_png)]; + } + + const unsigned char twitter_icon2x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x24, 0x8, 0x6, 0x0, 0x0, 0x0, 0xf2, 0xd7, 0xd8, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x1, 0xcb, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x29, 0x2e, 0xcd, 0x3d, 0x0, 0x0, 0x5, 0xbd, 0x49, 0x44, 0x41, 0x54, 0x58, 0x9, 0xc5, 0x98, 0x5b, 0xa8, 0x95, 0x45, 0x14, 0xc7, 0xdd, 0x9e, 0xa3, 0x42, 0x56, 0x50, 0x91, 0x5, 0x86, 0x9c, 0xae, 0x44, 0x50, 0x68, 0x46, 0xd1, 0x85, 0x12, 0x7a, 0x28, 0x22, 0x88, 0xa0, 0x22, 0x2, 0xe9, 0x25, 0x90, 0x82, 0xea, 0x21, 0xe8, 0xf6, 0x90, 0xd1, 0x4b, 0x29, 0xbd, 0x56, 0x4, 0x81, 0x51, 0x50, 0xd4, 0x93, 0x2f, 0x85, 0x74, 0x21, 0x3a, 0xa6, 0x60, 0x54, 0x90, 0x12, 0x89, 0x91, 0xfa, 0x90, 0x5d, 0xf5, 0xa1, 0x9b, 0x78, 0x3d, 0xbb, 0xdf, 0x6f, 0xed, 0x59, 0xdf, 0xf9, 0xbe, 0x7d, 0xf6, 0xf6, 0xec, 0xbd, 0xf, 0xda, 0x82, 0xff, 0x59, 0x6b, 0xd6, 0xcc, 0xac, 0xf9, 0xcf, 0xcc, 0x9a, 0xd9, 0xf3, 0x9d, 0x79, 0xf3, 0x90, 0x76, 0xbb, 0xdd, 0x52, 0x2b, 0xd8, 0xf3, 0x3b, 0xd6, 0xdc, 0xff, 0xd6, 0x63, 0x61, 0x4f, 0x80, 0xe7, 0xc1, 0x24, 0xf8, 0x19, 0x1c, 0x7, 0x3f, 0x81, 0xcf, 0xc0, 0x33, 0xe0, 0x82, 0x1c, 0x11, 0x7b, 0x1, 0x18, 0xcf, 0xb2, 0x9a, 0xf2, 0x32, 0xd0, 0xd2, 0x8, 0xb2, 0xe8, 0xd3, 0xc0, 0xe9, 0xa5, 0x72, 0xac, 0xde, 0x78, 0x14, 0x9b, 0x58, 0x55, 0xc, 0x6c, 0x89, 0x1e, 0x4, 0x75, 0x99, 0xaa, 0x17, 0xb0, 0xff, 0x6, 0x4f, 0x74, 0x8f, 0x85, 0x6f, 0x5, 0xf8, 0x10, 0x3c, 0x1b, 0x75, 0x18, 0x31, 0x13, 0xf4, 0x23, 0x60, 0x5b, 0x76, 0x48, 0x7f, 0x96, 0x87, 0xd1, 0xf4, 0x8d, 0x5d, 0x42, 0xb7, 0xc0, 0x46, 0x90, 0x72, 0x4, 0x43, 0x1c, 0x3, 0x12, 0x56, 0xa7, 0xf, 0x33, 0xe4, 0x2d, 0xfe, 0xde, 0x8, 0xd6, 0x82, 0xcd, 0x40, 0x39, 0x4, 0xce, 0x4b, 0xc2, 0xb9, 0xc2, 0x9b, 0xa2, 0xaa, 0xdd, 0x7e, 0x27, 0xc9, 0x51, 0x6e, 0x6c, 0x4b, 0xfa, 0x67, 0xd3, 0xf4, 0xcb, 0x98, 0x6f, 0x96, 0x98, 0xe, 0x98, 0x24, 0x25, 0xda, 0xb, 0xd6, 0x8b, 0x5e, 0x72, 0x7b, 0x63, 0x4c, 0x5a, 0x2c, 0x1, 0x7f, 0xd4, 0x5a, 0x7e, 0x80, 0xbd, 0xc0, 0x46, 0xe8, 0x85, 0x8d, 0xc6, 0xb3, 0x14, 0x68, 0x9f, 0x3b, 0x76, 0x7f, 0x89, 0xe7, 0xa, 0xf6, 0x22, 0xd8, 0xcf, 0x97, 0xc4, 0xd5, 0x7f, 0x82, 0x7b, 0xa, 0x8f, 0x55, 0xd8, 0xf, 0xe4, 0x1, 0x73, 0xb9, 0xcf, 0x2e, 0x5c, 0x8e, 0xa0, 0xef, 0x0, 0x5b, 0x69, 0xb0, 0xb4, 0xd5, 0x6a, 0x39, 0xe0, 0x98, 0x28, 0xf5, 0x7d, 0x15, 0x6d, 0x68, 0xde, 0x3a, 0x56, 0x1a, 0x3c, 0xd9, 0xb7, 0xe1, 0x9, 0x2a, 0x88, 0x21, 0x27, 0x77, 0xc8, 0xf1, 0x3e, 0x5, 0x57, 0xe2, 0xdb, 0x89, 0xfe, 0x8, 0xec, 0x5, 0xb1, 0x8a, 0x57, 0xe3, 0xc, 0x99, 0x42, 0x30, 0xe, 0x97, 0xe2, 0x7e, 0xf4, 0xbd, 0xd1, 0xa8, 0xd3, 0x6e, 0x9c, 0x72, 0x5f, 0xe2, 0x59, 0x87, 0xbe, 0x16, 0x28, 0xc6, 0xf2, 0x36, 0xe8, 0xb7, 0x9a, 0x3d, 0xfd, 0x1d, 0xa, 0x55, 0x3f, 0xe3, 0x28, 0x77, 0x26, 0xf, 0xb7, 0xfd, 0x22, 0xf0, 0xaf, 0x5e, 0xc4, 0x1, 0x14, 0x57, 0x36, 0xe5, 0x5d, 0x8c, 0x89, 0xec, 0x80, 0xed, 0x8a, 0x4b, 0x3e, 0x72, 0xb5, 0xe6, 0xcf, 0x74, 0x78, 0x94, 0x3a, 0xe5, 0x28, 0xe8, 0x49, 0xaa, 0xdb, 0x5f, 0x48, 0xd6, 0xdb, 0x66, 0x3e, 0xbb, 0x78, 0x91, 0xc3, 0xe8, 0xce, 0x99, 0xc2, 0xf0, 0xde, 0xfb, 0x1e, 0x28, 0x39, 0x48, 0xda, 0xd9, 0xd1, 0x6b, 0xe7, 0x45, 0xb0, 0x34, 0x9, 0xaa, 0x29, 0xcf, 0x7, 0xb1, 0xf2, 0xe8, 0xc8, 0x77, 0xf4, 0x3a, 0xa0, 0xc, 0x9c, 0xbf, 0x5d, 0x84, 0x73, 0xd1, 0xf6, 0x11, 0xe3, 0xa6, 0x32, 0x4e, 0x90, 0x75, 0xb0, 0x31, 0xf2, 0xee, 0x28, 0xce, 0x4d, 0x56, 0x20, 0x53, 0x1d, 0x25, 0x97, 0x98, 0x91, 0x39, 0x65, 0xbd, 0x77, 0xf4, 0xd3, 0xc0, 0x89, 0x79, 0xf5, 0xdc, 0x6, 0x16, 0xd3, 0x77, 0xca, 0xbc, 0x5, 0xc7, 0x81, 0xf9, 0xaf, 0xe4, 0xd9, 0x30, 0x48, 0x63, 0x17, 0x3a, 0xd5, 0x33, 0xff, 0xd2, 0xb7, 0x5d, 0xf3, 0x1e, 0x2f, 0xf6, 0x1b, 0xf8, 0xbf, 0x20, 0xc6, 0x22, 0x74, 0x9c, 0xd, 0x3, 0x67, 0xc3, 0xd7, 0xb1, 0x1d, 0xd0, 0x55, 0xa, 0xd2, 0x19, 0x84, 0xe, 0xde, 0x18, 0xfa, 0x24, 0x7e, 0x6, 0x58, 0xd, 0x9c, 0xe0, 0xe, 0xea, 0xde, 0x6, 0x6b, 0xc0, 0xd, 0x60, 0x2, 0x9f, 0xf2, 0x4b, 0x47, 0xcd, 0x23, 0x44, 0x83, 0x48, 0x71, 0xf, 0xac, 0xf2, 0x86, 0xca, 0x83, 0x1c, 0x5b, 0x5a, 0xad, 0x0, 0x3, 0x3e, 0x7, 0x94, 0x13, 0x1d, 0x14, 0xeb, 0xdc, 0xea, 0x4c, 0x15, 0xdb, 0xa7, 0x78, 0xd, 0xed, 0x2, 0x7b, 0x8b, 0xa3, 0x9e, 0x93, 0xc3, 0xd8, 0xa6, 0xa5, 0xb2, 0xd6, 0xa9, 0xa1, 0x3b, 0xb9, 0x8b, 0x3d, 0xee, 0xa, 0x20, 0x4b, 0x8a, 0xfd, 0x2, 0xb6, 0xd7, 0xdb, 0xe3, 0xc0, 0x6d, 0xa9, 0xb6, 0x16, 0x3b, 0xc5, 0xe, 0xb, 0xca, 0xca, 0xd9, 0xc6, 0x95, 0xd7, 0x67, 0xac, 0x33, 0xb1, 0x45, 0xe4, 0x13, 0x65, 0xcd, 0xb9, 0xc8, 0xfe, 0xee, 0xce, 0x49, 0xe8, 0xa, 0x2a, 0xbe, 0x63, 0xd0, 0x57, 0xd0, 0x5b, 0xc1, 0x16, 0xe0, 0xf5, 0x15, 0xa9, 0x81, 0xbf, 0x31, 0x72, 0x21, 0x4b, 0x75, 0x4c, 0xc8, 0xd9, 0x7b, 0xe, 0x6c, 0x2b, 0x9c, 0x84, 0xf9, 0x8c, 0x1a, 0x5c, 0xba, 0xc6, 0xc8, 0xce, 0xfb, 0xba, 0x23, 0x44, 0x5, 0x8d, 0x2f, 0xa4, 0x62, 0x17, 0xc8, 0xa5, 0x3f, 0x8c, 0x9d, 0xf9, 0xd3, 0xdd, 0xe7, 0x64, 0x97, 0x3d, 0x53, 0x2e, 0xa4, 0x79, 0xbb, 0x92, 0x89, 0x6f, 0x87, 0x9f, 0xb, 0xe2, 0x42, 0x74, 0xb6, 0x9c, 0xc2, 0x1e, 0xec, 0x6f, 0x74, 0x20, 0x1e, 0xac, 0x45, 0x61, 0xfd, 0x3f, 0x7f, 0xf2, 0x12, 0x70, 0x75, 0xe5, 0xa5, 0xc4, 0x4e, 0x6b, 0xc4, 0x1d, 0xaa, 0x81, 0xbc, 0xdf, 0x51, 0x71, 0x6b, 0x64, 0xa7, 0xe2, 0x3a, 0x35, 0xaa, 0xa4, 0x45, 0x92, 0xfb, 0x96, 0x85, 0xf4, 0xee, 0x9f, 0x8f, 0xae, 0xf8, 0xb8, 0xf4, 0xb1, 0xd4, 0x68, 0xf3, 0xd7, 0xb4, 0x30, 0x15, 0x5c, 0xe5, 0xcc, 0x23, 0xcc, 0x69, 0x29, 0x41, 0xc3, 0x51, 0xb7, 0xa7, 0x5b, 0x8c, 0x6e, 0xd5, 0x89, 0x11, 0xe5, 0x93, 0x12, 0x29, 0xcf, 0x59, 0x14, 0x83, 0x3d, 0x3, 0x7b, 0xc2, 0xf, 0xe1, 0x79, 0xa8, 0x34, 0x92, 0xb4, 0x79, 0x3c, 0x43, 0xba, 0x82, 0xce, 0xa8, 0x9f, 0xa3, 0xc3, 0x95, 0xf4, 0xce, 0x77, 0x11, 0x3f, 0x2e, 0xb1, 0xaa, 0xd5, 0xb5, 0x1c, 0xec, 0x21, 0xe1, 0x9d, 0x2a, 0xe9, 0xcd, 0xf8, 0xee, 0x6, 0x76, 0xc8, 0x3c, 0x6e, 0x74, 0xc0, 0x5f, 0xc9, 0x49, 0x20, 0x9f, 0xbb, 0xfd, 0x39, 0xb1, 0x77, 0x96, 0x1d, 0xcc, 0x14, 0x89, 0x71, 0xab, 0xe5, 0xa6, 0x41, 0x92, 0xde, 0x48, 0xcd, 0x25, 0xc0, 0x5f, 0x32, 0xd3, 0xc2, 0x3c, 0x47, 0x9d, 0x52, 0xd9, 0x50, 0x46, 0xf3, 0x76, 0x68, 0xc, 0x3e, 0x23, 0x4f, 0x21, 0xe7, 0x9b, 0x61, 0x31, 0x30, 0x45, 0xee, 0x2, 0x8f, 0x81, 0x95, 0xc0, 0x99, 0xfa, 0x3, 0x41, 0x8c, 0x4e, 0x90, 0xba, 0x4d, 0xdd, 0x5c, 0xc5, 0xd5, 0xf5, 0x5a, 0xfd, 0x1, 0x5c, 0xc5, 0x18, 0x7e, 0xa5, 0x54, 0x63, 0x65, 0xf0, 0xbc, 0x77, 0xe3, 0x91, 0x52, 0x88, 0x1c, 0xa4, 0xd2, 0xfc, 0x59, 0xe, 0x7e, 0x5, 0xe7, 0x0, 0x25, 0x26, 0x97, 0x64, 0xc3, 0xd1, 0x35, 0xfb, 0x68, 0x35, 0xf7, 0x3f, 0xeb, 0xa, 0x59, 0x53, 0x74, 0xfa, 0xd, 0xd1, 0x2b, 0x2e, 0x33, 0x8a, 0x9, 0xa0, 0x57, 0x83, 0xba, 0xf4, 0x7c, 0x7, 0x74, 0x3d, 0x9, 0x7b, 0xb6, 0x21, 0xc8, 0x6c, 0x7e, 0xc7, 0xf1, 0x6d, 0xa2, 0x7c, 0x9d, 0xbc, 0xb0, 0x67, 0xec, 0x7e, 0xd6, 0xd5, 0x75, 0x6c, 0xb9, 0xe, 0x3a, 0xbc, 0x67, 0x4, 0xc4, 0xcf, 0xf3, 0x7c, 0x9f, 0xce, 0x36, 0xf8, 0x28, 0xf5, 0xc6, 0xce, 0xf8, 0x37, 0x97, 0xb1, 0xab, 0x9d, 0xaf, 0x93, 0xeb, 0x69, 0xd3, 0x39, 0xe, 0x22, 0x7a, 0x11, 0x98, 0x4, 0x6d, 0x56, 0x52, 0x22, 0xbe, 0xfc, 0x3d, 0x98, 0x6, 0x1f, 0x85, 0x58, 0xbf, 0x3e, 0xf9, 0x39, 0xb6, 0x7e, 0x68, 0xb2, 0x39, 0x3, 0x8, 0xc5, 0x77, 0x1b, 0x7a, 0x21, 0x78, 0xd, 0xf4, 0x92, 0x7e, 0x4, 0x6, 0xf5, 0x1b, 0x33, 0xc9, 0x4e, 0xd6, 0xc6, 0x1e, 0x28, 0x15, 0xb2, 0x7d, 0xa5, 0x9, 0x56, 0x7d, 0x6c, 0x62, 0x5f, 0x7, 0x36, 0x80, 0xdd, 0xe0, 0x77, 0x90, 0x3, 0xd, 0x4a, 0xae, 0xbb, 0x1d, 0x21, 0xe2, 0x9f, 0x23, 0xea, 0x3d, 0x20, 0xbe, 0xd8, 0xd1, 0xd5, 0x98, 0x15, 0x91, 0x61, 0xc, 0x2, 0x78, 0x7, 0x37, 0x5e, 0x6d, 0x94, 0x1f, 0x6, 0xe6, 0xb5, 0x32, 0x6a, 0x7a, 0x78, 0x65, 0x29, 0x7e, 0x95, 0x5f, 0x26, 0x27, 0xb4, 0xbf, 0x70, 0xb3, 0xb, 0xd, 0x25, 0x55, 0x47, 0x7e, 0x11, 0x37, 0xbe, 0x8a, 0x69, 0x73, 0x3e, 0x78, 0x19, 0xe4, 0xea, 0xe, 0x4b, 0x96, 0xae, 0x31, 0xc1, 0xec, 0xbf, 0x87, 0xf2, 0xa5, 0x32, 0x44, 0xf, 0x46, 0x76, 0xf6, 0xe9, 0x44, 0xb0, 0xcb, 0x9, 0xf8, 0x12, 0x38, 0x0, 0x52, 0x86, 0x25, 0x6b, 0x4a, 0x78, 0x75, 0xa9, 0x95, 0xcd, 0x20, 0xee, 0x77, 0xf4, 0x50, 0x64, 0xbd, 0xc6, 0x96, 0x41, 0xdc, 0x44, 0x3f, 0x0, 0xdc, 0x7e, 0x3f, 0x97, 0x26, 0xc0, 0x35, 0xe0, 0x56, 0x70, 0x3d, 0xc8, 0x77, 0x85, 0xaf, 0x38, 0xf3, 0x6c, 0x90, 0x83, 0x61, 0x1b, 0x7f, 0x56, 0xf3, 0x7d, 0x90, 0x57, 0xd5, 0x7a, 0x7e, 0x10, 0x9e, 0xc2, 0x1f, 0x2b, 0x8b, 0x6d, 0xcc, 0xc1, 0x5, 0xc2, 0xcb, 0x81, 0xff, 0x61, 0xfc, 0x7, 0xb8, 0x5d, 0xb9, 0x65, 0x98, 0x95, 0xb8, 0x3a, 0xae, 0xaa, 0xd2, 0x38, 0x40, 0xde, 0x78, 0x89, 0x52, 0x67, 0x3b, 0xaf, 0x3f, 0xe3, 0x64, 0x1f, 0xcc, 0xf6, 0x16, 0xe0, 0xe4, 0x43, 0xb0, 0x47, 0x3f, 0x60, 0x74, 0x36, 0x5f, 0x1f, 0x4, 0x5f, 0x81, 0xba, 0xf8, 0xf5, 0x2a, 0x59, 0xa1, 0x2d, 0x24, 0xe3, 0xd5, 0x2c, 0x71, 0xed, 0xee, 0x36, 0xb8, 0x1a, 0xb2, 0x8d, 0xd2, 0x7d, 0x35, 0xa2, 0x8d, 0xb3, 0x91, 0xfe, 0x81, 0x35, 0xc1, 0xaa, 0x17, 0x9b, 0x9d, 0x28, 0xdf, 0x2, 0x5e, 0x5, 0x3f, 0x82, 0x51, 0xc5, 0xbe, 0xde, 0xdf, 0xab, 0xea, 0x44, 0x28, 0x67, 0x5a, 0xd4, 0xdd, 0x43, 0xd9, 0x91, 0x8b, 0x4, 0x52, 0xfb, 0x94, 0xab, 0x1e, 0x1b, 0xf8, 0xcc, 0xdb, 0x15, 0xc0, 0x5c, 0xf6, 0x21, 0x74, 0x31, 0x38, 0x17, 0x9c, 0x5, 0xbc, 0x37, 0x6d, 0x6b, 0xde, 0xff, 0x5, 0x7e, 0x3, 0xbb, 0xc1, 0x76, 0xf0, 0x25, 0xd8, 0x41, 0x2c, 0x5f, 0x7b, 0x21, 0x85, 0xa8, 0x5f, 0xd2, 0x8d, 0xa7, 0x62, 0xd6, 0xf, 0xa3, 0xff, 0x3, 0xf7, 0x93, 0x7f, 0xd4, 0xd5, 0x86, 0x9a, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)twitter_icon2x_png { + return [NSData dataWithBytes:twitter_icon2x_png length:sizeof(twitter_icon2x_png)]; + } + + const unsigned char twitter_icon3x_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x36, 0x8, 0x6, 0x0, 0x0, 0x0, 0x73, 0xe7, 0x4f, 0x3f, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x1, 0xcb, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x29, 0x2e, 0xcd, 0x3d, 0x0, 0x0, 0xb, 0x9, 0x49, 0x44, 0x41, 0x54, 0x68, 0x5, 0xd5, 0x9a, 0x7b, 0xa8, 0xe5, 0x55, 0x15, 0xc7, 0xe7, 0xce, 0x1d, 0xc7, 0xd1, 0x44, 0xcd, 0xde, 0x33, 0x15, 0xa9, 0x94, 0x56, 0x14, 0x64, 0x30, 0x44, 0xa1, 0x5d, 0x1a, 0x2c, 0x6d, 0xe8, 0x61, 0x94, 0x45, 0x30, 0x4a, 0x49, 0x52, 0x50, 0x4a, 0x18, 0x18, 0x64, 0xd0, 0x3, 0x26, 0x8, 0x89, 0xe8, 0x41, 0xfd, 0xa3, 0x21, 0x15, 0xa5, 0xff, 0x68, 0x62, 0x89, 0x77, 0xd4, 0x92, 0xc9, 0xc7, 0x58, 0x8, 0x9a, 0x64, 0xce, 0x4c, 0x83, 0x36, 0x39, 0x5a, 0x96, 0x8f, 0xd1, 0xd2, 0x79, 0xdc, 0xb9, 0x7d, 0x3e, 0xfb, 0xec, 0xf5, 0x73, 0xff, 0x7e, 0xe7, 0xf7, 0x3b, 0xf7, 0x9e, 0x39, 0xe7, 0xdc, 0x99, 0x16, 0x7c, 0xcf, 0x5a, 0x7b, 0xed, 0xb5, 0xd7, 0xde, 0x7b, 0xed, 0xb5, 0xf7, 0x6f, 0x9f, 0x73, 0x7e, 0x53, 0xcb, 0x1a, 0x34, 0x3f, 0x3f, 0x3f, 0x8d, 0x6a, 0x7e, 0x6a, 0x6a, 0xea, 0x40, 0xa3, 0x6a, 0x49, 0x8a, 0xf4, 0xbf, 0xdc, 0x8e, 0xa2, 0x7f, 0xca, 0xab, 0x29, 0xce, 0x80, 0x33, 0xc0, 0xa9, 0xe0, 0x64, 0xf0, 0x6a, 0xb0, 0x1b, 0x6c, 0x7, 0x3b, 0xc0, 0xdd, 0xe0, 0x16, 0xda, 0xdc, 0x3, 0x4f, 0x44, 0xbb, 0x15, 0x94, 0xf7, 0x17, 0xe5, 0x29, 0x64, 0xe7, 0xa6, 0xef, 0x52, 0x7f, 0x24, 0xe5, 0x3d, 0x61, 0xb7, 0x8c, 0x86, 0x1a, 0x56, 0x44, 0x39, 0x35, 0xaa, 0x14, 0x4b, 0x20, 0x94, 0x7d, 0x22, 0xbf, 0x1c, 0x5c, 0x6, 0xb6, 0x82, 0x1a, 0x1d, 0x80, 0x6a, 0x8a, 0x5e, 0xe1, 0x19, 0xd8, 0x2f, 0xc0, 0xbb, 0x62, 0xa8, 0xc8, 0x2b, 0xc1, 0x11, 0x22, 0x74, 0xc1, 0xd1, 0xad, 0x5, 0x5f, 0x7, 0x6b, 0x42, 0x67, 0x10, 0xd2, 0x2a, 0xc0, 0xa7, 0xc1, 0x9b, 0xc0, 0x31, 0x56, 0xc2, 0xfb, 0x1c, 0x54, 0x8d, 0xc6, 0x2c, 0xd8, 0x77, 0xb8, 0x44, 0x7e, 0x1f, 0xb8, 0x17, 0x24, 0xca, 0xf3, 0xde, 0x47, 0x61, 0x2f, 0xd8, 0xf, 0xc, 0xc4, 0x1c, 0x28, 0x75, 0x14, 0x13, 0x3d, 0xc7, 0xa7, 0x13, 0x5c, 0x15, 0xfe, 0x82, 0xab, 0x3, 0xa7, 0x83, 0xef, 0x2, 0xdb, 0x5f, 0x13, 0x75, 0x89, 0xa3, 0x48, 0x13, 0x86, 0xaf, 0x1, 0xf7, 0x81, 0x8d, 0x61, 0x80, 0xbc, 0x22, 0xe4, 0x49, 0x71, 0xfa, 0x48, 0xb, 0xa1, 0x7f, 0xe4, 0xb, 0xc1, 0x1e, 0x20, 0xc5, 0x44, 0x1d, 0xb4, 0x93, 0x6f, 0x45, 0xce, 0x10, 0x6d, 0x9f, 0x7, 0x41, 0xd7, 0x20, 0xbc, 0x12, 0x1c, 0xb, 0xce, 0x0, 0x97, 0x80, 0x9b, 0xc0, 0x6e, 0x20, 0x19, 0xd4, 0x37, 0xe7, 0x3e, 0xa7, 0xd3, 0x96, 0x40, 0x91, 0xb6, 0x5, 0x7c, 0x3, 0x8, 0xba, 0x20, 0x26, 0x8e, 0x62, 0x62, 0xc1, 0xc0, 0xf7, 0x94, 0xc8, 0x3, 0xba, 0x20, 0x3a, 0x87, 0xbb, 0xb2, 0x73, 0x39, 0x1b, 0x5a, 0x3, 0x40, 0x7d, 0x4d, 0x9f, 0x6d, 0x23, 0x88, 0xba, 0xba, 0x7, 0x6c, 0x6, 0xcf, 0x5a, 0x68, 0xd0, 0xa5, 0xb9, 0x4f, 0xb7, 0x4e, 0xa, 0x44, 0x35, 0x49, 0x14, 0xee, 0xb1, 0x20, 0x23, 0x76, 0xde, 0xa4, 0x83, 0x41, 0x1f, 0xa9, 0x7f, 0xf8, 0xbb, 0x41, 0xc, 0xf8, 0xf9, 0x61, 0x2, 0x40, 0xbb, 0x32, 0x20, 0x14, 0xd3, 0xf6, 0x71, 0xb, 0x95, 0xe4, 0x7c, 0xc, 0xae, 0x74, 0x3d, 0x38, 0x3a, 0xe6, 0x96, 0x38, 0x8a, 0x18, 0x88, 0x69, 0xf4, 0x0, 0x90, 0xfe, 0xd3, 0x63, 0x29, 0x7d, 0x2e, 0x8c, 0x6, 0xe8, 0xc6, 0x7a, 0x66, 0xe0, 0x2f, 0xfa, 0x3e, 0x6, 0xf9, 0x8e, 0xdc, 0xa7, 0x2b, 0x5a, 0x4e, 0x6c, 0x68, 0xb9, 0x8, 0xa2, 0xbe, 0xdc, 0x2e, 0xe5, 0x96, 0xf9, 0x15, 0xe5, 0xf2, 0x3c, 0x7a, 0xb, 0xe5, 0xf5, 0x6e, 0x8d, 0xa4, 0x84, 0x9f, 0x6, 0x9e, 0x6, 0x92, 0xd1, 0x2c, 0x53, 0xec, 0xb2, 0x32, 0x18, 0xd4, 0xd5, 0x9e, 0x30, 0x51, 0x37, 0x2c, 0xc7, 0x4f, 0xf4, 0x7d, 0x11, 0x72, 0x90, 0x7b, 0x5d, 0x1a, 0x3a, 0x0, 0x2d, 0x6d, 0xc2, 0x8f, 0xdc, 0x6c, 0xf8, 0x11, 0x58, 0xd, 0x5e, 0x7, 0xce, 0x7, 0xee, 0x80, 0xad, 0xe0, 0x23, 0x6, 0x22, 0x9e, 0x18, 0xeb, 0x90, 0x3d, 0x94, 0xa4, 0x48, 0xab, 0x2a, 0x18, 0x73, 0x73, 0x73, 0x3f, 0x41, 0x7f, 0x9c, 0x93, 0x85, 0x2f, 0x7, 0x55, 0x54, 0x87, 0xd, 0x40, 0xf6, 0x11, 0xd9, 0xe0, 0x61, 0x76, 0x27, 0x90, 0xa2, 0xbf, 0x71, 0x4, 0x21, 0x7c, 0xc4, 0x9c, 0x1e, 0xc6, 0xff, 0xcf, 0xc0, 0x2c, 0x78, 0x14, 0x4, 0xf5, 0x1e, 0xc, 0x94, 0x22, 0x10, 0x67, 0x45, 0xd, 0x3c, 0x1e, 0x51, 0xaa, 0xf6, 0x16, 0xfa, 0x2d, 0xc8, 0xef, 0x8c, 0x89, 0x23, 0xaf, 0x0, 0x6, 0x65, 0xe8, 0xc, 0xa1, 0xcd, 0xca, 0x1c, 0x90, 0xb3, 0x91, 0x63, 0xef, 0x46, 0x5f, 0x31, 0x89, 0x81, 0xbc, 0xd8, 0x2, 0x3, 0xed, 0xf0, 0x6f, 0x7d, 0x2c, 0x2e, 0x62, 0x45, 0xdf, 0x8f, 0xb9, 0x18, 0x84, 0x98, 0xc4, 0x73, 0xc8, 0xfb, 0xa2, 0x22, 0xf3, 0x79, 0xb8, 0xe7, 0x82, 0x37, 0x31, 0x6f, 0x9a, 0x6b, 0xc1, 0x8d, 0xb8, 0xf9, 0xa, 0x58, 0xe5, 0xd, 0xd, 0xa8, 0x37, 0x18, 0x8b, 0xe, 0x8, 0xb6, 0xf6, 0x19, 0xb7, 0x3b, 0x7d, 0xfa, 0xcc, 0xb7, 0x2f, 0xb3, 0x4c, 0x5e, 0xa3, 0x6c, 0x5f, 0xd3, 0x75, 0x15, 0x6, 0xd8, 0xea, 0x7b, 0x2e, 0xc3, 0xe6, 0x57, 0x80, 0x8b, 0x15, 0x68, 0xb3, 0xa2, 0xf7, 0xd1, 0x2b, 0x78, 0x91, 0x7a, 0x1c, 0x48, 0xee, 0xd3, 0x32, 0xca, 0xea, 0x8c, 0x68, 0xec, 0x5f, 0xcb, 0xb7, 0x83, 0xf7, 0xeb, 0x28, 0x88, 0x72, 0xda, 0x32, 0xf2, 0xd0, 0xb5, 0x71, 0xea, 0xab, 0x6d, 0x85, 0xfc, 0x73, 0x20, 0x99, 0xd, 0x65, 0x9f, 0x63, 0x93, 0x73, 0xe6, 0x94, 0x63, 0xff, 0x31, 0x7d, 0xc5, 0xd6, 0x4c, 0xbc, 0xc, 0xc4, 0xf1, 0x54, 0xfe, 0x1, 0x48, 0xee, 0xd5, 0xda, 0x40, 0x8a, 0x34, 0x2c, 0x1d, 0xba, 0xff, 0xae, 0x6, 0xd5, 0xb5, 0x36, 0x26, 0x8e, 0xce, 0x5b, 0xaa, 0x5b, 0x47, 0x1e, 0x59, 0x97, 0xaa, 0xd5, 0x2b, 0xc0, 0x8f, 0x2, 0x9b, 0x80, 0x34, 0x89, 0xf3, 0x21, 0xe6, 0x10, 0xe7, 0x84, 0xc1, 0xfe, 0x6a, 0x31, 0xc6, 0x5e, 0x10, 0x50, 0xb8, 0x72, 0x29, 0x15, 0x49, 0xf1, 0xa7, 0x90, 0xef, 0xca, 0x46, 0xb5, 0x81, 0xab, 0xa3, 0x3e, 0x52, 0x36, 0x52, 0xcc, 0xd4, 0xb6, 0xfd, 0xb9, 0xe0, 0x16, 0x3a, 0xf0, 0x26, 0xb7, 0x1e, 0xbc, 0x88, 0xb2, 0xf6, 0x73, 0xc0, 0xad, 0x63, 0x3a, 0x7a, 0x69, 0x32, 0x28, 0xbd, 0xcb, 0x4b, 0xfe, 0xf2, 0x83, 0xde, 0x73, 0xa2, 0xef, 0x2a, 0x8c, 0x6e, 0x9c, 0xe4, 0xb8, 0x23, 0x43, 0xbf, 0xcc, 0x78, 0xbe, 0xa9, 0x73, 0xc7, 0xe3, 0xf8, 0xa2, 0xa3, 0xe5, 0xe, 0x14, 0x65, 0xa4, 0xea, 0x6f, 0x72, 0x85, 0xe7, 0x82, 0x7b, 0xbf, 0x95, 0xb4, 0xcf, 0x6d, 0x62, 0xcf, 0x1d, 0x89, 0xe1, 0xc7, 0xc0, 0xd, 0xe0, 0x66, 0xea, 0x2e, 0x5, 0x6f, 0x7, 0x47, 0xe9, 0x80, 0x3e, 0xe, 0xd8, 0x29, 0xd8, 0x67, 0x7f, 0xa8, 0xe2, 0x2c, 0xb2, 0xf, 0xcb, 0x93, 0xa4, 0x58, 0x40, 0xfb, 0x78, 0xc0, 0xf, 0xc6, 0x75, 0x84, 0xe3, 0x51, 0xe, 0x8a, 0xd4, 0x88, 0xc, 0xb8, 0x95, 0x8a, 0x59, 0xf0, 0x5e, 0xa0, 0xa1, 0x13, 0x2c, 0x1d, 0x51, 0xac, 0x65, 0x87, 0x67, 0x42, 0x5a, 0xfd, 0x6c, 0xa7, 0xbf, 0x77, 0x64, 0x3c, 0xb, 0xff, 0x23, 0xf5, 0xbf, 0x97, 0x83, 0x7, 0xc1, 0x2e, 0x6, 0xb0, 0x1b, 0xa4, 0x20, 0xc3, 0xfd, 0xc6, 0xf8, 0x77, 0xf4, 0x92, 0x63, 0xe8, 0xeb, 0x2b, 0xd5, 0x8c, 0xef, 0x23, 0x7d, 0x99, 0xc4, 0x5d, 0x5f, 0xf0, 0x53, 0x20, 0x8c, 0x4e, 0x8e, 0x92, 0xfb, 0xf4, 0x3b, 0x18, 0xae, 0x3, 0x6, 0x61, 0x2f, 0x30, 0x7d, 0x3b, 0x7, 0x48, 0x5b, 0xaa, 0xab, 0xd4, 0x73, 0x82, 0x42, 0xa5, 0x9d, 0xce, 0x64, 0xc0, 0x96, 0x3d, 0x6, 0x76, 0xe0, 0x7f, 0x7, 0x7c, 0x27, 0x78, 0x34, 0xe3, 0x78, 0xb8, 0x14, 0x59, 0x99, 0xa, 0xd8, 0xe1, 0xba, 0xb7, 0x1d, 0x4b, 0x39, 0x55, 0xe6, 0x8f, 0x36, 0x7d, 0xa9, 0xcb, 0x72, 0xd9, 0xc4, 0xf9, 0x48, 0xb1, 0xf0, 0xbd, 0x52, 0x9b, 0xc2, 0x1a, 0x1c, 0x5c, 0xe, 0xbb, 0x44, 0x11, 0x98, 0x19, 0x6e, 0x95, 0xce, 0x60, 0x50, 0xd7, 0x24, 0x6d, 0x45, 0x4, 0x25, 0x5, 0xbc, 0x69, 0x94, 0xcb, 0x74, 0x97, 0xb2, 0xaa, 0xa3, 0x7a, 0x24, 0xb5, 0x13, 0x76, 0xf5, 0xd, 0xb2, 0xf3, 0x58, 0x4f, 0x70, 0x67, 0xe9, 0xaf, 0x76, 0x3e, 0xa0, 0xaf, 0x56, 0xd2, 0xc9, 0x9b, 0x1, 0x89, 0x30, 0xfe, 0x12, 0x82, 0xdf, 0xd5, 0x75, 0x64, 0x10, 0x3c, 0x47, 0xfa, 0xa2, 0x88, 0xbe, 0x8b, 0xb4, 0xf5, 0x80, 0x32, 0x0, 0xe, 0xc2, 0x80, 0x78, 0x2e, 0xb8, 0x22, 0xc2, 0x41, 0xa9, 0x93, 0x5c, 0xf9, 0x9e, 0x54, 0x7c, 0x96, 0xfd, 0x95, 0x72, 0x61, 0xe2, 0x98, 0xfb, 0x1a, 0x96, 0x3a, 0x64, 0xcd, 0x63, 0x1, 0xbd, 0x27, 0x3d, 0x53, 0xb6, 0x2f, 0xe5, 0x38, 0x4d, 0xd5, 0x9d, 0x49, 0xc3, 0x2b, 0xc0, 0xba, 0x6c, 0xf0, 0x49, 0xf8, 0xf, 0x80, 0x13, 0x98, 0x66, 0xb0, 0x31, 0xf0, 0x5c, 0x3d, 0x14, 0xf3, 0xa9, 0x61, 0x40, 0xdd, 0x66, 0xf2, 0xd8, 0x6, 0xfa, 0x6c, 0xf5, 0x4b, 0x7f, 0x31, 0x1, 0x23, 0x55, 0xc9, 0xd8, 0x57, 0xd4, 0xa6, 0x2f, 0x75, 0xc8, 0xda, 0x46, 0x5b, 0x7f, 0xda, 0x7b, 0x32, 0x37, 0xee, 0xb, 0x60, 0xba, 0x22, 0xd3, 0xc0, 0xc1, 0xb8, 0xa7, 0x3f, 0xd, 0xce, 0x65, 0xd0, 0xf7, 0xc2, 0x37, 0x83, 0x47, 0xc0, 0x76, 0xf0, 0x46, 0x30, 0x12, 0x95, 0x3, 0xcc, 0x8e, 0xd2, 0xef, 0x10, 0x2d, 0xfa, 0x91, 0xfa, 0x69, 0x69, 0x1c, 0x81, 0x78, 0x9c, 0xba, 0x27, 0x5a, 0xea, 0x93, 0xca, 0xd4, 0x8d, 0xe8, 0xfc, 0x15, 0xd9, 0x93, 0xde, 0x80, 0x78, 0x41, 0x12, 0x92, 0x29, 0x45, 0x6c, 0xe6, 0x7d, 0xd4, 0x86, 0xd3, 0x94, 0x96, 0x51, 0xa6, 0xe, 0xf1, 0x85, 0x83, 0xcd, 0x46, 0x51, 0x56, 0xee, 0xa2, 0x41, 0x36, 0x4d, 0x9f, 0x6d, 0xb6, 0xa5, 0x4d, 0x57, 0x1f, 0x85, 0x7e, 0x27, 0x72, 0x64, 0x44, 0x35, 0x8f, 0xa8, 0x77, 0x6b, 0x44, 0x20, 0x34, 0x14, 0xd2, 0x7f, 0x41, 0x3c, 0x62, 0xe2, 0x2e, 0x50, 0x6b, 0x5c, 0xe, 0xac, 0x29, 0x97, 0xe5, 0xe4, 0xed, 0x20, 0x3e, 0x4a, 0x1f, 0xa5, 0x5c, 0xba, 0xea, 0xd2, 0x17, 0x36, 0x8e, 0x39, 0xb6, 0xff, 0x36, 0xec, 0xdd, 0xe6, 0x52, 0xdf, 0x76, 0xd4, 0x28, 0x4d, 0x10, 0x23, 0x1f, 0x6f, 0x77, 0x6a, 0x5, 0xb9, 0x8f, 0xad, 0xf3, 0x50, 0x8b, 0x80, 0x20, 0xfe, 0xdf, 0x91, 0x13, 0x8e, 0x27, 0xd6, 0x9f, 0x1d, 0xbd, 0x99, 0x2d, 0x53, 0x2e, 0x49, 0xa5, 0xf7, 0x71, 0xb9, 0x74, 0x7d, 0x8f, 0xa5, 0x40, 0x84, 0x93, 0xa8, 0xcb, 0x55, 0x3d, 0x66, 0x5a, 0x86, 0x62, 0x31, 0x72, 0xd8, 0x6, 0xb7, 0x4d, 0xd9, 0xae, 0xd4, 0x2b, 0x97, 0x75, 0xa5, 0x1c, 0x76, 0x4d, 0x9b, 0x52, 0x9f, 0x65, 0xc7, 0x17, 0x2b, 0xef, 0x96, 0xb8, 0x3f, 0xeb, 0x3d, 0xf8, 0xfb, 0x3, 0x91, 0x95, 0x31, 0xd9, 0x59, 0x8c, 0x6f, 0xce, 0xd, 0x22, 0x8d, 0x72, 0xb1, 0xce, 0x4a, 0x67, 0x8b, 0x91, 0xeb, 0xad, 0x7b, 0x67, 0x48, 0xd9, 0x2e, 0xea, 0x43, 0x17, 0x5c, 0x7d, 0x29, 0x87, 0xdd, 0x20, 0x7d, 0x61, 0x13, 0x81, 0xf8, 0xb, 0xba, 0xad, 0x85, 0xbe, 0x4f, 0x4c, 0x1, 0xa0, 0x23, 0x6f, 0x96, 0x46, 0xca, 0x83, 0xd1, 0xcb, 0x94, 0x5b, 0xc2, 0x2f, 0x43, 0x3e, 0xf3, 0xab, 0x95, 0x47, 0xee, 0xa3, 0xae, 0xd5, 0xea, 0x33, 0x5c, 0x7a, 0x85, 0xab, 0x3e, 0x9d, 0xbb, 0xdd, 0xc2, 0xdc, 0xe2, 0xa0, 0x6c, 0xdd, 0xea, 0x91, 0x9, 0xda, 0xc7, 0x59, 0x71, 0x13, 0xf2, 0xc6, 0xec, 0xc0, 0xe7, 0x7e, 0x4, 0xa3, 0x35, 0x20, 0x5d, 0xab, 0x95, 0xdb, 0x1f, 0x2a, 0xe6, 0x58, 0x9d, 0xb0, 0xe7, 0x83, 0xfc, 0x36, 0xe0, 0x76, 0xf3, 0xcb, 0x56, 0x64, 0x89, 0xaa, 0x8a, 0xaa, 0x40, 0x68, 0x80, 0x61, 0x1c, 0x2c, 0xdf, 0xc0, 0xe2, 0xca, 0x6c, 0x65, 0x30, 0xfc, 0x6f, 0x50, 0x7, 0x76, 0xd0, 0x1a, 0x10, 0xf4, 0x87, 0x1b, 0xc5, 0xca, 0xdf, 0xc7, 0xc0, 0xee, 0x58, 0x68, 0x70, 0x55, 0x20, 0x34, 0x24, 0x18, 0x6e, 0x11, 0xef, 0xe1, 0x3a, 0xf9, 0x2c, 0xf8, 0xa1, 0x7a, 0x28, 0xae, 0xdf, 0x6, 0x64, 0xe0, 0xd9, 0x91, 0xac, 0xf, 0xfd, 0x87, 0x8b, 0xe6, 0x93, 0x4f, 0x9a, 0x65, 0x3e, 0xff, 0xec, 0x89, 0x69, 0xcb, 0x67, 0xb1, 0xce, 0x6a, 0x81, 0xb0, 0xaa, 0x8, 0x86, 0xbf, 0x1d, 0x7c, 0x1e, 0xd5, 0x45, 0x60, 0x17, 0xd0, 0xd6, 0x80, 0xd8, 0x41, 0x67, 0x56, 0x94, 0x67, 0x46, 0x29, 0xd3, 0xa6, 0x46, 0xd6, 0xb5, 0xd5, 0x87, 0x2e, 0xb8, 0x8d, 0x4a, 0xb9, 0x74, 0xd2, 0xa1, 0x77, 0x6c, 0x9e, 0x71, 0x8e, 0xd7, 0x73, 0xe1, 0x3a, 0xa0, 0x8f, 0x95, 0xcc, 0xa7, 0xef, 0x69, 0x61, 0x9d, 0x34, 0x68, 0x42, 0xee, 0xa7, 0xb4, 0xfa, 0x38, 0x79, 0x29, 0xb6, 0x9f, 0x1, 0xeb, 0xc1, 0x29, 0xe0, 0xc5, 0xa0, 0x2f, 0x88, 0xe8, 0xe, 0x17, 0x8a, 0xf3, 0xe1, 0x6a, 0xe6, 0xf0, 0x9, 0x7, 0xc5, 0x1c, 0xbc, 0x19, 0xb7, 0x9e, 0xf, 0xd6, 0x77, 0x6, 0x22, 0x37, 0xf6, 0x3b, 0x86, 0x93, 0xde, 0x9e, 0x6d, 0xd7, 0xc2, 0x75, 0xfc, 0x41, 0xe0, 0x55, 0x3c, 0xce, 0xd, 0xc4, 0xc3, 0x82, 0x9c, 0x8f, 0x8b, 0x67, 0xd6, 0xba, 0x8d, 0xcf, 0x61, 0xf2, 0xfe, 0xea, 0x5e, 0x2d, 0x2a, 0xba, 0x56, 0x8a, 0xc3, 0xb1, 0x56, 0x49, 0xc3, 0xf8, 0xbe, 0xee, 0xc4, 0xfd, 0xa1, 0x46, 0xa7, 0x5e, 0xbb, 0x25, 0x3, 0xe0, 0xa3, 0x55, 0x1a, 0x18, 0xc8, 0x9e, 0xc9, 0xd2, 0x7d, 0x32, 0xee, 0xf4, 0xa5, 0x27, 0xf7, 0x78, 0xa3, 0x41, 0xc8, 0x72, 0x1c, 0x9c, 0x9d, 0x83, 0x69, 0xd, 0x44, 0x61, 0xbd, 0x9, 0xf9, 0x69, 0x70, 0x62, 0xa1, 0xb, 0xb1, 0x75, 0xbf, 0xe5, 0xb1, 0xb4, 0xd6, 0x45, 0xc3, 0x9, 0x71, 0xe6, 0x9d, 0xb6, 0xb2, 0xd9, 0xe0, 0x7d, 0xe8, 0x7b, 0xf6, 0xc3, 0x78, 0x16, 0xcc, 0x6, 0xed, 0x5a, 0xf7, 0x39, 0xe, 0xe3, 0xe9, 0xe1, 0x21, 0xb9, 0x51, 0x43, 0xc8, 0xa8, 0x9a, 0x15, 0xd6, 0x75, 0x4e, 0x94, 0xb6, 0x9d, 0x75, 0x3a, 0x99, 0x4, 0x19, 0x7c, 0xfc, 0xba, 0x4d, 0xe3, 0x2, 0xf5, 0x53, 0xc6, 0xf1, 0xdb, 0xdc, 0x97, 0x7, 0xe7, 0x82, 0xd4, 0x99, 0xda, 0x38, 0xf7, 0xa6, 0x19, 0xbf, 0x4c, 0x5d, 0x8b, 0xa7, 0xf, 0x81, 0x48, 0x31, 0x3b, 0x5c, 0xf2, 0x9, 0x2f, 0x30, 0x1b, 0x27, 0x6c, 0x36, 0x3c, 0x4, 0x66, 0x18, 0xfb, 0xc3, 0xcc, 0x21, 0xb6, 0x38, 0xaa, 0xc1, 0xd4, 0x9a, 0x11, 0x36, 0xc9, 0x41, 0xd0, 0x91, 0x13, 0xfe, 0x2, 0xf8, 0x13, 0x30, 0x0, 0x46, 0x3e, 0x45, 0x39, 0xaf, 0x4, 0xc5, 0x43, 0x4a, 0x2e, 0xa6, 0xb, 0x64, 0x10, 0xa4, 0xaf, 0xe5, 0x20, 0xb8, 0x90, 0x8b, 0xca, 0x6, 0x1b, 0x75, 0x6, 0xc2, 0x4a, 0x1d, 0xe5, 0xa8, 0xee, 0xa4, 0xf8, 0x71, 0x60, 0x30, 0xec, 0x30, 0x5d, 0x5d, 0xa9, 0x77, 0x0, 0x91, 0x19, 0x9d, 0xd9, 0x85, 0xcd, 0x24, 0xa9, 0x7c, 0x72, 0x5d, 0xc9, 0x98, 0xae, 0xca, 0x9d, 0xc5, 0xb8, 0xc6, 0xd3, 0xb7, 0xab, 0xe, 0xd2, 0xde, 0x83, 0xbf, 0x6, 0x5c, 0xb, 0x4a, 0x72, 0xfb, 0xf8, 0x37, 0xa0, 0x7f, 0xa7, 0x19, 0xb8, 0xf8, 0x9b, 0x6d, 0xa9, 0xb8, 0xfd, 0x4a, 0xfe, 0x87, 0xf2, 0x12, 0x67, 0xd, 0x5f, 0xe8, 0x21, 0x70, 0x70, 0xc1, 0xc1, 0x71, 0x19, 0xc, 0xff, 0xd4, 0xf1, 0x85, 0xaf, 0xfb, 0x41, 0x1b, 0x2d, 0x55, 0x0, 0xec, 0x27, 0xfe, 0x2f, 0xfd, 0x7, 0xf2, 0x69, 0xa3, 0x4, 0x61, 0xa8, 0x74, 0xa6, 0xb3, 0x74, 0x80, 0xe6, 0xe, 0xbd, 0x68, 0x7d, 0x20, 0xe3, 0x6d, 0x70, 0xef, 0x17, 0x2b, 0xc1, 0x71, 0xc0, 0x2d, 0x57, 0x4b, 0x4d, 0xda, 0x92, 0xb5, 0x2f, 0x3c, 0x51, 0x2c, 0x63, 0xe3, 0xf6, 0x1b, 0x68, 0xa7, 0x4d, 0x7, 0x79, 0x71, 0xb2, 0x3f, 0xef, 0x38, 0x1f, 0xc5, 0xcf, 0xd, 0xf8, 0x4c, 0xfd, 0x36, 0x7d, 0x76, 0xb4, 0x1f, 0x4d, 0x6d, 0x67, 0x31, 0x89, 0xf0, 0x44, 0xd9, 0x3f, 0x77, 0x7d, 0x17, 0xe9, 0x97, 0x20, 0xc8, 0x2d, 0x33, 0xd6, 0xec, 0x28, 0xfe, 0x91, 0x8f, 0x4c, 0xd0, 0xff, 0x86, 0x62, 0x1c, 0x3, 0xcf, 0xbc, 0xb0, 0x1b, 0xb, 0xa7, 0x63, 0x3, 0xe1, 0x1b, 0xad, 0xd5, 0x3e, 0x44, 0x7e, 0x15, 0xf0, 0x25, 0xce, 0x78, 0x2b, 0xae, 0x2f, 0x8, 0xc5, 0x24, 0x52, 0x70, 0x2c, 0x37, 0x75, 0xb4, 0x1f, 0x14, 0x38, 0xaa, 0xd3, 0xab, 0x4d, 0xf1, 0x62, 0x98, 0xe7, 0xd1, 0xa7, 0x62, 0x52, 0xc8, 0xe9, 0x1c, 0x8b, 0xf2, 0xd8, 0x39, 0x1d, 0xa4, 0xf3, 0x1, 0xee, 0xaa, 0xc7, 0x23, 0x2a, 0xf5, 0x43, 0xf9, 0x4, 0xe0, 0x8b, 0x60, 0x7f, 0x3, 0x41, 0xfb, 0x87, 0x9c, 0xe0, 0xa0, 0xc9, 0x47, 0x9d, 0xbe, 0x9d, 0x78, 0x1c, 0x8c, 0xbe, 0xf4, 0xe6, 0x53, 0x2c, 0xc6, 0x31, 0x7a, 0x10, 0x70, 0x18, 0x2b, 0x9c, 0x26, 0x4a, 0x59, 0xee, 0x8a, 0xb, 0xe5, 0xbe, 0x74, 0x43, 0xf7, 0x5a, 0xf0, 0x39, 0xb0, 0x5, 0x4, 0xf9, 0xe4, 0x18, 0x6b, 0x10, 0x72, 0x40, 0xf5, 0xbf, 0xa7, 0x90, 0xb7, 0x51, 0x7e, 0xcf, 0x58, 0x83, 0x80, 0xb3, 0x45, 0x1f, 0x96, 0x74, 0xbe, 0x6, 0x7b, 0xf, 0xc5, 0x75, 0xe0, 0x2c, 0x70, 0x2a, 0x90, 0x3c, 0xec, 0xe2, 0x1b, 0x5f, 0x52, 0x8c, 0xe9, 0xc3, 0xb1, 0x79, 0x4f, 0xf1, 0x52, 0x14, 0x3f, 0xc, 0xfd, 0x1a, 0xf9, 0x8b, 0x1c, 0x86, 0x6, 0x23, 0x2d, 0x10, 0xb2, 0xf7, 0x88, 0x91, 0xc9, 0xb4, 0x7f, 0x3d, 0x5e, 0x7c, 0xfe, 0x3e, 0x6, 0xfc, 0x2e, 0xe1, 0x1f, 0x3a, 0x3e, 0x1, 0x7c, 0x2a, 0xac, 0x6, 0x27, 0x3, 0xdf, 0x59, 0x7e, 0x2b, 0x70, 0xf2, 0xe5, 0xf6, 0xf0, 0xf7, 0x4c, 0xcf, 0xa, 0x7, 0x55, 0x3b, 0xfd, 0x29, 0x1f, 0x14, 0x31, 0x1e, 0xe6, 0x96, 0x26, 0x67, 0x70, 0x23, 0x0, 0xbb, 0x91, 0xbf, 0xd, 0x2e, 0xa7, 0xce, 0x83, 0x32, 0x2e, 0x74, 0x63, 0xe9, 0xd3, 0x81, 0xea, 0xd0, 0xce, 0xce, 0x3, 0x67, 0x83, 0x55, 0xc0, 0x4e, 0xd5, 0xfb, 0xde, 0x82, 0x68, 0x6e, 0xd, 0x57, 0xc8, 0x55, 0xd0, 0xc6, 0xc7, 0x97, 0x83, 0x19, 0xd7, 0x80, 0x7c, 0xb3, 0x26, 0xae, 0xcb, 0x11, 0x4, 0xb3, 0xe0, 0x5b, 0xe8, 0x6f, 0x87, 0x2f, 0xfa, 0xdb, 0xa4, 0xb6, 0x43, 0x13, 0x11, 0x7e, 0x5, 0xb8, 0x18, 0x3c, 0x8, 0x9a, 0xe4, 0x21, 0xe5, 0x2a, 0x78, 0x5a, 0x7b, 0x58, 0x79, 0x80, 0x55, 0x27, 0xfe, 0x28, 0x7, 0x23, 0x6d, 0xf5, 0x55, 0xde, 0x4c, 0x75, 0x1d, 0x74, 0x17, 0xc2, 0x6, 0x60, 0xb0, 0x13, 0x21, 0x1b, 0xfc, 0x89, 0x50, 0xed, 0x8c, 0xa0, 0x23, 0x5f, 0x4, 0xfb, 0x30, 0x38, 0x1f, 0xcc, 0x80, 0x72, 0x1b, 0xb8, 0x52, 0x41, 0x55, 0x3b, 0xda, 0xb0, 0x58, 0x55, 0x31, 0xea, 0x9b, 0xbc, 0x99, 0x31, 0x65, 0x16, 0x35, 0x27, 0xf7, 0x3b, 0x1a, 0x5f, 0x5, 0xae, 0xc3, 0xef, 0x53, 0x3a, 0xa2, 0xf, 0xc7, 0xe1, 0xcb, 0x69, 0x63, 0x39, 0xf, 0xf4, 0xd9, 0x24, 0xcf, 0x8, 0x67, 0xe1, 0x60, 0xaa, 0x8e, 0xd0, 0x1d, 0x4d, 0xf9, 0x74, 0x70, 0xe, 0x38, 0x13, 0x9c, 0x4, 0x9a, 0xe4, 0xa0, 0x9a, 0x13, 0xd4, 0x97, 0x3e, 0xbd, 0x31, 0xb6, 0xd, 0xda, 0x6d, 0xd6, 0x16, 0xb5, 0x1d, 0xe8, 0x37, 0x1, 0xbf, 0xee, 0x6f, 0xa6, 0x6d, 0xfa, 0x35, 0xc, 0x3f, 0xda, 0xfb, 0x5b, 0xe3, 0xa2, 0xbf, 0x45, 0x62, 0x7f, 0x50, 0x54, 0xd, 0x2a, 0x7, 0xc4, 0xe7, 0xb1, 0x1, 0xa9, 0x26, 0x88, 0xde, 0xa7, 0x85, 0x2f, 0x88, 0xcd, 0x80, 0xb5, 0xe0, 0xd, 0xc0, 0xb3, 0x63, 0x14, 0xfa, 0x37, 0x8d, 0xb7, 0x81, 0xbb, 0xc1, 0x6d, 0xc0, 0x7f, 0xa2, 0x1e, 0x81, 0x27, 0xea, 0x1a, 0x4b, 0xd4, 0x4f, 0x82, 0x57, 0x81, 0x8, 0xe7, 0xc5, 0x20, 0x3c, 0xb8, 0x6a, 0xab, 0x4a, 0x9d, 0x5b, 0xe7, 0x44, 0x70, 0x4a, 0x86, 0x99, 0xe2, 0x93, 0xe5, 0x4, 0x70, 0x2c, 0x78, 0x19, 0xd0, 0xc6, 0x76, 0x4f, 0x82, 0x27, 0x80, 0xef, 0x5c, 0xfc, 0xb, 0xec, 0x2, 0xae, 0xbc, 0x1, 0xd8, 0xa, 0x1e, 0xc2, 0x7f, 0x5a, 0x79, 0xe4, 0x44, 0xf8, 0x4f, 0x19, 0x40, 0xa1, 0xb6, 0x18, 0xb9, 0x7a, 0xa2, 0xec, 0x7f, 0x27, 0x77, 0xca, 0xf8, 0x2b, 0x28, 0xe6, 0x63, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; + + + + + (NSData *)twitter_icon3x_png { + return [NSData dataWithBytes:twitter_icon3x_png length:sizeof(twitter_icon3x_png)]; + } + +@end diff --git a/Pods/ParseUI/ParseUI/Other/ParseUI.h b/Pods/ParseUI/ParseUI/Other/ParseUI.h new file mode 100644 index 0000000..788a4b0 --- /dev/null +++ b/Pods/ParseUI/ParseUI/Other/ParseUI.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Pods/ParseUI/ParseUI/Other/ParseUIConstants.h b/Pods/ParseUI/ParseUI/Other/ParseUIConstants.h new file mode 100644 index 0000000..19cfa1d --- /dev/null +++ b/Pods/ParseUI/ParseUI/Other/ParseUIConstants.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#import +#import + +#ifndef ParseUI_ParseUIConstants_h +#define ParseUI_ParseUIConstants_h + +///-------------------------------------- +/// @name Deprecated Macros +///-------------------------------------- + +#ifndef PARSE_UI_DEPRECATED +# ifdef __deprecated_msg +# define PARSE_UI_DEPRECATED(_MSG) (deprecated(_MSG)) +# else +# ifdef __deprecated +# define PARSE_UI_DEPRECATED(_MSG) (deprecated) +# else +# define PARSE_UI_DEPRECATED(_MSG) +# endif +# endif +#endif + +///-------------------------------------- +/// @name Nullability Support +///-------------------------------------- + +#if __has_feature(nullability) +# define PFUI_NULLABLE nullable +# define PFUI_NULLABLE_S __nullable +# define PFUI_NULL_UNSPECIFIED null_unspecified +# define PFUI_NULLABLE_PROPERTY nullable, +#else +# define PFUI_NULLABLE +# define PFUI_NULLABLE_S +# define PFUI_NULL_UNSPECIFIED +# define PFUI_NULLABLE_PROPERTY +#endif + +#if __has_feature(assume_nonnull) +# ifdef NS_ASSUME_NONNULL_BEGIN +# define PFUI_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN +# else +# define PFUI_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +# endif +# ifdef NS_ASSUME_NONNULL_END +# define PFUI_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END +# else +# define PFUI_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +# endif +#else +# define PFUI_ASSUME_NONNULL_BEGIN +# define PFUI_ASSUME_NONNULL_END +#endif + +#endif diff --git a/Pods/ParseUI/ParseUI/Resources/Localization/en.lproj/ParseUI.strings b/Pods/ParseUI/ParseUI/Resources/Localization/en.lproj/ParseUI.strings new file mode 100644 index 0000000..9986622 Binary files /dev/null and b/Pods/ParseUI/ParseUI/Resources/Localization/en.lproj/ParseUI.strings differ diff --git a/Pods/ParseUI/README.md b/Pods/ParseUI/README.md new file mode 100644 index 0000000..7cb8a41 --- /dev/null +++ b/Pods/ParseUI/README.md @@ -0,0 +1,115 @@ +# ParseUI + +[![Build Status](http://img.shields.io/travis/ParsePlatform/ParseUI-iOS/master.svg?style=flat)](https://travis-ci.org/ParsePlatform/ParseUI-iOS) +[![Pod Version](http://img.shields.io/cocoapods/v/ParseUI.svg?style=flat)](http://cocoadocs.org/docsets/ParseUI/) +[![Pod Platform](http://img.shields.io/cocoapods/p/ParseUI.svg?style=flat)](http://cocoadocs.org/docsets/ParseUI/) +[![Pod License](http://img.shields.io/cocoapods/l/ParseUI.svg?style=flat)](https://github.com/ParsePlatform/ParseUI-iOS/blob/master/LICENSE) + +## Overview + +`ParseUI` is a collection of a handy user interface components to be used with Parse iOS SDK, +which streamline and simplify logging in / signing up `PFUser`s and displaying a list of `PFObject`s. + +## Getting Started + +#### CocoaPods + +`ParseUI` is available on CocoaPods. +Add the following to your `Podfile`: + +```ruby +pod 'ParseUI' +``` + +#### Packaged Releases + +You can download the latest release in a form of `ParseUI.framework` from our [Releases](https://github.com/ParsePlatform/ParseUI-iOS/releases) page. + +Add `ParseUI.framework` to your Xcode project by dragging it into your project folder target, then add the following to any files that use `ParseUI` components: + + #import + +#### Build from Source + +`ParseUI` can also be built from source and supports Xcode subproject referencing. +Follow these steps to build and run via source code: +- Download the source code via `git clone` or in an archive +- Run `pod install` in the repo root to download all the dependencies +- Open `ParseUI.xcworkspace` +- Build and Run `ParseUIDemo` target + +## Components + +#### PFLogInViewController +If you are using Parse to manage users in your mobile app, you are already familiar with the `PFUser` class. +At some point in your app, you might want to present a screen to log in your `PFUser`. +`ParseUI` provides a view controller that does exactly this: +```objective-c +PFLogInViewController *logInViewController = [[PFLogInViewController alloc] init]; +logInViewController.delegate = self; +[self presentViewController:logInViewController animated:YES completion:nil]; +``` + +#### PFSignUpViewController +If you are using `PFLogInViewController` with the `PFLogInFieldsSignUpButton` option enabled, +you do not need to do any additional work to enable the sign up functionality. +When your user taps on the sign up button on the log in screen - a sign up screen will appear. +However, there are occasions where you might want to use the sign up screen independently of the log in screen. +This is when the `PFSignUpViewController` comes in handy. +```objective-c +PFSignUpViewController *controller = [[PFSignUpViewController alloc] init]; +controller.delegate = self; +[self presentViewController:controller animated:YES completion:nil]; +``` + +#### PFQueryTableViewController +Data oriented iOS applications are mostly a collection of `UITableViewController`s and corresponding `UITableView`s. +When using Parse, each cell of a `UITableView` typically represents data from a `PFObject`. +`PFQueryTableViewController` is a sub-class of `UITableViewController` that provides a layer of abstraction that lets you easily display data from one of your Parse classes. +```objective-c +PFQueryTableViewController *controller = [[PFQueryTableViewController alloc] initWithStyle:UITableViewStylePlain className:@"Todo"]; +[self presentViewController:controller animated:YES completion:nil]; +``` + +#### PFQueryCollectionViewController +A lot of advanced use cases usually include displaying data in a custom dynamic layout that is different from a simple list. +`PFQueryTableViewController` is a sub-class of `UICollectionViewController` that provides a layer of abstraction that lets you easily display data from one of your Parse classes in any dynamic and custom layout you might think of + +To display data in a simple grid layout you can use the default `UICollectionViewFlowLayout`: +```objective-c +PFQueryCollectionViewController *controller = [[PFQueryCollectionViewController alloc] initWithClassName:@"Todo"]; +UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)controller.collectionViewLayout; +flowLayout.itemSize = CGSizeMake(100.0f, 100.0f); +[self presentViewController:controller animated:YES completion:nil]; +``` + +And, for example, to display data in a circular layout - you can pass an instance of `UICollectionViewLayout` at initialization time: +```objective-c +UICollectionViewLayout *customCircularLayout = ...; +PFQueryCollectionViewController *controller = [[PFQueryCollectionViewController alloc] initWithCollectionViewLayout:customCircularLayout + className:@"Todo"]; +[self presentViewController:controller animated:YES completion:nil]; +``` + +#### PFImageView +Many apps need to display images stored in the Parse Cloud as `PFFile`s. +However, to load remote images with the built-in `UIImageView` involves writing many lines of boilerplate code. +`PFImageView` simplifies this task by abstracting away these parts. +```objective-c +PFImageView *imageView = [[PFImageView alloc] init]; +imageView.image = [UIImage imageNamed:@"..."]; // placeholder image +imageView.file = (PFFile *)someObject[@"picture"]; // remote image +[imageView loadInBackground]; +``` + +#### PFProductTableViewController +`PFProductTableViewController` is a subclass of `PFQueryTableViewController` that displays all IAP products in a table view. Some content apps, such as an app that sells comic books or video tutorials, may find it handy to use `PFProductTableViewController` to sell the products. By default, each cell is a product, and tapping on a cell initiates the purchase for the product. If the product has associated downloadable content, the download will start when the cell is selected and a progress bar is displayed to indicate the progress of the download. + +## Learn More +- Check out [ParseUIDemo](https://github.com/ParsePlatform/ParseUI-iOS/tree/master/ParseUIDemo) project +- Read the [iOS Guides](https://parse.com/docs/ios_guide#ui/iOS) +- Browse official [API Reference](https://parse.com/docs/ios/api/) +- Follow few [tutorials](https://parse.com/tutorials/) + +## Contributing +See the CONTRIBUTING file for how to help out. diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..48acfb8 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,3281 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00BD5BD822B2BD7B861DAE0FFFFCB539 /* PFURLSessionCommandRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 22AF66FAEC41A180692A915DFAA6C452 /* PFURLSessionCommandRunner.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 00DA73F8F06A6A3CF52EC6A908F01AE7 /* PFCurrentInstallationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 106FE48FB855E72F7842C099E98FA52D /* PFCurrentInstallationController.m */; }; + 01398DC1B86F80EB75EE0894DC440D6C /* ParseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 943FE65B8F32DCA748FD4B871663F421 /* ParseManager.m */; }; + 016EFFA38F17275483C1BF648B5D7324 /* PFLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 058FE1B26228FFF21C295DF2DF680081 /* PFLocationManager.m */; }; + 019811B1D67F59A225A5ADA70257444E /* AFNetworking-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 16E7F774F4751157B63DB04DB459BC55 /* AFNetworking-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 02E33B61D269EABF398A476592412A27 /* PFDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = D3592860B73625779473717C79E14C33 /* PFDevice.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 045864165E699D965D1D6C8AC89F46F4 /* PFMutablePushState.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E524941C9BC2F24FCC92215E85F2BF6 /* PFMutablePushState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 04E4E6AF1EC5843A657C4A3931CC5CE0 /* PFAnonymousAuthenticationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AB0E2E0BA627EEC178EBB58FEB61DCD /* PFAnonymousAuthenticationProvider.m */; }; + 0552F35AE0E7BFC8D7B7A76B91231948 /* BFTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F8CB4C16F79838F1FBAC6D1F4FADE30 /* BFTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0578CF412D50D2967A728990BCD5A5A7 /* PFMultiProcessFileLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0256B924DCFE7BACEB6568CD8A2C6C59 /* PFMultiProcessFileLock.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 06EAE725360DDE449CEE256BED435CC0 /* AFNetworkReachabilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 08BD2B6BFAC23DEC69F1D3FCA0B38071 /* AFNetworkReachabilityManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 078FA3A20BDFB81A4FFD0A31D4B5111E /* PFBaseState.h in Headers */ = {isa = PBXBuildFile; fileRef = A797A1CD69213D018C84F6D976394A27 /* PFBaseState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 08106DF3AF42B507248F4E808A0897AF /* PFCollectionViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A5E5655A0381C0C739C229727C1F3802 /* PFCollectionViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 08BAEB2246F51B5EE8BA16B804A2F259 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6546A78D7DDEF82B4CB1A6BAFE39FD6 /* SystemConfiguration.framework */; }; + 09335FD01568F3B002AAC964192F969B /* PFPrimaryButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 06FFD67B3E3A338C0616E33AC82E271F /* PFPrimaryButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 094FBF0A794D5BF1C85DCB72C7CEB462 /* PFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D9670272FFEABFE8085CAD7256B26BF /* PFImageCache.m */; }; + 0A536922388C321F57A81443158DACDE /* PFLoadingView.h in Headers */ = {isa = PBXBuildFile; fileRef = D6CA9C05834B083659D1A1C33EB15AD0 /* PFLoadingView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0AAC75CB2FCDD4F8E53D553BE107DA90 /* PFMutableACLState.m in Sources */ = {isa = PBXBuildFile; fileRef = B640687713469EFE42C0475F6192A31F /* PFMutableACLState.m */; }; + 0B4A604D105FCB3DAE6B98BD1FB3910A /* PFObjectState.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EE7A749F0FA95FFDA3601E6582B943 /* PFObjectState.m */; }; + 0BD38250D122B15783D258DE1977C0DD /* AFNetworking-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E7653C557D29E7F48FC2C3DE41CC735C /* AFNetworking-dummy.m */; }; + 0C2E1C50D52AD202C6986A21C35E9B98 /* PFAnonymousUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 74CE67880E6C87B2A6E1DD4E8F148691 /* PFAnonymousUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0C734B29E5608655312734DD93939138 /* PFAnalytics.h in Headers */ = {isa = PBXBuildFile; fileRef = 632635171DFBADC6DD391BC3C9FBD721 /* PFAnalytics.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0D8A5FE22EE90B1E4008CB1A1D35C3DC /* PFThreadsafety.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A23CAEC9BE172289170838599F9F895 /* PFThreadsafety.m */; }; + 0DA782AD0BCA5BF57EBD9D5F4D92392C /* AFHTTPRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D6FE3EE8F0D3CAE0224A63EC2E293D7 /* AFHTTPRequestOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0E20ACB7A86434366CF57C0C1FD99A22 /* PFConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = A40A9A6110C6D841C877E13C7886F5B2 /* PFConfig.m */; }; + 0E60D24FA0C38CF3B38881C50A60968C /* PFObjectFileCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 52917348ED99708803AB4571CBF7448F /* PFObjectFileCoder.m */; }; + 0F06E69D48EA3A9505C5B39038ECBD45 /* PFNetworkActivityIndicatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E7AC9992862CA136E8E65BF8DF8F3D15 /* PFNetworkActivityIndicatorManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0FBA8F37F9314C43FB6804EA1543F175 /* PFInstallation.h in Headers */ = {isa = PBXBuildFile; fileRef = D3339AE7574EA468A11239825C11ECC5 /* PFInstallation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1030F02D79D67B45E17D2C391621786E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + 11B3F1A7B5CE93E9B8A20FD11698DB5F /* PFPushState.h in Headers */ = {isa = PBXBuildFile; fileRef = 44DA91E0DF5B0A8CBEA09F5223C6F1C8 /* PFPushState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 127301843CBDF317164B7E181CB7DE71 /* PFSessionController.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA33D6FD4CA15B4EDB933F4A4E75327 /* PFSessionController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 145685209448E41A05DF8B53183B7711 /* PFPushState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 794BFE228ECF6346EF495E3238904178 /* PFPushState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 14A353C6005E964A160C5937FC711780 /* PFRelationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 989B472DE53DB8DD10EA5CEB92D70912 /* PFRelationPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1565665806FC25DC6AE914B7BA7B2553 /* AFHTTPRequestOperationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 13D360FA4AE2720A4786C6058456F2DC /* AFHTTPRequestOperationManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15B5CFA0B2EED02F63F70BF2D2B3E1D3 /* CGPointEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C20CDE358825835E6B40286F5163A863 /* CGPointEx.swift */; }; + 15D50F7DB95440DA43E8E2AA2471ED8A /* BFExecutor.h in Headers */ = {isa = PBXBuildFile; fileRef = B94C7AA42C195AC77E28C347AD8BFF46 /* BFExecutor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1613B47B1EEF674499C27C7AB558C4EB /* ParseUI-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 10785B227444AD6A5CCC16CC93FDED1F /* ParseUI-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1630D46574637CECF1FA2447F08892B0 /* PFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AE4FD7D79AD079CDF0E0992224F6277C /* PFNetworkActivityIndicatorManager.m */; }; + 16D80822E7C12B2E59DDF83749C9A6A2 /* PFLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D879C61F873DA897D8CDCFD529DD718F /* PFLocationManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 17950032F4E1AEC0308396ACF5DED48F /* ParseManager.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7E387CE6912A38238CECCC234B822D /* ParseManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 179674B0B17481060FA4981ABF6EFF85 /* UIProgressView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FED8E692B2FF2E30A56C6D070BFAB06 /* UIProgressView+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1798DBC837F4F2EC3B68A113D11DCBFD /* PFCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D2FCDC0741A4571CCA8D7C1E99F48105 /* PFCollectionViewCell.m */; }; + 18170D405D4E07AAB5093996D5FF806A /* PFCategoryLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = C478D21353BA164DF20A78C96701C338 /* PFCategoryLoader.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 189ECA4A40E0CCCBEF2308D570774095 /* PFPushChannelsController.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B44D9A7073478A4D49371D61B2DACF5 /* PFPushChannelsController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 18BFDBEFA619B361899E126DAC28B345 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + 1921B71874A179ECF5CD1A7E18A3080F /* PFUserState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C4A6A6A2B2F9A47F6A8A876DC4E0983 /* PFUserState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 19C5B336E3E16B60F92682B59906295E /* PFResources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DA5D03F437F920BAB37A6380F6CA63F /* PFResources.m */; }; + 19E00AEFB9F41BAD813091D05FDDCEB3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89BF5589887D6023691A7740CFBC374 /* QuartzCore.framework */; }; + 1A180D118B0CCCC32049F0A2EB1626CA /* PFRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 1660ABF7B85069A170248B808256455D /* PFRect.m */; }; + 1A4D3B715AC215113E1C1311CCE7640F /* BFDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = B89BD9572DDE9528AB675A588A47A609 /* BFDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1A88AC0FD5D36F26EFC2909C07E61465 /* PFConfig_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FC6948D841E1FEED5688E9D076FFB17A /* PFConfig_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1B157E8F97EFEF7A0F6D1F4DDD08CF1D /* LiquidFloatingActionButton.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 97FF6FABD68A2485CB659846729BFFCD /* LiquidFloatingActionButton.bundle */; }; + 1B2915600B83810C7FAED0F25DEE1A75 /* PFCurrentConfigController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B8576A9952876C6D42C077262FD9FA9 /* PFCurrentConfigController.m */; }; + 1B78465543A9687FB133F944D163583C /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BEA4A9ECB98F43F4DD96BE3251E35 /* AFURLRequestSerialization.m */; }; + 1BF39039FD4EE83618B9EE1D8EF4785D /* PFDefaultACLController.h in Headers */ = {isa = PBXBuildFile; fileRef = 55A85024FFE04E028567F29F7ABA33E0 /* PFDefaultACLController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1C7E9F19AACD66C3E6416D40D598E80D /* PFURLSessionCommandRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = C743D5BE4BE093B67F21D64BDAA0D64F /* PFURLSessionCommandRunner.m */; }; + 1CA4984E2ABA469F1EACB26D21AE2350 /* PFPushUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 512A5EA836765109A6CA0BF4BF2BE6C0 /* PFPushUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1D16B974DF5E638237BF864A013FCC8C /* PFInstallationIdentifierStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 504090B38C287A418E1F8BE4850DB9F8 /* PFInstallationIdentifierStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1D2DD0C1A7D183E424DE783193CB4E8E /* PFHTTPURLRequestConstructor.m in Sources */ = {isa = PBXBuildFile; fileRef = A05C4014427DEBE8CDF3F7DF4EDF5B78 /* PFHTTPURLRequestConstructor.m */; }; + 1E70591D33CD8122762393BDF38E8782 /* PFDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C20844654F9A04C67386CC1D2C2D1B1 /* PFDecoder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1E90FB160DC21B9FE1221524941169A2 /* PFInstallationConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = A93DBE9D5C2687DF9723AAFA66F2A971 /* PFInstallationConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1EFA6ACD4D8E3526529AF287165EE70F /* BFCancellationTokenRegistration.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE85F8359D8DE8DD20C174352319881 /* BFCancellationTokenRegistration.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 1FB39DD7CE8EAEE4113561F260141EFE /* PFPushController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60D8FEA3A3B139B0A0F01500655A6DE8 /* PFPushController.m */; }; + 201EB0EBCBB978D30FE61D193981E792 /* PFPin.h in Headers */ = {isa = PBXBuildFile; fileRef = 47BBEF70FCF6A4D933AADEA763919924 /* PFPin.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2040FDF184F9FDC4EF229C4A73F990F8 /* PFInstallationIdentifierStore_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 43F928E8DB69EEDE303F3A4C7318F4DF /* PFInstallationIdentifierStore_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 20E80B94E0147D15323F2E0E9CDAF716 /* PFCurrentInstallationController.h in Headers */ = {isa = PBXBuildFile; fileRef = AF6140D6AA1728AD006CD6EAEA3B3CC0 /* PFCurrentInstallationController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2312D40E90058221C6E0206FC057CC31 /* PFEventuallyQueue_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EC1C24F1EAE7BA1CB22FA6349CE5E07F /* PFEventuallyQueue_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 24469F3BA342CC724B6BC8C254339850 /* PFInternalUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C455D006BA9847DD01AF4AD9D0A99C0 /* PFInternalUtils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 24C01390E2A670ADF511B2D28AEEBAE8 /* PFImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 632A9C2EB7D9F58D0A7E0A0734C27B83 /* PFImageCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 25749EC991182C94C321768FBB9B53EB /* PFRESTObjectBatchCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 672FF2A8DEDB8B294A94B94B44170E86 /* PFRESTObjectBatchCommand.m */; }; + 25EDAB4F90DCEEADF51B1260BC8638CB /* BFExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 055D7C6E9227DB20416C800E4F51A337 /* BFExecutor.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 2646DA8EC00C15C5B326790D44AA4096 /* PFUserController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E8BE2C27FB13B26072B0BD9EE14FBAE /* PFUserController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 270C1EA6421D70034FA2635DF116C88F /* PFFileState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B3B9DFEEEF919C024CAAB1D52C6FA83 /* PFFileState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 275ECE8ED462559F632E86559F49E059 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6546A78D7DDEF82B4CB1A6BAFE39FD6 /* SystemConfiguration.framework */; }; + 279AFE04B23F5E0232696B7DB3E4E02D /* PFObjectController.m in Sources */ = {isa = PBXBuildFile; fileRef = 96B84037727346B0A0B3E2F3A9668A1C /* PFObjectController.m */; }; + 2801DB05C8F2FD431ACF0BD58622874B /* en.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 2208328361FA7D0218574BE074627642 /* en.lproj */; }; + 2827B74CEE40DE2507B095595338AF40 /* AFURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 74E1103577F4DCB27E5C109F9329653B /* AFURLSessionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2857BF1C458AEBC871960C05A6ECAB1D /* Parse.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C9379B624B1D99D2ED8683BF8A27499 /* Parse.m */; }; + 286A7911AA124BAF979E6C477A1D399B /* UIAlertView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D3CD655EFF856D601BCADD2A12D20B1 /* UIAlertView+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 286FA64725A5209CD48FD20012FE644B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + 28C1C6EA992A6C6CF6028D2C1A6D78EE /* PFPropertyInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 258F5BCDF91B9AB891FBF085B379DA3C /* PFPropertyInfo.m */; }; + 29079B4000189CFF9ED8942817028F0C /* PFCurrentConfigController.h in Headers */ = {isa = PBXBuildFile; fileRef = E6973DA797DB68C919C66D4BA3D695B9 /* PFCurrentConfigController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2956954B2EE473918D419EF77447E1B8 /* PFObjectSubclassingController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D7587C874F53970DD6689504C43AE04 /* PFObjectSubclassingController.m */; }; + 2A16E0AC8BB73128B46A57F116F3D1C0 /* PFAnalyticsUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 4896C3D552B4755BE9E7C1C62D992F5E /* PFAnalyticsUtilities.m */; }; + 2A22D38A97847D209CE7310C2D0D4381 /* UIWebView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = C0F49D28B3C52C5467AE62585139EE0A /* UIWebView+AFNetworking.m */; }; + 2B356D314125B673E6B4BE8785997F38 /* PFSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E5E5504191D61A91B249136F90A7F29 /* PFSession.m */; }; + 2C43AEB6664F383055A152BE8742E1F1 /* PFSession_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BF69C85C516E15E59322803B5CB90959 /* PFSession_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2C7FFF08D22648BDFDCA04221F03292B /* PFPropertyInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3216FF24B9FA981F635F7D85A2444D31 /* PFPropertyInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2CAE5D5F7FEE4D6E3E02FF6B4FC55855 /* PFPurchaseTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DE72C53C03EAE4311F887EE6A186CFB /* PFPurchaseTableViewCell.m */; }; + 2D541155B362A010D360AF90A77C61B2 /* PFPushController.h in Headers */ = {isa = PBXBuildFile; fileRef = 815246C826CF2ADBE326786D6BA94F42 /* PFPushController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2D70672D79B0F0334BF2EDEF1D42E928 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FC471E6023A052E8FA627F61045398C /* AFNetworkActivityIndicatorManager.m */; }; + 2E3C9AC03335B047D286B1C0B2109B5E /* PFRESTCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 705C8317C0CF03B733A5A25FB46A66EC /* PFRESTCommand.m */; }; + 2EFC840C6647779B53FAA27106391C87 /* PFEventuallyQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D78597D58FFE85B4FB8C4561AA50BB5 /* PFEventuallyQueue.m */; }; + 2FDCE355A28690CCA7BD7110830D9D8F /* PFQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E812FF98BA70450EEE9168727E42BC7 /* PFQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30E2F0EF96EFD7A7373B984FA44137D4 /* PFMutableRelationState.m in Sources */ = {isa = PBXBuildFile; fileRef = DCCF3542E63589C3EDE6E22471B9C2C7 /* PFMutableRelationState.m */; }; + 3202BD5404C424FA2BEE387443DC609A /* PFURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = CFF05FDAA5BE92C3290288A41691E4EB /* PFURLSession.m */; }; + 3215AD848B3348BD44A226B412C57681 /* PFCommandURLRequestConstructor.m in Sources */ = {isa = PBXBuildFile; fileRef = AE22C9A9D5BEC3BC938693871AA98EE6 /* PFCommandURLRequestConstructor.m */; }; + 3253159ED9BC75770A0C733C36AB2803 /* PFAnalyticsUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 95920EE1067D57330EBA26ABC1F8A742 /* PFAnalyticsUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 326035FFFA592FB552CF238BEACAC5D2 /* PFHTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 6085298ACB2321485F0D3C12D351FEB5 /* PFHTTPRequest.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 33091EFB09D98BDF743CA6F5C2AFBA33 /* PFHash.m in Sources */ = {isa = PBXBuildFile; fileRef = 71EC800801E622412FB29F8D44D3CD3C /* PFHash.m */; }; + 3326E4DE6998C24487160FA02D480584 /* UIImageView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA5DAA6B6F95021F88E0374D52599C0 /* UIImageView+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 334E4873ADAA7E84D76696B1ADD98F6C /* LiquittableCircle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F4C394668FB6C72DEAC18029145B8D /* LiquittableCircle.swift */; }; + 33644DC407C29B3D9EAEF7ADCEB52478 /* Pods-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FC41D49301C5C346F942300A428EFE59 /* Pods-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 33983535E17BA783C02AB8A3BA462DDF /* PFPinningEventuallyQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D0359022BC718069CC2473CA068BFCD7 /* PFPinningEventuallyQueue.m */; }; + 339DB11BD1704A9C80E2460F411E466B /* PFUser.m in Sources */ = {isa = PBXBuildFile; fileRef = B4ABE5C41AF16EA4023696A3604580C9 /* PFUser.m */; }; + 33DAD155F6CE2CC1823D7AD97B3FB2AD /* PFRESTPushCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 2185E69315D22B3715A623F06696D5FC /* PFRESTPushCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 33E6F85F30FC19902B3D8DA8B88D0B7C /* PFRESTFileCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFB709FA1612173F12A1B53A27F1636 /* PFRESTFileCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3472E6DF2EAD219033569E73FDC389C5 /* ParseUI-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BC28B0883B9B9DB2F5F1F2DF82A956D /* ParseUI-dummy.m */; }; + 348DAF0BF5F5CBCD95FA719F1401E337 /* PFQueryTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = E3F30F4E54BFE18519B6553114DA944B /* PFQueryTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3522D04AB2924B8342CEB7DFC01CD60B /* PFAlertView.h in Headers */ = {isa = PBXBuildFile; fileRef = 19EB9918367D9576DC5BA66511FDDA59 /* PFAlertView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3554685EA350E46A8BF0B1A3F24011A7 /* PFColor.h in Headers */ = {isa = PBXBuildFile; fileRef = B8E1E38F2E91125CD57103EEBC3C475C /* PFColor.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 35668F1BB7B029C903CB802CA12D68C4 /* PFCommandRunning.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF997AF17A743017CD54A4193CC9AE6 /* PFCommandRunning.m */; }; + 358CEE0E9DB4815A038EDE76B8D6E617 /* UIRefreshControl+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 490DD45156F4A90833C74B5509134501 /* UIRefreshControl+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35E10DE9BAF946B7C2A73915CD71B113 /* PFInstallationConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D13079A63558D8C87DFDB1697B537 /* PFInstallationConstants.m */; }; + 36B8C3052C0A23C277DD01695411808C /* PFObjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DAA2CB4A82D8EB23F29780BFAAC1DCE /* PFObjectController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 36CF851FB6C6221F6B8723C9DC1E7DD0 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CA945CDAC44DDF1110329B15603AAF7 /* Security.framework */; }; + 36D1557E4A4012CE623C822D680F6733 /* PFApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = 09563E9AD709C4FE3E9B10F016D79F40 /* PFApplication.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 370E062806DD29730BCD4D5BE041C3D3 /* PFUserState.m in Sources */ = {isa = PBXBuildFile; fileRef = 462806D14A2A143E44F927E5742857E6 /* PFUserState.m */; }; + 3782D1D629A8E35048EB8ED5C985756C /* PFProductsRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 508F81A14503FDBEA2527D9048B06142 /* PFProductsRequestHandler.m */; }; + 37937DB27B7EC6C78D2D8DCCC4205454 /* UIActivityIndicatorView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = FED57DD7F261567A1E9174BAAF263E24 /* UIActivityIndicatorView+AFNetworking.m */; }; + 39B709F501A90E965517806018B70AA6 /* PFThreadsafety.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DA3B7EDAFEC180CEDD3C7393AD44FF3 /* PFThreadsafety.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3A5E6295EACF62FC5DE9A5198BCCA4F8 /* PFAnalytics_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 182CA1A9D7A94C67A9EB27D9B1D89590 /* PFAnalytics_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3A92C7DDC731F2C3E9CCA448616D05F7 /* PFMutablePushState.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C2D54A8FA658EE6C378587022D1F23 /* PFMutablePushState.m */; }; + 3B52D2A91737D642D72373FD3CD09C75 /* BFCancellationTokenSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 78FD692B8BFB4BE7D09FDBBA605AE451 /* BFCancellationTokenSource.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 3BFC9EA740843C3CDB40CEFD6BA5BF2B /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70B9D1F18A0D896262A4B138338F2901 /* MobileCoreServices.framework */; }; + 3C4F032BE9628EF88C5306F606BCB654 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58228477EDB607E07438A4DA7659B1AB /* CoreGraphics.framework */; }; + 3D134C66893736B8053D5765CAFB4612 /* PFColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5744A62C5DD57C35F95B23CD332A69B0 /* PFColor.m */; }; + 3DEAF8E13BDCD03934CF1C7DB3599C3D /* PFFieldOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8345935C5AC8AEE28996E8ADAFE07522 /* PFFieldOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3E015DAC9467586A55EFFF4258040045 /* PFMutableQueryState.h in Headers */ = {isa = PBXBuildFile; fileRef = A5DF1057EDD3E40B699F68523EB26FC1 /* PFMutableQueryState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3EAD53BE9AE4FB212FA1A92DB5EC3B6F /* PFFieldOperationDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C3F5A63922E21A53CC4884A45C5839E /* PFFieldOperationDecoder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3F57EEC7C9B39E131B3F3171CA696756 /* PFPushUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FD488736BA7D2666128B1D854BD802D /* PFPushUtilities.m */; }; + 401D8937417175A88D92BAE68734C18D /* PFAnonymousAuthenticationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 44270F881246E94C8F2A111F06702B83 /* PFAnonymousAuthenticationProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 40B7399BE9F3F3CF17373AB2EEEBE1AA /* LiquidFloatingActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6A18CBBDB72D9222355CD85E1E5AC96 /* LiquidFloatingActionButton.swift */; }; + 41ED8DF2035DEA37104F79E3E69F8788 /* PFEventuallyPin.h in Headers */ = {isa = PBXBuildFile; fileRef = D4DA6F666BF906399EDA329897330DE0 /* PFEventuallyPin.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4344D005FD2539A8133664EA4BC836AE /* UIRefreshControl+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EAE992B8BEAB950FE7E122420450483 /* UIRefreshControl+AFNetworking.m */; }; + 440ACE83D0D52F35AFBA5D549A563904 /* PFCachedQueryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F2B79C120F1E343333F0263C901B136 /* PFCachedQueryController.m */; }; + 455052E8E1BE9CD4FE8C3241FD0C52B4 /* PFFileState.m in Sources */ = {isa = PBXBuildFile; fileRef = F337DF8EB662B6A49FFEBC966CF745B6 /* PFFileState.m */; }; + 45808E59D592E09E43E1EAF3FE079966 /* PFTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = B13872C024A5C2B50AA18722FE15CDFA /* PFTextField.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 46AAFE4159990B1B24B19083E6D353EA /* PFPinningObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = A83E1CB2B25CB590D1D1E867BA4C0A1D /* PFPinningObjectStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 46C2799A8F59D05A02F70182137726DE /* PFLogInView.m in Sources */ = {isa = PBXBuildFile; fileRef = 053008B62E75445056E83EAF88F670AE /* PFLogInView.m */; }; + 46CAE74A812BDAE95CDBE19668E3FFAA /* PFAssert.h in Headers */ = {isa = PBXBuildFile; fileRef = C99492480C58DAAD671B694D06B2A984 /* PFAssert.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 46F6819FB8D22AC3FB2EED6BC5D68659 /* PFRESTObjectCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AC7EE67669C15374170942A5ADB449F /* PFRESTObjectCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4709FE9CAEE7FE66848EF36AF7110562 /* PFUserConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FC25E97FDE9217C1F682376CF331421 /* PFUserConstants.m */; }; + 47AD2DB520B62717195FDD28C649AC46 /* Parse.h in Headers */ = {isa = PBXBuildFile; fileRef = E5B0E2B0324CDAD8CD56AC4EBDE6529E /* Parse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 48AEF11FE1BBC34BE7DEA36B71F44F67 /* PFRESTSessionCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F148B720ADCC2D2110CC085A4F393C9 /* PFRESTSessionCommand.m */; }; + 492833D120B8C740A5C3108E060E0647 /* PFSQLiteDatabase_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 78B65A9361B5999AFC577A6CAE34D1D6 /* PFSQLiteDatabase_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 49A1DC63D25763EA8658CDF54CBE821D /* PFAnalytics.m in Sources */ = {isa = PBXBuildFile; fileRef = CCDFF9320F22A33DA2D0EB4C88FA9A95 /* PFAnalytics.m */; }; + 49A9F48CFB2D201E0936340A1FAFB650 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58228477EDB607E07438A4DA7659B1AB /* CoreGraphics.framework */; }; + 4A89C6C3C1D00D8D2C3FC08CDA4A4AC9 /* PFCommandCache_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E9FDC84201D32A4325B4BF25B7DE58DC /* PFCommandCache_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4ACE3C1215E0AA1F808A28890FE9930B /* LiquidUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE08D7134612A5CB853602B49829807 /* LiquidUtil.swift */; }; + 4B1BB47C1D2A7C76F6FD7DF66C36AB42 /* PFFileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C0E17697C9CAFCD60CAA1AE570630945 /* PFFileManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4B3B2B7D8582379D9435E9EC8F17A867 /* PFFileController.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A0675A9A52CC3B48F422CA4E940CD3 /* PFFileController.m */; }; + 4BBCFB1ECD9564B89796936451080BBE /* PFApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = E16CCC9C4042040D34C25745F5E93979 /* PFApplication.m */; }; + 4C3B4B259045C50F40DC38C925844D3B /* PFInternalUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E47B281C898AA47BCBDC4FD0172D821E /* PFInternalUtils.m */; }; + 4C6E285F00A2CDFE1FAF6DF06575403B /* PFDismissButton.m in Sources */ = {isa = PBXBuildFile; fileRef = E218DEB073F057E49C9302AEB766059F /* PFDismissButton.m */; }; + 4CB11F4E752B79BD7254A8C92251BDBA /* PFCommandCache.h in Headers */ = {isa = PBXBuildFile; fileRef = BC28C2D9801402D3385F95F86BD06CB6 /* PFCommandCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4CCB9CA05BFCAC3B4E68EACE11B55B96 /* PFRole.m in Sources */ = {isa = PBXBuildFile; fileRef = D5F164F971C806E166D9BD66BA1C0ED5 /* PFRole.m */; }; + 4D7FE531EAE79470599DACF805128A77 /* PFURLSessionDataTaskDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C8EE00FBF100049803CBD08E3488726 /* PFURLSessionDataTaskDelegate.m */; }; + 4DB1360969A847B0033539BF5F704E7D /* PFCommandRunningConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 97FE58DD961885B0B4A058AEAF7A02A8 /* PFCommandRunningConstants.m */; }; + 4EDC94927E0356BF765ED55C8BDD063C /* PFCategoryLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = D994A541599DACAF7137D8879F25C89E /* PFCategoryLoader.m */; }; + 4F13C46D1D3F2A4BAF8177B9B721DB18 /* en.lproj in Resources */ = {isa = PBXBuildFile; fileRef = 56467AABBB2F1EADE1FDAF4C1D07FBC5 /* en.lproj */; }; + 502E983D00899578A876A927E8C48BE8 /* PFAsyncTaskQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 286E6D876A21BD5D906EE5CA6743D2E0 /* PFAsyncTaskQueue.m */; }; + 5037B6B609FEFCC92BAE0B8FD5CA4A7B /* PFRESTCloudCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 15DC305FBA250D68B59D7EC94B7F39C8 /* PFRESTCloudCommand.m */; }; + 512DB852ECC39FCBF0F50BAE3E4A104E /* PFDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 899E7EFD0AD739A28B9021F4534B5545 /* PFDateFormatter.m */; }; + 526DA6851CA12A57C70B0FBD2A35235F /* PFQueryUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 204C2D996771F45E7C30302ECFA3D5C5 /* PFQueryUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 533DEC0963B064B1BF23E9C9D17332F7 /* PFURLSessionJSONDataTaskDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 507EEEA0E2D71194AF983A704F2B50B1 /* PFURLSessionJSONDataTaskDelegate.m */; }; + 5381B409F7381F2014B347B9F6B514DA /* PFLoadingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 412F790A066291B851813E4C9327BFB7 /* PFLoadingView.m */; }; + 546E2E51B4BCE1512A59EE9D01214E31 /* PFProduct.m in Sources */ = {isa = PBXBuildFile; fileRef = F908F8F01022F84F47E832CD33F366C0 /* PFProduct.m */; }; + 550DAF9B85B98E5BAA38D0DF49E812E4 /* PFPin.m in Sources */ = {isa = PBXBuildFile; fileRef = 57DBB8A59CEF5779FB2FEC6E3357F1D8 /* PFPin.m */; }; + 575CC7827848B936ED7187F1C1C2216C /* Bolts.m in Sources */ = {isa = PBXBuildFile; fileRef = 25DA2A63A41FDC2B72632E0F65A7250C /* Bolts.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 57E300D705DF13EE8CCB58F57A39279E /* BFCancellationToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B22145FF686BB047CD997E62D0DEF43 /* BFCancellationToken.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 57EC4132D85D4A0E94B2FD7FCB0075CC /* LiquidFloatingActionButton-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B4B55BB161D999FD5EC2B657E7B6B3 /* LiquidFloatingActionButton-dummy.m */; }; + 58372966A22D83669EE89B43EAF422A0 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC7294772B9396EF14228A8BD24860DE /* AudioToolbox.framework */; }; + 5A11D3C56318FE645F116EBD476944DA /* PFCloud.h in Headers */ = {isa = PBXBuildFile; fileRef = D9AF3C2322515C270D4C304A1D84789F /* PFCloud.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5A981A9BFFADC77591121022C15E92F4 /* PFConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8FA54606CCBC52550175EBAFEB25E8 /* PFConstants.m */; }; + 5AF4F83832A8494203D0BFB1C3E4CEA1 /* Bolts-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E2F646392C325F581AA7EEEB52C037E0 /* Bolts-dummy.m */; }; + 5B686292B1A3A8DC0CEAA5622464DC89 /* PFPurchaseTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DA7F4F5534EFC28E047F3039137BB0F /* PFPurchaseTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5B7C81385D9434AB6D4AC01CEB880D5C /* AFURLResponseSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = F87A7541A9593AC496503BD08923E52D /* AFURLResponseSerialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5BFF5F2DCEB86428DF0F31A5EAE8A57A /* PFBase64Encoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 829C2DF55D47AC9FF3B59122141B8F50 /* PFBase64Encoder.m */; }; + 5C54E1590E2C0BFB06E37FF101E1BC96 /* PFAnalyticsController.h in Headers */ = {isa = PBXBuildFile; fileRef = F64FC9AA07C23B6390B31487EEECC02F /* PFAnalyticsController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5DA1870A15C061EA6DA75F6E52F5CA89 /* PFWeakValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DBE2DCDD433C14FC53555C1A83AB199 /* PFWeakValue.m */; }; + 5DE730765DBD398F24500FD6FED46AD9 /* PFOperationSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FF98AED99DA5E23EF2405DB00EB5C15 /* PFOperationSet.m */; }; + 5E3F5671288D9DB57226C64C9FD2AE36 /* PFLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B5BD965A4C5DAA801C33535E0BB379 /* PFLocalization.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5F5814DD80E470A8C54ABD6C213F385F /* PFLogInViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 38C7D3C7362627C2E0E8B4F5BE09F5A2 /* PFLogInViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6002EDA85635EC3C9271362E11CF98A6 /* PFCommandResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB07DB9FE0C3E5E4C1EA3AD16600DC3 /* PFCommandResult.m */; }; + 60CFBA47EFF4DD715A40F240BE982FB2 /* PFACL.m in Sources */ = {isa = PBXBuildFile; fileRef = 54554DF4C25DD4127EC0364200393FE6 /* PFACL.m */; }; + 60D4C5AB361B8056D00CCF69DFCF3942 /* BFTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AB08773B8895841E9EDE2015D2A335D /* BFTask.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 60F56114B9878A79D52F1EF542BDDF46 /* PFURLSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 48BA00A680115FA8E8598713DD4F3F53 /* PFURLSession.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 61625E7D30934B7BC53621FA9C139B32 /* PFTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A99437D514635B420895CF6FB7336143 /* PFTableViewCell.m */; }; + 61662C3A27D0EBF95593A438D87794D7 /* PFSessionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18575CBC596E9A1218A762DF5E3CD72B /* PFSessionController.m */; }; + 621D2302EAFB9263E928B7FD16B54FB6 /* PFSessionUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 732BF5F5AADEEED43943EDD827DB02D2 /* PFSessionUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 628FF832AA99DC2D599293C0B435650C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89BF5589887D6023691A7740CFBC374 /* QuartzCore.framework */; }; + 64709E4EF8CB8A1E70F1744E948C7B9F /* PFPushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A24A3D3E4F16BBD1F267C848321CE5C /* PFPushManager.m */; }; + 6562D01F079C8361582F11CF4824438F /* BFTaskCompletionSource.m in Sources */ = {isa = PBXBuildFile; fileRef = FE82138DBE52528B5F9039EC4FA92C17 /* BFTaskCompletionSource.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 656ECDEF31D91456E7850E8E85370927 /* PFErrorUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F5FA5FAAFCEEF833D9142FF59994544 /* PFErrorUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6607F3D2D3BD489448924FF0C337995D /* PFObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E1756E787A2805CAFDC958AE3C4960D /* PFObject.m */; }; + 6628475D39D15292F5AA818FABACF173 /* PFGeoPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B573A03FCE6CF5B48F8CB91BAD43435 /* PFGeoPoint.m */; }; + 666CB5EDE86FAAFF588639C5998D7EBC /* PFFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 795840987CB6C725613D305D3D8DE0C5 /* PFFile.m */; }; + 67C83A746D19DD80AF019885B8EF373E /* PFPush.h in Headers */ = {isa = PBXBuildFile; fileRef = 356CD10091814DAEF0798313892C8C05 /* PFPush.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68B669C1D98F5D8B5ECB996FD10B0E88 /* UIImage+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FB2A37BB79697F99A0A53FA1B7F9B47 /* UIImage+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68C83552D7F1D6BFA851D45139FFFFE3 /* PFPurchase.h in Headers */ = {isa = PBXBuildFile; fileRef = DDE94D96A9C62A91D595D2E433F21F9A /* PFPurchase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68FC00787E858A0969E68E3E0F0FAF67 /* PFAnonymousUtils_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 977AB32A80ABE4BF539022F03D0403C6 /* PFAnonymousUtils_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 692EFF6E00784FCAA8113237329AA161 /* PFObjectLocalIdStore.m in Sources */ = {isa = PBXBuildFile; fileRef = CB76E8D570FAE396D5315056A3E7E24E /* PFObjectLocalIdStore.m */; }; + 69733C722E4B8B2E6013D10A159655BE /* PFTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 0ED8804894EC6C702E97CB2701FCD8B4 /* PFTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 698727C831749E84FA33523C00889B0A /* Pods-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = ED37C1A5862B3AE73D6077D7795B9474 /* Pods-dummy.m */; }; + 6A75B6D4AD48ABA0F24FF40F6B93E7AA /* PFRESTCommand_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CB28E2AD26FDC0C0ACEA80136C6E997 /* PFRESTCommand_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6A7C09F94CE721DA3E5DEAF823A16489 /* AFNetworkActivityIndicatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B2535EAEF95F25D2E77544BEE32588E9 /* AFNetworkActivityIndicatorManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6A9EC2B6225A295D92F851A47E6E9868 /* Bolts-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 286AB7391704B71658D0BCF8A49D2F4D /* Bolts-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6BDAC1CB326F50E98775F898C97CEA51 /* UIColorEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B7AA2E50772619C4657FB990A261225 /* UIColorEx.swift */; }; + 6CD7EF58347DE8E8F9302C1E4E48CD09 /* PFKeyValueCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8213143BCC700BC9B5EE28B2AFE47BC9 /* PFKeyValueCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6D2E9001DCB85565EB20DD91F92746D7 /* PFCurrentUserController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9F130216053B89EE75FBACC3BB4F65 /* PFCurrentUserController.m */; }; + 6E85D655533AB0656270E012633601F0 /* PFConfigController.h in Headers */ = {isa = PBXBuildFile; fileRef = 374066DC72A963CFFD3374DBBFA2DFAC /* PFConfigController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 700083BCBF89F1F2496A63EC135380B9 /* PFPaymentTransactionObserver_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4240B13C9AE359BF942D85E0DE35AFE2 /* PFPaymentTransactionObserver_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 70186153736C45D0242EA55CA995A91F /* PFSubclassing.h in Headers */ = {isa = PBXBuildFile; fileRef = 5444BD2EF92DA71A5E0E4E368258FCF7 /* PFSubclassing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 70CB1143B527B2FCEB046B2A0E63F433 /* PFQueryCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0396C6EE2D90BA2591A0BC7503FAF784 /* PFQueryCollectionViewController.m */; }; + 7117E42D48773D23BBB822C7C8EFF934 /* PFKeyValueCache_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D94237B26D6FD987E657CCBB1B2C868B /* PFKeyValueCache_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 718A3D999C43CA591A064B44D9CA4CC1 /* PFMutableRelationState.h in Headers */ = {isa = PBXBuildFile; fileRef = B89903554FFA78AADE8F525DDC0C7959 /* PFMutableRelationState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 71C5D30743E01525E0CFE90793723FCC /* PFURLSessionCommandRunner_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = FCC2C950B794131718A14019810BC78F /* PFURLSessionCommandRunner_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 72F791B09655165E996EB77B9620901A /* PFInstallation.m in Sources */ = {isa = PBXBuildFile; fileRef = ACD672B833F3DE5495704B56CF7BA509 /* PFInstallation.m */; }; + 74D69DF3AFFB7AB5DFA518A5B6527DDE /* PFQueryTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F578D2BE836F7E5FCD8B4F070D2CA62B /* PFQueryTableViewController.m */; }; + 74DEA9984AA2FF026E44CCCCACC64B94 /* PFQueryController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A7D8F6CA1BC15F07535717374D4678F /* PFQueryController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 750EDE0D5F97545B54BB3E3C50E3C086 /* PFObjectSubclassInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 7304B1EA9F7A8C12AD248A220266F7E6 /* PFObjectSubclassInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 75A0194BA98F24F47B2BF003A8CFFE8A /* PFNullability.h in Headers */ = {isa = PBXBuildFile; fileRef = 391F623207B6760C4D0AC086EE3CE49C /* PFNullability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 75EB4AA618AD72C243D405185DDE53CC /* PFCommandResult.h in Headers */ = {isa = PBXBuildFile; fileRef = D1DFBF1DE14726ABC1691D616B077B9B /* PFCommandResult.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 75F271B241FEF6E852ADB457839E3DA7 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 987AD1FB8A654DC31AE3E6601CCC7A36 /* UIImageView+AFNetworking.m */; }; + 774FC23D0813F55FB1FEEA0634844368 /* PFOfflineObjectController.h in Headers */ = {isa = PBXBuildFile; fileRef = 31A896932415303DC122139D6C700DED /* PFOfflineObjectController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 780C569F76863BE9D4BA20617FD6F225 /* PFCurrentObjectControlling.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E3BD169A6C2F8F814ACD98138308236 /* PFCurrentObjectControlling.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 785D935F51FAF8FCD765523DF777E1CE /* PFACLPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 42D0387D6F012A1706FDA9B3F9974E22 /* PFACLPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7874249F76B027BC9A4B8A51D0660592 /* PFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = A353C806ABBCE7480976922EA5EB7B75 /* PFImage.m */; }; + 78BFAEAF0B2EA186922D32B1785F76B2 /* PFObjectState.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A8256CB779D4E0EEFA7452FC60E3F2B /* PFObjectState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 78D905717976D101C7DA318C7FCDCAC0 /* PFCoreDataProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = ADFA0A2825114CECD32242EFA5607ED7 /* PFCoreDataProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7935B1FD00EAAAEC2671632131220FF6 /* PFCloudCodeController.h in Headers */ = {isa = PBXBuildFile; fileRef = EF2D613D36A3FA0A0A242D221AD828A0 /* PFCloudCodeController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7994F1F9AD63A1AB341DBBD97E83C944 /* PFQueryCollectionViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 86B5449588D7B659F947D7E39E060110 /* PFQueryCollectionViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A7EF5E2C542E50F933AEF6A64C8BEF2 /* PFURLSessionJSONDataTaskDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 56644183C8AFDEEF3AAC26943A7C8825 /* PFURLSessionJSONDataTaskDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7AB6B720A910774A4AB2CEFEC715FFBF /* PFMulticastDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B9F97EFD6FBB23BEA89804B1300441 /* PFMulticastDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7D3D7A872E68349C43E97C2844077756 /* SimpleCircleLiquidEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BB66694B1A1DAD9A0455379A1FE921 /* SimpleCircleLiquidEngine.swift */; }; + 7D4B9D3747F65C1E648DA08F03D1B7CB /* PFActionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D3FCD6D1CCE074756536544D3795CD9 /* PFActionButton.m */; }; + 7DC621930B2F4E596421DD0C2A61ED77 /* PFFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDBD79473F9FB40DB76DD65392048A7 /* PFFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ECF80989DDCDCD712118C8C181804F3 /* PFMulticastDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 22E583E17C29E569C438C2F5CFE8DE49 /* PFMulticastDelegate.m */; }; + 7F3E73EC4630EF8F1EB49AA991705DE4 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 242EE2B72A421D6B06312EA12A989EC1 /* AFURLConnectionOperation.m */; }; + 7F863FF278E81A50CC9BDB4CC2718287 /* PFTextButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 434C507EC671E5F0E3785E7D3020225B /* PFTextButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 80676C1184F6053DED9E700E221815AB /* PFOfflineQueryLogic.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD4EC7CD5391A7B25687EEDAD0E98E0 /* PFOfflineQueryLogic.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 809D0CEFA94C80633149B337287584E3 /* PFTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F429BC5C28A53E99C5E18A4668F5E24 /* PFTextField.m */; }; + 81A302025C6052BA208D7263F361DC44 /* PFProductTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 480A5B1630D4FE3B4BBB46EB667DD47F /* PFProductTableViewController.m */; }; + 81BBAED0788E8E5A1C6152A96F4E7D9A /* PFQueryState.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8A3CFFE733DBF362B551E0B80C6865 /* PFQueryState.m */; }; + 83B3E6925881BB03B74E66C12F38729D /* PFAnonymousUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 4303F783032C0C2846F32A846D5D4E5C /* PFAnonymousUtils.m */; }; + 83FF786E3A812E473C16DF15A0D012D0 /* PFObjectBatchController.h in Headers */ = {isa = PBXBuildFile; fileRef = FA656C1B60A3756129523AF9A74A803C /* PFObjectBatchController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 84154423F861A182F8C672CEF5AF2B99 /* UIButton+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 808AF6592966508933C25BC20FFCC320 /* UIButton+AFNetworking.m */; }; + 8430FC2AEBAA5F32C191D11C1552DDB4 /* PFLogInViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EE9E02E1C1B46F3ACFE5EF292CEDDAC /* PFLogInViewController.m */; }; + 844F4D0160C8B7AF5190B64E290F17E6 /* PFInstallationIdentifierStore.m in Sources */ = {isa = PBXBuildFile; fileRef = C99AD2F0824E2247A161578BD8373909 /* PFInstallationIdentifierStore.m */; }; + 848AB16C7E9619CED28F76E270CB5947 /* PFLogInView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = ABF64DFD9F115A0E816F8F5BBDF0DA14 /* PFLogInView_Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 84944515F98B102C3B136CCF8CAF3653 /* AFHTTPSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB4A34092FC1C5298CF33B7F20A6E09 /* AFHTTPSessionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 864573D466DDF7D459E6770A6B6C11A4 /* PFCachedQueryController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3684ED9D4BB5E30D2F75909D264EFF6A /* PFCachedQueryController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 86A0BB3FFEED4E9308F09E58C4331056 /* UIWebView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F6F533DC7C28FD9B021564F418631E7 /* UIWebView+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 872E8E63A0E32353BFC57EB6AD717656 /* PFDefaultACLController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D44679B3B60446F276A7452751C977C /* PFDefaultACLController.m */; }; + 874F446E0B804BABFE522EA7F0FC1EB3 /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = AFA1140609306B1C8C88E61FE5186100 /* AFSecurityPolicy.m */; }; + 87924B476947B8D5E7A25EBAE9AA8ECD /* PFRelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 22855645AE9EE73E2D1C09E998BEEC66 /* PFRelation.m */; }; + 888AEADD68D84E88A1676DDE8E76DC70 /* PFObjectFilePersistenceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36E262B1B4BEE4F1DDB6F3DF88064F02 /* PFObjectFilePersistenceController.m */; }; + 88B8A2037E08CA5071DC6DF6391EA8AA /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 13AD5FBF9469D47C394C584D6E88F244 /* AFURLResponseSerialization.m */; }; + 88E4A109BCBFD669EE458AEFD7B5ACB7 /* PFURLSessionUploadTaskDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DBF37D953B714180930569746452F6E5 /* PFURLSessionUploadTaskDelegate.m */; }; + 89EB630A24D56C6000D18FFA0CC1F019 /* PFOfflineObjectController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC13566BBA4A6E3F586E6A880A2689DD /* PFOfflineObjectController.m */; }; + 8A0C6DDE11044F1E0632BDB39065A5CD /* PFPropertyInfo_Runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 31673CEE600D31E9C12A1F2F14C74715 /* PFPropertyInfo_Runtime.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8A81554451206C4EB47FC738CC987853 /* PFURLSessionDataTaskDelegate_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 61A68A9C9F4DECBC21FBD79D006013B1 /* PFURLSessionDataTaskDelegate_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8A8CD0FF9AB48353984FE2E0CBF3B3B6 /* PFObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 9EE3A5821D30B3DF09BE105CA7B7FEDF /* PFObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8AF944C4E12B20614F182BF100876EF4 /* PFObjectEstimatedData.m in Sources */ = {isa = PBXBuildFile; fileRef = 44B78AF0E04A202E8C137C3F34CE6F17 /* PFObjectEstimatedData.m */; }; + 8B1EBEC941EEB487F8E31F12FF27C4BE /* PFRole.h in Headers */ = {isa = PBXBuildFile; fileRef = E32CAB189409C70ED348320F66BA97D3 /* PFRole.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8C254A48F3F863993BB08938A496D1DE /* PFGeoPointPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 979B9D6DD6CC9A7FC7F04469E1E2B5A6 /* PFGeoPointPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8CBF93AAE366ABCB98C520916F897C2B /* PFInstallationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CDEB255A1EFD2D56CF8836FF93C208D /* PFInstallationController.m */; }; + 8CD9B40C2AF93448262EE57C1BE4DDE9 /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7533A71248AEB62C9FC4FE38B6FBCE43 /* AFHTTPSessionManager.m */; }; + 8D71E7E4684EF2F6B5CA72DC2C5DB3F9 /* PFPurchase.m in Sources */ = {isa = PBXBuildFile; fileRef = 4148D9154FC86736CE6D49722DF31E3A /* PFPurchase.m */; }; + 8DC6FEA184189AD19550D4B93E0BFF3A /* PFURLConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 51672A9A990271236F6EEF5F4BE45E36 /* PFURLConstructor.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8E9E1A96CC750BA1EDDBB9A8C173CD9E /* PFCommandRunning.h in Headers */ = {isa = PBXBuildFile; fileRef = 9AA042538499B62E2B7C0921FCB5F01B /* PFCommandRunning.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8EB1135323B836CD9A411906B4CC355C /* PFRESTSessionCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = EF18CF4976108C743495C278626BE89F /* PFRESTSessionCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8EFEA3E1E419AC0B614DE1EE75A16542 /* PFSQLiteDatabaseResult.h in Headers */ = {isa = PBXBuildFile; fileRef = E3B8C005EBA4D0C4EEDEA5777C45D260 /* PFSQLiteDatabaseResult.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8F2FCDFB0D49482D2566E84C51E884E9 /* PFMutableACLState.h in Headers */ = {isa = PBXBuildFile; fileRef = FC71FB7E0EB9ACA6F1DACD4E1D5781CB /* PFMutableACLState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8FC8B2D947B8ED5DC54D650443DF9821 /* AFHTTPRequestOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 85F264D88C24402A09C3C9C8B5EF4601 /* AFHTTPRequestOperationManager.m */; }; + 9116FADD3A8ADE5390F11746981D796C /* PFSQLiteDatabaseController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EACD763C478D5DB9B0370480A11B1FD /* PFSQLiteDatabaseController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 92636C5483939EFC0ACB26E15F53FDE8 /* PFJSONSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = EF44078D9B90D1CAE0849F7FAE2927AC /* PFJSONSerialization.m */; }; + 92AB69B98AD7C84ECBFB2407B1D83C08 /* PFPropertyInfo_Runtime.m in Sources */ = {isa = PBXBuildFile; fileRef = A18517CA52623E4381D5DC0108AFB9ED /* PFPropertyInfo_Runtime.m */; }; + 92E52AF0F9AA58F1DDF4E39E1E419F1C /* BFCancellationToken.m in Sources */ = {isa = PBXBuildFile; fileRef = A0E158D52333FE98E51485773BD463F0 /* BFCancellationToken.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 92FEFFF6E03231D715018E15EA02A414 /* UIAlertView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3115BC0E1F291BCDAA52C97E90DA21 /* UIAlertView+AFNetworking.m */; }; + 934298B0150D2961AFE68A7191A1DCCA /* PFMutableQueryState.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B90E31FC30056504354B6B9CD64ABB /* PFMutableQueryState.m */; }; + 93E26AB74B58B7E5B42617ECEDE3E873 /* PFRESTConfigCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = E606965BE9A1625B19ACD6701319EDAC /* PFRESTConfigCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 93F1DFEC537C952B413A397CB53A66EA /* PFObjectUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE0AC637C3C70874A9C7E29EB8E9F69 /* PFObjectUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9400AC2DB5D43ECA054FE3B06E8BB37A /* PFMutableObjectState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0ACC8E7504C95E08B22B373F5EED0779 /* PFMutableObjectState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 942024336383DAF1D5B99DB9F2DC21C9 /* PFObjectControlling.h in Headers */ = {isa = PBXBuildFile; fileRef = E5B4B025F17BAA1B3A677D658784243F /* PFObjectControlling.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 94461C9CD41815E16E390945FD4585AB /* PFRESTFileCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 760C685581C23015279373B19B8816EE /* PFRESTFileCommand.m */; }; + 965FAF38F77ED3FEF7D0D8C50C8C1DA3 /* PFFileStagingController.h in Headers */ = {isa = PBXBuildFile; fileRef = 43B20D1E0DCC5A0A1D0ABC06E6C76F1A /* PFFileStagingController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 968B64EE8DF5B607A42DE7D9C49A4818 /* PFLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = DD78A6DA4B3E5F2479E701FA15D4D424 /* PFLogging.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 96A8F4E2D7C097F2368A168F1D4F13F3 /* PFUIAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 57557DDB79AE2CA8FB3449F3A6EB4369 /* PFUIAlertView.m */; }; + 98B454C78DCA44E4C57459F3313CA18A /* PFOperationSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B56E95C16FD6DAC8A7EA8CE384CF79B /* PFOperationSet.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 98DD45B682B721438042FC1A4E9BCC58 /* PFURLSessionDataTaskDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 24627238BB902D6C44D91F716A3B4005 /* PFURLSessionDataTaskDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 991FF5E1E35201E80B6BEAB721A4CB43 /* PFQueryState.h in Headers */ = {isa = PBXBuildFile; fileRef = 3E72FA549FC9512FB4BDF3D360A35D8F /* PFQueryState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 998AAB205311795AA50C6318523C4314 /* Parse_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = BBD4D42F15B404983137C87294C33298 /* Parse_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 99C8D41863F21A4A74B6230BC1267C10 /* PFCloudCodeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E0011F34B6F1370264442D8135FC5CA /* PFCloudCodeController.m */; }; + 9A3CBD2ADD5F98ECA4940A6489D249BB /* PFOfflineQueryLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = AC649CF0667A08BF5C299B5FCDB01DF7 /* PFOfflineQueryLogic.m */; }; + 9B9439D4790DB807F1084F262EEC33B7 /* PFRelationState.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7ED9D15FFE34ED094DC7FC1163FB2B /* PFRelationState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9C775A7CEC256634C458F3DF9D2EC80C /* PFLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E2E8D5C9D085E8016D489CA6F418A909 /* PFLogger.m */; }; + 9D54E06DAD98AB6BBAA811607B6FA647 /* PFAsyncTaskQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 335CC941000B202CD5BB59078C34DDF2 /* PFAsyncTaskQueue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9DD3597AEA92F3780BABE3C06618611E /* PFUserFileCodingLogic.h in Headers */ = {isa = PBXBuildFile; fileRef = 429BE42396DC64B472A5559E886AEA42 /* PFUserFileCodingLogic.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9E004B9B3A7E68127CC3113FB6386CC0 /* PFObjectBatchController.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9856B6BF2ECFD60B284F10DDC277BE /* PFObjectBatchController.m */; }; + 9F20656218CF341B2E1E258D6B5636DE /* PFActivityIndicatorCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 963130EBA1505774B88A6206C7117AD9 /* PFActivityIndicatorCollectionReusableView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9F53B2F8582A3EE55C01E5430C6DE58E /* PFOfflineStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EFD4CCE36EFE93ACCBCC2D407FF6D4FB /* PFOfflineStore.m */; }; + 9F600D37D91D1A347E13AA9F312F54D4 /* PFUserState.h in Headers */ = {isa = PBXBuildFile; fileRef = F676ADEBE61FEA6C5EE24C12595B59B4 /* PFUserState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9F7EAF9DE82EE443155A4C21F5197564 /* PFQueryPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B3641265C060CC5C48EE34815FA7F68 /* PFQueryPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A060D301B393FD942E24357F70466F26 /* PFRESTQueryCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DB17A9B3DDAC837FD6453FEA310E848 /* PFRESTQueryCommand.m */; }; + A0755244ACB6D4446E5848A4146EE0C0 /* PFPrimaryButton.m in Sources */ = {isa = PBXBuildFile; fileRef = A72606B3DBDB6147A21BF44AB547E440 /* PFPrimaryButton.m */; }; + A10C34E2245BEBEA3E71717521DC60A8 /* PFPaymentTransactionObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = E8621BD61A365200D38234EE73EF24CA /* PFPaymentTransactionObserver.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A19B38AE88BF60F1E0C15CE84C11748F /* PFObjectSubclassingController.h in Headers */ = {isa = PBXBuildFile; fileRef = 23B9E89D64EA7A1356B9BBCEE11CC809 /* PFObjectSubclassingController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A245EE82A2CBF7B209BE48C2C1E3703A /* AFURLRequestSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 50B9BC2A232324947BCFD7EF8C6DF1B8 /* AFURLRequestSerialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A2857C0F58EB3F5637FBFA1D4C311891 /* PFEventuallyPin.m in Sources */ = {isa = PBXBuildFile; fileRef = E998520B0AE9F07988C912A4F83B6BCB /* PFEventuallyPin.m */; }; + A34200F16E337F39152862E2676D1F45 /* PFMutableUserState.h in Headers */ = {isa = PBXBuildFile; fileRef = C1BE4F5D33EE98831A53F0E87D0343B4 /* PFMutableUserState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A435900BF6320810472F54B97F3E8F88 /* PFFileState.h in Headers */ = {isa = PBXBuildFile; fileRef = 82B9E087EE91277B762C1E55E025EF89 /* PFFileState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A48A38259F9F93155AE30E6CB589A570 /* PFRelationState.m in Sources */ = {isa = PBXBuildFile; fileRef = 0655621C56EA9C2125947E9B169D3D63 /* PFRelationState.m */; }; + A68FE14FE2956378F078CFFA7AF2ED1C /* PFMutableUserState.m in Sources */ = {isa = PBXBuildFile; fileRef = 266DD3785853AF2B087ECB683111FBDF /* PFMutableUserState.m */; }; + A730FB13DB74C80787B19682ADD28A4D /* UIKit+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 78A2111B342B9B80EC48C4F51AA04B69 /* UIKit+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A8AEC3C670B3871B50AAE40A8DA9A92C /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37222E9C27B945A7B72A144663090FCB /* StoreKit.framework */; }; + A914B7418E782BAD7F60497841D2145B /* PFPurchaseController.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B1EB3A0FAE8016CFDE64DE338F8BDD7 /* PFPurchaseController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A91AE12725616B763AE00A41BC002679 /* PFUserAuthenticationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E1C9CCFB4EA178BC262A1EF44D11ED1 /* PFUserAuthenticationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9573C013CD6EF6AD84775DA6341C9B4 /* PFRESTQueryCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = BE60DEF5EAA342275F7F61958BEEC1A2 /* PFRESTQueryCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A9E63E749F651448435169ADAF5DC93E /* AFSecurityPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = EAFD4107509F91789F51A3194CC54229 /* AFSecurityPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9F35EEE5158FFA497C625648F1C339A /* PFURLSessionFileDownloadTaskDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F6399FA5304D192B1A4A7C0D2EB53A1 /* PFURLSessionFileDownloadTaskDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA01152D542E7E12082FC9C9A0398905 /* PFACLState.h in Headers */ = {isa = PBXBuildFile; fileRef = 43575DB6BB216F37BCA1AB250B9F09BF /* PFACLState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA36320307C96E4ED20F579ACC8E4959 /* PFActionButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 07EB9E619B4B720424CC061A99D2BABB /* PFActionButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB0C8BC2C160D2D3E1B926D610DB61BE /* Parse.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C68DEF4C4561831CECF5DAE37F4E1926 /* Parse.framework */; }; + AB736B19B7EEB8FDA22320B6AA9E62A4 /* PFQueryController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA1F50181C77DE8DD24C638D484FAB4E /* PFQueryController.m */; }; + ABF3BDDE2F977F7A2B3C72891A4C36DB /* PFOfflineQueryController.h in Headers */ = {isa = PBXBuildFile; fileRef = B89E57173FF8D9E3745D41F14BB2E463 /* PFOfflineQueryController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + ACAFB1D90546EFA64AE2735E8F503EF0 /* PFPush.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E5865FCD20277B95CE81D8313B04B91 /* PFPush.m */; }; + AD22B8B8A24B0630375406012F70B3E2 /* PFQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = 3819E8A9BE558DAF234B89FC4CC41178 /* PFQuery.m */; }; + ADABE24D512FCFF459CE5D0F8E252D6E /* PFPinningObjectStore.m in Sources */ = {isa = PBXBuildFile; fileRef = E4F396EB5B518CB78205BD9A35D95FBC /* PFPinningObjectStore.m */; }; + ADEB7F252E4D8644894759C130122B39 /* PFFile_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AD42871D9C935D2C3C640E893BFFC564 /* PFFile_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AE25982BB866C5E33F8C7EC11111C4C0 /* PFRESTCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EBA0F2EDA08DA37F7D8E7B9150E9A29 /* PFRESTCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AE96FD5DA9CE65984EC706E1FE3FEC45 /* ArrayEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BE045EE908BDA1501A41A429BC7A646 /* ArrayEx.swift */; }; + AEF1B66D4AA99B06499C0C7E8E5A111E /* PFConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 411246798151B846841853F673E37435 /* PFConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF286D38173EA5B941C10161C66B30F5 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C7C9C2804D450E4F0A969D264281DBE /* AFHTTPRequestOperation.m */; }; + AF31F796B1CBEFD65DE7445B99DE40F5 /* PFRESTAnalyticsCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D61280C3437A784DBF19D45B384C011 /* PFRESTAnalyticsCommand.m */; }; + AFB0CD00E4EC4FECA54202A58CE7F2F2 /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EFC89B254D8AAE9141EF6851EA5277D /* AFNetworkReachabilityManager.m */; }; + B18713A1C3565C9DD53BACCA8480FEE9 /* PFTextButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C90F76F74AA0E9A353A68E4994EDCBF /* PFTextButton.m */; }; + B290012C1BFD473992D40F3028D9714A /* PFProductsRequestHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = E075ED2B6706ECB8A6B28B81F9FD35C7 /* PFProductsRequestHandler.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B2A54F6322617DE35C8E3ACE8933BA72 /* BFCancellationTokenRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = 489F324A39862A52246BA56B81766653 /* BFCancellationTokenRegistration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B2B712D1FD284ECF39175E2A9D484005 /* PFEventuallyQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = D031DB68EC76D7496F1750449C536A4E /* PFEventuallyQueue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B2CB8CB0EC408A26F112F6A43412D07C /* PFImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = A20A37119F2FDB5210E5F294E3477B2E /* PFImageView.m */; }; + B2E905E16C8373CA8FA693AFAF0F26CF /* PFFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8230FA9936E0CC774177096CCC805BFC /* PFFileManager.m */; }; + B4BB7E489E1FD1F9A28773CA2441625F /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F71E915821A8435DD4437B712A573BD8 /* Bolts.framework */; }; + B4FDC6E9D59ABE91C18092E4DD991945 /* PFPushChannelsController.m in Sources */ = {isa = PBXBuildFile; fileRef = B439486D6310E570C25D882BF6B831F1 /* PFPushChannelsController.m */; }; + B532331FAF75B2B2F18967531F375723 /* PFURLConstructor.m in Sources */ = {isa = PBXBuildFile; fileRef = E54CDA1D27EA0FCD1AB663DD1BCC22DE /* PFURLConstructor.m */; }; + B67A2D8518758097E23A41E560C913D3 /* BFTask+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 64DB319647B47CD74F8DA4F72A903D14 /* BFTask+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B696F655FF5E1603A4996A55FDB93DC4 /* PFConfigController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BA81F2BF3DBCACCD4672ED697498A /* PFConfigController.m */; }; + B6BBFB374E4B671479942F03E1679FF8 /* PFCoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E3AEAC049A5741DEDE9E5C91592038C1 /* PFCoreManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B74C351F5944C2717452B80E3FA53F69 /* PFObjectState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 16ED09F9D67408A7EFF12AFECDA9A63B /* PFObjectState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B9419321553FD57039C0F8F67830DB16 /* AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DFA9B6E0EEB0C0CC47910DB6F871C95 /* AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B94DD88DCFA4FA68E2DB9D0EF5038178 /* PFInstallationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 18399FE7C356696A82C3B177BBA86230 /* PFInstallationController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B9556120221EFA0E77536284877FC930 /* PFDateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = CA79DC9A677C52A5CB0149D3711EDC4C /* PFDateFormatter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B955BF2258BCCA71293EA6A7C1BF626C /* PFActivityIndicatorCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 473DFEC13A8925DDCC0F6375D0AA1FDD /* PFActivityIndicatorCollectionReusableView.m */; }; + BA3C5BA167AA8A30FAD75D7707560B2B /* PFResources.h in Headers */ = {isa = PBXBuildFile; fileRef = D585DF26C444D0FDF2346A1B69A9342C /* PFResources.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BAA3FE6F5D29E102D52EB92605E9D7FE /* PFObjectFilePersistenceController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BF997DF2BA7E971B44D0DE2163F7FF8 /* PFObjectFilePersistenceController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BAECB6CD09EE5ACEE9C4F714DA371DCB /* PFFieldOperationDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C586370E28704EE0E4D7689B917753B /* PFFieldOperationDecoder.m */; }; + BB0DF26FB7FD53A7B6D08FFA0B2A3C55 /* PFPurchaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = FD88241CAE6DA71B4D70C103FE15ADC4 /* PFPurchaseController.m */; }; + BBB2FAEBBAA806158616685DB15157A6 /* PFFileStagingController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77FF71B41A0A7E4C30E000416639CAD2 /* PFFileStagingController.m */; }; + BBB6C54E1A2A7336608EA35018C90C8F /* PFObjectLocalIdStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 88BAE879E3D71A97056B63720B3DAD5E /* PFObjectLocalIdStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BCF12DA109BA9A9FBE47414A93DF5022 /* PFPushState.m in Sources */ = {isa = PBXBuildFile; fileRef = 50C7A024063739A9D4A5CD90B47FE50E /* PFPushState.m */; }; + BEB01521D76C24A6006F2375E62EBE9A /* PFACLState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D5D9ACD4B24C679EB5789A8B2300783 /* PFACLState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BF0CE89CA2E90B1DBDD860C8627A5CA0 /* PFCloud.m in Sources */ = {isa = PBXBuildFile; fileRef = 98FB41E4640BA6923CDFC45E1C3422C3 /* PFCloud.m */; }; + BFF56B83A2E55DF25DC40DFC6434BB0C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + C06ACA81B1DE4ADFD3E431EED2FABAEA /* PFLogInView.h in Headers */ = {isa = PBXBuildFile; fileRef = BFF7C6B60180E4A5C942887184602AF5 /* PFLogInView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C0A53FB9AAB55C2215AE28B7552DE060 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC6D23DEBF98F865D988524F98884065 /* UIKit.framework */; }; + C0DCE7B51C9D03F06A3C3DDA64525971 /* PFURLSessionUploadTaskDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F7B2DE64FB97BC59338A0F063F7286B3 /* PFURLSessionUploadTaskDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C178BFD5E2DC1673FB7BCB1E2EB5A315 /* PFQueryState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = F7404597F10DBE7FD0CFA519AF9692E9 /* PFQueryState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C1A4214B6F941388EE967D9C78FC1EA4 /* PFBase64Encoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D58C74135B703B4BA78ACFDE7B10DF17 /* PFBase64Encoder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C1B05E4A12D43587A60E1B9946796B18 /* PFProduct+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEF1672D1EBB3EC3C6F7B1D9F2C0B80 /* PFProduct+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C1EA98783317E069BC38EF24E8E4C3A0 /* UIActivityIndicatorView+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 17184911F1E825E04E81231EB596A28E /* UIActivityIndicatorView+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C4062AB431E3B5922E0607E6BE1E0E5A /* PFActivityIndicatorTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 49314B7929DAC6D78738BF75309C6409 /* PFActivityIndicatorTableViewCell.m */; }; + C413266D8ED60F6651251936DEF63C24 /* PFOfflineQueryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F96A5E8E7BE0A8F092CB9BE81FA722A /* PFOfflineQueryController.m */; }; + C471FC167AD7C220455BD11929E602CD /* PFSQLiteDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 991F2AB1EAD16285E8C86C68489117B2 /* PFSQLiteDatabaseController.m */; }; + C489C653E604077718DC890DBB239D3B /* PFMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 03453B418D0B0A3C1A61F031BCB33C8C /* PFMacros.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C4AB1E1C68ED4F07AC2F08DBFD72D33C /* AFURLConnectionOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 528B85889F63E99EC61F9BC47CC31A8E /* AFURLConnectionOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C4BD45F260E029240F766D46397003D5 /* PFObjectFileCodingLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F63976C35095E9FB91D72CAE2FDB7D /* PFObjectFileCodingLogic.m */; }; + C4EC9F08AC7FF335750C4CE87A41DA59 /* ParseUIConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F9299B2C2E6C0D460E3B857D3E1931 /* ParseUIConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C556BF04447873BB9E2DBF3DBD92B7B3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + C61C66EAB8F7563FA9C1D4434A3F84A1 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F3BE50471AAC985EF28416C9EB1C185 /* CoreLocation.framework */; }; + C6524C189EDE63AF7D294A6C5358D6AF /* PFFileDataStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B393ACCB61C4E998CC4330DAC20ACEC /* PFFileDataStream.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C661587D325AB943770C396151BCC10B /* PFHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 201F561822AF1B8FD7578936456F4264 /* PFHash.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C6A19A7E7E0B9AC4D3122986AEDE3B65 /* PFImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 927B2E472B9B4600105524234E02BBEF /* PFImage.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C6D03A8CC40B89A276CC3DB1CCC70E61 /* PFRESTAnalyticsCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = FB3E17BA104D20642FE22B05ED16F84B /* PFRESTAnalyticsCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C6E564817BFE774E6635838076C7A3A7 /* PFJSONSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = EB4D83C8AA70A082952CB547050AE43B /* PFJSONSerialization.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C7119587C9AC58601B906C394A3DA295 /* PFSignUpViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 49457ABA4299045560404E829773DF26 /* PFSignUpViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C8ADDF24915937C8CA9E62F09DF5F0DB /* BFTask+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E29EE0F72D64655290376D8E2025A3C /* BFTask+Private.m */; }; + C8B39614F7A143FD25D2414E8E4C0885 /* PFRESTCloudCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = E9F581D64C0B691E6A0B6B3A83240F45 /* PFRESTCloudCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C8E6C301D27E180B527353C3EBE91D16 /* Parse-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ABC7BA535DC20890BDADEE775FB84BC /* Parse-dummy.m */; }; + C98CCE7712239EC7D6839E8429E0B71B /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F71E915821A8435DD4437B712A573BD8 /* Bolts.framework */; }; + CA8743057D791E2A1B03B93CB8D3CC71 /* PFURLSessionFileDownloadTaskDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FB104646E63B185FAFECD3B01CDA7DD /* PFURLSessionFileDownloadTaskDelegate.m */; }; + CAB9A7FE02C498EB667CF6A4DAFBD314 /* PFKeyValueCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A2CCB3F17D3BA81EBDC4AC1E6CF5E8A /* PFKeyValueCache.m */; }; + CB8F587614BC2C7758AC08D2EFB16B45 /* PFWeakValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FAD8490F27C97455330C5FD12D68BB7 /* PFWeakValue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CBFD398A6EC778FCDBA6B44E15704899 /* PFCoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 55ED6CB9CFA4FDEE1DEC32FE8583DFE0 /* PFCoreManager.m */; }; + CC6F2E4191E9283AFB8144CD08325E88 /* PFOfflineStore.h in Headers */ = {isa = PBXBuildFile; fileRef = FF22D95936873FA51D8C9C983150C21D /* PFOfflineStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CC82E5DFB7A9CCB82D1866B8883ED10C /* PFCurrentUserController.h in Headers */ = {isa = PBXBuildFile; fileRef = DFC1DA889CE1311BE00B9D07030E2D91 /* PFCurrentUserController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CCD2A046B799983FE3CD0A859C9A66C4 /* PFEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = F6491E92ACE75905C0A36BBBAC1F3070 /* PFEncoder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CD4CA1E54F495AE3C0760FFB9AFBBD4C /* PFKeychainStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BC983AFF27F041F598C25C2CD37CFDCB /* PFKeychainStore.m */; }; + CD899A81849EC051B571CB322E422DBE /* PFRESTObjectBatchCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FFF174FF00A3AD178F468EC8D45ADC /* PFRESTObjectBatchCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CDCEA48FE048597A56BD5E5218FEA8F5 /* PFObjectFileCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D6133D18D0CBDFE7A51DC2ABD2DA8956 /* PFObjectFileCoder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CE0E925439858FFAC1675700D4F1808D /* PFDataProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A57B2A1A122A5645B1B336DEDEABF67 /* PFDataProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CE94D9C23DA746A3F83FAACA7DB0866D /* PFSignUpView.m in Sources */ = {isa = PBXBuildFile; fileRef = F7CBF95140D5E75739637EC8272D9488 /* PFSignUpView.m */; }; + CED7B949ACFAB437975D211E982738B3 /* PFRelationState_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = F9FDF2BF02C33F3194A8064A2D1268D4 /* PFRelationState_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CEFA2437E70B9FA7380076C38D61E499 /* PFImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C62627778FD1291CC19265484E8A35B /* PFImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CFC2FB94F8818416123342F50F714372 /* PFObject+Subclass.h in Headers */ = {isa = PBXBuildFile; fileRef = C28C9D46AE0FE690A649A1BE46A2BBCD /* PFObject+Subclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CFC7FDCE35D5355D88A95151A9F811DE /* PFUserAuthenticationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 05A2290271BB3E67835291187DAB8318 /* PFUserAuthenticationController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D059BF84C427D0926F831091E8DC35A3 /* PFUserFileCodingLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = C7F5B41EB0986C8391083C4127B553F0 /* PFUserFileCodingLogic.m */; }; + D08751DD45A35666D2A21C5D8FAE7D55 /* PFActivityIndicatorTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F901A1A06A6B452D091DE5EEF0C8136 /* PFActivityIndicatorTableViewCell.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D13043EC0E35764A23D65B7A0EC24D1F /* PFSessionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BD39E8AC572A0A0356F1EFCBE146731F /* PFSessionUtilities.m */; }; + D1751D1D843B63BD1AA4EA153BEF96F8 /* PFDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = CFD2BCA0FDC1E5923DDC3256A7F82B48 /* PFDevice.m */; }; + D1BAAA3B9C6887F2F23752769C3020F5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */; }; + D1F0BF670D9D53F930FF535324C883F4 /* PFObjectEstimatedData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A186D964995C7B9E347DED4AD34C997 /* PFObjectEstimatedData.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D24CFDD40A5643EAA6AFE24FC83CEDE8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CA945CDAC44DDF1110329B15603AAF7 /* Security.framework */; }; + D252449F0A676976166C795E6045F48D /* PFKeychainStore.h in Headers */ = {isa = PBXBuildFile; fileRef = AA573A271BED522E0458A7AE97926873 /* PFKeychainStore.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D35D2CE6056C72D35B88BA436BFF624B /* BFCancellationTokenSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D29EC69D4226E7C3D3D7EEAFC9E96F3 /* BFCancellationTokenSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D391C0EC06404C804F9DC76831D953C1 /* PFSQLiteDatabaseResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C27666D25206D0C9228E2B561CFCEA9 /* PFSQLiteDatabaseResult.m */; }; + D3F6218A5157FD65597FAEAED95E2C94 /* LiquidFloatingActionButton-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = BA2B0874AAFA2F48338A7586EA1D3BF9 /* LiquidFloatingActionButton-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4FCBED6C982A98D9C2C211D2D993575 /* Bolts.h in Headers */ = {isa = PBXBuildFile; fileRef = A9F48668DB7B720FE509D549A92004D3 /* Bolts.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D524D9C4B2BFEBA46D98587BB3E5EEDB /* PFRESTPushCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 76AFC841FC9BC38C174F77AE81A883BA /* PFRESTPushCommand.m */; }; + D52E42CF13845E973D80DA9764EC0A7E /* PFReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = FF500FE268A2149DF765B8D6732382E1 /* PFReachability.m */; }; + D54B351F16D0170A1BEEE122A9956D99 /* PFMutableFileState.m in Sources */ = {isa = PBXBuildFile; fileRef = 61351FBD424C7B2328FDF4BE4B7ECE3A /* PFMutableFileState.m */; }; + D5680DC4F4807F789036FFCFB8E4A2FB /* PFUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 3FEB8673C94646984FA528D09A6716B3 /* PFUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D56B1994B74E244DB001467270959B3B /* PFACLState.m in Sources */ = {isa = PBXBuildFile; fileRef = 834F31050B11D55D2C450E902318E944 /* PFACLState.m */; }; + D59CCD92C8ACD36507363BF351D9ED55 /* PFRESTConfigCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = E059144E243A519EF7E69EE1C229A7E6 /* PFRESTConfigCommand.m */; }; + D5EB9EF641D6A9658B27111E36DEC1A8 /* PFUserAuthenticationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B19C063A5F2D93FEFE0F4D66A9DEA4 /* PFUserAuthenticationController.m */; }; + D6D849304265B47FE1FFD7983A5E6E9A /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E05C2B4295C609B8107CC303A977728 /* AFURLSessionManager.m */; }; + D6DB8392C0A03EDF979FAB4CEE4E6910 /* PFURLSession_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DD1F25F1B4E921548F381AD4C410B41C /* PFURLSession_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D7F7CBF073AF3E67F72D9781DE055CE9 /* PFUserConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 00A646C084294FC23A482874264A9E10 /* PFUserConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; + D8E947D4B37996BE40DD68E74C2A5E79 /* PFAnalyticsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BE9E8FFC7586640B02294D86CF884 /* PFAnalyticsController.m */; }; + DA0E5C6423B691AC6129DDD4755ED361 /* PFFieldOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5C3BFFF0DB6969C2B50A5CD483F812 /* PFFieldOperation.m */; }; + DA3941CA4F6E79C371612E5A47E48EE9 /* ParseInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = DB79A3ED3B802D7B73925524C32EEDE7 /* ParseInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DB0217249C32224E91A48864E061B951 /* PFSQLiteDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 355416180C97110767845F8B71F32709 /* PFSQLiteDatabase.m */; }; + DB956A9E22CEC6FAB58AFDBFB79D1DD5 /* PFACL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7135D79AC2406919243CA6DA7F8290E3 /* PFACL.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DC66F61BB187483967D2D7275353B4C3 /* PFConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E92D2ACC1EB93D285C31C15C7960FB4F /* PFConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCBD22933F6F7C79D30FDD222A9C3FDD /* PFUserPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 845AECFE8DD82849CB3F5B87E2537205 /* PFUserPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DD9B950C3002FDC444423323E47633AE /* PFDismissButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B0D1B89D4514C28C3C698E6564D5CC0 /* PFDismissButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DE5B43DB552929FED6D87AF8FBF2A271 /* BoltsVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 24ED10A4EE83A682837CCD2514C1FE1E /* BoltsVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE66B92ED03D4AF38F76CC5CE19E5B48 /* PFSQLiteStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = D53047A416F23A9006EDD19E7BDFBF65 /* PFSQLiteStatement.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DEE5BCE9AFBA178D30233B54597CCD80 /* PFProduct.h in Headers */ = {isa = PBXBuildFile; fileRef = DFAB2F23DBAAB01BDD3098996526D1A3 /* PFProduct.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DFD220860A4E0A6970BB5574E905463A /* ParseUI.h in Headers */ = {isa = PBXBuildFile; fileRef = BB7AF609EF0BFAD9C6EC6123F16D7E2F /* ParseUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E01923339F9C5BFE99FC3EE4E4936742 /* PFObjectSubclassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CD22F039DCBF6319754D584066D895 /* PFObjectSubclassInfo.m */; }; + E06CD9CAEA46B547013DC4CFA6D65EE7 /* UIProgressView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 06932232D53EEC91CFD3A681CB300096 /* UIProgressView+AFNetworking.m */; }; + E2219536CEA9BEC7A76770AF29A650DD /* PFPropertyInfo_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 480D89A7E47EF11AA3FBB5053B2C9781 /* PFPropertyInfo_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E222FB880049F8BF95AAF41ACFEF5541 /* PFCommandURLRequestConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E4419B914285E9CFA2B0C4905E3717F /* PFCommandURLRequestConstructor.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E338DEBC75FABD977350EA8740945F27 /* PFSession.h in Headers */ = {isa = PBXBuildFile; fileRef = EF10A3E0E41722320B57E8C181D31679 /* PFSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E34D1AC12E3CFF058D9974C1062CA3D4 /* PFPushManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 610C79F260D7DE6FBE6856EF3349D7E2 /* PFPushManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E390E29D45E4DCC855A59B2F96D1709B /* PFQueryUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 99DE5A4D7BE3DFF919CE242FB1FD465D /* PFQueryUtilities.m */; }; + E3EFFB5B782650B12F4E6E47ACC30F45 /* PFSQLiteDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = CFB015761F43EBA9801A2489A67F1918 /* PFSQLiteDatabase.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E446F8E01E139AB0E33B2734594BBF5D /* PFMultiProcessFileLockController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EA7251DC3418FDD089B4BE7DB866896 /* PFMultiProcessFileLockController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E4E5F3DF33BDA4EF4012C73A924AEC02 /* PFErrorUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 508F40F7D00DB921AC2A70A820C56E68 /* PFErrorUtilities.m */; }; + E565E1D35899A503365C19F3E4FBD63A /* PFRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 933C4C66F46988536759B7A7AA3AB260 /* PFRect.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E58E6CD4D9F888BF805EAB0D61AC9C39 /* ParseModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F4B2107EEEB8DF1A30920BAB2D9CA6C /* ParseModule.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E6452E0C294D4311EFCA899EB7078E0D /* PFHTTPURLRequestConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 843DAD1E807B7D28D13FCD55BAA643C9 /* PFHTTPURLRequestConstructor.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E7C9C47C276C47F4C4D0802F170935A8 /* PFObjectFileCodingLogic.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DE4F7C454A15E12B39ED6023325FA3A /* PFObjectFileCodingLogic.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E864C1BEB7E4FBD4EAECF0A1AE90771D /* PFTaskQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC0772B25ED51809E78EE58DC1C44C0 /* PFTaskQueue.m */; }; + E89760E83C89096C84689740006F8FF6 /* PFRelation.h in Headers */ = {isa = PBXBuildFile; fileRef = 56833EA79C5134523EDBB3F9CED7E72F /* PFRelation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E8D255D61748A673394F29E8E685451C /* PFGeoPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = E9B8580777097F914F9981794BB8DE24 /* PFGeoPoint.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E8DE4FB78CBD67CE6F4A9549F5D567EF /* PFLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A2D779D62119B7029D39A6D67FF2D63 /* PFLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E8E8383F6896F4A23BD44D554B176214 /* PFSignUpView.h in Headers */ = {isa = PBXBuildFile; fileRef = EF93FA2C0175C88F708912B000641CC8 /* PFSignUpView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E98B7DBA71E8087DA1B1F52B997BDA65 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DE30302543CAA1030741C690BC2AC46 /* CFNetwork.framework */; }; + E98C3A80DCE27BBC4726B7789809FFF0 /* PFAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = F73E652B07D06D8EE514D15810A9F8F1 /* PFAlertView.m */; }; + EA0707AEC6E783EE7A3E59DBBA7BB159 /* PFRESTUserCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = F585A21BC1DD8875804B22148772F089 /* PFRESTUserCommand.m */; }; + EA5748A0206BED75751EECF565E27BF1 /* PFFileController.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D3272484AF55B5BC7CD1C32A067FA85 /* PFFileController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EC0AE4ACC555A6E60015D73FA5EDAEA3 /* PFCommandRunningConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E712CF96CBB0489B4C7A04F5147BB32D /* PFCommandRunningConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; + ECB75C80159A0B7A7FB9F6966B778EFD /* PFReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = D6B24FD6C8A63E8AC6B7A647B38F005D /* PFReachability.h */; settings = {ATTRIBUTES = (Project, ); }; }; + ECCAEFEAE2CA7A873E00E3A86F916350 /* UIButton+AFNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 66636BE79215A604578D0F0E12C79103 /* UIButton+AFNetworking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EDB941FC0B56936293F8CAF06EE5D816 /* PFMutableFileState.h in Headers */ = {isa = PBXBuildFile; fileRef = 2FDD4D768605FD329F1CA002D177D3CD /* PFMutableFileState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F0CD11A4CE4F909511C1ED18E2022B77 /* Parse-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 39555E2A1C0726E869769E5E64AD7F75 /* Parse-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F200FD9B8B4D6FE117BB400C0654CC1A /* PFObjectUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = B402CED15BEC363BBB72FE42CFFA19B7 /* PFObjectUtilities.m */; }; + F230ACD1550BA6A59F6E742DD67C170C /* PFObjectConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = BCFF6319D2EF9091302E5B96F4E0B43B /* PFObjectConstants.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F238D2300BA08E34BA81C090C11F3B12 /* PFPaymentTransactionObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = A98219265A93C0BC6572D73A5D1EAEB2 /* PFPaymentTransactionObserver.m */; }; + F2A86D992230867FB33256D7AD9BB6E0 /* PFObjectPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 11641F1E2B0F6BFAEB5FE89FA349A02A /* PFObjectPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F2AE695C9128B6183659654C1F5BEB52 /* PFCommandCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 36D423CBA06FDB7473035E0CAD68C111 /* PFCommandCache.m */; }; + F2FF12651AD453407868C5A9FDE1D356 /* PFNetworkCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A0D21B75DD89371A7800E0F07CCDD3C /* PFNetworkCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F3125E8E5787E882BEC4C6ECE5200AD6 /* PFRESTUserCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7516FAB60771B12B16D06A78AB5986AF /* PFRESTUserCommand.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F3A28913EF79533BDFA91D252F5543BF /* PFInstallationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2864AE64CD2B48522D2926BD76DB48A2 /* PFInstallationPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F49C9802B02749EB2F6BD4FA65DEEF51 /* ParseModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 02054704AC6B3A8FF8526EA73E182B80 /* ParseModule.m */; }; + F55E1D0D9A935666D456A85EACD2A071 /* PFTaskQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = D6F5D08B400E69B890800EB5B5DCCB3F /* PFTaskQueue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F5AD0E2716B3CA4BC791A700B69F5DB6 /* PFSQLiteStatement.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BF22022C7257232D4C9D9938A8AA1F0 /* PFSQLiteStatement.m */; }; + F5F0D5E6239309C5920C14E56DCD2403 /* PFEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 515EDE43F2D9FB9C27C6C8783384DCE9 /* PFEncoder.m */; }; + F61B1982A3B7BE89BA56AE9C6141B7F1 /* BFTaskCompletionSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 35F772D3D7B1BC27FD87ABE0059732FF /* BFTaskCompletionSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F6AB90B0F80E6C866766C2DE6D8C83C2 /* PFFileDataStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 1284F0A5E10B2B233B0C53219469EA5F /* PFFileDataStream.m */; }; + F6F02BC2B7AB20AE3D39467BAC5E9641 /* PFRESTObjectCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C48F5063ADC03C48C88120E75A86527 /* PFRESTObjectCommand.m */; }; + F7CBB621F38AAE3EDF1DAA7D314A8A5B /* PFSignUpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C26CD46DBFBC0826774EE2BABF221F5 /* PFSignUpViewController.m */; }; + F82E6E99470BFB4E5FF15E4527F264EC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58228477EDB607E07438A4DA7659B1AB /* CoreGraphics.framework */; }; + F878F331507111514D56218C31A7A3CB /* PFMultiProcessFileLock.m in Sources */ = {isa = PBXBuildFile; fileRef = B31B277478C40801EDACD6F671D9F9D3 /* PFMultiProcessFileLock.m */; }; + F8856CC3DBA9D9B6455C568F8796BC20 /* PFPinningEventuallyQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 22BBA5CC87BB1C58194494B4A1E02687 /* PFPinningEventuallyQueue.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F889510066601CF5ADD47D03C4E7E243 /* PFBaseState.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C0F04EEF7F8C7BC4C6FAC4AAF8E68DC /* PFBaseState.m */; }; + F8F775667407AD2AC21065F263AFE374 /* PFUserController.m in Sources */ = {isa = PBXBuildFile; fileRef = 40AEA2C7205FE550939B0F7F408FCA85 /* PFUserController.m */; }; + F95E160C43A5426EF1CE7C41BA653AEC /* PFUIAlertView.h in Headers */ = {isa = PBXBuildFile; fileRef = B9EDA77BEADC909F4B7793C3C24DB96F /* PFUIAlertView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FA7FE52B70834921A9A11FFE5DF1FDF8 /* PFPushPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F2DC5E77E5C9D2DECE225B993BC0DBAA /* PFPushPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FAB18BCD6E2BEA3572300AFC1A1C8956 /* PFObjectController_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C449090D4B4F78564B631A75077787A9 /* PFObjectController_Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FB83633D6A4E517EF52ECA6789A8A840 /* PFProductTableViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DACDC24D9356617A721A05C9B6ACBC2C /* PFProductTableViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FBD3BF6E2A67EA7B7843F2C311A60EFE /* PFObjectConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C00A99BD6D0DB2325AD32749EEE4D05 /* PFObjectConstants.m */; }; + FD13FD577974DAFD4127467D7C8AFB0D /* PFMutableObjectState.m in Sources */ = {isa = PBXBuildFile; fileRef = E64CE28EB8ABC8BC399D729811CABF32 /* PFMutableObjectState.m */; }; + FE98B1BC5667A89098FED050424FCFCC /* PFDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = EE6D7A403F638983E94CC48F7369D44D /* PFDecoder.m */; }; + FED55EC1F65C1F9E6B608559F7F7F0CD /* PFMultiProcessFileLockController.m in Sources */ = {isa = PBXBuildFile; fileRef = D319F69C8F009DDC8CF96B18FEC4583E /* PFMultiProcessFileLockController.m */; }; + FFDB1D2A8351A7C3DDB8CE3BA970C02A /* CGRectEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89683EF21BC95E77BBF06818132C923E /* CGRectEx.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0EE8BEF01819C84E3669BF5A6E024DA5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6F551023845F01D3B4326C1A426E12EF; + remoteInfo = AFNetworking; + }; + 18D533FE93435CFA12227234DD9C33D3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 136FE3EDD694CDE21E3B556E3A8831E3; + remoteInfo = "LiquidFloatingActionButton-LiquidFloatingActionButton"; + }; + 3157096E56F5CC8AAF21974C0E909770 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 255B486D02599C8571584A81D3995B9C; + remoteInfo = Parse; + }; + 579026D29A26BCB5C81B7276872428CD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CFD6D06267A85EA321AFA80384E49C4; + remoteInfo = Bolts; + }; + 5AAF2A48D9FEACE1C8B0FAA4E0E14931 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = EBB54F8CD09E891EC7CDEEBB645A5141; + remoteInfo = LiquidFloatingActionButton; + }; + 81143337D0B99E5BB72D75FF2BFF94F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CFD6D06267A85EA321AFA80384E49C4; + remoteInfo = Bolts; + }; + 87438DD3727F78E05960BC9BEC835551 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CFD6D06267A85EA321AFA80384E49C4; + remoteInfo = Bolts; + }; + 8C9B01C63A85A8E466E8C7DB93AB8DBD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 255B486D02599C8571584A81D3995B9C; + remoteInfo = Parse; + }; + D9052FE3BEDCE9DE873F38DBB341EB30 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 702D990824EC762012B0DCFFCF50AE79; + remoteInfo = ParseUI; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00A646C084294FC23A482874264A9E10 /* PFUserConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserConstants.h; path = Parse/Internal/User/Constants/PFUserConstants.h; sourceTree = ""; }; + 02054704AC6B3A8FF8526EA73E182B80 /* ParseModule.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = ParseModule.m; path = Parse/Internal/ParseModule.m; sourceTree = ""; }; + 0256B924DCFE7BACEB6568CD8A2C6C59 /* PFMultiProcessFileLock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMultiProcessFileLock.h; path = Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.h; sourceTree = ""; }; + 03453B418D0B0A3C1A61F031BCB33C8C /* PFMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMacros.h; path = Parse/Internal/PFMacros.h; sourceTree = ""; }; + 0396C6EE2D90BA2591A0BC7503FAF784 /* PFQueryCollectionViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQueryCollectionViewController.m; path = ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.m; sourceTree = ""; }; + 053008B62E75445056E83EAF88F670AE /* PFLogInView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFLogInView.m; path = ParseUI/Classes/LogInViewController/PFLogInView.m; sourceTree = ""; }; + 055D7C6E9227DB20416C800E4F51A337 /* BFExecutor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFExecutor.m; path = Bolts/Common/BFExecutor.m; sourceTree = ""; }; + 058FE1B26228FFF21C295DF2DF680081 /* PFLocationManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFLocationManager.m; path = Parse/Internal/PFLocationManager.m; sourceTree = ""; }; + 05A2290271BB3E67835291187DAB8318 /* PFUserAuthenticationController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserAuthenticationController.h; path = Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.h; sourceTree = ""; }; + 0655621C56EA9C2125947E9B169D3D63 /* PFRelationState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRelationState.m; path = Parse/Internal/Relation/State/PFRelationState.m; sourceTree = ""; }; + 06932232D53EEC91CFD3A681CB300096 /* UIProgressView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIProgressView+AFNetworking.m"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.m"; sourceTree = ""; }; + 06C9E684885FB5B216BF361176DDBB5D /* LiquidFloatingActionButton.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LiquidFloatingActionButton.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 06FFD67B3E3A338C0616E33AC82E271F /* PFPrimaryButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPrimaryButton.h; path = ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.h; sourceTree = ""; }; + 07EB9E619B4B720424CC061A99D2BABB /* PFActionButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFActionButton.h; path = ParseUI/Classes/Internal/Views/Buttons/PFActionButton.h; sourceTree = ""; }; + 08BD2B6BFAC23DEC69F1D3FCA0B38071 /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkReachabilityManager.h; path = AFNetworking/AFNetworkReachabilityManager.h; sourceTree = ""; }; + 09563E9AD709C4FE3E9B10F016D79F40 /* PFApplication.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFApplication.h; path = Parse/Internal/PFApplication.h; sourceTree = ""; }; + 0969818CCB352658F7216EA607E5A588 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0A186D964995C7B9E347DED4AD34C997 /* PFObjectEstimatedData.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectEstimatedData.h; path = Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.h; sourceTree = ""; }; + 0ACC8E7504C95E08B22B373F5EED0779 /* PFMutableObjectState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableObjectState.h; path = Parse/Internal/Object/State/PFMutableObjectState.h; sourceTree = ""; }; + 0B8576A9952876C6D42C077262FD9FA9 /* PFCurrentConfigController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCurrentConfigController.m; path = Parse/Internal/Config/Controller/PFCurrentConfigController.m; sourceTree = ""; }; + 0BFB709FA1612173F12A1B53A27F1636 /* PFRESTFileCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTFileCommand.h; path = Parse/Internal/Commands/PFRESTFileCommand.h; sourceTree = ""; }; + 0C00A99BD6D0DB2325AD32749EEE4D05 /* PFObjectConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectConstants.m; path = Parse/Internal/Object/Constants/PFObjectConstants.m; sourceTree = ""; }; + 0C0F04EEF7F8C7BC4C6FAC4AAF8E68DC /* PFBaseState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFBaseState.m; path = Parse/Internal/PFBaseState.m; sourceTree = ""; }; + 0CB07DB9FE0C3E5E4C1EA3AD16600DC3 /* PFCommandResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCommandResult.m; path = Parse/Internal/PFCommandResult.m; sourceTree = ""; }; + 0D9670272FFEABFE8085CAD7256B26BF /* PFImageCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFImageCache.m; path = ParseUI/Classes/Internal/PFImageCache.m; sourceTree = ""; }; + 0E05C2B4295C609B8107CC303A977728 /* AFURLSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLSessionManager.m; path = AFNetworking/AFURLSessionManager.m; sourceTree = ""; }; + 0E5E5504191D61A91B249136F90A7F29 /* PFSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSession.m; path = Parse/PFSession.m; sourceTree = ""; }; + 0E8FA54606CCBC52550175EBAFEB25E8 /* PFConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFConstants.m; path = Parse/PFConstants.m; sourceTree = ""; }; + 0ED8804894EC6C702E97CB2701FCD8B4 /* PFTableViewCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFTableViewCell.h; path = ParseUI/Classes/Cells/PFTableViewCell.h; sourceTree = ""; }; + 0F3BE50471AAC985EF28416C9EB1C185 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; }; + 0FDBD79473F9FB40DB76DD65392048A7 /* PFFile.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFile.h; path = Parse/PFFile.h; sourceTree = ""; }; + 0FED8E692B2FF2E30A56C6D070BFAB06 /* UIProgressView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIProgressView+AFNetworking.h"; path = "UIKit+AFNetworking/UIProgressView+AFNetworking.h"; sourceTree = ""; }; + 106FE48FB855E72F7842C099E98FA52D /* PFCurrentInstallationController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCurrentInstallationController.m; path = Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.m; sourceTree = ""; }; + 10785B227444AD6A5CCC16CC93FDED1F /* ParseUI-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ParseUI-umbrella.h"; sourceTree = ""; }; + 11641F1E2B0F6BFAEB5FE89FA349A02A /* PFObjectPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectPrivate.h; path = Parse/Internal/Object/PFObjectPrivate.h; sourceTree = ""; }; + 1284F0A5E10B2B233B0C53219469EA5F /* PFFileDataStream.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFileDataStream.m; path = Parse/Internal/File/FileDataStream/PFFileDataStream.m; sourceTree = ""; }; + 13AD5FBF9469D47C394C584D6E88F244 /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLResponseSerialization.m; path = AFNetworking/AFURLResponseSerialization.m; sourceTree = ""; }; + 13CD22F039DCBF6319754D584066D895 /* PFObjectSubclassInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectSubclassInfo.m; path = Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.m; sourceTree = ""; }; + 13D360FA4AE2720A4786C6058456F2DC /* AFHTTPRequestOperationManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPRequestOperationManager.h; path = AFNetworking/AFHTTPRequestOperationManager.h; sourceTree = ""; }; + 1461A3C96F9D5F77C27EB1AEF3772D96 /* Bolts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Bolts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 15D1EE3D8D96732C161572D298D09B0A /* GoogleMaps.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = GoogleMaps.bundle; path = Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle; sourceTree = ""; }; + 15DC305FBA250D68B59D7EC94B7F39C8 /* PFRESTCloudCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTCloudCommand.m; path = Parse/Internal/Commands/PFRESTCloudCommand.m; sourceTree = ""; }; + 1660ABF7B85069A170248B808256455D /* PFRect.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRect.m; path = ParseUI/Classes/Internal/Extensions/PFRect.m; sourceTree = ""; }; + 16E7F774F4751157B63DB04DB459BC55 /* AFNetworking-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AFNetworking-umbrella.h"; sourceTree = ""; }; + 16ED09F9D67408A7EFF12AFECDA9A63B /* PFObjectState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectState_Private.h; path = Parse/Internal/Object/State/PFObjectState_Private.h; sourceTree = ""; }; + 17184911F1E825E04E81231EB596A28E /* UIActivityIndicatorView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIActivityIndicatorView+AFNetworking.h"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h"; sourceTree = ""; }; + 182CA1A9D7A94C67A9EB27D9B1D89590 /* PFAnalytics_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnalytics_Private.h; path = Parse/Internal/Analytics/PFAnalytics_Private.h; sourceTree = ""; }; + 18399FE7C356696A82C3B177BBA86230 /* PFInstallationController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallationController.h; path = Parse/Internal/Installation/Controller/PFInstallationController.h; sourceTree = ""; }; + 1844474781B402090680328B2DB5420B /* AFNetworking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AFNetworking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 18575CBC596E9A1218A762DF5E3CD72B /* PFSessionController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSessionController.m; path = Parse/Internal/Session/Controller/PFSessionController.m; sourceTree = ""; }; + 19EB9918367D9576DC5BA66511FDDA59 /* PFAlertView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAlertView.h; path = Parse/Internal/PFAlertView.h; sourceTree = ""; }; + 1A23CAEC9BE172289170838599F9F895 /* PFThreadsafety.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFThreadsafety.m; path = Parse/Internal/ThreadSafety/PFThreadsafety.m; sourceTree = ""; }; + 1ABC7BA535DC20890BDADEE775FB84BC /* Parse-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Parse-dummy.m"; sourceTree = ""; }; + 1B3B9DFEEEF919C024CAAB1D52C6FA83 /* PFFileState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileState_Private.h; path = Parse/Internal/File/State/PFFileState_Private.h; sourceTree = ""; }; + 1B573A03FCE6CF5B48F8CB91BAD43435 /* PFGeoPoint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFGeoPoint.m; path = Parse/PFGeoPoint.m; sourceTree = ""; }; + 1B7AA2E50772619C4657FB990A261225 /* UIColorEx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIColorEx.swift; path = Pod/Classes/UIColorEx.swift; sourceTree = ""; }; + 1C48F5063ADC03C48C88120E75A86527 /* PFRESTObjectCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTObjectCommand.m; path = Parse/Internal/Commands/PFRESTObjectCommand.m; sourceTree = ""; }; + 1C62627778FD1291CC19265484E8A35B /* PFImageView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFImageView.h; path = ParseUI/Classes/Views/PFImageView.h; sourceTree = ""; }; + 1C667BBDD9E243B4C72CB91696743FEE /* AFNetworking-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "AFNetworking-prefix.pch"; sourceTree = ""; }; + 1D44679B3B60446F276A7452751C977C /* PFDefaultACLController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFDefaultACLController.m; path = Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.m; sourceTree = ""; }; + 1D8E781682341AD164504DA993129F1D /* Parse-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Parse-prefix.pch"; sourceTree = ""; }; + 1EACD763C478D5DB9B0370480A11B1FD /* PFSQLiteDatabaseController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSQLiteDatabaseController.h; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.h; sourceTree = ""; }; + 1EEF1672D1EBB3EC3C6F7B1D9F2C0B80 /* PFProduct+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PFProduct+Private.h"; path = "Parse/Internal/Product/PFProduct+Private.h"; sourceTree = ""; }; + 1F3A1D95B129EC27A4597159C4684430 /* ParseUI.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = ParseUI.xcconfig; sourceTree = ""; }; + 1F8CB4C16F79838F1FBAC6D1F4FADE30 /* BFTask.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFTask.h; path = Bolts/Common/BFTask.h; sourceTree = ""; }; + 1F96A5E8E7BE0A8F092CB9BE81FA722A /* PFOfflineQueryController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFOfflineQueryController.m; path = Parse/Internal/Query/Controller/PFOfflineQueryController.m; sourceTree = ""; }; + 1FAD8490F27C97455330C5FD12D68BB7 /* PFWeakValue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFWeakValue.h; path = Parse/Internal/PFWeakValue.h; sourceTree = ""; }; + 1FC471E6023A052E8FA627F61045398C /* AFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkActivityIndicatorManager.m; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m"; sourceTree = ""; }; + 1FF98AED99DA5E23EF2405DB00EB5C15 /* PFOperationSet.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFOperationSet.m; path = Parse/Internal/Object/OperationSet/PFOperationSet.m; sourceTree = ""; }; + 201F561822AF1B8FD7578936456F4264 /* PFHash.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFHash.h; path = Parse/Internal/PFHash.h; sourceTree = ""; }; + 204C2D996771F45E7C30302ECFA3D5C5 /* PFQueryUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryUtilities.h; path = Parse/Internal/Query/Utilities/PFQueryUtilities.h; sourceTree = ""; }; + 20F63976C35095E9FB91D72CAE2FDB7D /* PFObjectFileCodingLogic.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectFileCodingLogic.m; path = Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.m; sourceTree = ""; }; + 2185E69315D22B3715A623F06696D5FC /* PFRESTPushCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTPushCommand.h; path = Parse/Internal/Commands/PFRESTPushCommand.h; sourceTree = ""; }; + 2208328361FA7D0218574BE074627642 /* en.lproj */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder; name = en.lproj; path = Parse/Resources/en.lproj; sourceTree = ""; }; + 22855645AE9EE73E2D1C09E998BEEC66 /* PFRelation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRelation.m; path = Parse/PFRelation.m; sourceTree = ""; }; + 22AF66FAEC41A180692A915DFAA6C452 /* PFURLSessionCommandRunner.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionCommandRunner.h; path = Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.h; sourceTree = ""; }; + 22BBA5CC87BB1C58194494B4A1E02687 /* PFPinningEventuallyQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPinningEventuallyQueue.h; path = Parse/Internal/PFPinningEventuallyQueue.h; sourceTree = ""; }; + 22E583E17C29E569C438C2F5CFE8DE49 /* PFMulticastDelegate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMulticastDelegate.m; path = Parse/Internal/PFMulticastDelegate.m; sourceTree = ""; }; + 23B9E89D64EA7A1356B9BBCEE11CC809 /* PFObjectSubclassingController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectSubclassingController.h; path = Parse/Internal/Object/Subclassing/PFObjectSubclassingController.h; sourceTree = ""; }; + 242EE2B72A421D6B06312EA12A989EC1 /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLConnectionOperation.m; path = AFNetworking/AFURLConnectionOperation.m; sourceTree = ""; }; + 24627238BB902D6C44D91F716A3B4005 /* PFURLSessionDataTaskDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionDataTaskDelegate.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.h; sourceTree = ""; }; + 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "LiquidFloatingActionButton-Private.xcconfig"; sourceTree = ""; }; + 24ED10A4EE83A682837CCD2514C1FE1E /* BoltsVersion.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BoltsVersion.h; path = Bolts/Common/BoltsVersion.h; sourceTree = ""; }; + 258F5BCDF91B9AB891FBF085B379DA3C /* PFPropertyInfo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPropertyInfo.m; path = Parse/Internal/PropertyInfo/PFPropertyInfo.m; sourceTree = ""; }; + 25DA2A63A41FDC2B72632E0F65A7250C /* Bolts.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Bolts.m; path = Bolts/Common/Bolts.m; sourceTree = ""; }; + 266DD3785853AF2B087ECB683111FBDF /* PFMutableUserState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableUserState.m; path = Parse/Internal/User/State/PFMutableUserState.m; sourceTree = ""; }; + 2864AE64CD2B48522D2926BD76DB48A2 /* PFInstallationPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallationPrivate.h; path = Parse/Internal/Installation/PFInstallationPrivate.h; sourceTree = ""; }; + 286AB7391704B71658D0BCF8A49D2F4D /* Bolts-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Bolts-umbrella.h"; sourceTree = ""; }; + 286E6D876A21BD5D906EE5CA6743D2E0 /* PFAsyncTaskQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAsyncTaskQueue.m; path = Parse/Internal/PFAsyncTaskQueue.m; sourceTree = ""; }; + 2A24A3D3E4F16BBD1F267C848321CE5C /* PFPushManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPushManager.m; path = Parse/Internal/Push/Manager/PFPushManager.m; sourceTree = ""; }; + 2A7D8F6CA1BC15F07535717374D4678F /* PFQueryController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryController.h; path = Parse/Internal/Query/Controller/PFQueryController.h; sourceTree = ""; }; + 2B3641265C060CC5C48EE34815FA7F68 /* PFQueryPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryPrivate.h; path = Parse/Internal/Query/PFQueryPrivate.h; sourceTree = ""; }; + 2B9BE9E8FFC7586640B02294D86CF884 /* PFAnalyticsController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAnalyticsController.m; path = Parse/Internal/Analytics/Controller/PFAnalyticsController.m; sourceTree = ""; }; + 2BC28B0883B9B9DB2F5F1F2DF82A956D /* ParseUI-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "ParseUI-dummy.m"; sourceTree = ""; }; + 2D3FCD6D1CCE074756536544D3795CD9 /* PFActionButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFActionButton.m; path = ParseUI/Classes/Internal/Views/Buttons/PFActionButton.m; sourceTree = ""; }; + 2DB17A9B3DDAC837FD6453FEA310E848 /* PFRESTQueryCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTQueryCommand.m; path = Parse/Internal/Commands/PFRESTQueryCommand.m; sourceTree = ""; }; + 2DE72C53C03EAE4311F887EE6A186CFB /* PFPurchaseTableViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPurchaseTableViewCell.m; path = ParseUI/Classes/Cells/PFPurchaseTableViewCell.m; sourceTree = ""; }; + 2DFA9B6E0EEB0C0CC47910DB6F871C95 /* AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworking.h; path = AFNetworking/AFNetworking.h; sourceTree = ""; }; + 2E3BD169A6C2F8F814ACD98138308236 /* PFCurrentObjectControlling.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCurrentObjectControlling.h; path = Parse/Internal/Object/CurrentController/PFCurrentObjectControlling.h; sourceTree = ""; }; + 2E4419B914285E9CFA2B0C4905E3717F /* PFCommandURLRequestConstructor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandURLRequestConstructor.h; path = Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.h; sourceTree = ""; }; + 2E6364E03CBC47FA3C194780506E4A88 /* AFNetworking.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = AFNetworking.xcconfig; sourceTree = ""; }; + 2EFC89B254D8AAE9141EF6851EA5277D /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFNetworkReachabilityManager.m; path = AFNetworking/AFNetworkReachabilityManager.m; sourceTree = ""; }; + 2F4B2107EEEB8DF1A30920BAB2D9CA6C /* ParseModule.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ParseModule.h; path = Parse/Internal/ParseModule.h; sourceTree = ""; }; + 2FDD4D768605FD329F1CA002D177D3CD /* PFMutableFileState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableFileState.h; path = Parse/Internal/File/State/PFMutableFileState.h; sourceTree = ""; }; + 31673CEE600D31E9C12A1F2F14C74715 /* PFPropertyInfo_Runtime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPropertyInfo_Runtime.h; path = Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.h; sourceTree = ""; }; + 31A896932415303DC122139D6C700DED /* PFOfflineObjectController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFOfflineObjectController.h; path = Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.h; sourceTree = ""; }; + 3216FF24B9FA981F635F7D85A2444D31 /* PFPropertyInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPropertyInfo.h; path = Parse/Internal/PropertyInfo/PFPropertyInfo.h; sourceTree = ""; }; + 335CC941000B202CD5BB59078C34DDF2 /* PFAsyncTaskQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAsyncTaskQueue.h; path = Parse/Internal/PFAsyncTaskQueue.h; sourceTree = ""; }; + 355416180C97110767845F8B71F32709 /* PFSQLiteDatabase.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSQLiteDatabase.m; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.m; sourceTree = ""; }; + 356CD10091814DAEF0798313892C8C05 /* PFPush.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPush.h; path = Parse/PFPush.h; sourceTree = ""; }; + 35F772D3D7B1BC27FD87ABE0059732FF /* BFTaskCompletionSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFTaskCompletionSource.h; path = Bolts/Common/BFTaskCompletionSource.h; sourceTree = ""; }; + 3684ED9D4BB5E30D2F75909D264EFF6A /* PFCachedQueryController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCachedQueryController.h; path = Parse/Internal/Query/Controller/PFCachedQueryController.h; sourceTree = ""; }; + 36D423CBA06FDB7473035E0CAD68C111 /* PFCommandCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCommandCache.m; path = Parse/Internal/PFCommandCache.m; sourceTree = ""; }; + 36E262B1B4BEE4F1DDB6F3DF88064F02 /* PFObjectFilePersistenceController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectFilePersistenceController.m; path = Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.m; sourceTree = ""; }; + 37222E9C27B945A7B72A144663090FCB /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; + 374066DC72A963CFFD3374DBBFA2DFAC /* PFConfigController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFConfigController.h; path = Parse/Internal/Config/Controller/PFConfigController.h; sourceTree = ""; }; + 3819E8A9BE558DAF234B89FC4CC41178 /* PFQuery.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQuery.m; path = Parse/PFQuery.m; sourceTree = ""; }; + 38C7D3C7362627C2E0E8B4F5BE09F5A2 /* PFLogInViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLogInViewController.h; path = ParseUI/Classes/LogInViewController/PFLogInViewController.h; sourceTree = ""; }; + 391F623207B6760C4D0AC086EE3CE49C /* PFNullability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFNullability.h; path = Parse/PFNullability.h; sourceTree = ""; }; + 39555E2A1C0726E869769E5E64AD7F75 /* Parse-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Parse-umbrella.h"; sourceTree = ""; }; + 3BE045EE908BDA1501A41A429BC7A646 /* ArrayEx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ArrayEx.swift; path = Pod/Classes/ArrayEx.swift; sourceTree = ""; }; + 3CA945CDAC44DDF1110329B15603AAF7 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; }; + 3DBE2DCDD433C14FC53555C1A83AB199 /* PFWeakValue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFWeakValue.m; path = Parse/Internal/PFWeakValue.m; sourceTree = ""; }; + 3E0011F34B6F1370264442D8135FC5CA /* PFCloudCodeController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCloudCodeController.m; path = Parse/Internal/CloudCode/PFCloudCodeController.m; sourceTree = ""; }; + 3E1756E787A2805CAFDC958AE3C4960D /* PFObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObject.m; path = Parse/PFObject.m; sourceTree = ""; }; + 3E5865FCD20277B95CE81D8313B04B91 /* PFPush.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPush.m; path = Parse/PFPush.m; sourceTree = ""; }; + 3E72FA549FC9512FB4BDF3D360A35D8F /* PFQueryState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryState.h; path = Parse/Internal/Query/State/PFQueryState.h; sourceTree = ""; }; + 3EBA0F2EDA08DA37F7D8E7B9150E9A29 /* PFRESTCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTCommand.h; path = Parse/Internal/Commands/PFRESTCommand.h; sourceTree = ""; }; + 3F6F533DC7C28FD9B021564F418631E7 /* UIWebView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIWebView+AFNetworking.h"; path = "UIKit+AFNetworking/UIWebView+AFNetworking.h"; sourceTree = ""; }; + 3FB2A37BB79697F99A0A53FA1B7F9B47 /* UIImage+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+AFNetworking.h"; path = "UIKit+AFNetworking/UIImage+AFNetworking.h"; sourceTree = ""; }; + 3FD488736BA7D2666128B1D854BD802D /* PFPushUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPushUtilities.m; path = Parse/Internal/Push/Utilites/PFPushUtilities.m; sourceTree = ""; }; + 3FEB8673C94646984FA528D09A6716B3 /* PFUser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUser.h; path = Parse/PFUser.h; sourceTree = ""; }; + 402884917651870142D4DEF1CD7D4DBF /* Parse.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Parse.modulemap; sourceTree = ""; }; + 4047A1EB9846648DAD467918C55ED03A /* Parse-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Parse-Private.xcconfig"; sourceTree = ""; }; + 40AEA2C7205FE550939B0F7F408FCA85 /* PFUserController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUserController.m; path = Parse/Internal/User/Controller/PFUserController.m; sourceTree = ""; }; + 411246798151B846841853F673E37435 /* PFConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFConfig.h; path = Parse/PFConfig.h; sourceTree = ""; }; + 412F790A066291B851813E4C9327BFB7 /* PFLoadingView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFLoadingView.m; path = ParseUI/Classes/Internal/Views/PFLoadingView.m; sourceTree = ""; }; + 4148D9154FC86736CE6D49722DF31E3A /* PFPurchase.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPurchase.m; path = Parse/PFPurchase.m; sourceTree = ""; }; + 4240B13C9AE359BF942D85E0DE35AFE2 /* PFPaymentTransactionObserver_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPaymentTransactionObserver_Private.h; path = Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver_Private.h; sourceTree = ""; }; + 429BE42396DC64B472A5559E886AEA42 /* PFUserFileCodingLogic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserFileCodingLogic.h; path = Parse/Internal/User/Coder/File/PFUserFileCodingLogic.h; sourceTree = ""; }; + 42D0387D6F012A1706FDA9B3F9974E22 /* PFACLPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFACLPrivate.h; path = Parse/Internal/ACL/PFACLPrivate.h; sourceTree = ""; }; + 4303F783032C0C2846F32A846D5D4E5C /* PFAnonymousUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAnonymousUtils.m; path = Parse/PFAnonymousUtils.m; sourceTree = ""; }; + 434C507EC671E5F0E3785E7D3020225B /* PFTextButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFTextButton.h; path = ParseUI/Classes/Internal/Views/Buttons/PFTextButton.h; sourceTree = ""; }; + 43575DB6BB216F37BCA1AB250B9F09BF /* PFACLState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFACLState.h; path = Parse/Internal/ACL/State/PFACLState.h; sourceTree = ""; }; + 43B20D1E0DCC5A0A1D0ABC06E6C76F1A /* PFFileStagingController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileStagingController.h; path = Parse/Internal/File/Controller/PFFileStagingController.h; sourceTree = ""; }; + 43F928E8DB69EEDE303F3A4C7318F4DF /* PFInstallationIdentifierStore_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallationIdentifierStore_Private.h; path = Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore_Private.h; sourceTree = ""; }; + 44270F881246E94C8F2A111F06702B83 /* PFAnonymousAuthenticationProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnonymousAuthenticationProvider.h; path = Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.h; sourceTree = ""; }; + 44B78AF0E04A202E8C137C3F34CE6F17 /* PFObjectEstimatedData.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectEstimatedData.m; path = Parse/Internal/Object/EstimatedData/PFObjectEstimatedData.m; sourceTree = ""; }; + 44DA91E0DF5B0A8CBEA09F5223C6F1C8 /* PFPushState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushState.h; path = Parse/Internal/Push/State/PFPushState.h; sourceTree = ""; }; + 45C2D54A8FA658EE6C378587022D1F23 /* PFMutablePushState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutablePushState.m; path = Parse/Internal/Push/State/PFMutablePushState.m; sourceTree = ""; }; + 462806D14A2A143E44F927E5742857E6 /* PFUserState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUserState.m; path = Parse/Internal/User/State/PFUserState.m; sourceTree = ""; }; + 473DFEC13A8925DDCC0F6375D0AA1FDD /* PFActivityIndicatorCollectionReusableView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFActivityIndicatorCollectionReusableView.m; path = ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.m; sourceTree = ""; }; + 47BBEF70FCF6A4D933AADEA763919924 /* PFPin.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPin.h; path = Parse/Internal/LocalDataStore/Pin/PFPin.h; sourceTree = ""; }; + 480A5B1630D4FE3B4BBB46EB667DD47F /* PFProductTableViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFProductTableViewController.m; path = ParseUI/Classes/ProductTableViewController/PFProductTableViewController.m; sourceTree = ""; }; + 480D89A7E47EF11AA3FBB5053B2C9781 /* PFPropertyInfo_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPropertyInfo_Private.h; path = Parse/Internal/PropertyInfo/PFPropertyInfo_Private.h; sourceTree = ""; }; + 4896C3D552B4755BE9E7C1C62D992F5E /* PFAnalyticsUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAnalyticsUtilities.m; path = Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.m; sourceTree = ""; }; + 489F324A39862A52246BA56B81766653 /* BFCancellationTokenRegistration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFCancellationTokenRegistration.h; path = Bolts/Common/BFCancellationTokenRegistration.h; sourceTree = ""; }; + 48BA00A680115FA8E8598713DD4F3F53 /* PFURLSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSession.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.h; sourceTree = ""; }; + 490DD45156F4A90833C74B5509134501 /* UIRefreshControl+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIRefreshControl+AFNetworking.h"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.h"; sourceTree = ""; }; + 49314B7929DAC6D78738BF75309C6409 /* PFActivityIndicatorTableViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFActivityIndicatorTableViewCell.m; path = ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.m; sourceTree = ""; }; + 49457ABA4299045560404E829773DF26 /* PFSignUpViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSignUpViewController.h; path = ParseUI/Classes/SignUpViewController/PFSignUpViewController.h; sourceTree = ""; }; + 4A2CCB3F17D3BA81EBDC4AC1E6CF5E8A /* PFKeyValueCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFKeyValueCache.m; path = Parse/Internal/KeyValueCache/PFKeyValueCache.m; sourceTree = ""; }; + 4A57B2A1A122A5645B1B336DEDEABF67 /* PFDataProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDataProvider.h; path = Parse/Internal/PFDataProvider.h; sourceTree = ""; }; + 4B393ACCB61C4E998CC4330DAC20ACEC /* PFFileDataStream.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileDataStream.h; path = Parse/Internal/File/FileDataStream/PFFileDataStream.h; sourceTree = ""; }; + 4B56E95C16FD6DAC8A7EA8CE384CF79B /* PFOperationSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFOperationSet.h; path = Parse/Internal/Object/OperationSet/PFOperationSet.h; sourceTree = ""; }; + 4B5D13079A63558D8C87DFDB1697B537 /* PFInstallationConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFInstallationConstants.m; path = Parse/Internal/Installation/Constants/PFInstallationConstants.m; sourceTree = ""; }; + 4B6436B37ECC59A38F96B1ADE342EB64 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4B9BA81F2BF3DBCACCD4672ED697498A /* PFConfigController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFConfigController.m; path = Parse/Internal/Config/Controller/PFConfigController.m; sourceTree = ""; }; + 4C26CD46DBFBC0826774EE2BABF221F5 /* PFSignUpViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSignUpViewController.m; path = ParseUI/Classes/SignUpViewController/PFSignUpViewController.m; sourceTree = ""; }; + 4C3F5A63922E21A53CC4884A45C5839E /* PFFieldOperationDecoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFieldOperationDecoder.h; path = Parse/Internal/FieldOperation/PFFieldOperationDecoder.h; sourceTree = ""; }; + 4F901A1A06A6B452D091DE5EEF0C8136 /* PFActivityIndicatorTableViewCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFActivityIndicatorTableViewCell.h; path = ParseUI/Classes/Internal/Cells/PFActivityIndicatorTableViewCell.h; sourceTree = ""; }; + 4FB104646E63B185FAFECD3B01CDA7DD /* PFURLSessionFileDownloadTaskDelegate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSessionFileDownloadTaskDelegate.m; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.m; sourceTree = ""; }; + 504090B38C287A418E1F8BE4850DB9F8 /* PFInstallationIdentifierStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallationIdentifierStore.h; path = Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.h; sourceTree = ""; }; + 507E749D722A9CC247F8CE2B138A1548 /* Bolts-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Bolts-prefix.pch"; sourceTree = ""; }; + 507EEEA0E2D71194AF983A704F2B50B1 /* PFURLSessionJSONDataTaskDelegate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSessionJSONDataTaskDelegate.m; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.m; sourceTree = ""; }; + 508F40F7D00DB921AC2A70A820C56E68 /* PFErrorUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFErrorUtilities.m; path = Parse/Internal/PFErrorUtilities.m; sourceTree = ""; }; + 508F81A14503FDBEA2527D9048B06142 /* PFProductsRequestHandler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFProductsRequestHandler.m; path = Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.m; sourceTree = ""; }; + 50B9BC2A232324947BCFD7EF8C6DF1B8 /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLRequestSerialization.h; path = AFNetworking/AFURLRequestSerialization.h; sourceTree = ""; }; + 50C7A024063739A9D4A5CD90B47FE50E /* PFPushState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPushState.m; path = Parse/Internal/Push/State/PFPushState.m; sourceTree = ""; }; + 512A5EA836765109A6CA0BF4BF2BE6C0 /* PFPushUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushUtilities.h; path = Parse/Internal/Push/Utilites/PFPushUtilities.h; sourceTree = ""; }; + 515EDE43F2D9FB9C27C6C8783384DCE9 /* PFEncoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFEncoder.m; path = Parse/Internal/PFEncoder.m; sourceTree = ""; }; + 51672A9A990271236F6EEF5F4BE45E36 /* PFURLConstructor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLConstructor.h; path = Parse/Internal/HTTPRequest/PFURLConstructor.h; sourceTree = ""; }; + 51F9299B2C2E6C0D460E3B857D3E1931 /* ParseUIConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ParseUIConstants.h; path = ParseUI/Other/ParseUIConstants.h; sourceTree = ""; }; + 528B85889F63E99EC61F9BC47CC31A8E /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLConnectionOperation.h; path = AFNetworking/AFURLConnectionOperation.h; sourceTree = ""; }; + 52917348ED99708803AB4571CBF7448F /* PFObjectFileCoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectFileCoder.m; path = Parse/Internal/Object/Coder/File/PFObjectFileCoder.m; sourceTree = ""; }; + 5296B4416AC12F2190F879868AC55E77 /* AFNetworking-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "AFNetworking-Private.xcconfig"; sourceTree = ""; }; + 5444BD2EF92DA71A5E0E4E368258FCF7 /* PFSubclassing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSubclassing.h; path = Parse/PFSubclassing.h; sourceTree = ""; }; + 54554DF4C25DD4127EC0364200393FE6 /* PFACL.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFACL.m; path = Parse/PFACL.m; sourceTree = ""; }; + 54B19C063A5F2D93FEFE0F4D66A9DEA4 /* PFUserAuthenticationController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUserAuthenticationController.m; path = Parse/Internal/User/AuthenticationProviders/Controller/PFUserAuthenticationController.m; sourceTree = ""; }; + 55A85024FFE04E028567F29F7ABA33E0 /* PFDefaultACLController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDefaultACLController.h; path = Parse/Internal/ACL/DefaultACLController/PFDefaultACLController.h; sourceTree = ""; }; + 55ED6CB9CFA4FDEE1DEC32FE8583DFE0 /* PFCoreManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCoreManager.m; path = Parse/Internal/PFCoreManager.m; sourceTree = ""; }; + 56467AABBB2F1EADE1FDAF4C1D07FBC5 /* en.lproj */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder; name = en.lproj; path = ParseUI/Resources/Localization/en.lproj; sourceTree = ""; }; + 56644183C8AFDEEF3AAC26943A7C8825 /* PFURLSessionJSONDataTaskDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionJSONDataTaskDelegate.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionJSONDataTaskDelegate.h; sourceTree = ""; }; + 56833EA79C5134523EDBB3F9CED7E72F /* PFRelation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRelation.h; path = Parse/PFRelation.h; sourceTree = ""; }; + 5744A62C5DD57C35F95B23CD332A69B0 /* PFColor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFColor.m; path = ParseUI/Classes/Internal/Extensions/PFColor.m; sourceTree = ""; }; + 57557DDB79AE2CA8FB3449F3A6EB4369 /* PFUIAlertView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUIAlertView.m; path = ParseUI/Classes/Internal/Extensions/PFUIAlertView.m; sourceTree = ""; }; + 57DBB8A59CEF5779FB2FEC6E3357F1D8 /* PFPin.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPin.m; path = Parse/Internal/LocalDataStore/Pin/PFPin.m; sourceTree = ""; }; + 58228477EDB607E07438A4DA7659B1AB /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + 5AB0E2E0BA627EEC178EBB58FEB61DCD /* PFAnonymousAuthenticationProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAnonymousAuthenticationProvider.m; path = Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousAuthenticationProvider.m; sourceTree = ""; }; + 5AC7EE67669C15374170942A5ADB449F /* PFRESTObjectCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTObjectCommand.h; path = Parse/Internal/Commands/PFRESTObjectCommand.h; sourceTree = ""; }; + 5B0D1B89D4514C28C3C698E6564D5CC0 /* PFDismissButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDismissButton.h; path = ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.h; sourceTree = ""; }; + 5B22145FF686BB047CD997E62D0DEF43 /* BFCancellationToken.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFCancellationToken.h; path = Bolts/Common/BFCancellationToken.h; sourceTree = ""; }; + 5BB2711832525BD41951DBF302F796E1 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5C20844654F9A04C67386CC1D2C2D1B1 /* PFDecoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDecoder.h; path = Parse/Internal/PFDecoder.h; sourceTree = ""; }; + 5C27666D25206D0C9228E2B561CFCEA9 /* PFSQLiteDatabaseResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSQLiteDatabaseResult.m; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.m; sourceTree = ""; }; + 5C8EE00FBF100049803CBD08E3488726 /* PFURLSessionDataTaskDelegate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSessionDataTaskDelegate.m; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate.m; sourceTree = ""; }; + 5D7587C874F53970DD6689504C43AE04 /* PFObjectSubclassingController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectSubclassingController.m; path = Parse/Internal/Object/Subclassing/PFObjectSubclassingController.m; sourceTree = ""; }; + 5DE30302543CAA1030741C690BC2AC46 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; + 5E29EE0F72D64655290376D8E2025A3C /* BFTask+Private.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "BFTask+Private.m"; path = "Parse/Internal/BFTask+Private.m"; sourceTree = ""; }; + 5EA7251DC3418FDD089B4BE7DB866896 /* PFMultiProcessFileLockController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMultiProcessFileLockController.h; path = Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.h; sourceTree = ""; }; + 5EAE992B8BEAB950FE7E122420450483 /* UIRefreshControl+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIRefreshControl+AFNetworking.m"; path = "UIKit+AFNetworking/UIRefreshControl+AFNetworking.m"; sourceTree = ""; }; + 5F2B79C120F1E343333F0263C901B136 /* PFCachedQueryController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCachedQueryController.m; path = Parse/Internal/Query/Controller/PFCachedQueryController.m; sourceTree = ""; }; + 6085298ACB2321485F0D3C12D351FEB5 /* PFHTTPRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFHTTPRequest.h; path = Parse/Internal/HTTPRequest/PFHTTPRequest.h; sourceTree = ""; }; + 60D8FEA3A3B139B0A0F01500655A6DE8 /* PFPushController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPushController.m; path = Parse/Internal/Push/Controller/PFPushController.m; sourceTree = ""; }; + 610C79F260D7DE6FBE6856EF3349D7E2 /* PFPushManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushManager.h; path = Parse/Internal/Push/Manager/PFPushManager.h; sourceTree = ""; }; + 61351FBD424C7B2328FDF4BE4B7ECE3A /* PFMutableFileState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableFileState.m; path = Parse/Internal/File/State/PFMutableFileState.m; sourceTree = ""; }; + 61A68A9C9F4DECBC21FBD79D006013B1 /* PFURLSessionDataTaskDelegate_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionDataTaskDelegate_Private.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionDataTaskDelegate_Private.h; sourceTree = ""; }; + 61B90E31FC30056504354B6B9CD64ABB /* PFMutableQueryState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableQueryState.m; path = Parse/Internal/Query/State/PFMutableQueryState.m; sourceTree = ""; }; + 632635171DFBADC6DD391BC3C9FBD721 /* PFAnalytics.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnalytics.h; path = Parse/PFAnalytics.h; sourceTree = ""; }; + 632A9C2EB7D9F58D0A7E0A0734C27B83 /* PFImageCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFImageCache.h; path = ParseUI/Classes/Internal/PFImageCache.h; sourceTree = ""; }; + 64DB319647B47CD74F8DA4F72A903D14 /* BFTask+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "BFTask+Private.h"; path = "Parse/Internal/BFTask+Private.h"; sourceTree = ""; }; + 66636BE79215A604578D0F0E12C79103 /* UIButton+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIButton+AFNetworking.h"; path = "UIKit+AFNetworking/UIButton+AFNetworking.h"; sourceTree = ""; }; + 672FF2A8DEDB8B294A94B94B44170E86 /* PFRESTObjectBatchCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTObjectBatchCommand.m; path = Parse/Internal/Commands/PFRESTObjectBatchCommand.m; sourceTree = ""; }; + 68B9F97EFD6FBB23BEA89804B1300441 /* PFMulticastDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMulticastDelegate.h; path = Parse/Internal/PFMulticastDelegate.h; sourceTree = ""; }; + 6B1EB3A0FAE8016CFDE64DE338F8BDD7 /* PFPurchaseController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPurchaseController.h; path = Parse/Internal/Purchase/Controller/PFPurchaseController.h; sourceTree = ""; }; + 6CDEB255A1EFD2D56CF8836FF93C208D /* PFInstallationController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFInstallationController.m; path = Parse/Internal/Installation/Controller/PFInstallationController.m; sourceTree = ""; }; + 6D29EC69D4226E7C3D3D7EEAFC9E96F3 /* BFCancellationTokenSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFCancellationTokenSource.h; path = Bolts/Common/BFCancellationTokenSource.h; sourceTree = ""; }; + 6D3CD655EFF856D601BCADD2A12D20B1 /* UIAlertView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIAlertView+AFNetworking.h"; path = "UIKit+AFNetworking/UIAlertView+AFNetworking.h"; sourceTree = ""; }; + 6D78597D58FFE85B4FB8C4561AA50BB5 /* PFEventuallyQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFEventuallyQueue.m; path = Parse/Internal/PFEventuallyQueue.m; sourceTree = ""; }; + 6DA3B7EDAFEC180CEDD3C7393AD44FF3 /* PFThreadsafety.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFThreadsafety.h; path = Parse/Internal/ThreadSafety/PFThreadsafety.h; sourceTree = ""; }; + 6DAA2CB4A82D8EB23F29780BFAAC1DCE /* PFObjectController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectController.h; path = Parse/Internal/Object/Controller/PFObjectController.h; sourceTree = ""; }; + 6F5FA5FAAFCEEF833D9142FF59994544 /* PFErrorUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFErrorUtilities.h; path = Parse/Internal/PFErrorUtilities.h; sourceTree = ""; }; + 6F8BEA4A9ECB98F43F4DD96BE3251E35 /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFURLRequestSerialization.m; path = AFNetworking/AFURLRequestSerialization.m; sourceTree = ""; }; + 705C8317C0CF03B733A5A25FB46A66EC /* PFRESTCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTCommand.m; path = Parse/Internal/Commands/PFRESTCommand.m; sourceTree = ""; }; + 70B9D1F18A0D896262A4B138338F2901 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; }; + 7135D79AC2406919243CA6DA7F8290E3 /* PFACL.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFACL.h; path = Parse/PFACL.h; sourceTree = ""; }; + 71EC800801E622412FB29F8D44D3CD3C /* PFHash.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFHash.m; path = Parse/Internal/PFHash.m; sourceTree = ""; }; + 7304B1EA9F7A8C12AD248A220266F7E6 /* PFObjectSubclassInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectSubclassInfo.h; path = Parse/Internal/Object/Subclassing/PFObjectSubclassInfo.h; sourceTree = ""; }; + 732BF5F5AADEEED43943EDD827DB02D2 /* PFSessionUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSessionUtilities.h; path = Parse/Internal/Session/Utilities/PFSessionUtilities.h; sourceTree = ""; }; + 74CE67880E6C87B2A6E1DD4E8F148691 /* PFAnonymousUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnonymousUtils.h; path = Parse/PFAnonymousUtils.h; sourceTree = ""; }; + 74E1103577F4DCB27E5C109F9329653B /* AFURLSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLSessionManager.h; path = AFNetworking/AFURLSessionManager.h; sourceTree = ""; }; + 7516FAB60771B12B16D06A78AB5986AF /* PFRESTUserCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTUserCommand.h; path = Parse/Internal/Commands/PFRESTUserCommand.h; sourceTree = ""; }; + 7533A71248AEB62C9FC4FE38B6FBCE43 /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPSessionManager.m; path = AFNetworking/AFHTTPSessionManager.m; sourceTree = ""; }; + 75F25EFFB4D4BE6DA19F30E328EA3E81 /* ParseUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ParseUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 760C685581C23015279373B19B8816EE /* PFRESTFileCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTFileCommand.m; path = Parse/Internal/Commands/PFRESTFileCommand.m; sourceTree = ""; }; + 76AFC841FC9BC38C174F77AE81A883BA /* PFRESTPushCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTPushCommand.m; path = Parse/Internal/Commands/PFRESTPushCommand.m; sourceTree = ""; }; + 77FF71B41A0A7E4C30E000416639CAD2 /* PFFileStagingController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFileStagingController.m; path = Parse/Internal/File/Controller/PFFileStagingController.m; sourceTree = ""; }; + 78A2111B342B9B80EC48C4F51AA04B69 /* UIKit+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIKit+AFNetworking.h"; path = "UIKit+AFNetworking/UIKit+AFNetworking.h"; sourceTree = ""; }; + 78B65A9361B5999AFC577A6CAE34D1D6 /* PFSQLiteDatabase_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSQLiteDatabase_Private.h; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase_Private.h; sourceTree = ""; }; + 78FD692B8BFB4BE7D09FDBBA605AE451 /* BFCancellationTokenSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFCancellationTokenSource.m; path = Bolts/Common/BFCancellationTokenSource.m; sourceTree = ""; }; + 794BFE228ECF6346EF495E3238904178 /* PFPushState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushState_Private.h; path = Parse/Internal/Push/State/PFPushState_Private.h; sourceTree = ""; }; + 795840987CB6C725613D305D3D8DE0C5 /* PFFile.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFile.m; path = Parse/PFFile.m; sourceTree = ""; }; + 79A0675A9A52CC3B48F422CA4E940CD3 /* PFFileController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFileController.m; path = Parse/Internal/File/Controller/PFFileController.m; sourceTree = ""; }; + 79A9DEDC89FE8336BF5FEDAAF75BF7FC /* Pods.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Pods.modulemap; sourceTree = ""; }; + 7BF997DF2BA7E971B44D0DE2163F7FF8 /* PFObjectFilePersistenceController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectFilePersistenceController.h; path = Parse/Internal/Object/FilePersistence/PFObjectFilePersistenceController.h; sourceTree = ""; }; + 7C455D006BA9847DD01AF4AD9D0A99C0 /* PFInternalUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInternalUtils.h; path = Parse/Internal/PFInternalUtils.h; sourceTree = ""; }; + 7C586370E28704EE0E4D7689B917753B /* PFFieldOperationDecoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFieldOperationDecoder.m; path = Parse/Internal/FieldOperation/PFFieldOperationDecoder.m; sourceTree = ""; }; + 7C90F76F74AA0E9A353A68E4994EDCBF /* PFTextButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFTextButton.m; path = ParseUI/Classes/Internal/Views/Buttons/PFTextButton.m; sourceTree = ""; }; + 7DA5D03F437F920BAB37A6380F6CA63F /* PFResources.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFResources.m; path = ParseUI/Generated/PFResources.m; sourceTree = ""; }; + 7E1C9CCFB4EA178BC262A1EF44D11ED1 /* PFUserAuthenticationDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserAuthenticationDelegate.h; path = Parse/PFUserAuthenticationDelegate.h; sourceTree = ""; }; + 7E8BE2C27FB13B26072B0BD9EE14FBAE /* PFUserController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserController.h; path = Parse/Internal/User/Controller/PFUserController.h; sourceTree = ""; }; + 7F148B720ADCC2D2110CC085A4F393C9 /* PFRESTSessionCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTSessionCommand.m; path = Parse/Internal/Commands/PFRESTSessionCommand.m; sourceTree = ""; }; + 7F6399FA5304D192B1A4A7C0D2EB53A1 /* PFURLSessionFileDownloadTaskDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionFileDownloadTaskDelegate.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionFileDownloadTaskDelegate.h; sourceTree = ""; }; + 808AF6592966508933C25BC20FFCC320 /* UIButton+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIButton+AFNetworking.m"; path = "UIKit+AFNetworking/UIButton+AFNetworking.m"; sourceTree = ""; }; + 815246C826CF2ADBE326786D6BA94F42 /* PFPushController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushController.h; path = Parse/Internal/Push/Controller/PFPushController.h; sourceTree = ""; }; + 8213143BCC700BC9B5EE28B2AFE47BC9 /* PFKeyValueCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFKeyValueCache.h; path = Parse/Internal/KeyValueCache/PFKeyValueCache.h; sourceTree = ""; }; + 8230FA9936E0CC774177096CCC805BFC /* PFFileManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFileManager.m; path = Parse/Internal/PFFileManager.m; sourceTree = ""; }; + 829C2DF55D47AC9FF3B59122141B8F50 /* PFBase64Encoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFBase64Encoder.m; path = Parse/Internal/PFBase64Encoder.m; sourceTree = ""; }; + 82B9E087EE91277B762C1E55E025EF89 /* PFFileState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileState.h; path = Parse/Internal/File/State/PFFileState.h; sourceTree = ""; }; + 8345935C5AC8AEE28996E8ADAFE07522 /* PFFieldOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFieldOperation.h; path = Parse/Internal/FieldOperation/PFFieldOperation.h; sourceTree = ""; }; + 834F31050B11D55D2C450E902318E944 /* PFACLState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFACLState.m; path = Parse/Internal/ACL/State/PFACLState.m; sourceTree = ""; }; + 83FFF174FF00A3AD178F468EC8D45ADC /* PFRESTObjectBatchCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTObjectBatchCommand.h; path = Parse/Internal/Commands/PFRESTObjectBatchCommand.h; sourceTree = ""; }; + 843DAD1E807B7D28D13FCD55BAA643C9 /* PFHTTPURLRequestConstructor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFHTTPURLRequestConstructor.h; path = Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.h; sourceTree = ""; }; + 845AECFE8DD82849CB3F5B87E2537205 /* PFUserPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserPrivate.h; path = Parse/Internal/User/PFUserPrivate.h; sourceTree = ""; }; + 85F264D88C24402A09C3C9C8B5EF4601 /* AFHTTPRequestOperationManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperationManager.m; path = AFNetworking/AFHTTPRequestOperationManager.m; sourceTree = ""; }; + 86B5449588D7B659F947D7E39E060110 /* PFQueryCollectionViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryCollectionViewController.h; path = ParseUI/Classes/QueryCollectionViewController/PFQueryCollectionViewController.h; sourceTree = ""; }; + 87B213035BAC5F75386F62D3C75D2342 /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; }; + 88BAE879E3D71A97056B63720B3DAD5E /* PFObjectLocalIdStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectLocalIdStore.h; path = Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.h; sourceTree = ""; }; + 89683EF21BC95E77BBF06818132C923E /* CGRectEx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CGRectEx.swift; path = Pod/Classes/CGRectEx.swift; sourceTree = ""; }; + 899E7EFD0AD739A28B9021F4534B5545 /* PFDateFormatter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFDateFormatter.m; path = Parse/Internal/PFDateFormatter.m; sourceTree = ""; }; + 8A0D21B75DD89371A7800E0F07CCDD3C /* PFNetworkCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFNetworkCommand.h; path = Parse/Internal/PFNetworkCommand.h; sourceTree = ""; }; + 8B5C3BFFF0DB6969C2B50A5CD483F812 /* PFFieldOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFieldOperation.m; path = Parse/Internal/FieldOperation/PFFieldOperation.m; sourceTree = ""; }; + 8C7C9C2804D450E4F0A969D264281DBE /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperation.m; path = AFNetworking/AFHTTPRequestOperation.m; sourceTree = ""; }; + 8CB28E2AD26FDC0C0ACEA80136C6E997 /* PFRESTCommand_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTCommand_Private.h; path = Parse/Internal/Commands/PFRESTCommand_Private.h; sourceTree = ""; }; + 8D3272484AF55B5BC7CD1C32A067FA85 /* PFFileController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileController.h; path = Parse/Internal/File/Controller/PFFileController.h; sourceTree = ""; }; + 8D61280C3437A784DBF19D45B384C011 /* PFRESTAnalyticsCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTAnalyticsCommand.m; path = Parse/Internal/Commands/PFRESTAnalyticsCommand.m; sourceTree = ""; }; + 8DA7F4F5534EFC28E047F3039137BB0F /* PFPurchaseTableViewCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPurchaseTableViewCell.h; path = ParseUI/Classes/Cells/PFPurchaseTableViewCell.h; sourceTree = ""; }; + 8DE4F7C454A15E12B39ED6023325FA3A /* PFObjectFileCodingLogic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectFileCodingLogic.h; path = Parse/Internal/Object/Coder/File/PFObjectFileCodingLogic.h; sourceTree = ""; }; + 8E524941C9BC2F24FCC92215E85F2BF6 /* PFMutablePushState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutablePushState.h; path = Parse/Internal/Push/State/PFMutablePushState.h; sourceTree = ""; }; + 8EE9E02E1C1B46F3ACFE5EF292CEDDAC /* PFLogInViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFLogInViewController.m; path = ParseUI/Classes/LogInViewController/PFLogInViewController.m; sourceTree = ""; }; + 8FC25E97FDE9217C1F682376CF331421 /* PFUserConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUserConstants.m; path = Parse/Internal/User/Constants/PFUserConstants.m; sourceTree = ""; }; + 927B2E472B9B4600105524234E02BBEF /* PFImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFImage.h; path = ParseUI/Classes/Internal/Extensions/PFImage.h; sourceTree = ""; }; + 933C4C66F46988536759B7A7AA3AB260 /* PFRect.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRect.h; path = ParseUI/Classes/Internal/Extensions/PFRect.h; sourceTree = ""; }; + 943FE65B8F32DCA748FD4B871663F421 /* ParseManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = ParseManager.m; path = Parse/Internal/ParseManager.m; sourceTree = ""; }; + 95920EE1067D57330EBA26ABC1F8A742 /* PFAnalyticsUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnalyticsUtilities.h; path = Parse/Internal/Analytics/Utilities/PFAnalyticsUtilities.h; sourceTree = ""; }; + 963130EBA1505774B88A6206C7117AD9 /* PFActivityIndicatorCollectionReusableView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFActivityIndicatorCollectionReusableView.h; path = ParseUI/Classes/Internal/Cells/PFActivityIndicatorCollectionReusableView.h; sourceTree = ""; }; + 965A29E050253DCA29D6D53DA6E80B5D /* Parse.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Parse.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 96B84037727346B0A0B3E2F3A9668A1C /* PFObjectController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectController.m; path = Parse/Internal/Object/Controller/PFObjectController.m; sourceTree = ""; }; + 977AB32A80ABE4BF539022F03D0403C6 /* PFAnonymousUtils_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnonymousUtils_Private.h; path = Parse/Internal/User/AuthenticationProviders/Providers/Anonymous/PFAnonymousUtils_Private.h; sourceTree = ""; }; + 979B9D6DD6CC9A7FC7F04469E1E2B5A6 /* PFGeoPointPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFGeoPointPrivate.h; path = Parse/Internal/PFGeoPointPrivate.h; sourceTree = ""; }; + 97FE58DD961885B0B4A058AEAF7A02A8 /* PFCommandRunningConstants.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCommandRunningConstants.m; path = Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.m; sourceTree = ""; }; + 97FF6FABD68A2485CB659846729BFFCD /* LiquidFloatingActionButton.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LiquidFloatingActionButton.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 987AD1FB8A654DC31AE3E6601CCC7A36 /* UIImageView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+AFNetworking.m"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.m"; sourceTree = ""; }; + 989B472DE53DB8DD10EA5CEB92D70912 /* PFRelationPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRelationPrivate.h; path = Parse/Internal/Relation/PFRelationPrivate.h; sourceTree = ""; }; + 98FB41E4640BA6923CDFC45E1C3422C3 /* PFCloud.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCloud.m; path = Parse/PFCloud.m; sourceTree = ""; }; + 991F2AB1EAD16285E8C86C68489117B2 /* PFSQLiteDatabaseController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSQLiteDatabaseController.m; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseController.m; sourceTree = ""; }; + 99DE5A4D7BE3DFF919CE242FB1FD465D /* PFQueryUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQueryUtilities.m; path = Parse/Internal/Query/Utilities/PFQueryUtilities.m; sourceTree = ""; }; + 9A2D779D62119B7029D39A6D67FF2D63 /* PFLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLogger.h; path = Parse/Internal/PFLogger.h; sourceTree = ""; }; + 9A8256CB779D4E0EEFA7452FC60E3F2B /* PFObjectState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectState.h; path = Parse/Internal/Object/State/PFObjectState.h; sourceTree = ""; }; + 9AA042538499B62E2B7C0921FCB5F01B /* PFCommandRunning.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandRunning.h; path = Parse/Internal/Commands/CommandRunner/PFCommandRunning.h; sourceTree = ""; }; + 9AB08773B8895841E9EDE2015D2A335D /* BFTask.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFTask.m; path = Bolts/Common/BFTask.m; sourceTree = ""; }; + 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 9B44D9A7073478A4D49371D61B2DACF5 /* PFPushChannelsController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushChannelsController.h; path = Parse/Internal/Push/ChannelsController/PFPushChannelsController.h; sourceTree = ""; }; + 9B94D92A7B20E46739BDF9FAB81A2828 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9BF22022C7257232D4C9D9938A8AA1F0 /* PFSQLiteStatement.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSQLiteStatement.m; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.m; sourceTree = ""; }; + 9C4A6A6A2B2F9A47F6A8A876DC4E0983 /* PFUserState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserState_Private.h; path = Parse/Internal/User/State/PFUserState_Private.h; sourceTree = ""; }; + 9C9379B624B1D99D2ED8683BF8A27499 /* Parse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Parse.m; path = Parse/Parse.m; sourceTree = ""; }; + 9CFB6412CFFF4076089FE7FFEEEF17D5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9D5D9ACD4B24C679EB5789A8B2300783 /* PFACLState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFACLState_Private.h; path = Parse/Internal/ACL/State/PFACLState_Private.h; sourceTree = ""; }; + 9D6FE3EE8F0D3CAE0224A63EC2E293D7 /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPRequestOperation.h; path = AFNetworking/AFHTTPRequestOperation.h; sourceTree = ""; }; + 9E812FF98BA70450EEE9168727E42BC7 /* PFQuery.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQuery.h; path = Parse/PFQuery.h; sourceTree = ""; }; + 9EE3A5821D30B3DF09BE105CA7B7FEDF /* PFObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObject.h; path = Parse/PFObject.h; sourceTree = ""; }; + 9F429BC5C28A53E99C5E18A4668F5E24 /* PFTextField.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFTextField.m; path = ParseUI/Classes/Views/PFTextField.m; sourceTree = ""; }; + A02502BAA5243EDF918943006C174647 /* LiquidFloatingActionButton.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = LiquidFloatingActionButton.modulemap; sourceTree = ""; }; + A05C4014427DEBE8CDF3F7DF4EDF5B78 /* PFHTTPURLRequestConstructor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFHTTPURLRequestConstructor.m; path = Parse/Internal/HTTPRequest/PFHTTPURLRequestConstructor.m; sourceTree = ""; }; + A0E158D52333FE98E51485773BD463F0 /* BFCancellationToken.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFCancellationToken.m; path = Bolts/Common/BFCancellationToken.m; sourceTree = ""; }; + A18517CA52623E4381D5DC0108AFB9ED /* PFPropertyInfo_Runtime.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPropertyInfo_Runtime.m; path = Parse/Internal/PropertyInfo/PFPropertyInfo_Runtime.m; sourceTree = ""; }; + A1B4B55BB161D999FD5EC2B657E7B6B3 /* LiquidFloatingActionButton-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "LiquidFloatingActionButton-dummy.m"; sourceTree = ""; }; + A20A37119F2FDB5210E5F294E3477B2E /* PFImageView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFImageView.m; path = ParseUI/Classes/Views/PFImageView.m; sourceTree = ""; }; + A353C806ABBCE7480976922EA5EB7B75 /* PFImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFImage.m; path = ParseUI/Classes/Internal/Extensions/PFImage.m; sourceTree = ""; }; + A40A9A6110C6D841C877E13C7886F5B2 /* PFConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFConfig.m; path = Parse/PFConfig.m; sourceTree = ""; }; + A4E0C78BAB222F98F39D7C9118CCA385 /* Parse.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Parse.xcconfig; sourceTree = ""; }; + A5251A90622521ACCBE90C8912D8200F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A5DF1057EDD3E40B699F68523EB26FC1 /* PFMutableQueryState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableQueryState.h; path = Parse/Internal/Query/State/PFMutableQueryState.h; sourceTree = ""; }; + A5E5655A0381C0C739C229727C1F3802 /* PFCollectionViewCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCollectionViewCell.h; path = ParseUI/Classes/Cells/PFCollectionViewCell.h; sourceTree = ""; }; + A72606B3DBDB6147A21BF44AB547E440 /* PFPrimaryButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPrimaryButton.m; path = ParseUI/Classes/Internal/Views/Buttons/PFPrimaryButton.m; sourceTree = ""; }; + A797A1CD69213D018C84F6D976394A27 /* PFBaseState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFBaseState.h; path = Parse/Internal/PFBaseState.h; sourceTree = ""; }; + A83E1CB2B25CB590D1D1E867BA4C0A1D /* PFPinningObjectStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPinningObjectStore.h; path = Parse/Internal/Object/PinningStore/PFPinningObjectStore.h; sourceTree = ""; }; + A93DBE9D5C2687DF9723AAFA66F2A971 /* PFInstallationConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallationConstants.h; path = Parse/Internal/Installation/Constants/PFInstallationConstants.h; sourceTree = ""; }; + A98219265A93C0BC6572D73A5D1EAEB2 /* PFPaymentTransactionObserver.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPaymentTransactionObserver.m; path = Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.m; sourceTree = ""; }; + A99437D514635B420895CF6FB7336143 /* PFTableViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFTableViewCell.m; path = ParseUI/Classes/Cells/PFTableViewCell.m; sourceTree = ""; }; + A9F48668DB7B720FE509D549A92004D3 /* Bolts.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Bolts.h; path = Bolts/Common/Bolts.h; sourceTree = ""; }; + AA573A271BED522E0458A7AE97926873 /* PFKeychainStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFKeychainStore.h; path = Parse/Internal/PFKeychainStore.h; sourceTree = ""; }; + ABF64DFD9F115A0E816F8F5BBDF0DA14 /* PFLogInView_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLogInView_Private.h; path = ParseUI/Classes/LogInViewController/PFLogInView_Private.h; sourceTree = ""; }; + AC649CF0667A08BF5C299B5FCDB01DF7 /* PFOfflineQueryLogic.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFOfflineQueryLogic.m; path = Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.m; sourceTree = ""; }; + AC68AD5CDBD49B1906847DDDEE8433E7 /* LiquidFloatingActionButton.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LiquidFloatingActionButton.xcconfig; sourceTree = ""; }; + ACD672B833F3DE5495704B56CF7BA509 /* PFInstallation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFInstallation.m; path = Parse/PFInstallation.m; sourceTree = ""; }; + ACE08D7134612A5CB853602B49829807 /* LiquidUtil.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LiquidUtil.swift; path = Pod/Classes/LiquidUtil.swift; sourceTree = ""; }; + AD42871D9C935D2C3C640E893BFFC564 /* PFFile_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFile_Private.h; path = Parse/Internal/File/PFFile_Private.h; sourceTree = ""; }; + ADFA0A2825114CECD32242EFA5607ED7 /* PFCoreDataProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCoreDataProvider.h; path = Parse/Internal/PFCoreDataProvider.h; sourceTree = ""; }; + AE22C9A9D5BEC3BC938693871AA98EE6 /* PFCommandURLRequestConstructor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCommandURLRequestConstructor.m; path = Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.m; sourceTree = ""; }; + AE4FD7D79AD079CDF0E0992224F6277C /* PFNetworkActivityIndicatorManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFNetworkActivityIndicatorManager.m; path = Parse/PFNetworkActivityIndicatorManager.m; sourceTree = ""; }; + AF3115BC0E1F291BCDAA52C97E90DA21 /* UIAlertView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIAlertView+AFNetworking.m"; path = "UIKit+AFNetworking/UIAlertView+AFNetworking.m"; sourceTree = ""; }; + AF6140D6AA1728AD006CD6EAEA3B3CC0 /* PFCurrentInstallationController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCurrentInstallationController.h; path = Parse/Internal/Installation/CurrentInstallationController/PFCurrentInstallationController.h; sourceTree = ""; }; + AFA1140609306B1C8C88E61FE5186100 /* AFSecurityPolicy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AFSecurityPolicy.m; path = AFNetworking/AFSecurityPolicy.m; sourceTree = ""; }; + B13872C024A5C2B50AA18722FE15CDFA /* PFTextField.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFTextField.h; path = ParseUI/Classes/Views/PFTextField.h; sourceTree = ""; }; + B2535EAEF95F25D2E77544BEE32588E9 /* AFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFNetworkActivityIndicatorManager.h; path = "UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h"; sourceTree = ""; }; + B31B277478C40801EDACD6F671D9F9D3 /* PFMultiProcessFileLock.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMultiProcessFileLock.m; path = Parse/Internal/MultiProcessLock/PFMultiProcessFileLock.m; sourceTree = ""; }; + B402CED15BEC363BBB72FE42CFFA19B7 /* PFObjectUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectUtilities.m; path = Parse/Internal/Object/Utilities/PFObjectUtilities.m; sourceTree = ""; }; + B439486D6310E570C25D882BF6B831F1 /* PFPushChannelsController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPushChannelsController.m; path = Parse/Internal/Push/ChannelsController/PFPushChannelsController.m; sourceTree = ""; }; + B4ABE5C41AF16EA4023696A3604580C9 /* PFUser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUser.m; path = Parse/PFUser.m; sourceTree = ""; }; + B640687713469EFE42C0475F6192A31F /* PFMutableACLState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableACLState.m; path = Parse/Internal/ACL/State/PFMutableACLState.m; sourceTree = ""; }; + B6A18CBBDB72D9222355CD85E1E5AC96 /* LiquidFloatingActionButton.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LiquidFloatingActionButton.swift; path = Pod/Classes/LiquidFloatingActionButton.swift; sourceTree = ""; }; + B7F4C394668FB6C72DEAC18029145B8D /* LiquittableCircle.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LiquittableCircle.swift; path = Pod/Classes/LiquittableCircle.swift; sourceTree = ""; }; + B82573787A30C373316857E73B756945 /* ParseUI-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "ParseUI-Private.xcconfig"; sourceTree = ""; }; + B89903554FFA78AADE8F525DDC0C7959 /* PFMutableRelationState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableRelationState.h; path = Parse/Internal/Relation/State/PFMutableRelationState.h; sourceTree = ""; }; + B89BD9572DDE9528AB675A588A47A609 /* BFDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFDefines.h; path = Bolts/Common/BFDefines.h; sourceTree = ""; }; + B89E57173FF8D9E3745D41F14BB2E463 /* PFOfflineQueryController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFOfflineQueryController.h; path = Parse/Internal/Query/Controller/PFOfflineQueryController.h; sourceTree = ""; }; + B8E1E38F2E91125CD57103EEBC3C475C /* PFColor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFColor.h; path = ParseUI/Classes/Internal/Extensions/PFColor.h; sourceTree = ""; }; + B94C7AA42C195AC77E28C347AD8BFF46 /* BFExecutor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = BFExecutor.h; path = Bolts/Common/BFExecutor.h; sourceTree = ""; }; + B9EDA77BEADC909F4B7793C3C24DB96F /* PFUIAlertView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUIAlertView.h; path = ParseUI/Classes/Internal/Extensions/PFUIAlertView.h; sourceTree = ""; }; + BA2B0874AAFA2F48338A7586EA1D3BF9 /* LiquidFloatingActionButton-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LiquidFloatingActionButton-umbrella.h"; sourceTree = ""; }; + BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + BB7AF609EF0BFAD9C6EC6123F16D7E2F /* ParseUI.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ParseUI.h; path = ParseUI/Other/ParseUI.h; sourceTree = ""; }; + BB7BF9759D7B78AB520BE410BA97011F /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.release.xcconfig; sourceTree = ""; }; + BBD4D42F15B404983137C87294C33298 /* Parse_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Parse_Private.h; path = Parse/Internal/Parse_Private.h; sourceTree = ""; }; + BC13566BBA4A6E3F586E6A880A2689DD /* PFOfflineObjectController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFOfflineObjectController.m; path = Parse/Internal/Object/Controller/OfflineController/PFOfflineObjectController.m; sourceTree = ""; }; + BC28C2D9801402D3385F95F86BD06CB6 /* PFCommandCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandCache.h; path = Parse/Internal/PFCommandCache.h; sourceTree = ""; }; + BC983AFF27F041F598C25C2CD37CFDCB /* PFKeychainStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFKeychainStore.m; path = Parse/Internal/PFKeychainStore.m; sourceTree = ""; }; + BCFF6319D2EF9091302E5B96F4E0B43B /* PFObjectConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectConstants.h; path = Parse/Internal/Object/Constants/PFObjectConstants.h; sourceTree = ""; }; + BD39E8AC572A0A0356F1EFCBE146731F /* PFSessionUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSessionUtilities.m; path = Parse/Internal/Session/Utilities/PFSessionUtilities.m; sourceTree = ""; }; + BE60DEF5EAA342275F7F61958BEEC1A2 /* PFRESTQueryCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTQueryCommand.h; path = Parse/Internal/Commands/PFRESTQueryCommand.h; sourceTree = ""; }; + BE9223229E8325CB5F21B8ADDA6B9960 /* Bolts-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Bolts-Private.xcconfig"; sourceTree = ""; }; + BF69C85C516E15E59322803B5CB90959 /* PFSession_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSession_Private.h; path = Parse/Internal/Session/PFSession_Private.h; sourceTree = ""; }; + BFF7C6B60180E4A5C942887184602AF5 /* PFLogInView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLogInView.h; path = ParseUI/Classes/LogInViewController/PFLogInView.h; sourceTree = ""; }; + C0E17697C9CAFCD60CAA1AE570630945 /* PFFileManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFFileManager.h; path = Parse/Internal/PFFileManager.h; sourceTree = ""; }; + C0F49D28B3C52C5467AE62585139EE0A /* UIWebView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIWebView+AFNetworking.m"; path = "UIKit+AFNetworking/UIWebView+AFNetworking.m"; sourceTree = ""; }; + C1BE4F5D33EE98831A53F0E87D0343B4 /* PFMutableUserState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableUserState.h; path = Parse/Internal/User/State/PFMutableUserState.h; sourceTree = ""; }; + C20CDE358825835E6B40286F5163A863 /* CGPointEx.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CGPointEx.swift; path = Pod/Classes/CGPointEx.swift; sourceTree = ""; }; + C28C9D46AE0FE690A649A1BE46A2BBCD /* PFObject+Subclass.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PFObject+Subclass.h"; path = "Parse/PFObject+Subclass.h"; sourceTree = ""; }; + C3BB66694B1A1DAD9A0455379A1FE921 /* SimpleCircleLiquidEngine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SimpleCircleLiquidEngine.swift; path = Pod/Classes/SimpleCircleLiquidEngine.swift; sourceTree = ""; }; + C449090D4B4F78564B631A75077787A9 /* PFObjectController_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectController_Private.h; path = Parse/Internal/Object/Controller/PFObjectController_Private.h; sourceTree = ""; }; + C478D21353BA164DF20A78C96701C338 /* PFCategoryLoader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCategoryLoader.h; path = Parse/Internal/PFCategoryLoader.h; sourceTree = ""; }; + C57AA8424009C8745B20CAC511A50222 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.debug.xcconfig; sourceTree = ""; }; + C5934E9381ECE8A0A01CD996573C5EE1 /* Bolts.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Bolts.modulemap; sourceTree = ""; }; + C68DEF4C4561831CECF5DAE37F4E1926 /* Parse.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Parse.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C743D5BE4BE093B67F21D64BDAA0D64F /* PFURLSessionCommandRunner.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSessionCommandRunner.m; path = Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.m; sourceTree = ""; }; + C7F5B41EB0986C8391083C4127B553F0 /* PFUserFileCodingLogic.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFUserFileCodingLogic.m; path = Parse/Internal/User/Coder/File/PFUserFileCodingLogic.m; sourceTree = ""; }; + C8DF725A4DEDC5E894E3F0DECBD6E2D3 /* LiquidFloatingActionButton-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LiquidFloatingActionButton-prefix.pch"; sourceTree = ""; }; + C99492480C58DAAD671B694D06B2A984 /* PFAssert.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAssert.h; path = Parse/Internal/PFAssert.h; sourceTree = ""; }; + C99AD2F0824E2247A161578BD8373909 /* PFInstallationIdentifierStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFInstallationIdentifierStore.m; path = Parse/Internal/Installation/InstallationIdentifierStore/PFInstallationIdentifierStore.m; sourceTree = ""; }; + CA1F50181C77DE8DD24C638D484FAB4E /* PFQueryController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQueryController.m; path = Parse/Internal/Query/Controller/PFQueryController.m; sourceTree = ""; }; + CA79DC9A677C52A5CB0149D3711EDC4C /* PFDateFormatter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDateFormatter.h; path = Parse/Internal/PFDateFormatter.h; sourceTree = ""; }; + CB76E8D570FAE396D5315056A3E7E24E /* PFObjectLocalIdStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectLocalIdStore.m; path = Parse/Internal/Object/LocalIdStore/PFObjectLocalIdStore.m; sourceTree = ""; }; + CBC0F7C552B739C909B650A0F42F7F38 /* Pods-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-resources.sh"; sourceTree = ""; }; + CCDFF9320F22A33DA2D0EB4C88FA9A95 /* PFAnalytics.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAnalytics.m; path = Parse/PFAnalytics.m; sourceTree = ""; }; + CE7E387CE6912A38238CECCC234B822D /* ParseManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ParseManager.h; path = Parse/Internal/ParseManager.h; sourceTree = ""; }; + CFB015761F43EBA9801A2489A67F1918 /* PFSQLiteDatabase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSQLiteDatabase.h; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabase.h; sourceTree = ""; }; + CFD2BCA0FDC1E5923DDC3256A7F82B48 /* PFDevice.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFDevice.m; path = Parse/Internal/PFDevice.m; sourceTree = ""; }; + CFF05FDAA5BE92C3290288A41691E4EB /* PFURLSession.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSession.m; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession.m; sourceTree = ""; }; + D031DB68EC76D7496F1750449C536A4E /* PFEventuallyQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFEventuallyQueue.h; path = Parse/Internal/PFEventuallyQueue.h; sourceTree = ""; }; + D0359022BC718069CC2473CA068BFCD7 /* PFPinningEventuallyQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPinningEventuallyQueue.m; path = Parse/Internal/PFPinningEventuallyQueue.m; sourceTree = ""; }; + D0405803033A2A777B8E4DFA0C1800ED /* Pods-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-acknowledgements.markdown"; sourceTree = ""; }; + D1DFBF1DE14726ABC1691D616B077B9B /* PFCommandResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandResult.h; path = Parse/Internal/PFCommandResult.h; sourceTree = ""; }; + D2FCDC0741A4571CCA8D7C1E99F48105 /* PFCollectionViewCell.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCollectionViewCell.m; path = ParseUI/Classes/Cells/PFCollectionViewCell.m; sourceTree = ""; }; + D319F69C8F009DDC8CF96B18FEC4583E /* PFMultiProcessFileLockController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMultiProcessFileLockController.m; path = Parse/Internal/MultiProcessLock/PFMultiProcessFileLockController.m; sourceTree = ""; }; + D3339AE7574EA468A11239825C11ECC5 /* PFInstallation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFInstallation.h; path = Parse/PFInstallation.h; sourceTree = ""; }; + D3592860B73625779473717C79E14C33 /* PFDevice.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFDevice.h; path = Parse/Internal/PFDevice.h; sourceTree = ""; }; + D4DA6F666BF906399EDA329897330DE0 /* PFEventuallyPin.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFEventuallyPin.h; path = Parse/Internal/PFEventuallyPin.h; sourceTree = ""; }; + D53047A416F23A9006EDD19E7BDFBF65 /* PFSQLiteStatement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSQLiteStatement.h; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteStatement.h; sourceTree = ""; }; + D585DF26C444D0FDF2346A1B69A9342C /* PFResources.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFResources.h; path = ParseUI/Generated/PFResources.h; sourceTree = ""; }; + D58C74135B703B4BA78ACFDE7B10DF17 /* PFBase64Encoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFBase64Encoder.h; path = Parse/Internal/PFBase64Encoder.h; sourceTree = ""; }; + D5F164F971C806E166D9BD66BA1C0ED5 /* PFRole.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRole.m; path = Parse/PFRole.m; sourceTree = ""; }; + D6133D18D0CBDFE7A51DC2ABD2DA8956 /* PFObjectFileCoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectFileCoder.h; path = Parse/Internal/Object/Coder/File/PFObjectFileCoder.h; sourceTree = ""; }; + D6B24FD6C8A63E8AC6B7A647B38F005D /* PFReachability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFReachability.h; path = Parse/Internal/PFReachability.h; sourceTree = ""; }; + D6CA9C05834B083659D1A1C33EB15AD0 /* PFLoadingView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLoadingView.h; path = ParseUI/Classes/Internal/Views/PFLoadingView.h; sourceTree = ""; }; + D6F5D08B400E69B890800EB5B5DCCB3F /* PFTaskQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFTaskQueue.h; path = Parse/Internal/PFTaskQueue.h; sourceTree = ""; }; + D879C61F873DA897D8CDCFD529DD718F /* PFLocationManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLocationManager.h; path = Parse/Internal/PFLocationManager.h; sourceTree = ""; }; + D8EE7A749F0FA95FFDA3601E6582B943 /* PFObjectState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectState.m; path = Parse/Internal/Object/State/PFObjectState.m; sourceTree = ""; }; + D94237B26D6FD987E657CCBB1B2C868B /* PFKeyValueCache_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFKeyValueCache_Private.h; path = Parse/Internal/KeyValueCache/PFKeyValueCache_Private.h; sourceTree = ""; }; + D994A541599DACAF7137D8879F25C89E /* PFCategoryLoader.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCategoryLoader.m; path = Parse/Internal/PFCategoryLoader.m; sourceTree = ""; }; + D9AF3C2322515C270D4C304A1D84789F /* PFCloud.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCloud.h; path = Parse/PFCloud.h; sourceTree = ""; }; + D9DB202029F6BEE0B5B41F8145AFAC0F /* ParseUI-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "ParseUI-prefix.pch"; sourceTree = ""; }; + DACDC24D9356617A721A05C9B6ACBC2C /* PFProductTableViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFProductTableViewController.h; path = ParseUI/Classes/ProductTableViewController/PFProductTableViewController.h; sourceTree = ""; }; + DAD4EC7CD5391A7B25687EEDAD0E98E0 /* PFOfflineQueryLogic.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFOfflineQueryLogic.h; path = Parse/Internal/LocalDataStore/OfflineQueryLogic/PFOfflineQueryLogic.h; sourceTree = ""; }; + DAE85F8359D8DE8DD20C174352319881 /* BFCancellationTokenRegistration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFCancellationTokenRegistration.m; path = Bolts/Common/BFCancellationTokenRegistration.m; sourceTree = ""; }; + DB79A3ED3B802D7B73925524C32EEDE7 /* ParseInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ParseInternal.h; path = Parse/Internal/ParseInternal.h; sourceTree = ""; }; + DBF37D953B714180930569746452F6E5 /* PFURLSessionUploadTaskDelegate.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLSessionUploadTaskDelegate.m; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.m; sourceTree = ""; }; + DC6D23DEBF98F865D988524F98884065 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + DC7294772B9396EF14228A8BD24860DE /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/AudioToolbox.framework; sourceTree = DEVELOPER_DIR; }; + DCA33D6FD4CA15B4EDB933F4A4E75327 /* PFSessionController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSessionController.h; path = Parse/Internal/Session/Controller/PFSessionController.h; sourceTree = ""; }; + DCA5DAA6B6F95021F88E0374D52599C0 /* UIImageView+AFNetworking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImageView+AFNetworking.h"; path = "UIKit+AFNetworking/UIImageView+AFNetworking.h"; sourceTree = ""; }; + DCC0772B25ED51809E78EE58DC1C44C0 /* PFTaskQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFTaskQueue.m; path = Parse/Internal/PFTaskQueue.m; sourceTree = ""; }; + DCCF3542E63589C3EDE6E22471B9C2C7 /* PFMutableRelationState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableRelationState.m; path = Parse/Internal/Relation/State/PFMutableRelationState.m; sourceTree = ""; }; + DD1F25F1B4E921548F381AD4C410B41C /* PFURLSession_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSession_Private.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/PFURLSession_Private.h; sourceTree = ""; }; + DD78A6DA4B3E5F2479E701FA15D4D424 /* PFLogging.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLogging.h; path = Parse/Internal/PFLogging.h; sourceTree = ""; }; + DDE94D96A9C62A91D595D2E433F21F9A /* PFPurchase.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPurchase.h; path = Parse/PFPurchase.h; sourceTree = ""; }; + DE8A3CFFE733DBF362B551E0B80C6865 /* PFQueryState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQueryState.m; path = Parse/Internal/Query/State/PFQueryState.m; sourceTree = ""; }; + DE96842CB6DC229E92D71EEEFC92335C /* Bolts.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Bolts.xcconfig; sourceTree = ""; }; + DE9856B6BF2ECFD60B284F10DDC277BE /* PFObjectBatchController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFObjectBatchController.m; path = Parse/Internal/Object/BatchController/PFObjectBatchController.m; sourceTree = ""; }; + DE992F9DCB99338348525B7D3C38802C /* AFNetworking.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = AFNetworking.modulemap; sourceTree = ""; }; + DEF997AF17A743017CD54A4193CC9AE6 /* PFCommandRunning.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCommandRunning.m; path = Parse/Internal/Commands/CommandRunner/PFCommandRunning.m; sourceTree = ""; }; + DFAB2F23DBAAB01BDD3098996526D1A3 /* PFProduct.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFProduct.h; path = Parse/PFProduct.h; sourceTree = ""; }; + DFC1DA889CE1311BE00B9D07030E2D91 /* PFCurrentUserController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCurrentUserController.h; path = Parse/Internal/User/CurrentUserController/PFCurrentUserController.h; sourceTree = ""; }; + E059144E243A519EF7E69EE1C229A7E6 /* PFRESTConfigCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTConfigCommand.m; path = Parse/Internal/Commands/PFRESTConfigCommand.m; sourceTree = ""; }; + E075ED2B6706ECB8A6B28B81F9FD35C7 /* PFProductsRequestHandler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFProductsRequestHandler.h; path = Parse/Internal/Product/ProductsRequestHandler/PFProductsRequestHandler.h; sourceTree = ""; }; + E14260EF83EAB3EE8F0AE4FC52092A8B /* ParseUI.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = ParseUI.modulemap; sourceTree = ""; }; + E16CCC9C4042040D34C25745F5E93979 /* PFApplication.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFApplication.m; path = Parse/Internal/PFApplication.m; sourceTree = ""; }; + E218DEB073F057E49C9302AEB766059F /* PFDismissButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFDismissButton.m; path = ParseUI/Classes/Internal/Views/Buttons/PFDismissButton.m; sourceTree = ""; }; + E2E8D5C9D085E8016D489CA6F418A909 /* PFLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFLogger.m; path = Parse/Internal/PFLogger.m; sourceTree = ""; }; + E2F646392C325F581AA7EEEB52C037E0 /* Bolts-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Bolts-dummy.m"; sourceTree = ""; }; + E32CAB189409C70ED348320F66BA97D3 /* PFRole.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRole.h; path = Parse/PFRole.h; sourceTree = ""; }; + E3AEAC049A5741DEDE9E5C91592038C1 /* PFCoreManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCoreManager.h; path = Parse/Internal/PFCoreManager.h; sourceTree = ""; }; + E3B8C005EBA4D0C4EEDEA5777C45D260 /* PFSQLiteDatabaseResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSQLiteDatabaseResult.h; path = Parse/Internal/LocalDataStore/SQLite/PFSQLiteDatabaseResult.h; sourceTree = ""; }; + E3F30F4E54BFE18519B6553114DA944B /* PFQueryTableViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryTableViewController.h; path = ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.h; sourceTree = ""; }; + E47B281C898AA47BCBDC4FD0172D821E /* PFInternalUtils.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFInternalUtils.m; path = Parse/Internal/PFInternalUtils.m; sourceTree = ""; }; + E4B5BD965A4C5DAA801C33535E0BB379 /* PFLocalization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFLocalization.h; path = ParseUI/Classes/Internal/PFLocalization.h; sourceTree = ""; }; + E4F396EB5B518CB78205BD9A35D95FBC /* PFPinningObjectStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPinningObjectStore.m; path = Parse/Internal/Object/PinningStore/PFPinningObjectStore.m; sourceTree = ""; }; + E54CDA1D27EA0FCD1AB663DD1BCC22DE /* PFURLConstructor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFURLConstructor.m; path = Parse/Internal/HTTPRequest/PFURLConstructor.m; sourceTree = ""; }; + E5B0E2B0324CDAD8CD56AC4EBDE6529E /* Parse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Parse.h; path = Parse/Parse.h; sourceTree = ""; }; + E5B4B025F17BAA1B3A677D658784243F /* PFObjectControlling.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectControlling.h; path = Parse/Internal/Object/Controller/PFObjectControlling.h; sourceTree = ""; }; + E606965BE9A1625B19ACD6701319EDAC /* PFRESTConfigCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTConfigCommand.h; path = Parse/Internal/Commands/PFRESTConfigCommand.h; sourceTree = ""; }; + E64CE28EB8ABC8BC399D729811CABF32 /* PFMutableObjectState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFMutableObjectState.m; path = Parse/Internal/Object/State/PFMutableObjectState.m; sourceTree = ""; }; + E6973DA797DB68C919C66D4BA3D695B9 /* PFCurrentConfigController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCurrentConfigController.h; path = Parse/Internal/Config/Controller/PFCurrentConfigController.h; sourceTree = ""; }; + E712CF96CBB0489B4C7A04F5147BB32D /* PFCommandRunningConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandRunningConstants.h; path = Parse/Internal/Commands/CommandRunner/PFCommandRunningConstants.h; sourceTree = ""; }; + E7653C557D29E7F48FC2C3DE41CC735C /* AFNetworking-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "AFNetworking-dummy.m"; sourceTree = ""; }; + E7AC9992862CA136E8E65BF8DF8F3D15 /* PFNetworkActivityIndicatorManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFNetworkActivityIndicatorManager.h; path = Parse/PFNetworkActivityIndicatorManager.h; sourceTree = ""; }; + E7F21354943D9F42A70697D5A5EF72E9 /* Pods-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-frameworks.sh"; sourceTree = ""; }; + E8446514FBAD26C0E18F24A5715AEF67 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E8621BD61A365200D38234EE73EF24CA /* PFPaymentTransactionObserver.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPaymentTransactionObserver.h; path = Parse/Internal/Purchase/PaymentTransactionObserver/PFPaymentTransactionObserver.h; sourceTree = ""; }; + E89BF5589887D6023691A7740CFBC374 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + E92D2ACC1EB93D285C31C15C7960FB4F /* PFConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFConstants.h; path = Parse/PFConstants.h; sourceTree = ""; }; + E998520B0AE9F07988C912A4F83B6BCB /* PFEventuallyPin.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFEventuallyPin.m; path = Parse/Internal/PFEventuallyPin.m; sourceTree = ""; }; + E9B8580777097F914F9981794BB8DE24 /* PFGeoPoint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFGeoPoint.h; path = Parse/PFGeoPoint.h; sourceTree = ""; }; + E9F581D64C0B691E6A0B6B3A83240F45 /* PFRESTCloudCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTCloudCommand.h; path = Parse/Internal/Commands/PFRESTCloudCommand.h; sourceTree = ""; }; + E9FDC84201D32A4325B4BF25B7DE58DC /* PFCommandCache_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCommandCache_Private.h; path = Parse/Internal/PFCommandCache_Private.h; sourceTree = ""; }; + EAFD4107509F91789F51A3194CC54229 /* AFSecurityPolicy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFSecurityPolicy.h; path = AFNetworking/AFSecurityPolicy.h; sourceTree = ""; }; + EB4D83C8AA70A082952CB547050AE43B /* PFJSONSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFJSONSerialization.h; path = Parse/Internal/PFJSONSerialization.h; sourceTree = ""; }; + EC1C24F1EAE7BA1CB22FA6349CE5E07F /* PFEventuallyQueue_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFEventuallyQueue_Private.h; path = Parse/Internal/PFEventuallyQueue_Private.h; sourceTree = ""; }; + ECE0AC637C3C70874A9C7E29EB8E9F69 /* PFObjectUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectUtilities.h; path = Parse/Internal/Object/Utilities/PFObjectUtilities.h; sourceTree = ""; }; + ED37C1A5862B3AE73D6077D7795B9474 /* Pods-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dummy.m"; sourceTree = ""; }; + EE6D7A403F638983E94CC48F7369D44D /* PFDecoder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFDecoder.m; path = Parse/Internal/PFDecoder.m; sourceTree = ""; }; + EF10A3E0E41722320B57E8C181D31679 /* PFSession.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSession.h; path = Parse/PFSession.h; sourceTree = ""; }; + EF18CF4976108C743495C278626BE89F /* PFRESTSessionCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTSessionCommand.h; path = Parse/Internal/Commands/PFRESTSessionCommand.h; sourceTree = ""; }; + EF2D613D36A3FA0A0A242D221AD828A0 /* PFCloudCodeController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFCloudCodeController.h; path = Parse/Internal/CloudCode/PFCloudCodeController.h; sourceTree = ""; }; + EF44078D9B90D1CAE0849F7FAE2927AC /* PFJSONSerialization.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFJSONSerialization.m; path = Parse/Internal/PFJSONSerialization.m; sourceTree = ""; }; + EF93FA2C0175C88F708912B000641CC8 /* PFSignUpView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFSignUpView.h; path = ParseUI/Classes/SignUpViewController/PFSignUpView.h; sourceTree = ""; }; + EFD4CCE36EFE93ACCBCC2D407FF6D4FB /* PFOfflineStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFOfflineStore.m; path = Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m; sourceTree = ""; }; + F2DC5E77E5C9D2DECE225B993BC0DBAA /* PFPushPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFPushPrivate.h; path = Parse/Internal/Push/PFPushPrivate.h; sourceTree = ""; }; + F337DF8EB662B6A49FFEBC966CF745B6 /* PFFileState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFFileState.m; path = Parse/Internal/File/State/PFFileState.m; sourceTree = ""; }; + F578D2BE836F7E5FCD8B4F070D2CA62B /* PFQueryTableViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFQueryTableViewController.m; path = ParseUI/Classes/QueryTableViewController/PFQueryTableViewController.m; sourceTree = ""; }; + F585A21BC1DD8875804B22148772F089 /* PFRESTUserCommand.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFRESTUserCommand.m; path = Parse/Internal/Commands/PFRESTUserCommand.m; sourceTree = ""; }; + F6491E92ACE75905C0A36BBBAC1F3070 /* PFEncoder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFEncoder.h; path = Parse/Internal/PFEncoder.h; sourceTree = ""; }; + F64FC9AA07C23B6390B31487EEECC02F /* PFAnalyticsController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFAnalyticsController.h; path = Parse/Internal/Analytics/Controller/PFAnalyticsController.h; sourceTree = ""; }; + F6546A78D7DDEF82B4CB1A6BAFE39FD6 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; + F676ADEBE61FEA6C5EE24C12595B59B4 /* PFUserState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFUserState.h; path = Parse/Internal/User/State/PFUserState.h; sourceTree = ""; }; + F71E915821A8435DD4437B712A573BD8 /* Bolts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Bolts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F73E652B07D06D8EE514D15810A9F8F1 /* PFAlertView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFAlertView.m; path = Parse/Internal/PFAlertView.m; sourceTree = ""; }; + F7404597F10DBE7FD0CFA519AF9692E9 /* PFQueryState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFQueryState_Private.h; path = Parse/Internal/Query/State/PFQueryState_Private.h; sourceTree = ""; }; + F7B2DE64FB97BC59338A0F063F7286B3 /* PFURLSessionUploadTaskDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionUploadTaskDelegate.h; path = Parse/Internal/Commands/CommandRunner/URLSession/Session/TaskDelegate/PFURLSessionUploadTaskDelegate.h; sourceTree = ""; }; + F7CBF95140D5E75739637EC8272D9488 /* PFSignUpView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFSignUpView.m; path = ParseUI/Classes/SignUpViewController/PFSignUpView.m; sourceTree = ""; }; + F87A7541A9593AC496503BD08923E52D /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFURLResponseSerialization.h; path = AFNetworking/AFURLResponseSerialization.h; sourceTree = ""; }; + F908F8F01022F84F47E832CD33F366C0 /* PFProduct.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFProduct.m; path = Parse/PFProduct.m; sourceTree = ""; }; + F9FDF2BF02C33F3194A8064A2D1268D4 /* PFRelationState_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRelationState_Private.h; path = Parse/Internal/Relation/State/PFRelationState_Private.h; sourceTree = ""; }; + FA656C1B60A3756129523AF9A74A803C /* PFObjectBatchController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFObjectBatchController.h; path = Parse/Internal/Object/BatchController/PFObjectBatchController.h; sourceTree = ""; }; + FA7ED9D15FFE34ED094DC7FC1163FB2B /* PFRelationState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRelationState.h; path = Parse/Internal/Relation/State/PFRelationState.h; sourceTree = ""; }; + FAB4A34092FC1C5298CF33B7F20A6E09 /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AFHTTPSessionManager.h; path = AFNetworking/AFHTTPSessionManager.h; sourceTree = ""; }; + FB3E17BA104D20642FE22B05ED16F84B /* PFRESTAnalyticsCommand.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFRESTAnalyticsCommand.h; path = Parse/Internal/Commands/PFRESTAnalyticsCommand.h; sourceTree = ""; }; + FB9F130216053B89EE75FBACC3BB4F65 /* PFCurrentUserController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFCurrentUserController.m; path = Parse/Internal/User/CurrentUserController/PFCurrentUserController.m; sourceTree = ""; }; + FC30909C191418455F13A0AAC1DC948B /* GoogleMaps.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleMaps.framework; path = Frameworks/GoogleMaps.framework; sourceTree = ""; }; + FC41D49301C5C346F942300A428EFE59 /* Pods-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-umbrella.h"; sourceTree = ""; }; + FC6948D841E1FEED5688E9D076FFB17A /* PFConfig_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFConfig_Private.h; path = Parse/Internal/Config/PFConfig_Private.h; sourceTree = ""; }; + FC71FB7E0EB9ACA6F1DACD4E1D5781CB /* PFMutableACLState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFMutableACLState.h; path = Parse/Internal/ACL/State/PFMutableACLState.h; sourceTree = ""; }; + FCC2C950B794131718A14019810BC78F /* PFURLSessionCommandRunner_Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFURLSessionCommandRunner_Private.h; path = Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner_Private.h; sourceTree = ""; }; + FD88241CAE6DA71B4D70C103FE15ADC4 /* PFPurchaseController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFPurchaseController.m; path = Parse/Internal/Purchase/Controller/PFPurchaseController.m; sourceTree = ""; }; + FE82138DBE52528B5F9039EC4FA92C17 /* BFTaskCompletionSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = BFTaskCompletionSource.m; path = Bolts/Common/BFTaskCompletionSource.m; sourceTree = ""; }; + FED57DD7F261567A1E9174BAAF263E24 /* UIActivityIndicatorView+AFNetworking.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIActivityIndicatorView+AFNetworking.m"; path = "UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m"; sourceTree = ""; }; + FF22D95936873FA51D8C9C983150C21D /* PFOfflineStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PFOfflineStore.h; path = Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.h; sourceTree = ""; }; + FF500FE268A2149DF765B8D6732382E1 /* PFReachability.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PFReachability.m; path = Parse/Internal/PFReachability.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 251E76C54507A3FC6E71B84287C06A33 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58372966A22D83669EE89B43EAF422A0 /* AudioToolbox.framework in Frameworks */, + C98CCE7712239EC7D6839E8429E0B71B /* Bolts.framework in Frameworks */, + E98B7DBA71E8087DA1B1F52B997BDA65 /* CFNetwork.framework in Frameworks */, + F82E6E99470BFB4E5FF15E4527F264EC /* CoreGraphics.framework in Frameworks */, + C61C66EAB8F7563FA9C1D4434A3F84A1 /* CoreLocation.framework in Frameworks */, + 286FA64725A5209CD48FD20012FE644B /* Foundation.framework in Frameworks */, + 19E00AEFB9F41BAD813091D05FDDCEB3 /* QuartzCore.framework in Frameworks */, + 36CF851FB6C6221F6B8723C9DC1E7DD0 /* Security.framework in Frameworks */, + A8AEC3C670B3871B50AAE40A8DA9A92C /* StoreKit.framework in Frameworks */, + 08BAEB2246F51B5EE8BA16B804A2F259 /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 95715F41470114BD35638B7BF4831BAD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C556BF04447873BB9E2DBF3DBD92B7B3 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 95B3CF59FD4D70C6ECFB2F6895408321 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1030F02D79D67B45E17D2C391621786E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 96927D9D82F9851F96869FCE8D3EE83E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 49A9F48CFB2D201E0936340A1FAFB650 /* CoreGraphics.framework in Frameworks */, + D1BAAA3B9C6887F2F23752769C3020F5 /* Foundation.framework in Frameworks */, + 3BFC9EA740843C3CDB40CEFD6BA5BF2B /* MobileCoreServices.framework in Frameworks */, + D24CFDD40A5643EAA6AFE24FC83CEDE8 /* Security.framework in Frameworks */, + 275ECE8ED462559F632E86559F49E059 /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9DA283AA60EEB82C8011330FCCC35810 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B4BB7E489E1FD1F9A28773CA2441625F /* Bolts.framework in Frameworks */, + 3C4F032BE9628EF88C5306F606BCB654 /* CoreGraphics.framework in Frameworks */, + 18BFDBEFA619B361899E126DAC28B345 /* Foundation.framework in Frameworks */, + AB0C8BC2C160D2D3E1B926D610DB61BE /* Parse.framework in Frameworks */, + 628FF832AA99DC2D599293C0B435650C /* QuartzCore.framework in Frameworks */, + C0A53FB9AAB55C2215AE28B7552DE060 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B2539DD3C9FA15A3D99A1D697609E77F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFF56B83A2E55DF25DC40DFC6434BB0C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F85CDCC5343143807EF71AE225920BA7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 036203CC7A8F579CF8A788446B371472 /* Tasks */ = { + isa = PBXGroup; + children = ( + 5B22145FF686BB047CD997E62D0DEF43 /* BFCancellationToken.h */, + A0E158D52333FE98E51485773BD463F0 /* BFCancellationToken.m */, + 489F324A39862A52246BA56B81766653 /* BFCancellationTokenRegistration.h */, + DAE85F8359D8DE8DD20C174352319881 /* BFCancellationTokenRegistration.m */, + 6D29EC69D4226E7C3D3D7EEAFC9E96F3 /* BFCancellationTokenSource.h */, + 78FD692B8BFB4BE7D09FDBBA605AE451 /* BFCancellationTokenSource.m */, + B89BD9572DDE9528AB675A588A47A609 /* BFDefines.h */, + B94C7AA42C195AC77E28C347AD8BFF46 /* BFExecutor.h */, + 055D7C6E9227DB20416C800E4F51A337 /* BFExecutor.m */, + 1F8CB4C16F79838F1FBAC6D1F4FADE30 /* BFTask.h */, + 9AB08773B8895841E9EDE2015D2A335D /* BFTask.m */, + 35F772D3D7B1BC27FD87ABE0059732FF /* BFTaskCompletionSource.h */, + FE82138DBE52528B5F9039EC4FA92C17 /* BFTaskCompletionSource.m */, + A9F48668DB7B720FE509D549A92004D3 /* Bolts.h */, + 25DA2A63A41FDC2B72632E0F65A7250C /* Bolts.m */, + 24ED10A4EE83A682837CCD2514C1FE1E /* BoltsVersion.h */, + ); + name = Tasks; + sourceTree = ""; + }; + 08C00C6AB927C05DB810336C2BF38EA1 /* Reachability */ = { + isa = PBXGroup; + children = ( + 08BD2B6BFAC23DEC69F1D3FCA0B38071 /* AFNetworkReachabilityManager.h */, + 2EFC89B254D8AAE9141EF6851EA5277D /* AFNetworkReachabilityManager.m */, + ); + name = Reachability; + sourceTree = ""; + }; + 1ACB7B344F544E74312EBCC2EEF27A4F /* ParseUI */ = { + isa = PBXGroup; + children = ( + 07EB9E619B4B720424CC061A99D2BABB /* PFActionButton.h */, + 2D3FCD6D1CCE074756536544D3795CD9 /* PFActionButton.m */, + 963130EBA1505774B88A6206C7117AD9 /* PFActivityIndicatorCollectionReusableView.h */, + 473DFEC13A8925DDCC0F6375D0AA1FDD /* PFActivityIndicatorCollectionReusableView.m */, + 4F901A1A06A6B452D091DE5EEF0C8136 /* PFActivityIndicatorTableViewCell.h */, + 49314B7929DAC6D78738BF75309C6409 /* PFActivityIndicatorTableViewCell.m */, + A5E5655A0381C0C739C229727C1F3802 /* PFCollectionViewCell.h */, + D2FCDC0741A4571CCA8D7C1E99F48105 /* PFCollectionViewCell.m */, + B8E1E38F2E91125CD57103EEBC3C475C /* PFColor.h */, + 5744A62C5DD57C35F95B23CD332A69B0 /* PFColor.m */, + 5B0D1B89D4514C28C3C698E6564D5CC0 /* PFDismissButton.h */, + E218DEB073F057E49C9302AEB766059F /* PFDismissButton.m */, + 927B2E472B9B4600105524234E02BBEF /* PFImage.h */, + A353C806ABBCE7480976922EA5EB7B75 /* PFImage.m */, + 632A9C2EB7D9F58D0A7E0A0734C27B83 /* PFImageCache.h */, + 0D9670272FFEABFE8085CAD7256B26BF /* PFImageCache.m */, + 1C62627778FD1291CC19265484E8A35B /* PFImageView.h */, + A20A37119F2FDB5210E5F294E3477B2E /* PFImageView.m */, + D6CA9C05834B083659D1A1C33EB15AD0 /* PFLoadingView.h */, + 412F790A066291B851813E4C9327BFB7 /* PFLoadingView.m */, + E4B5BD965A4C5DAA801C33535E0BB379 /* PFLocalization.h */, + BFF7C6B60180E4A5C942887184602AF5 /* PFLogInView.h */, + 053008B62E75445056E83EAF88F670AE /* PFLogInView.m */, + 38C7D3C7362627C2E0E8B4F5BE09F5A2 /* PFLogInViewController.h */, + 8EE9E02E1C1B46F3ACFE5EF292CEDDAC /* PFLogInViewController.m */, + ABF64DFD9F115A0E816F8F5BBDF0DA14 /* PFLogInView_Private.h */, + 06FFD67B3E3A338C0616E33AC82E271F /* PFPrimaryButton.h */, + A72606B3DBDB6147A21BF44AB547E440 /* PFPrimaryButton.m */, + DACDC24D9356617A721A05C9B6ACBC2C /* PFProductTableViewController.h */, + 480A5B1630D4FE3B4BBB46EB667DD47F /* PFProductTableViewController.m */, + 8DA7F4F5534EFC28E047F3039137BB0F /* PFPurchaseTableViewCell.h */, + 2DE72C53C03EAE4311F887EE6A186CFB /* PFPurchaseTableViewCell.m */, + 86B5449588D7B659F947D7E39E060110 /* PFQueryCollectionViewController.h */, + 0396C6EE2D90BA2591A0BC7503FAF784 /* PFQueryCollectionViewController.m */, + E3F30F4E54BFE18519B6553114DA944B /* PFQueryTableViewController.h */, + F578D2BE836F7E5FCD8B4F070D2CA62B /* PFQueryTableViewController.m */, + 933C4C66F46988536759B7A7AA3AB260 /* PFRect.h */, + 1660ABF7B85069A170248B808256455D /* PFRect.m */, + D585DF26C444D0FDF2346A1B69A9342C /* PFResources.h */, + 7DA5D03F437F920BAB37A6380F6CA63F /* PFResources.m */, + EF93FA2C0175C88F708912B000641CC8 /* PFSignUpView.h */, + F7CBF95140D5E75739637EC8272D9488 /* PFSignUpView.m */, + 49457ABA4299045560404E829773DF26 /* PFSignUpViewController.h */, + 4C26CD46DBFBC0826774EE2BABF221F5 /* PFSignUpViewController.m */, + 0ED8804894EC6C702E97CB2701FCD8B4 /* PFTableViewCell.h */, + A99437D514635B420895CF6FB7336143 /* PFTableViewCell.m */, + 434C507EC671E5F0E3785E7D3020225B /* PFTextButton.h */, + 7C90F76F74AA0E9A353A68E4994EDCBF /* PFTextButton.m */, + B13872C024A5C2B50AA18722FE15CDFA /* PFTextField.h */, + 9F429BC5C28A53E99C5E18A4668F5E24 /* PFTextField.m */, + B9EDA77BEADC909F4B7793C3C24DB96F /* PFUIAlertView.h */, + 57557DDB79AE2CA8FB3449F3A6EB4369 /* PFUIAlertView.m */, + BB7AF609EF0BFAD9C6EC6123F16D7E2F /* ParseUI.h */, + 51F9299B2C2E6C0D460E3B857D3E1931 /* ParseUIConstants.h */, + 4FB1E5B734B775CE61AEB4123E739D08 /* Resources */, + B42F1E4173B2664CC6238621DE6C9177 /* Support Files */, + ); + path = ParseUI; + sourceTree = ""; + }; + 1EDBD2B154FE1D2E777F7DABFFF603DC /* Security */ = { + isa = PBXGroup; + children = ( + EAFD4107509F91789F51A3194CC54229 /* AFSecurityPolicy.h */, + AFA1140609306B1C8C88E61FE5186100 /* AFSecurityPolicy.m */, + ); + name = Security; + sourceTree = ""; + }; + 3089E971632BEEDE9552A8E0864A03C5 /* Resources */ = { + isa = PBXGroup; + children = ( + 2208328361FA7D0218574BE074627642 /* en.lproj */, + ); + name = Resources; + sourceTree = ""; + }; + 402666EF7B7A4DB8B35EBE9CDFDAB1FA /* Support Files */ = { + isa = PBXGroup; + children = ( + A5251A90622521ACCBE90C8912D8200F /* Info.plist */, + A02502BAA5243EDF918943006C174647 /* LiquidFloatingActionButton.modulemap */, + AC68AD5CDBD49B1906847DDDEE8433E7 /* LiquidFloatingActionButton.xcconfig */, + 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */, + A1B4B55BB161D999FD5EC2B657E7B6B3 /* LiquidFloatingActionButton-dummy.m */, + C8DF725A4DEDC5E894E3F0DECBD6E2D3 /* LiquidFloatingActionButton-prefix.pch */, + BA2B0874AAFA2F48338A7586EA1D3BF9 /* LiquidFloatingActionButton-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/LiquidFloatingActionButton"; + sourceTree = ""; + }; + 4FB1E5B734B775CE61AEB4123E739D08 /* Resources */ = { + isa = PBXGroup; + children = ( + 56467AABBB2F1EADE1FDAF4C1D07FBC5 /* en.lproj */, + ); + name = Resources; + sourceTree = ""; + }; + 51EB4673ABB346A65C70E812DCCEB185 /* LiquidFloatingActionButton */ = { + isa = PBXGroup; + children = ( + 3BE045EE908BDA1501A41A429BC7A646 /* ArrayEx.swift */, + C20CDE358825835E6B40286F5163A863 /* CGPointEx.swift */, + 89683EF21BC95E77BBF06818132C923E /* CGRectEx.swift */, + B6A18CBBDB72D9222355CD85E1E5AC96 /* LiquidFloatingActionButton.swift */, + ACE08D7134612A5CB853602B49829807 /* LiquidUtil.swift */, + B7F4C394668FB6C72DEAC18029145B8D /* LiquittableCircle.swift */, + C3BB66694B1A1DAD9A0455379A1FE921 /* SimpleCircleLiquidEngine.swift */, + 1B7AA2E50772619C4657FB990A261225 /* UIColorEx.swift */, + 402666EF7B7A4DB8B35EBE9CDFDAB1FA /* Support Files */, + ); + path = LiquidFloatingActionButton; + sourceTree = ""; + }; + 587C8E0AED8B8918BB0FB7549D205993 /* iOS */ = { + isa = PBXGroup; + children = ( + DC7294772B9396EF14228A8BD24860DE /* AudioToolbox.framework */, + 5DE30302543CAA1030741C690BC2AC46 /* CFNetwork.framework */, + 58228477EDB607E07438A4DA7659B1AB /* CoreGraphics.framework */, + 0F3BE50471AAC985EF28416C9EB1C185 /* CoreLocation.framework */, + 9ACB411629F1F262E4FC3571434FDD91 /* Foundation.framework */, + 70B9D1F18A0D896262A4B138338F2901 /* MobileCoreServices.framework */, + E89BF5589887D6023691A7740CFBC374 /* QuartzCore.framework */, + 3CA945CDAC44DDF1110329B15603AAF7 /* Security.framework */, + 37222E9C27B945A7B72A144663090FCB /* StoreKit.framework */, + F6546A78D7DDEF82B4CB1A6BAFE39FD6 /* SystemConfiguration.framework */, + DC6D23DEBF98F865D988524F98884065 /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 5962375B6ECC49E97C8D3CE127EB485D /* NSURLSession */ = { + isa = PBXGroup; + children = ( + FAB4A34092FC1C5298CF33B7F20A6E09 /* AFHTTPSessionManager.h */, + 7533A71248AEB62C9FC4FE38B6FBCE43 /* AFHTTPSessionManager.m */, + 74E1103577F4DCB27E5C109F9329653B /* AFURLSessionManager.h */, + 0E05C2B4295C609B8107CC303A977728 /* AFURLSessionManager.m */, + ); + name = NSURLSession; + sourceTree = ""; + }; + 61CCB3BDF06795644DC6950F83B0A654 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F71E915821A8435DD4437B712A573BD8 /* Bolts.framework */, + C68DEF4C4561831CECF5DAE37F4E1926 /* Parse.framework */, + 587C8E0AED8B8918BB0FB7549D205993 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 65CC4798C6ECA26E703A4EAB0451A322 /* Pods */ = { + isa = PBXGroup; + children = ( + 841CC1DC3E315A522EC4F9A98AADBE4A /* AFNetworking */, + CA1591D559B95A9139590AF2BCAB3D5A /* Bolts */, + CCCDE197AB3F3117429ED49B31E044AC /* GoogleMaps */, + 51EB4673ABB346A65C70E812DCCEB185 /* LiquidFloatingActionButton */, + F11A471449698348238C3240A0354078 /* Parse */, + 1ACB7B344F544E74312EBCC2EEF27A4F /* ParseUI */, + ); + name = Pods; + sourceTree = ""; + }; + 6F7EE1803D3972C8135461290D3614A2 /* Serialization */ = { + isa = PBXGroup; + children = ( + 50B9BC2A232324947BCFD7EF8C6DF1B8 /* AFURLRequestSerialization.h */, + 6F8BEA4A9ECB98F43F4DD96BE3251E35 /* AFURLRequestSerialization.m */, + F87A7541A9593AC496503BD08923E52D /* AFURLResponseSerialization.h */, + 13AD5FBF9469D47C394C584D6E88F244 /* AFURLResponseSerialization.m */, + ); + name = Serialization; + sourceTree = ""; + }; + 72270C7852B0636BFCBBCF914D44B683 /* Support Files */ = { + isa = PBXGroup; + children = ( + DE992F9DCB99338348525B7D3C38802C /* AFNetworking.modulemap */, + 2E6364E03CBC47FA3C194780506E4A88 /* AFNetworking.xcconfig */, + 5296B4416AC12F2190F879868AC55E77 /* AFNetworking-Private.xcconfig */, + E7653C557D29E7F48FC2C3DE41CC735C /* AFNetworking-dummy.m */, + 1C667BBDD9E243B4C72CB91696743FEE /* AFNetworking-prefix.pch */, + 16E7F774F4751157B63DB04DB459BC55 /* AFNetworking-umbrella.h */, + 9B94D92A7B20E46739BDF9FAB81A2828 /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/AFNetworking"; + sourceTree = ""; + }; + 75D98FF52E597A11900E131B6C4E1ADA /* Pods */ = { + isa = PBXGroup; + children = ( + E8446514FBAD26C0E18F24A5715AEF67 /* Info.plist */, + 79A9DEDC89FE8336BF5FEDAAF75BF7FC /* Pods.modulemap */, + D0405803033A2A777B8E4DFA0C1800ED /* Pods-acknowledgements.markdown */, + 87B213035BAC5F75386F62D3C75D2342 /* Pods-acknowledgements.plist */, + ED37C1A5862B3AE73D6077D7795B9474 /* Pods-dummy.m */, + E7F21354943D9F42A70697D5A5EF72E9 /* Pods-frameworks.sh */, + CBC0F7C552B739C909B650A0F42F7F38 /* Pods-resources.sh */, + FC41D49301C5C346F942300A428EFE59 /* Pods-umbrella.h */, + C57AA8424009C8745B20CAC511A50222 /* Pods.debug.xcconfig */, + BB7BF9759D7B78AB520BE410BA97011F /* Pods.release.xcconfig */, + ); + name = Pods; + path = "Target Support Files/Pods"; + sourceTree = ""; + }; + 76A617FF3EB6E142A6A1FB2527E07E65 /* UIKit */ = { + isa = PBXGroup; + children = ( + B2535EAEF95F25D2E77544BEE32588E9 /* AFNetworkActivityIndicatorManager.h */, + 1FC471E6023A052E8FA627F61045398C /* AFNetworkActivityIndicatorManager.m */, + 17184911F1E825E04E81231EB596A28E /* UIActivityIndicatorView+AFNetworking.h */, + FED57DD7F261567A1E9174BAAF263E24 /* UIActivityIndicatorView+AFNetworking.m */, + 6D3CD655EFF856D601BCADD2A12D20B1 /* UIAlertView+AFNetworking.h */, + AF3115BC0E1F291BCDAA52C97E90DA21 /* UIAlertView+AFNetworking.m */, + 66636BE79215A604578D0F0E12C79103 /* UIButton+AFNetworking.h */, + 808AF6592966508933C25BC20FFCC320 /* UIButton+AFNetworking.m */, + 3FB2A37BB79697F99A0A53FA1B7F9B47 /* UIImage+AFNetworking.h */, + DCA5DAA6B6F95021F88E0374D52599C0 /* UIImageView+AFNetworking.h */, + 987AD1FB8A654DC31AE3E6601CCC7A36 /* UIImageView+AFNetworking.m */, + 78A2111B342B9B80EC48C4F51AA04B69 /* UIKit+AFNetworking.h */, + 0FED8E692B2FF2E30A56C6D070BFAB06 /* UIProgressView+AFNetworking.h */, + 06932232D53EEC91CFD3A681CB300096 /* UIProgressView+AFNetworking.m */, + 490DD45156F4A90833C74B5509134501 /* UIRefreshControl+AFNetworking.h */, + 5EAE992B8BEAB950FE7E122420450483 /* UIRefreshControl+AFNetworking.m */, + 3F6F533DC7C28FD9B021564F418631E7 /* UIWebView+AFNetworking.h */, + C0F49D28B3C52C5467AE62585139EE0A /* UIWebView+AFNetworking.m */, + ); + name = UIKit; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */, + 61CCB3BDF06795644DC6950F83B0A654 /* Frameworks */, + 65CC4798C6ECA26E703A4EAB0451A322 /* Pods */, + CCA510CFBEA2D207524CDA0D73C3B561 /* Products */, + B7B80995527643776607AFFA75B91E24 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 841CC1DC3E315A522EC4F9A98AADBE4A /* AFNetworking */ = { + isa = PBXGroup; + children = ( + 2DFA9B6E0EEB0C0CC47910DB6F871C95 /* AFNetworking.h */, + EA4D4EAD71214448A8C0E9EC22B5A93A /* NSURLConnection */, + 5962375B6ECC49E97C8D3CE127EB485D /* NSURLSession */, + 08C00C6AB927C05DB810336C2BF38EA1 /* Reachability */, + 1EDBD2B154FE1D2E777F7DABFFF603DC /* Security */, + 6F7EE1803D3972C8135461290D3614A2 /* Serialization */, + 72270C7852B0636BFCBBCF914D44B683 /* Support Files */, + 76A617FF3EB6E142A6A1FB2527E07E65 /* UIKit */, + ); + path = AFNetworking; + sourceTree = ""; + }; + A5C31ED73CA3EF5FB7390DA9358A2E0B /* Frameworks */ = { + isa = PBXGroup; + children = ( + FC30909C191418455F13A0AAC1DC948B /* GoogleMaps.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B42F1E4173B2664CC6238621DE6C9177 /* Support Files */ = { + isa = PBXGroup; + children = ( + 4B6436B37ECC59A38F96B1ADE342EB64 /* Info.plist */, + E14260EF83EAB3EE8F0AE4FC52092A8B /* ParseUI.modulemap */, + 1F3A1D95B129EC27A4597159C4684430 /* ParseUI.xcconfig */, + B82573787A30C373316857E73B756945 /* ParseUI-Private.xcconfig */, + 2BC28B0883B9B9DB2F5F1F2DF82A956D /* ParseUI-dummy.m */, + D9DB202029F6BEE0B5B41F8145AFAC0F /* ParseUI-prefix.pch */, + 10785B227444AD6A5CCC16CC93FDED1F /* ParseUI-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/ParseUI"; + sourceTree = ""; + }; + B794AFBF355CBC63E4FE67CA0EDC5CF7 /* Support Files */ = { + isa = PBXGroup; + children = ( + 9CFB6412CFFF4076089FE7FFEEEF17D5 /* Info.plist */, + 402884917651870142D4DEF1CD7D4DBF /* Parse.modulemap */, + A4E0C78BAB222F98F39D7C9118CCA385 /* Parse.xcconfig */, + 4047A1EB9846648DAD467918C55ED03A /* Parse-Private.xcconfig */, + 1ABC7BA535DC20890BDADEE775FB84BC /* Parse-dummy.m */, + 1D8E781682341AD164504DA993129F1D /* Parse-prefix.pch */, + 39555E2A1C0726E869769E5E64AD7F75 /* Parse-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/Parse"; + sourceTree = ""; + }; + B7B80995527643776607AFFA75B91E24 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 75D98FF52E597A11900E131B6C4E1ADA /* Pods */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + CA1591D559B95A9139590AF2BCAB3D5A /* Bolts */ = { + isa = PBXGroup; + children = ( + D7FAAD81FEC86A394B5ED152A5B04171 /* Support Files */, + 036203CC7A8F579CF8A788446B371472 /* Tasks */, + ); + path = Bolts; + sourceTree = ""; + }; + CCA510CFBEA2D207524CDA0D73C3B561 /* Products */ = { + isa = PBXGroup; + children = ( + 1844474781B402090680328B2DB5420B /* AFNetworking.framework */, + 1461A3C96F9D5F77C27EB1AEF3772D96 /* Bolts.framework */, + 97FF6FABD68A2485CB659846729BFFCD /* LiquidFloatingActionButton.bundle */, + 06C9E684885FB5B216BF361176DDBB5D /* LiquidFloatingActionButton.framework */, + 965A29E050253DCA29D6D53DA6E80B5D /* Parse.framework */, + 75F25EFFB4D4BE6DA19F30E328EA3E81 /* ParseUI.framework */, + 5BB2711832525BD41951DBF302F796E1 /* Pods.framework */, + ); + name = Products; + sourceTree = ""; + }; + CCCDE197AB3F3117429ED49B31E044AC /* GoogleMaps */ = { + isa = PBXGroup; + children = ( + A5C31ED73CA3EF5FB7390DA9358A2E0B /* Frameworks */, + DD007C4081F8C2DA1E03131028CDB5D9 /* Resources */, + ); + path = GoogleMaps; + sourceTree = ""; + }; + D7FAAD81FEC86A394B5ED152A5B04171 /* Support Files */ = { + isa = PBXGroup; + children = ( + C5934E9381ECE8A0A01CD996573C5EE1 /* Bolts.modulemap */, + DE96842CB6DC229E92D71EEEFC92335C /* Bolts.xcconfig */, + BE9223229E8325CB5F21B8ADDA6B9960 /* Bolts-Private.xcconfig */, + E2F646392C325F581AA7EEEB52C037E0 /* Bolts-dummy.m */, + 507E749D722A9CC247F8CE2B138A1548 /* Bolts-prefix.pch */, + 286AB7391704B71658D0BCF8A49D2F4D /* Bolts-umbrella.h */, + 0969818CCB352658F7216EA607E5A588 /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/Bolts"; + sourceTree = ""; + }; + DD007C4081F8C2DA1E03131028CDB5D9 /* Resources */ = { + isa = PBXGroup; + children = ( + 15D1EE3D8D96732C161572D298D09B0A /* GoogleMaps.bundle */, + ); + name = Resources; + sourceTree = ""; + }; + EA4D4EAD71214448A8C0E9EC22B5A93A /* NSURLConnection */ = { + isa = PBXGroup; + children = ( + 9D6FE3EE8F0D3CAE0224A63EC2E293D7 /* AFHTTPRequestOperation.h */, + 8C7C9C2804D450E4F0A969D264281DBE /* AFHTTPRequestOperation.m */, + 13D360FA4AE2720A4786C6058456F2DC /* AFHTTPRequestOperationManager.h */, + 85F264D88C24402A09C3C9C8B5EF4601 /* AFHTTPRequestOperationManager.m */, + 528B85889F63E99EC61F9BC47CC31A8E /* AFURLConnectionOperation.h */, + 242EE2B72A421D6B06312EA12A989EC1 /* AFURLConnectionOperation.m */, + ); + name = NSURLConnection; + sourceTree = ""; + }; + F11A471449698348238C3240A0354078 /* Parse */ = { + isa = PBXGroup; + children = ( + 64DB319647B47CD74F8DA4F72A903D14 /* BFTask+Private.h */, + 5E29EE0F72D64655290376D8E2025A3C /* BFTask+Private.m */, + 7135D79AC2406919243CA6DA7F8290E3 /* PFACL.h */, + 54554DF4C25DD4127EC0364200393FE6 /* PFACL.m */, + 42D0387D6F012A1706FDA9B3F9974E22 /* PFACLPrivate.h */, + 43575DB6BB216F37BCA1AB250B9F09BF /* PFACLState.h */, + 834F31050B11D55D2C450E902318E944 /* PFACLState.m */, + 9D5D9ACD4B24C679EB5789A8B2300783 /* PFACLState_Private.h */, + 19EB9918367D9576DC5BA66511FDDA59 /* PFAlertView.h */, + F73E652B07D06D8EE514D15810A9F8F1 /* PFAlertView.m */, + 632635171DFBADC6DD391BC3C9FBD721 /* PFAnalytics.h */, + CCDFF9320F22A33DA2D0EB4C88FA9A95 /* PFAnalytics.m */, + F64FC9AA07C23B6390B31487EEECC02F /* PFAnalyticsController.h */, + 2B9BE9E8FFC7586640B02294D86CF884 /* PFAnalyticsController.m */, + 95920EE1067D57330EBA26ABC1F8A742 /* PFAnalyticsUtilities.h */, + 4896C3D552B4755BE9E7C1C62D992F5E /* PFAnalyticsUtilities.m */, + 182CA1A9D7A94C67A9EB27D9B1D89590 /* PFAnalytics_Private.h */, + 44270F881246E94C8F2A111F06702B83 /* PFAnonymousAuthenticationProvider.h */, + 5AB0E2E0BA627EEC178EBB58FEB61DCD /* PFAnonymousAuthenticationProvider.m */, + 74CE67880E6C87B2A6E1DD4E8F148691 /* PFAnonymousUtils.h */, + 4303F783032C0C2846F32A846D5D4E5C /* PFAnonymousUtils.m */, + 977AB32A80ABE4BF539022F03D0403C6 /* PFAnonymousUtils_Private.h */, + 09563E9AD709C4FE3E9B10F016D79F40 /* PFApplication.h */, + E16CCC9C4042040D34C25745F5E93979 /* PFApplication.m */, + C99492480C58DAAD671B694D06B2A984 /* PFAssert.h */, + 335CC941000B202CD5BB59078C34DDF2 /* PFAsyncTaskQueue.h */, + 286E6D876A21BD5D906EE5CA6743D2E0 /* PFAsyncTaskQueue.m */, + D58C74135B703B4BA78ACFDE7B10DF17 /* PFBase64Encoder.h */, + 829C2DF55D47AC9FF3B59122141B8F50 /* PFBase64Encoder.m */, + A797A1CD69213D018C84F6D976394A27 /* PFBaseState.h */, + 0C0F04EEF7F8C7BC4C6FAC4AAF8E68DC /* PFBaseState.m */, + 3684ED9D4BB5E30D2F75909D264EFF6A /* PFCachedQueryController.h */, + 5F2B79C120F1E343333F0263C901B136 /* PFCachedQueryController.m */, + C478D21353BA164DF20A78C96701C338 /* PFCategoryLoader.h */, + D994A541599DACAF7137D8879F25C89E /* PFCategoryLoader.m */, + D9AF3C2322515C270D4C304A1D84789F /* PFCloud.h */, + 98FB41E4640BA6923CDFC45E1C3422C3 /* PFCloud.m */, + EF2D613D36A3FA0A0A242D221AD828A0 /* PFCloudCodeController.h */, + 3E0011F34B6F1370264442D8135FC5CA /* PFCloudCodeController.m */, + BC28C2D9801402D3385F95F86BD06CB6 /* PFCommandCache.h */, + 36D423CBA06FDB7473035E0CAD68C111 /* PFCommandCache.m */, + E9FDC84201D32A4325B4BF25B7DE58DC /* PFCommandCache_Private.h */, + D1DFBF1DE14726ABC1691D616B077B9B /* PFCommandResult.h */, + 0CB07DB9FE0C3E5E4C1EA3AD16600DC3 /* PFCommandResult.m */, + 9AA042538499B62E2B7C0921FCB5F01B /* PFCommandRunning.h */, + DEF997AF17A743017CD54A4193CC9AE6 /* PFCommandRunning.m */, + E712CF96CBB0489B4C7A04F5147BB32D /* PFCommandRunningConstants.h */, + 97FE58DD961885B0B4A058AEAF7A02A8 /* PFCommandRunningConstants.m */, + 2E4419B914285E9CFA2B0C4905E3717F /* PFCommandURLRequestConstructor.h */, + AE22C9A9D5BEC3BC938693871AA98EE6 /* PFCommandURLRequestConstructor.m */, + 411246798151B846841853F673E37435 /* PFConfig.h */, + A40A9A6110C6D841C877E13C7886F5B2 /* PFConfig.m */, + 374066DC72A963CFFD3374DBBFA2DFAC /* PFConfigController.h */, + 4B9BA81F2BF3DBCACCD4672ED697498A /* PFConfigController.m */, + FC6948D841E1FEED5688E9D076FFB17A /* PFConfig_Private.h */, + E92D2ACC1EB93D285C31C15C7960FB4F /* PFConstants.h */, + 0E8FA54606CCBC52550175EBAFEB25E8 /* PFConstants.m */, + ADFA0A2825114CECD32242EFA5607ED7 /* PFCoreDataProvider.h */, + E3AEAC049A5741DEDE9E5C91592038C1 /* PFCoreManager.h */, + 55ED6CB9CFA4FDEE1DEC32FE8583DFE0 /* PFCoreManager.m */, + E6973DA797DB68C919C66D4BA3D695B9 /* PFCurrentConfigController.h */, + 0B8576A9952876C6D42C077262FD9FA9 /* PFCurrentConfigController.m */, + AF6140D6AA1728AD006CD6EAEA3B3CC0 /* PFCurrentInstallationController.h */, + 106FE48FB855E72F7842C099E98FA52D /* PFCurrentInstallationController.m */, + 2E3BD169A6C2F8F814ACD98138308236 /* PFCurrentObjectControlling.h */, + DFC1DA889CE1311BE00B9D07030E2D91 /* PFCurrentUserController.h */, + FB9F130216053B89EE75FBACC3BB4F65 /* PFCurrentUserController.m */, + 4A57B2A1A122A5645B1B336DEDEABF67 /* PFDataProvider.h */, + CA79DC9A677C52A5CB0149D3711EDC4C /* PFDateFormatter.h */, + 899E7EFD0AD739A28B9021F4534B5545 /* PFDateFormatter.m */, + 5C20844654F9A04C67386CC1D2C2D1B1 /* PFDecoder.h */, + EE6D7A403F638983E94CC48F7369D44D /* PFDecoder.m */, + 55A85024FFE04E028567F29F7ABA33E0 /* PFDefaultACLController.h */, + 1D44679B3B60446F276A7452751C977C /* PFDefaultACLController.m */, + D3592860B73625779473717C79E14C33 /* PFDevice.h */, + CFD2BCA0FDC1E5923DDC3256A7F82B48 /* PFDevice.m */, + F6491E92ACE75905C0A36BBBAC1F3070 /* PFEncoder.h */, + 515EDE43F2D9FB9C27C6C8783384DCE9 /* PFEncoder.m */, + 6F5FA5FAAFCEEF833D9142FF59994544 /* PFErrorUtilities.h */, + 508F40F7D00DB921AC2A70A820C56E68 /* PFErrorUtilities.m */, + D4DA6F666BF906399EDA329897330DE0 /* PFEventuallyPin.h */, + E998520B0AE9F07988C912A4F83B6BCB /* PFEventuallyPin.m */, + D031DB68EC76D7496F1750449C536A4E /* PFEventuallyQueue.h */, + 6D78597D58FFE85B4FB8C4561AA50BB5 /* PFEventuallyQueue.m */, + EC1C24F1EAE7BA1CB22FA6349CE5E07F /* PFEventuallyQueue_Private.h */, + 8345935C5AC8AEE28996E8ADAFE07522 /* PFFieldOperation.h */, + 8B5C3BFFF0DB6969C2B50A5CD483F812 /* PFFieldOperation.m */, + 4C3F5A63922E21A53CC4884A45C5839E /* PFFieldOperationDecoder.h */, + 7C586370E28704EE0E4D7689B917753B /* PFFieldOperationDecoder.m */, + 0FDBD79473F9FB40DB76DD65392048A7 /* PFFile.h */, + 795840987CB6C725613D305D3D8DE0C5 /* PFFile.m */, + 8D3272484AF55B5BC7CD1C32A067FA85 /* PFFileController.h */, + 79A0675A9A52CC3B48F422CA4E940CD3 /* PFFileController.m */, + 4B393ACCB61C4E998CC4330DAC20ACEC /* PFFileDataStream.h */, + 1284F0A5E10B2B233B0C53219469EA5F /* PFFileDataStream.m */, + C0E17697C9CAFCD60CAA1AE570630945 /* PFFileManager.h */, + 8230FA9936E0CC774177096CCC805BFC /* PFFileManager.m */, + 43B20D1E0DCC5A0A1D0ABC06E6C76F1A /* PFFileStagingController.h */, + 77FF71B41A0A7E4C30E000416639CAD2 /* PFFileStagingController.m */, + 82B9E087EE91277B762C1E55E025EF89 /* PFFileState.h */, + F337DF8EB662B6A49FFEBC966CF745B6 /* PFFileState.m */, + 1B3B9DFEEEF919C024CAAB1D52C6FA83 /* PFFileState_Private.h */, + AD42871D9C935D2C3C640E893BFFC564 /* PFFile_Private.h */, + E9B8580777097F914F9981794BB8DE24 /* PFGeoPoint.h */, + 1B573A03FCE6CF5B48F8CB91BAD43435 /* PFGeoPoint.m */, + 979B9D6DD6CC9A7FC7F04469E1E2B5A6 /* PFGeoPointPrivate.h */, + 6085298ACB2321485F0D3C12D351FEB5 /* PFHTTPRequest.h */, + 843DAD1E807B7D28D13FCD55BAA643C9 /* PFHTTPURLRequestConstructor.h */, + A05C4014427DEBE8CDF3F7DF4EDF5B78 /* PFHTTPURLRequestConstructor.m */, + 201F561822AF1B8FD7578936456F4264 /* PFHash.h */, + 71EC800801E622412FB29F8D44D3CD3C /* PFHash.m */, + D3339AE7574EA468A11239825C11ECC5 /* PFInstallation.h */, + ACD672B833F3DE5495704B56CF7BA509 /* PFInstallation.m */, + A93DBE9D5C2687DF9723AAFA66F2A971 /* PFInstallationConstants.h */, + 4B5D13079A63558D8C87DFDB1697B537 /* PFInstallationConstants.m */, + 18399FE7C356696A82C3B177BBA86230 /* PFInstallationController.h */, + 6CDEB255A1EFD2D56CF8836FF93C208D /* PFInstallationController.m */, + 504090B38C287A418E1F8BE4850DB9F8 /* PFInstallationIdentifierStore.h */, + C99AD2F0824E2247A161578BD8373909 /* PFInstallationIdentifierStore.m */, + 43F928E8DB69EEDE303F3A4C7318F4DF /* PFInstallationIdentifierStore_Private.h */, + 2864AE64CD2B48522D2926BD76DB48A2 /* PFInstallationPrivate.h */, + 7C455D006BA9847DD01AF4AD9D0A99C0 /* PFInternalUtils.h */, + E47B281C898AA47BCBDC4FD0172D821E /* PFInternalUtils.m */, + EB4D83C8AA70A082952CB547050AE43B /* PFJSONSerialization.h */, + EF44078D9B90D1CAE0849F7FAE2927AC /* PFJSONSerialization.m */, + 8213143BCC700BC9B5EE28B2AFE47BC9 /* PFKeyValueCache.h */, + 4A2CCB3F17D3BA81EBDC4AC1E6CF5E8A /* PFKeyValueCache.m */, + D94237B26D6FD987E657CCBB1B2C868B /* PFKeyValueCache_Private.h */, + AA573A271BED522E0458A7AE97926873 /* PFKeychainStore.h */, + BC983AFF27F041F598C25C2CD37CFDCB /* PFKeychainStore.m */, + D879C61F873DA897D8CDCFD529DD718F /* PFLocationManager.h */, + 058FE1B26228FFF21C295DF2DF680081 /* PFLocationManager.m */, + 9A2D779D62119B7029D39A6D67FF2D63 /* PFLogger.h */, + E2E8D5C9D085E8016D489CA6F418A909 /* PFLogger.m */, + DD78A6DA4B3E5F2479E701FA15D4D424 /* PFLogging.h */, + 03453B418D0B0A3C1A61F031BCB33C8C /* PFMacros.h */, + 0256B924DCFE7BACEB6568CD8A2C6C59 /* PFMultiProcessFileLock.h */, + B31B277478C40801EDACD6F671D9F9D3 /* PFMultiProcessFileLock.m */, + 5EA7251DC3418FDD089B4BE7DB866896 /* PFMultiProcessFileLockController.h */, + D319F69C8F009DDC8CF96B18FEC4583E /* PFMultiProcessFileLockController.m */, + 68B9F97EFD6FBB23BEA89804B1300441 /* PFMulticastDelegate.h */, + 22E583E17C29E569C438C2F5CFE8DE49 /* PFMulticastDelegate.m */, + FC71FB7E0EB9ACA6F1DACD4E1D5781CB /* PFMutableACLState.h */, + B640687713469EFE42C0475F6192A31F /* PFMutableACLState.m */, + 2FDD4D768605FD329F1CA002D177D3CD /* PFMutableFileState.h */, + 61351FBD424C7B2328FDF4BE4B7ECE3A /* PFMutableFileState.m */, + 0ACC8E7504C95E08B22B373F5EED0779 /* PFMutableObjectState.h */, + E64CE28EB8ABC8BC399D729811CABF32 /* PFMutableObjectState.m */, + 8E524941C9BC2F24FCC92215E85F2BF6 /* PFMutablePushState.h */, + 45C2D54A8FA658EE6C378587022D1F23 /* PFMutablePushState.m */, + A5DF1057EDD3E40B699F68523EB26FC1 /* PFMutableQueryState.h */, + 61B90E31FC30056504354B6B9CD64ABB /* PFMutableQueryState.m */, + B89903554FFA78AADE8F525DDC0C7959 /* PFMutableRelationState.h */, + DCCF3542E63589C3EDE6E22471B9C2C7 /* PFMutableRelationState.m */, + C1BE4F5D33EE98831A53F0E87D0343B4 /* PFMutableUserState.h */, + 266DD3785853AF2B087ECB683111FBDF /* PFMutableUserState.m */, + E7AC9992862CA136E8E65BF8DF8F3D15 /* PFNetworkActivityIndicatorManager.h */, + AE4FD7D79AD079CDF0E0992224F6277C /* PFNetworkActivityIndicatorManager.m */, + 8A0D21B75DD89371A7800E0F07CCDD3C /* PFNetworkCommand.h */, + 391F623207B6760C4D0AC086EE3CE49C /* PFNullability.h */, + 9EE3A5821D30B3DF09BE105CA7B7FEDF /* PFObject.h */, + 3E1756E787A2805CAFDC958AE3C4960D /* PFObject.m */, + C28C9D46AE0FE690A649A1BE46A2BBCD /* PFObject+Subclass.h */, + FA656C1B60A3756129523AF9A74A803C /* PFObjectBatchController.h */, + DE9856B6BF2ECFD60B284F10DDC277BE /* PFObjectBatchController.m */, + BCFF6319D2EF9091302E5B96F4E0B43B /* PFObjectConstants.h */, + 0C00A99BD6D0DB2325AD32749EEE4D05 /* PFObjectConstants.m */, + 6DAA2CB4A82D8EB23F29780BFAAC1DCE /* PFObjectController.h */, + 96B84037727346B0A0B3E2F3A9668A1C /* PFObjectController.m */, + C449090D4B4F78564B631A75077787A9 /* PFObjectController_Private.h */, + E5B4B025F17BAA1B3A677D658784243F /* PFObjectControlling.h */, + 0A186D964995C7B9E347DED4AD34C997 /* PFObjectEstimatedData.h */, + 44B78AF0E04A202E8C137C3F34CE6F17 /* PFObjectEstimatedData.m */, + D6133D18D0CBDFE7A51DC2ABD2DA8956 /* PFObjectFileCoder.h */, + 52917348ED99708803AB4571CBF7448F /* PFObjectFileCoder.m */, + 8DE4F7C454A15E12B39ED6023325FA3A /* PFObjectFileCodingLogic.h */, + 20F63976C35095E9FB91D72CAE2FDB7D /* PFObjectFileCodingLogic.m */, + 7BF997DF2BA7E971B44D0DE2163F7FF8 /* PFObjectFilePersistenceController.h */, + 36E262B1B4BEE4F1DDB6F3DF88064F02 /* PFObjectFilePersistenceController.m */, + 88BAE879E3D71A97056B63720B3DAD5E /* PFObjectLocalIdStore.h */, + CB76E8D570FAE396D5315056A3E7E24E /* PFObjectLocalIdStore.m */, + 11641F1E2B0F6BFAEB5FE89FA349A02A /* PFObjectPrivate.h */, + 9A8256CB779D4E0EEFA7452FC60E3F2B /* PFObjectState.h */, + D8EE7A749F0FA95FFDA3601E6582B943 /* PFObjectState.m */, + 16ED09F9D67408A7EFF12AFECDA9A63B /* PFObjectState_Private.h */, + 7304B1EA9F7A8C12AD248A220266F7E6 /* PFObjectSubclassInfo.h */, + 13CD22F039DCBF6319754D584066D895 /* PFObjectSubclassInfo.m */, + 23B9E89D64EA7A1356B9BBCEE11CC809 /* PFObjectSubclassingController.h */, + 5D7587C874F53970DD6689504C43AE04 /* PFObjectSubclassingController.m */, + ECE0AC637C3C70874A9C7E29EB8E9F69 /* PFObjectUtilities.h */, + B402CED15BEC363BBB72FE42CFFA19B7 /* PFObjectUtilities.m */, + 31A896932415303DC122139D6C700DED /* PFOfflineObjectController.h */, + BC13566BBA4A6E3F586E6A880A2689DD /* PFOfflineObjectController.m */, + B89E57173FF8D9E3745D41F14BB2E463 /* PFOfflineQueryController.h */, + 1F96A5E8E7BE0A8F092CB9BE81FA722A /* PFOfflineQueryController.m */, + DAD4EC7CD5391A7B25687EEDAD0E98E0 /* PFOfflineQueryLogic.h */, + AC649CF0667A08BF5C299B5FCDB01DF7 /* PFOfflineQueryLogic.m */, + FF22D95936873FA51D8C9C983150C21D /* PFOfflineStore.h */, + EFD4CCE36EFE93ACCBCC2D407FF6D4FB /* PFOfflineStore.m */, + 4B56E95C16FD6DAC8A7EA8CE384CF79B /* PFOperationSet.h */, + 1FF98AED99DA5E23EF2405DB00EB5C15 /* PFOperationSet.m */, + E8621BD61A365200D38234EE73EF24CA /* PFPaymentTransactionObserver.h */, + A98219265A93C0BC6572D73A5D1EAEB2 /* PFPaymentTransactionObserver.m */, + 4240B13C9AE359BF942D85E0DE35AFE2 /* PFPaymentTransactionObserver_Private.h */, + 47BBEF70FCF6A4D933AADEA763919924 /* PFPin.h */, + 57DBB8A59CEF5779FB2FEC6E3357F1D8 /* PFPin.m */, + 22BBA5CC87BB1C58194494B4A1E02687 /* PFPinningEventuallyQueue.h */, + D0359022BC718069CC2473CA068BFCD7 /* PFPinningEventuallyQueue.m */, + A83E1CB2B25CB590D1D1E867BA4C0A1D /* PFPinningObjectStore.h */, + E4F396EB5B518CB78205BD9A35D95FBC /* PFPinningObjectStore.m */, + DFAB2F23DBAAB01BDD3098996526D1A3 /* PFProduct.h */, + F908F8F01022F84F47E832CD33F366C0 /* PFProduct.m */, + 1EEF1672D1EBB3EC3C6F7B1D9F2C0B80 /* PFProduct+Private.h */, + E075ED2B6706ECB8A6B28B81F9FD35C7 /* PFProductsRequestHandler.h */, + 508F81A14503FDBEA2527D9048B06142 /* PFProductsRequestHandler.m */, + 3216FF24B9FA981F635F7D85A2444D31 /* PFPropertyInfo.h */, + 258F5BCDF91B9AB891FBF085B379DA3C /* PFPropertyInfo.m */, + 480D89A7E47EF11AA3FBB5053B2C9781 /* PFPropertyInfo_Private.h */, + 31673CEE600D31E9C12A1F2F14C74715 /* PFPropertyInfo_Runtime.h */, + A18517CA52623E4381D5DC0108AFB9ED /* PFPropertyInfo_Runtime.m */, + DDE94D96A9C62A91D595D2E433F21F9A /* PFPurchase.h */, + 4148D9154FC86736CE6D49722DF31E3A /* PFPurchase.m */, + 6B1EB3A0FAE8016CFDE64DE338F8BDD7 /* PFPurchaseController.h */, + FD88241CAE6DA71B4D70C103FE15ADC4 /* PFPurchaseController.m */, + 356CD10091814DAEF0798313892C8C05 /* PFPush.h */, + 3E5865FCD20277B95CE81D8313B04B91 /* PFPush.m */, + 9B44D9A7073478A4D49371D61B2DACF5 /* PFPushChannelsController.h */, + B439486D6310E570C25D882BF6B831F1 /* PFPushChannelsController.m */, + 815246C826CF2ADBE326786D6BA94F42 /* PFPushController.h */, + 60D8FEA3A3B139B0A0F01500655A6DE8 /* PFPushController.m */, + 610C79F260D7DE6FBE6856EF3349D7E2 /* PFPushManager.h */, + 2A24A3D3E4F16BBD1F267C848321CE5C /* PFPushManager.m */, + F2DC5E77E5C9D2DECE225B993BC0DBAA /* PFPushPrivate.h */, + 44DA91E0DF5B0A8CBEA09F5223C6F1C8 /* PFPushState.h */, + 50C7A024063739A9D4A5CD90B47FE50E /* PFPushState.m */, + 794BFE228ECF6346EF495E3238904178 /* PFPushState_Private.h */, + 512A5EA836765109A6CA0BF4BF2BE6C0 /* PFPushUtilities.h */, + 3FD488736BA7D2666128B1D854BD802D /* PFPushUtilities.m */, + 9E812FF98BA70450EEE9168727E42BC7 /* PFQuery.h */, + 3819E8A9BE558DAF234B89FC4CC41178 /* PFQuery.m */, + 2A7D8F6CA1BC15F07535717374D4678F /* PFQueryController.h */, + CA1F50181C77DE8DD24C638D484FAB4E /* PFQueryController.m */, + 2B3641265C060CC5C48EE34815FA7F68 /* PFQueryPrivate.h */, + 3E72FA549FC9512FB4BDF3D360A35D8F /* PFQueryState.h */, + DE8A3CFFE733DBF362B551E0B80C6865 /* PFQueryState.m */, + F7404597F10DBE7FD0CFA519AF9692E9 /* PFQueryState_Private.h */, + 204C2D996771F45E7C30302ECFA3D5C5 /* PFQueryUtilities.h */, + 99DE5A4D7BE3DFF919CE242FB1FD465D /* PFQueryUtilities.m */, + FB3E17BA104D20642FE22B05ED16F84B /* PFRESTAnalyticsCommand.h */, + 8D61280C3437A784DBF19D45B384C011 /* PFRESTAnalyticsCommand.m */, + E9F581D64C0B691E6A0B6B3A83240F45 /* PFRESTCloudCommand.h */, + 15DC305FBA250D68B59D7EC94B7F39C8 /* PFRESTCloudCommand.m */, + 3EBA0F2EDA08DA37F7D8E7B9150E9A29 /* PFRESTCommand.h */, + 705C8317C0CF03B733A5A25FB46A66EC /* PFRESTCommand.m */, + 8CB28E2AD26FDC0C0ACEA80136C6E997 /* PFRESTCommand_Private.h */, + E606965BE9A1625B19ACD6701319EDAC /* PFRESTConfigCommand.h */, + E059144E243A519EF7E69EE1C229A7E6 /* PFRESTConfigCommand.m */, + 0BFB709FA1612173F12A1B53A27F1636 /* PFRESTFileCommand.h */, + 760C685581C23015279373B19B8816EE /* PFRESTFileCommand.m */, + 83FFF174FF00A3AD178F468EC8D45ADC /* PFRESTObjectBatchCommand.h */, + 672FF2A8DEDB8B294A94B94B44170E86 /* PFRESTObjectBatchCommand.m */, + 5AC7EE67669C15374170942A5ADB449F /* PFRESTObjectCommand.h */, + 1C48F5063ADC03C48C88120E75A86527 /* PFRESTObjectCommand.m */, + 2185E69315D22B3715A623F06696D5FC /* PFRESTPushCommand.h */, + 76AFC841FC9BC38C174F77AE81A883BA /* PFRESTPushCommand.m */, + BE60DEF5EAA342275F7F61958BEEC1A2 /* PFRESTQueryCommand.h */, + 2DB17A9B3DDAC837FD6453FEA310E848 /* PFRESTQueryCommand.m */, + EF18CF4976108C743495C278626BE89F /* PFRESTSessionCommand.h */, + 7F148B720ADCC2D2110CC085A4F393C9 /* PFRESTSessionCommand.m */, + 7516FAB60771B12B16D06A78AB5986AF /* PFRESTUserCommand.h */, + F585A21BC1DD8875804B22148772F089 /* PFRESTUserCommand.m */, + D6B24FD6C8A63E8AC6B7A647B38F005D /* PFReachability.h */, + FF500FE268A2149DF765B8D6732382E1 /* PFReachability.m */, + 56833EA79C5134523EDBB3F9CED7E72F /* PFRelation.h */, + 22855645AE9EE73E2D1C09E998BEEC66 /* PFRelation.m */, + 989B472DE53DB8DD10EA5CEB92D70912 /* PFRelationPrivate.h */, + FA7ED9D15FFE34ED094DC7FC1163FB2B /* PFRelationState.h */, + 0655621C56EA9C2125947E9B169D3D63 /* PFRelationState.m */, + F9FDF2BF02C33F3194A8064A2D1268D4 /* PFRelationState_Private.h */, + E32CAB189409C70ED348320F66BA97D3 /* PFRole.h */, + D5F164F971C806E166D9BD66BA1C0ED5 /* PFRole.m */, + CFB015761F43EBA9801A2489A67F1918 /* PFSQLiteDatabase.h */, + 355416180C97110767845F8B71F32709 /* PFSQLiteDatabase.m */, + 1EACD763C478D5DB9B0370480A11B1FD /* PFSQLiteDatabaseController.h */, + 991F2AB1EAD16285E8C86C68489117B2 /* PFSQLiteDatabaseController.m */, + E3B8C005EBA4D0C4EEDEA5777C45D260 /* PFSQLiteDatabaseResult.h */, + 5C27666D25206D0C9228E2B561CFCEA9 /* PFSQLiteDatabaseResult.m */, + 78B65A9361B5999AFC577A6CAE34D1D6 /* PFSQLiteDatabase_Private.h */, + D53047A416F23A9006EDD19E7BDFBF65 /* PFSQLiteStatement.h */, + 9BF22022C7257232D4C9D9938A8AA1F0 /* PFSQLiteStatement.m */, + EF10A3E0E41722320B57E8C181D31679 /* PFSession.h */, + 0E5E5504191D61A91B249136F90A7F29 /* PFSession.m */, + DCA33D6FD4CA15B4EDB933F4A4E75327 /* PFSessionController.h */, + 18575CBC596E9A1218A762DF5E3CD72B /* PFSessionController.m */, + 732BF5F5AADEEED43943EDD827DB02D2 /* PFSessionUtilities.h */, + BD39E8AC572A0A0356F1EFCBE146731F /* PFSessionUtilities.m */, + BF69C85C516E15E59322803B5CB90959 /* PFSession_Private.h */, + 5444BD2EF92DA71A5E0E4E368258FCF7 /* PFSubclassing.h */, + D6F5D08B400E69B890800EB5B5DCCB3F /* PFTaskQueue.h */, + DCC0772B25ED51809E78EE58DC1C44C0 /* PFTaskQueue.m */, + 6DA3B7EDAFEC180CEDD3C7393AD44FF3 /* PFThreadsafety.h */, + 1A23CAEC9BE172289170838599F9F895 /* PFThreadsafety.m */, + 51672A9A990271236F6EEF5F4BE45E36 /* PFURLConstructor.h */, + E54CDA1D27EA0FCD1AB663DD1BCC22DE /* PFURLConstructor.m */, + 48BA00A680115FA8E8598713DD4F3F53 /* PFURLSession.h */, + CFF05FDAA5BE92C3290288A41691E4EB /* PFURLSession.m */, + 22AF66FAEC41A180692A915DFAA6C452 /* PFURLSessionCommandRunner.h */, + C743D5BE4BE093B67F21D64BDAA0D64F /* PFURLSessionCommandRunner.m */, + FCC2C950B794131718A14019810BC78F /* PFURLSessionCommandRunner_Private.h */, + 24627238BB902D6C44D91F716A3B4005 /* PFURLSessionDataTaskDelegate.h */, + 5C8EE00FBF100049803CBD08E3488726 /* PFURLSessionDataTaskDelegate.m */, + 61A68A9C9F4DECBC21FBD79D006013B1 /* PFURLSessionDataTaskDelegate_Private.h */, + 7F6399FA5304D192B1A4A7C0D2EB53A1 /* PFURLSessionFileDownloadTaskDelegate.h */, + 4FB104646E63B185FAFECD3B01CDA7DD /* PFURLSessionFileDownloadTaskDelegate.m */, + 56644183C8AFDEEF3AAC26943A7C8825 /* PFURLSessionJSONDataTaskDelegate.h */, + 507EEEA0E2D71194AF983A704F2B50B1 /* PFURLSessionJSONDataTaskDelegate.m */, + F7B2DE64FB97BC59338A0F063F7286B3 /* PFURLSessionUploadTaskDelegate.h */, + DBF37D953B714180930569746452F6E5 /* PFURLSessionUploadTaskDelegate.m */, + DD1F25F1B4E921548F381AD4C410B41C /* PFURLSession_Private.h */, + 3FEB8673C94646984FA528D09A6716B3 /* PFUser.h */, + B4ABE5C41AF16EA4023696A3604580C9 /* PFUser.m */, + 05A2290271BB3E67835291187DAB8318 /* PFUserAuthenticationController.h */, + 54B19C063A5F2D93FEFE0F4D66A9DEA4 /* PFUserAuthenticationController.m */, + 7E1C9CCFB4EA178BC262A1EF44D11ED1 /* PFUserAuthenticationDelegate.h */, + 00A646C084294FC23A482874264A9E10 /* PFUserConstants.h */, + 8FC25E97FDE9217C1F682376CF331421 /* PFUserConstants.m */, + 7E8BE2C27FB13B26072B0BD9EE14FBAE /* PFUserController.h */, + 40AEA2C7205FE550939B0F7F408FCA85 /* PFUserController.m */, + 429BE42396DC64B472A5559E886AEA42 /* PFUserFileCodingLogic.h */, + C7F5B41EB0986C8391083C4127B553F0 /* PFUserFileCodingLogic.m */, + 845AECFE8DD82849CB3F5B87E2537205 /* PFUserPrivate.h */, + F676ADEBE61FEA6C5EE24C12595B59B4 /* PFUserState.h */, + 462806D14A2A143E44F927E5742857E6 /* PFUserState.m */, + 9C4A6A6A2B2F9A47F6A8A876DC4E0983 /* PFUserState_Private.h */, + 1FAD8490F27C97455330C5FD12D68BB7 /* PFWeakValue.h */, + 3DBE2DCDD433C14FC53555C1A83AB199 /* PFWeakValue.m */, + E5B0E2B0324CDAD8CD56AC4EBDE6529E /* Parse.h */, + 9C9379B624B1D99D2ED8683BF8A27499 /* Parse.m */, + DB79A3ED3B802D7B73925524C32EEDE7 /* ParseInternal.h */, + CE7E387CE6912A38238CECCC234B822D /* ParseManager.h */, + 943FE65B8F32DCA748FD4B871663F421 /* ParseManager.m */, + 2F4B2107EEEB8DF1A30920BAB2D9CA6C /* ParseModule.h */, + 02054704AC6B3A8FF8526EA73E182B80 /* ParseModule.m */, + BBD4D42F15B404983137C87294C33298 /* Parse_Private.h */, + 3089E971632BEEDE9552A8E0864A03C5 /* Resources */, + B794AFBF355CBC63E4FE67CA0EDC5CF7 /* Support Files */, + ); + path = Parse; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 4A110FC22D96655DDDE604E022DAF408 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AA36320307C96E4ED20F579ACC8E4959 /* PFActionButton.h in Headers */, + 9F20656218CF341B2E1E258D6B5636DE /* PFActivityIndicatorCollectionReusableView.h in Headers */, + D08751DD45A35666D2A21C5D8FAE7D55 /* PFActivityIndicatorTableViewCell.h in Headers */, + 08106DF3AF42B507248F4E808A0897AF /* PFCollectionViewCell.h in Headers */, + 3554685EA350E46A8BF0B1A3F24011A7 /* PFColor.h in Headers */, + DD9B950C3002FDC444423323E47633AE /* PFDismissButton.h in Headers */, + C6A19A7E7E0B9AC4D3122986AEDE3B65 /* PFImage.h in Headers */, + 24C01390E2A670ADF511B2D28AEEBAE8 /* PFImageCache.h in Headers */, + CEFA2437E70B9FA7380076C38D61E499 /* PFImageView.h in Headers */, + 0A536922388C321F57A81443158DACDE /* PFLoadingView.h in Headers */, + 5E3F5671288D9DB57226C64C9FD2AE36 /* PFLocalization.h in Headers */, + C06ACA81B1DE4ADFD3E431EED2FABAEA /* PFLogInView.h in Headers */, + 5F5814DD80E470A8C54ABD6C213F385F /* PFLogInViewController.h in Headers */, + 848AB16C7E9619CED28F76E270CB5947 /* PFLogInView_Private.h in Headers */, + 09335FD01568F3B002AAC964192F969B /* PFPrimaryButton.h in Headers */, + FB83633D6A4E517EF52ECA6789A8A840 /* PFProductTableViewController.h in Headers */, + 5B686292B1A3A8DC0CEAA5622464DC89 /* PFPurchaseTableViewCell.h in Headers */, + 7994F1F9AD63A1AB341DBBD97E83C944 /* PFQueryCollectionViewController.h in Headers */, + 348DAF0BF5F5CBCD95FA719F1401E337 /* PFQueryTableViewController.h in Headers */, + E565E1D35899A503365C19F3E4FBD63A /* PFRect.h in Headers */, + BA3C5BA167AA8A30FAD75D7707560B2B /* PFResources.h in Headers */, + E8E8383F6896F4A23BD44D554B176214 /* PFSignUpView.h in Headers */, + C7119587C9AC58601B906C394A3DA295 /* PFSignUpViewController.h in Headers */, + 69733C722E4B8B2E6013D10A159655BE /* PFTableViewCell.h in Headers */, + 7F863FF278E81A50CC9BDB4CC2718287 /* PFTextButton.h in Headers */, + 45808E59D592E09E43E1EAF3FE079966 /* PFTextField.h in Headers */, + F95E160C43A5426EF1CE7C41BA653AEC /* PFUIAlertView.h in Headers */, + 1613B47B1EEF674499C27C7AB558C4EB /* ParseUI-umbrella.h in Headers */, + DFD220860A4E0A6970BB5574E905463A /* ParseUI.h in Headers */, + C4EC9F08AC7FF335750C4CE87A41DA59 /* ParseUIConstants.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5CE017DD41D0A5F403807B102D4263B1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B67A2D8518758097E23A41E560C913D3 /* BFTask+Private.h in Headers */, + DB956A9E22CEC6FAB58AFDBFB79D1DD5 /* PFACL.h in Headers */, + 785D935F51FAF8FCD765523DF777E1CE /* PFACLPrivate.h in Headers */, + AA01152D542E7E12082FC9C9A0398905 /* PFACLState.h in Headers */, + BEB01521D76C24A6006F2375E62EBE9A /* PFACLState_Private.h in Headers */, + 3522D04AB2924B8342CEB7DFC01CD60B /* PFAlertView.h in Headers */, + 0C734B29E5608655312734DD93939138 /* PFAnalytics.h in Headers */, + 5C54E1590E2C0BFB06E37FF101E1BC96 /* PFAnalyticsController.h in Headers */, + 3253159ED9BC75770A0C733C36AB2803 /* PFAnalyticsUtilities.h in Headers */, + 3A5E6295EACF62FC5DE9A5198BCCA4F8 /* PFAnalytics_Private.h in Headers */, + 401D8937417175A88D92BAE68734C18D /* PFAnonymousAuthenticationProvider.h in Headers */, + 0C2E1C50D52AD202C6986A21C35E9B98 /* PFAnonymousUtils.h in Headers */, + 68FC00787E858A0969E68E3E0F0FAF67 /* PFAnonymousUtils_Private.h in Headers */, + 36D1557E4A4012CE623C822D680F6733 /* PFApplication.h in Headers */, + 46CAE74A812BDAE95CDBE19668E3FFAA /* PFAssert.h in Headers */, + 9D54E06DAD98AB6BBAA811607B6FA647 /* PFAsyncTaskQueue.h in Headers */, + C1A4214B6F941388EE967D9C78FC1EA4 /* PFBase64Encoder.h in Headers */, + 078FA3A20BDFB81A4FFD0A31D4B5111E /* PFBaseState.h in Headers */, + 864573D466DDF7D459E6770A6B6C11A4 /* PFCachedQueryController.h in Headers */, + 18170D405D4E07AAB5093996D5FF806A /* PFCategoryLoader.h in Headers */, + 5A11D3C56318FE645F116EBD476944DA /* PFCloud.h in Headers */, + 7935B1FD00EAAAEC2671632131220FF6 /* PFCloudCodeController.h in Headers */, + 4CB11F4E752B79BD7254A8C92251BDBA /* PFCommandCache.h in Headers */, + 4A89C6C3C1D00D8D2C3FC08CDA4A4AC9 /* PFCommandCache_Private.h in Headers */, + 75EB4AA618AD72C243D405185DDE53CC /* PFCommandResult.h in Headers */, + 8E9E1A96CC750BA1EDDBB9A8C173CD9E /* PFCommandRunning.h in Headers */, + EC0AE4ACC555A6E60015D73FA5EDAEA3 /* PFCommandRunningConstants.h in Headers */, + E222FB880049F8BF95AAF41ACFEF5541 /* PFCommandURLRequestConstructor.h in Headers */, + AEF1B66D4AA99B06499C0C7E8E5A111E /* PFConfig.h in Headers */, + 6E85D655533AB0656270E012633601F0 /* PFConfigController.h in Headers */, + 1A88AC0FD5D36F26EFC2909C07E61465 /* PFConfig_Private.h in Headers */, + DC66F61BB187483967D2D7275353B4C3 /* PFConstants.h in Headers */, + 78D905717976D101C7DA318C7FCDCAC0 /* PFCoreDataProvider.h in Headers */, + B6BBFB374E4B671479942F03E1679FF8 /* PFCoreManager.h in Headers */, + 29079B4000189CFF9ED8942817028F0C /* PFCurrentConfigController.h in Headers */, + 20E80B94E0147D15323F2E0E9CDAF716 /* PFCurrentInstallationController.h in Headers */, + 780C569F76863BE9D4BA20617FD6F225 /* PFCurrentObjectControlling.h in Headers */, + CC82E5DFB7A9CCB82D1866B8883ED10C /* PFCurrentUserController.h in Headers */, + CE0E925439858FFAC1675700D4F1808D /* PFDataProvider.h in Headers */, + B9556120221EFA0E77536284877FC930 /* PFDateFormatter.h in Headers */, + 1E70591D33CD8122762393BDF38E8782 /* PFDecoder.h in Headers */, + 1BF39039FD4EE83618B9EE1D8EF4785D /* PFDefaultACLController.h in Headers */, + 02E33B61D269EABF398A476592412A27 /* PFDevice.h in Headers */, + CCD2A046B799983FE3CD0A859C9A66C4 /* PFEncoder.h in Headers */, + 656ECDEF31D91456E7850E8E85370927 /* PFErrorUtilities.h in Headers */, + 41ED8DF2035DEA37104F79E3E69F8788 /* PFEventuallyPin.h in Headers */, + B2B712D1FD284ECF39175E2A9D484005 /* PFEventuallyQueue.h in Headers */, + 2312D40E90058221C6E0206FC057CC31 /* PFEventuallyQueue_Private.h in Headers */, + 3DEAF8E13BDCD03934CF1C7DB3599C3D /* PFFieldOperation.h in Headers */, + 3EAD53BE9AE4FB212FA1A92DB5EC3B6F /* PFFieldOperationDecoder.h in Headers */, + 7DC621930B2F4E596421DD0C2A61ED77 /* PFFile.h in Headers */, + EA5748A0206BED75751EECF565E27BF1 /* PFFileController.h in Headers */, + C6524C189EDE63AF7D294A6C5358D6AF /* PFFileDataStream.h in Headers */, + 4B1BB47C1D2A7C76F6FD7DF66C36AB42 /* PFFileManager.h in Headers */, + 965FAF38F77ED3FEF7D0D8C50C8C1DA3 /* PFFileStagingController.h in Headers */, + A435900BF6320810472F54B97F3E8F88 /* PFFileState.h in Headers */, + 270C1EA6421D70034FA2635DF116C88F /* PFFileState_Private.h in Headers */, + ADEB7F252E4D8644894759C130122B39 /* PFFile_Private.h in Headers */, + E8D255D61748A673394F29E8E685451C /* PFGeoPoint.h in Headers */, + 8C254A48F3F863993BB08938A496D1DE /* PFGeoPointPrivate.h in Headers */, + 326035FFFA592FB552CF238BEACAC5D2 /* PFHTTPRequest.h in Headers */, + E6452E0C294D4311EFCA899EB7078E0D /* PFHTTPURLRequestConstructor.h in Headers */, + C661587D325AB943770C396151BCC10B /* PFHash.h in Headers */, + 0FBA8F37F9314C43FB6804EA1543F175 /* PFInstallation.h in Headers */, + 1E90FB160DC21B9FE1221524941169A2 /* PFInstallationConstants.h in Headers */, + B94DD88DCFA4FA68E2DB9D0EF5038178 /* PFInstallationController.h in Headers */, + 1D16B974DF5E638237BF864A013FCC8C /* PFInstallationIdentifierStore.h in Headers */, + 2040FDF184F9FDC4EF229C4A73F990F8 /* PFInstallationIdentifierStore_Private.h in Headers */, + F3A28913EF79533BDFA91D252F5543BF /* PFInstallationPrivate.h in Headers */, + 24469F3BA342CC724B6BC8C254339850 /* PFInternalUtils.h in Headers */, + C6E564817BFE774E6635838076C7A3A7 /* PFJSONSerialization.h in Headers */, + 6CD7EF58347DE8E8F9302C1E4E48CD09 /* PFKeyValueCache.h in Headers */, + 7117E42D48773D23BBB822C7C8EFF934 /* PFKeyValueCache_Private.h in Headers */, + D252449F0A676976166C795E6045F48D /* PFKeychainStore.h in Headers */, + 16D80822E7C12B2E59DDF83749C9A6A2 /* PFLocationManager.h in Headers */, + E8DE4FB78CBD67CE6F4A9549F5D567EF /* PFLogger.h in Headers */, + 968B64EE8DF5B607A42DE7D9C49A4818 /* PFLogging.h in Headers */, + C489C653E604077718DC890DBB239D3B /* PFMacros.h in Headers */, + 0578CF412D50D2967A728990BCD5A5A7 /* PFMultiProcessFileLock.h in Headers */, + E446F8E01E139AB0E33B2734594BBF5D /* PFMultiProcessFileLockController.h in Headers */, + 7AB6B720A910774A4AB2CEFEC715FFBF /* PFMulticastDelegate.h in Headers */, + 8F2FCDFB0D49482D2566E84C51E884E9 /* PFMutableACLState.h in Headers */, + EDB941FC0B56936293F8CAF06EE5D816 /* PFMutableFileState.h in Headers */, + 9400AC2DB5D43ECA054FE3B06E8BB37A /* PFMutableObjectState.h in Headers */, + 045864165E699D965D1D6C8AC89F46F4 /* PFMutablePushState.h in Headers */, + 3E015DAC9467586A55EFFF4258040045 /* PFMutableQueryState.h in Headers */, + 718A3D999C43CA591A064B44D9CA4CC1 /* PFMutableRelationState.h in Headers */, + A34200F16E337F39152862E2676D1F45 /* PFMutableUserState.h in Headers */, + 0F06E69D48EA3A9505C5B39038ECBD45 /* PFNetworkActivityIndicatorManager.h in Headers */, + F2FF12651AD453407868C5A9FDE1D356 /* PFNetworkCommand.h in Headers */, + 75A0194BA98F24F47B2BF003A8CFFE8A /* PFNullability.h in Headers */, + CFC2FB94F8818416123342F50F714372 /* PFObject+Subclass.h in Headers */, + 8A8CD0FF9AB48353984FE2E0CBF3B3B6 /* PFObject.h in Headers */, + 83FF786E3A812E473C16DF15A0D012D0 /* PFObjectBatchController.h in Headers */, + F230ACD1550BA6A59F6E742DD67C170C /* PFObjectConstants.h in Headers */, + 36B8C3052C0A23C277DD01695411808C /* PFObjectController.h in Headers */, + FAB18BCD6E2BEA3572300AFC1A1C8956 /* PFObjectController_Private.h in Headers */, + 942024336383DAF1D5B99DB9F2DC21C9 /* PFObjectControlling.h in Headers */, + D1F0BF670D9D53F930FF535324C883F4 /* PFObjectEstimatedData.h in Headers */, + CDCEA48FE048597A56BD5E5218FEA8F5 /* PFObjectFileCoder.h in Headers */, + E7C9C47C276C47F4C4D0802F170935A8 /* PFObjectFileCodingLogic.h in Headers */, + BAA3FE6F5D29E102D52EB92605E9D7FE /* PFObjectFilePersistenceController.h in Headers */, + BBB6C54E1A2A7336608EA35018C90C8F /* PFObjectLocalIdStore.h in Headers */, + F2A86D992230867FB33256D7AD9BB6E0 /* PFObjectPrivate.h in Headers */, + 78BFAEAF0B2EA186922D32B1785F76B2 /* PFObjectState.h in Headers */, + B74C351F5944C2717452B80E3FA53F69 /* PFObjectState_Private.h in Headers */, + 750EDE0D5F97545B54BB3E3C50E3C086 /* PFObjectSubclassInfo.h in Headers */, + A19B38AE88BF60F1E0C15CE84C11748F /* PFObjectSubclassingController.h in Headers */, + 93F1DFEC537C952B413A397CB53A66EA /* PFObjectUtilities.h in Headers */, + 774FC23D0813F55FB1FEEA0634844368 /* PFOfflineObjectController.h in Headers */, + ABF3BDDE2F977F7A2B3C72891A4C36DB /* PFOfflineQueryController.h in Headers */, + 80676C1184F6053DED9E700E221815AB /* PFOfflineQueryLogic.h in Headers */, + CC6F2E4191E9283AFB8144CD08325E88 /* PFOfflineStore.h in Headers */, + 98B454C78DCA44E4C57459F3313CA18A /* PFOperationSet.h in Headers */, + A10C34E2245BEBEA3E71717521DC60A8 /* PFPaymentTransactionObserver.h in Headers */, + 700083BCBF89F1F2496A63EC135380B9 /* PFPaymentTransactionObserver_Private.h in Headers */, + 201EB0EBCBB978D30FE61D193981E792 /* PFPin.h in Headers */, + F8856CC3DBA9D9B6455C568F8796BC20 /* PFPinningEventuallyQueue.h in Headers */, + 46AAFE4159990B1B24B19083E6D353EA /* PFPinningObjectStore.h in Headers */, + C1B05E4A12D43587A60E1B9946796B18 /* PFProduct+Private.h in Headers */, + DEE5BCE9AFBA178D30233B54597CCD80 /* PFProduct.h in Headers */, + B290012C1BFD473992D40F3028D9714A /* PFProductsRequestHandler.h in Headers */, + 2C7FFF08D22648BDFDCA04221F03292B /* PFPropertyInfo.h in Headers */, + E2219536CEA9BEC7A76770AF29A650DD /* PFPropertyInfo_Private.h in Headers */, + 8A0C6DDE11044F1E0632BDB39065A5CD /* PFPropertyInfo_Runtime.h in Headers */, + 68C83552D7F1D6BFA851D45139FFFFE3 /* PFPurchase.h in Headers */, + A914B7418E782BAD7F60497841D2145B /* PFPurchaseController.h in Headers */, + 67C83A746D19DD80AF019885B8EF373E /* PFPush.h in Headers */, + 189ECA4A40E0CCCBEF2308D570774095 /* PFPushChannelsController.h in Headers */, + 2D541155B362A010D360AF90A77C61B2 /* PFPushController.h in Headers */, + E34D1AC12E3CFF058D9974C1062CA3D4 /* PFPushManager.h in Headers */, + FA7FE52B70834921A9A11FFE5DF1FDF8 /* PFPushPrivate.h in Headers */, + 11B3F1A7B5CE93E9B8A20FD11698DB5F /* PFPushState.h in Headers */, + 145685209448E41A05DF8B53183B7711 /* PFPushState_Private.h in Headers */, + 1CA4984E2ABA469F1EACB26D21AE2350 /* PFPushUtilities.h in Headers */, + 2FDCE355A28690CCA7BD7110830D9D8F /* PFQuery.h in Headers */, + 74DEA9984AA2FF026E44CCCCACC64B94 /* PFQueryController.h in Headers */, + 9F7EAF9DE82EE443155A4C21F5197564 /* PFQueryPrivate.h in Headers */, + 991FF5E1E35201E80B6BEAB721A4CB43 /* PFQueryState.h in Headers */, + C178BFD5E2DC1673FB7BCB1E2EB5A315 /* PFQueryState_Private.h in Headers */, + 526DA6851CA12A57C70B0FBD2A35235F /* PFQueryUtilities.h in Headers */, + C6D03A8CC40B89A276CC3DB1CCC70E61 /* PFRESTAnalyticsCommand.h in Headers */, + C8B39614F7A143FD25D2414E8E4C0885 /* PFRESTCloudCommand.h in Headers */, + AE25982BB866C5E33F8C7EC11111C4C0 /* PFRESTCommand.h in Headers */, + 6A75B6D4AD48ABA0F24FF40F6B93E7AA /* PFRESTCommand_Private.h in Headers */, + 93E26AB74B58B7E5B42617ECEDE3E873 /* PFRESTConfigCommand.h in Headers */, + 33E6F85F30FC19902B3D8DA8B88D0B7C /* PFRESTFileCommand.h in Headers */, + CD899A81849EC051B571CB322E422DBE /* PFRESTObjectBatchCommand.h in Headers */, + 46F6819FB8D22AC3FB2EED6BC5D68659 /* PFRESTObjectCommand.h in Headers */, + 33DAD155F6CE2CC1823D7AD97B3FB2AD /* PFRESTPushCommand.h in Headers */, + A9573C013CD6EF6AD84775DA6341C9B4 /* PFRESTQueryCommand.h in Headers */, + 8EB1135323B836CD9A411906B4CC355C /* PFRESTSessionCommand.h in Headers */, + F3125E8E5787E882BEC4C6ECE5200AD6 /* PFRESTUserCommand.h in Headers */, + ECB75C80159A0B7A7FB9F6966B778EFD /* PFReachability.h in Headers */, + E89760E83C89096C84689740006F8FF6 /* PFRelation.h in Headers */, + 14A353C6005E964A160C5937FC711780 /* PFRelationPrivate.h in Headers */, + 9B9439D4790DB807F1084F262EEC33B7 /* PFRelationState.h in Headers */, + CED7B949ACFAB437975D211E982738B3 /* PFRelationState_Private.h in Headers */, + 8B1EBEC941EEB487F8E31F12FF27C4BE /* PFRole.h in Headers */, + E3EFFB5B782650B12F4E6E47ACC30F45 /* PFSQLiteDatabase.h in Headers */, + 9116FADD3A8ADE5390F11746981D796C /* PFSQLiteDatabaseController.h in Headers */, + 8EFEA3E1E419AC0B614DE1EE75A16542 /* PFSQLiteDatabaseResult.h in Headers */, + 492833D120B8C740A5C3108E060E0647 /* PFSQLiteDatabase_Private.h in Headers */, + DE66B92ED03D4AF38F76CC5CE19E5B48 /* PFSQLiteStatement.h in Headers */, + E338DEBC75FABD977350EA8740945F27 /* PFSession.h in Headers */, + 127301843CBDF317164B7E181CB7DE71 /* PFSessionController.h in Headers */, + 621D2302EAFB9263E928B7FD16B54FB6 /* PFSessionUtilities.h in Headers */, + 2C43AEB6664F383055A152BE8742E1F1 /* PFSession_Private.h in Headers */, + 70186153736C45D0242EA55CA995A91F /* PFSubclassing.h in Headers */, + F55E1D0D9A935666D456A85EACD2A071 /* PFTaskQueue.h in Headers */, + 39B709F501A90E965517806018B70AA6 /* PFThreadsafety.h in Headers */, + 8DC6FEA184189AD19550D4B93E0BFF3A /* PFURLConstructor.h in Headers */, + 60F56114B9878A79D52F1EF542BDDF46 /* PFURLSession.h in Headers */, + 00BD5BD822B2BD7B861DAE0FFFFCB539 /* PFURLSessionCommandRunner.h in Headers */, + 71C5D30743E01525E0CFE90793723FCC /* PFURLSessionCommandRunner_Private.h in Headers */, + 98DD45B682B721438042FC1A4E9BCC58 /* PFURLSessionDataTaskDelegate.h in Headers */, + 8A81554451206C4EB47FC738CC987853 /* PFURLSessionDataTaskDelegate_Private.h in Headers */, + A9F35EEE5158FFA497C625648F1C339A /* PFURLSessionFileDownloadTaskDelegate.h in Headers */, + 7A7EF5E2C542E50F933AEF6A64C8BEF2 /* PFURLSessionJSONDataTaskDelegate.h in Headers */, + C0DCE7B51C9D03F06A3C3DDA64525971 /* PFURLSessionUploadTaskDelegate.h in Headers */, + D6DB8392C0A03EDF979FAB4CEE4E6910 /* PFURLSession_Private.h in Headers */, + D5680DC4F4807F789036FFCFB8E4A2FB /* PFUser.h in Headers */, + CFC7FDCE35D5355D88A95151A9F811DE /* PFUserAuthenticationController.h in Headers */, + A91AE12725616B763AE00A41BC002679 /* PFUserAuthenticationDelegate.h in Headers */, + D7F7CBF073AF3E67F72D9781DE055CE9 /* PFUserConstants.h in Headers */, + 2646DA8EC00C15C5B326790D44AA4096 /* PFUserController.h in Headers */, + 9DD3597AEA92F3780BABE3C06618611E /* PFUserFileCodingLogic.h in Headers */, + DCBD22933F6F7C79D30FDD222A9C3FDD /* PFUserPrivate.h in Headers */, + 9F600D37D91D1A347E13AA9F312F54D4 /* PFUserState.h in Headers */, + 1921B71874A179ECF5CD1A7E18A3080F /* PFUserState_Private.h in Headers */, + CB8F587614BC2C7758AC08D2EFB16B45 /* PFWeakValue.h in Headers */, + F0CD11A4CE4F909511C1ED18E2022B77 /* Parse-umbrella.h in Headers */, + 47AD2DB520B62717195FDD28C649AC46 /* Parse.h in Headers */, + DA3941CA4F6E79C371612E5A47E48EE9 /* ParseInternal.h in Headers */, + 17950032F4E1AEC0308396ACF5DED48F /* ParseManager.h in Headers */, + E58E6CD4D9F888BF805EAB0D61AC9C39 /* ParseModule.h in Headers */, + 998AAB205311795AA50C6318523C4314 /* Parse_Private.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 93A30A518561D732934AF5862D4633DF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D3F6218A5157FD65597FAEAED95E2C94 /* LiquidFloatingActionButton-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A7827D62D7D26513EFE0E94E088DFF33 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 33644DC407C29B3D9EAEF7ADCEB52478 /* Pods-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BB26F12C410478C8319D64121FE18E71 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 57E300D705DF13EE8CCB58F57A39279E /* BFCancellationToken.h in Headers */, + B2A54F6322617DE35C8E3ACE8933BA72 /* BFCancellationTokenRegistration.h in Headers */, + D35D2CE6056C72D35B88BA436BFF624B /* BFCancellationTokenSource.h in Headers */, + 1A4D3B715AC215113E1C1311CCE7640F /* BFDefines.h in Headers */, + 15D50F7DB95440DA43E8E2AA2471ED8A /* BFExecutor.h in Headers */, + 0552F35AE0E7BFC8D7B7A76B91231948 /* BFTask.h in Headers */, + F61B1982A3B7BE89BA56AE9C6141B7F1 /* BFTaskCompletionSource.h in Headers */, + 6A9EC2B6225A295D92F851A47E6E9868 /* Bolts-umbrella.h in Headers */, + D4FCBED6C982A98D9C2C211D2D993575 /* Bolts.h in Headers */, + DE5B43DB552929FED6D87AF8FBF2A271 /* BoltsVersion.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EDBA133131B380AA6CD2237F8936A633 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0DA782AD0BCA5BF57EBD9D5F4D92392C /* AFHTTPRequestOperation.h in Headers */, + 1565665806FC25DC6AE914B7BA7B2553 /* AFHTTPRequestOperationManager.h in Headers */, + 84944515F98B102C3B136CCF8CAF3653 /* AFHTTPSessionManager.h in Headers */, + 6A7C09F94CE721DA3E5DEAF823A16489 /* AFNetworkActivityIndicatorManager.h in Headers */, + 06EAE725360DDE449CEE256BED435CC0 /* AFNetworkReachabilityManager.h in Headers */, + 019811B1D67F59A225A5ADA70257444E /* AFNetworking-umbrella.h in Headers */, + B9419321553FD57039C0F8F67830DB16 /* AFNetworking.h in Headers */, + A9E63E749F651448435169ADAF5DC93E /* AFSecurityPolicy.h in Headers */, + C4AB1E1C68ED4F07AC2F08DBFD72D33C /* AFURLConnectionOperation.h in Headers */, + A245EE82A2CBF7B209BE48C2C1E3703A /* AFURLRequestSerialization.h in Headers */, + 5B7C81385D9434AB6D4AC01CEB880D5C /* AFURLResponseSerialization.h in Headers */, + 2827B74CEE40DE2507B095595338AF40 /* AFURLSessionManager.h in Headers */, + C1EA98783317E069BC38EF24E8E4C3A0 /* UIActivityIndicatorView+AFNetworking.h in Headers */, + 286A7911AA124BAF979E6C477A1D399B /* UIAlertView+AFNetworking.h in Headers */, + ECCAEFEAE2CA7A873E00E3A86F916350 /* UIButton+AFNetworking.h in Headers */, + 68B669C1D98F5D8B5ECB996FD10B0E88 /* UIImage+AFNetworking.h in Headers */, + 3326E4DE6998C24487160FA02D480584 /* UIImageView+AFNetworking.h in Headers */, + A730FB13DB74C80787B19682ADD28A4D /* UIKit+AFNetworking.h in Headers */, + 179674B0B17481060FA4981ABF6EFF85 /* UIProgressView+AFNetworking.h in Headers */, + 358CEE0E9DB4815A038EDE76B8D6E617 /* UIRefreshControl+AFNetworking.h in Headers */, + 86A0BB3FFEED4E9308F09E58C4331056 /* UIWebView+AFNetworking.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 136FE3EDD694CDE21E3B556E3A8831E3 /* LiquidFloatingActionButton-LiquidFloatingActionButton */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D4EE60BA5ED84273CFB8978E385C2E4 /* Build configuration list for PBXNativeTarget "LiquidFloatingActionButton-LiquidFloatingActionButton" */; + buildPhases = ( + C769E9D9EE5AE761EE8780669AF09495 /* Sources */, + F85CDCC5343143807EF71AE225920BA7 /* Frameworks */, + B6880F0B86AE3D633A530BD9D611FA11 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "LiquidFloatingActionButton-LiquidFloatingActionButton"; + productName = "LiquidFloatingActionButton-LiquidFloatingActionButton"; + productReference = 97FF6FABD68A2485CB659846729BFFCD /* LiquidFloatingActionButton.bundle */; + productType = "com.apple.product-type.bundle"; + }; + 255B486D02599C8571584A81D3995B9C /* Parse */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7FDF94920301B682907024096C818539 /* Build configuration list for PBXNativeTarget "Parse" */; + buildPhases = ( + 587FDDDE76C8CC37DA545182C973E8E9 /* Sources */, + 251E76C54507A3FC6E71B84287C06A33 /* Frameworks */, + 5CE017DD41D0A5F403807B102D4263B1 /* Headers */, + 9FC2EA63B897673FCDE523B2C17B2BB5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 564B918261E3F62724E4551F74BA531F /* PBXTargetDependency */, + ); + name = Parse; + productName = Parse; + productReference = 965A29E050253DCA29D6D53DA6E80B5D /* Parse.framework */; + productType = "com.apple.product-type.framework"; + }; + 4CFD6D06267A85EA321AFA80384E49C4 /* Bolts */ = { + isa = PBXNativeTarget; + buildConfigurationList = 81172DA6C72CCE384596B834A0DDC7E7 /* Build configuration list for PBXNativeTarget "Bolts" */; + buildPhases = ( + 78CA8605EA5EA577DADE0EF033B6A17B /* Sources */, + B2539DD3C9FA15A3D99A1D697609E77F /* Frameworks */, + BB26F12C410478C8319D64121FE18E71 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Bolts; + productName = Bolts; + productReference = 1461A3C96F9D5F77C27EB1AEF3772D96 /* Bolts.framework */; + productType = "com.apple.product-type.framework"; + }; + 6F551023845F01D3B4326C1A426E12EF /* AFNetworking */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88A92B1696C5DC161F34752E2A294DBE /* Build configuration list for PBXNativeTarget "AFNetworking" */; + buildPhases = ( + BBB621C39EF13BC7899B6B6A8F15EF69 /* Sources */, + 96927D9D82F9851F96869FCE8D3EE83E /* Frameworks */, + EDBA133131B380AA6CD2237F8936A633 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AFNetworking; + productName = AFNetworking; + productReference = 1844474781B402090680328B2DB5420B /* AFNetworking.framework */; + productType = "com.apple.product-type.framework"; + }; + 702D990824EC762012B0DCFFCF50AE79 /* ParseUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = A07805171FD0C0ACCD779EB10A56EE0D /* Build configuration list for PBXNativeTarget "ParseUI" */; + buildPhases = ( + 10E7A859E0E752935FE293A25703F5D4 /* Sources */, + 9DA283AA60EEB82C8011330FCCC35810 /* Frameworks */, + 4A110FC22D96655DDDE604E022DAF408 /* Headers */, + 9216A81964A2439CCC9A5651EF25A268 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 16A41541C9FA1848B76E9F92D5418E26 /* PBXTargetDependency */, + C2F36DE1C4F2E91313A31E15BAE1EDDB /* PBXTargetDependency */, + ); + name = ParseUI; + productName = ParseUI; + productReference = 75F25EFFB4D4BE6DA19F30E328EA3E81 /* ParseUI.framework */; + productType = "com.apple.product-type.framework"; + }; + 91233EBFBA6CA890554F6EAE664CC12C /* Pods */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6194C4F5520D9DD6BA2194F8FC01F025 /* Build configuration list for PBXNativeTarget "Pods" */; + buildPhases = ( + BBC5CC087CBFE6A21AECBA3A47EE4932 /* Sources */, + 95B3CF59FD4D70C6ECFB2F6895408321 /* Frameworks */, + A7827D62D7D26513EFE0E94E088DFF33 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + DE1BB65BA0D5D2AA7D5D19A5A2C41ACE /* PBXTargetDependency */, + 55CBAB16A5819611D6193D8AABC78DEF /* PBXTargetDependency */, + E0726DDC42AD783FB3E571B33B7C4003 /* PBXTargetDependency */, + 0E4C88DCFE8254DE82A89905FBDE4E78 /* PBXTargetDependency */, + FBBD1CC1BE1F33D6EB2477D6612659E5 /* PBXTargetDependency */, + ); + name = Pods; + productName = Pods; + productReference = 5BB2711832525BD41951DBF302F796E1 /* Pods.framework */; + productType = "com.apple.product-type.framework"; + }; + EBB54F8CD09E891EC7CDEEBB645A5141 /* LiquidFloatingActionButton */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1AA7F0D1642003497E360B27771EBBA /* Build configuration list for PBXNativeTarget "LiquidFloatingActionButton" */; + buildPhases = ( + 3CF9AC0773E6C514412994D32ABC1356 /* Sources */, + 95715F41470114BD35638B7BF4831BAD /* Frameworks */, + 569E31CD1F534AF0D8EBB2D09FEEE8F9 /* Resources */, + 93A30A518561D732934AF5862D4633DF /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 0E3E2B434F6BB10F2338532DC989A8C0 /* PBXTargetDependency */, + ); + name = LiquidFloatingActionButton; + productName = LiquidFloatingActionButton; + productReference = 06C9E684885FB5B216BF361176DDBB5D /* LiquidFloatingActionButton.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = CCA510CFBEA2D207524CDA0D73C3B561 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6F551023845F01D3B4326C1A426E12EF /* AFNetworking */, + 4CFD6D06267A85EA321AFA80384E49C4 /* Bolts */, + EBB54F8CD09E891EC7CDEEBB645A5141 /* LiquidFloatingActionButton */, + 136FE3EDD694CDE21E3B556E3A8831E3 /* LiquidFloatingActionButton-LiquidFloatingActionButton */, + 255B486D02599C8571584A81D3995B9C /* Parse */, + 702D990824EC762012B0DCFFCF50AE79 /* ParseUI */, + 91233EBFBA6CA890554F6EAE664CC12C /* Pods */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 569E31CD1F534AF0D8EBB2D09FEEE8F9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1B157E8F97EFEF7A0F6D1F4DDD08CF1D /* LiquidFloatingActionButton.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9216A81964A2439CCC9A5651EF25A268 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4F13C46D1D3F2A4BAF8177B9B721DB18 /* en.lproj in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9FC2EA63B897673FCDE523B2C17B2BB5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2801DB05C8F2FD431ACF0BD58622874B /* en.lproj in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6880F0B86AE3D633A530BD9D611FA11 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 10E7A859E0E752935FE293A25703F5D4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7D4B9D3747F65C1E648DA08F03D1B7CB /* PFActionButton.m in Sources */, + B955BF2258BCCA71293EA6A7C1BF626C /* PFActivityIndicatorCollectionReusableView.m in Sources */, + C4062AB431E3B5922E0607E6BE1E0E5A /* PFActivityIndicatorTableViewCell.m in Sources */, + 1798DBC837F4F2EC3B68A113D11DCBFD /* PFCollectionViewCell.m in Sources */, + 3D134C66893736B8053D5765CAFB4612 /* PFColor.m in Sources */, + 4C6E285F00A2CDFE1FAF6DF06575403B /* PFDismissButton.m in Sources */, + 7874249F76B027BC9A4B8A51D0660592 /* PFImage.m in Sources */, + 094FBF0A794D5BF1C85DCB72C7CEB462 /* PFImageCache.m in Sources */, + B2CB8CB0EC408A26F112F6A43412D07C /* PFImageView.m in Sources */, + 5381B409F7381F2014B347B9F6B514DA /* PFLoadingView.m in Sources */, + 46C2799A8F59D05A02F70182137726DE /* PFLogInView.m in Sources */, + 8430FC2AEBAA5F32C191D11C1552DDB4 /* PFLogInViewController.m in Sources */, + A0755244ACB6D4446E5848A4146EE0C0 /* PFPrimaryButton.m in Sources */, + 81A302025C6052BA208D7263F361DC44 /* PFProductTableViewController.m in Sources */, + 2CAE5D5F7FEE4D6E3E02FF6B4FC55855 /* PFPurchaseTableViewCell.m in Sources */, + 70CB1143B527B2FCEB046B2A0E63F433 /* PFQueryCollectionViewController.m in Sources */, + 74D69DF3AFFB7AB5DFA518A5B6527DDE /* PFQueryTableViewController.m in Sources */, + 1A180D118B0CCCC32049F0A2EB1626CA /* PFRect.m in Sources */, + 19C5B336E3E16B60F92682B59906295E /* PFResources.m in Sources */, + CE94D9C23DA746A3F83FAACA7DB0866D /* PFSignUpView.m in Sources */, + F7CBB621F38AAE3EDF1DAA7D314A8A5B /* PFSignUpViewController.m in Sources */, + 61625E7D30934B7BC53621FA9C139B32 /* PFTableViewCell.m in Sources */, + B18713A1C3565C9DD53BACCA8480FEE9 /* PFTextButton.m in Sources */, + 809D0CEFA94C80633149B337287584E3 /* PFTextField.m in Sources */, + 96A8F4E2D7C097F2368A168F1D4F13F3 /* PFUIAlertView.m in Sources */, + 3472E6DF2EAD219033569E73FDC389C5 /* ParseUI-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3CF9AC0773E6C514412994D32ABC1356 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AE96FD5DA9CE65984EC706E1FE3FEC45 /* ArrayEx.swift in Sources */, + 15B5CFA0B2EED02F63F70BF2D2B3E1D3 /* CGPointEx.swift in Sources */, + FFDB1D2A8351A7C3DDB8CE3BA970C02A /* CGRectEx.swift in Sources */, + 57EC4132D85D4A0E94B2FD7FCB0075CC /* LiquidFloatingActionButton-dummy.m in Sources */, + 40B7399BE9F3F3CF17373AB2EEEBE1AA /* LiquidFloatingActionButton.swift in Sources */, + 4ACE3C1215E0AA1F808A28890FE9930B /* LiquidUtil.swift in Sources */, + 334E4873ADAA7E84D76696B1ADD98F6C /* LiquittableCircle.swift in Sources */, + 7D3D7A872E68349C43E97C2844077756 /* SimpleCircleLiquidEngine.swift in Sources */, + 6BDAC1CB326F50E98775F898C97CEA51 /* UIColorEx.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 587FDDDE76C8CC37DA545182C973E8E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C8ADDF24915937C8CA9E62F09DF5F0DB /* BFTask+Private.m in Sources */, + 60CFBA47EFF4DD715A40F240BE982FB2 /* PFACL.m in Sources */, + D56B1994B74E244DB001467270959B3B /* PFACLState.m in Sources */, + E98C3A80DCE27BBC4726B7789809FFF0 /* PFAlertView.m in Sources */, + 49A1DC63D25763EA8658CDF54CBE821D /* PFAnalytics.m in Sources */, + D8E947D4B37996BE40DD68E74C2A5E79 /* PFAnalyticsController.m in Sources */, + 2A16E0AC8BB73128B46A57F116F3D1C0 /* PFAnalyticsUtilities.m in Sources */, + 04E4E6AF1EC5843A657C4A3931CC5CE0 /* PFAnonymousAuthenticationProvider.m in Sources */, + 83B3E6925881BB03B74E66C12F38729D /* PFAnonymousUtils.m in Sources */, + 4BBCFB1ECD9564B89796936451080BBE /* PFApplication.m in Sources */, + 502E983D00899578A876A927E8C48BE8 /* PFAsyncTaskQueue.m in Sources */, + 5BFF5F2DCEB86428DF0F31A5EAE8A57A /* PFBase64Encoder.m in Sources */, + F889510066601CF5ADD47D03C4E7E243 /* PFBaseState.m in Sources */, + 440ACE83D0D52F35AFBA5D549A563904 /* PFCachedQueryController.m in Sources */, + 4EDC94927E0356BF765ED55C8BDD063C /* PFCategoryLoader.m in Sources */, + BF0CE89CA2E90B1DBDD860C8627A5CA0 /* PFCloud.m in Sources */, + 99C8D41863F21A4A74B6230BC1267C10 /* PFCloudCodeController.m in Sources */, + F2AE695C9128B6183659654C1F5BEB52 /* PFCommandCache.m in Sources */, + 6002EDA85635EC3C9271362E11CF98A6 /* PFCommandResult.m in Sources */, + 35668F1BB7B029C903CB802CA12D68C4 /* PFCommandRunning.m in Sources */, + 4DB1360969A847B0033539BF5F704E7D /* PFCommandRunningConstants.m in Sources */, + 3215AD848B3348BD44A226B412C57681 /* PFCommandURLRequestConstructor.m in Sources */, + 0E20ACB7A86434366CF57C0C1FD99A22 /* PFConfig.m in Sources */, + B696F655FF5E1603A4996A55FDB93DC4 /* PFConfigController.m in Sources */, + 5A981A9BFFADC77591121022C15E92F4 /* PFConstants.m in Sources */, + CBFD398A6EC778FCDBA6B44E15704899 /* PFCoreManager.m in Sources */, + 1B2915600B83810C7FAED0F25DEE1A75 /* PFCurrentConfigController.m in Sources */, + 00DA73F8F06A6A3CF52EC6A908F01AE7 /* PFCurrentInstallationController.m in Sources */, + 6D2E9001DCB85565EB20DD91F92746D7 /* PFCurrentUserController.m in Sources */, + 512DB852ECC39FCBF0F50BAE3E4A104E /* PFDateFormatter.m in Sources */, + FE98B1BC5667A89098FED050424FCFCC /* PFDecoder.m in Sources */, + 872E8E63A0E32353BFC57EB6AD717656 /* PFDefaultACLController.m in Sources */, + D1751D1D843B63BD1AA4EA153BEF96F8 /* PFDevice.m in Sources */, + F5F0D5E6239309C5920C14E56DCD2403 /* PFEncoder.m in Sources */, + E4E5F3DF33BDA4EF4012C73A924AEC02 /* PFErrorUtilities.m in Sources */, + A2857C0F58EB3F5637FBFA1D4C311891 /* PFEventuallyPin.m in Sources */, + 2EFC840C6647779B53FAA27106391C87 /* PFEventuallyQueue.m in Sources */, + DA0E5C6423B691AC6129DDD4755ED361 /* PFFieldOperation.m in Sources */, + BAECB6CD09EE5ACEE9C4F714DA371DCB /* PFFieldOperationDecoder.m in Sources */, + 666CB5EDE86FAAFF588639C5998D7EBC /* PFFile.m in Sources */, + 4B3B2B7D8582379D9435E9EC8F17A867 /* PFFileController.m in Sources */, + F6AB90B0F80E6C866766C2DE6D8C83C2 /* PFFileDataStream.m in Sources */, + B2E905E16C8373CA8FA693AFAF0F26CF /* PFFileManager.m in Sources */, + BBB2FAEBBAA806158616685DB15157A6 /* PFFileStagingController.m in Sources */, + 455052E8E1BE9CD4FE8C3241FD0C52B4 /* PFFileState.m in Sources */, + 6628475D39D15292F5AA818FABACF173 /* PFGeoPoint.m in Sources */, + 1D2DD0C1A7D183E424DE783193CB4E8E /* PFHTTPURLRequestConstructor.m in Sources */, + 33091EFB09D98BDF743CA6F5C2AFBA33 /* PFHash.m in Sources */, + 72F791B09655165E996EB77B9620901A /* PFInstallation.m in Sources */, + 35E10DE9BAF946B7C2A73915CD71B113 /* PFInstallationConstants.m in Sources */, + 8CBF93AAE366ABCB98C520916F897C2B /* PFInstallationController.m in Sources */, + 844F4D0160C8B7AF5190B64E290F17E6 /* PFInstallationIdentifierStore.m in Sources */, + 4C3B4B259045C50F40DC38C925844D3B /* PFInternalUtils.m in Sources */, + 92636C5483939EFC0ACB26E15F53FDE8 /* PFJSONSerialization.m in Sources */, + CAB9A7FE02C498EB667CF6A4DAFBD314 /* PFKeyValueCache.m in Sources */, + CD4CA1E54F495AE3C0760FFB9AFBBD4C /* PFKeychainStore.m in Sources */, + 016EFFA38F17275483C1BF648B5D7324 /* PFLocationManager.m in Sources */, + 9C775A7CEC256634C458F3DF9D2EC80C /* PFLogger.m in Sources */, + F878F331507111514D56218C31A7A3CB /* PFMultiProcessFileLock.m in Sources */, + FED55EC1F65C1F9E6B608559F7F7F0CD /* PFMultiProcessFileLockController.m in Sources */, + 7ECF80989DDCDCD712118C8C181804F3 /* PFMulticastDelegate.m in Sources */, + 0AAC75CB2FCDD4F8E53D553BE107DA90 /* PFMutableACLState.m in Sources */, + D54B351F16D0170A1BEEE122A9956D99 /* PFMutableFileState.m in Sources */, + FD13FD577974DAFD4127467D7C8AFB0D /* PFMutableObjectState.m in Sources */, + 3A92C7DDC731F2C3E9CCA448616D05F7 /* PFMutablePushState.m in Sources */, + 934298B0150D2961AFE68A7191A1DCCA /* PFMutableQueryState.m in Sources */, + 30E2F0EF96EFD7A7373B984FA44137D4 /* PFMutableRelationState.m in Sources */, + A68FE14FE2956378F078CFFA7AF2ED1C /* PFMutableUserState.m in Sources */, + 1630D46574637CECF1FA2447F08892B0 /* PFNetworkActivityIndicatorManager.m in Sources */, + 6607F3D2D3BD489448924FF0C337995D /* PFObject.m in Sources */, + 9E004B9B3A7E68127CC3113FB6386CC0 /* PFObjectBatchController.m in Sources */, + FBD3BF6E2A67EA7B7843F2C311A60EFE /* PFObjectConstants.m in Sources */, + 279AFE04B23F5E0232696B7DB3E4E02D /* PFObjectController.m in Sources */, + 8AF944C4E12B20614F182BF100876EF4 /* PFObjectEstimatedData.m in Sources */, + 0E60D24FA0C38CF3B38881C50A60968C /* PFObjectFileCoder.m in Sources */, + C4BD45F260E029240F766D46397003D5 /* PFObjectFileCodingLogic.m in Sources */, + 888AEADD68D84E88A1676DDE8E76DC70 /* PFObjectFilePersistenceController.m in Sources */, + 692EFF6E00784FCAA8113237329AA161 /* PFObjectLocalIdStore.m in Sources */, + 0B4A604D105FCB3DAE6B98BD1FB3910A /* PFObjectState.m in Sources */, + E01923339F9C5BFE99FC3EE4E4936742 /* PFObjectSubclassInfo.m in Sources */, + 2956954B2EE473918D419EF77447E1B8 /* PFObjectSubclassingController.m in Sources */, + F200FD9B8B4D6FE117BB400C0654CC1A /* PFObjectUtilities.m in Sources */, + 89EB630A24D56C6000D18FFA0CC1F019 /* PFOfflineObjectController.m in Sources */, + C413266D8ED60F6651251936DEF63C24 /* PFOfflineQueryController.m in Sources */, + 9A3CBD2ADD5F98ECA4940A6489D249BB /* PFOfflineQueryLogic.m in Sources */, + 9F53B2F8582A3EE55C01E5430C6DE58E /* PFOfflineStore.m in Sources */, + 5DE730765DBD398F24500FD6FED46AD9 /* PFOperationSet.m in Sources */, + F238D2300BA08E34BA81C090C11F3B12 /* PFPaymentTransactionObserver.m in Sources */, + 550DAF9B85B98E5BAA38D0DF49E812E4 /* PFPin.m in Sources */, + 33983535E17BA783C02AB8A3BA462DDF /* PFPinningEventuallyQueue.m in Sources */, + ADABE24D512FCFF459CE5D0F8E252D6E /* PFPinningObjectStore.m in Sources */, + 546E2E51B4BCE1512A59EE9D01214E31 /* PFProduct.m in Sources */, + 3782D1D629A8E35048EB8ED5C985756C /* PFProductsRequestHandler.m in Sources */, + 28C1C6EA992A6C6CF6028D2C1A6D78EE /* PFPropertyInfo.m in Sources */, + 92AB69B98AD7C84ECBFB2407B1D83C08 /* PFPropertyInfo_Runtime.m in Sources */, + 8D71E7E4684EF2F6B5CA72DC2C5DB3F9 /* PFPurchase.m in Sources */, + BB0DF26FB7FD53A7B6D08FFA0B2A3C55 /* PFPurchaseController.m in Sources */, + ACAFB1D90546EFA64AE2735E8F503EF0 /* PFPush.m in Sources */, + B4FDC6E9D59ABE91C18092E4DD991945 /* PFPushChannelsController.m in Sources */, + 1FB39DD7CE8EAEE4113561F260141EFE /* PFPushController.m in Sources */, + 64709E4EF8CB8A1E70F1744E948C7B9F /* PFPushManager.m in Sources */, + BCF12DA109BA9A9FBE47414A93DF5022 /* PFPushState.m in Sources */, + 3F57EEC7C9B39E131B3F3171CA696756 /* PFPushUtilities.m in Sources */, + AD22B8B8A24B0630375406012F70B3E2 /* PFQuery.m in Sources */, + AB736B19B7EEB8FDA22320B6AA9E62A4 /* PFQueryController.m in Sources */, + 81BBAED0788E8E5A1C6152A96F4E7D9A /* PFQueryState.m in Sources */, + E390E29D45E4DCC855A59B2F96D1709B /* PFQueryUtilities.m in Sources */, + AF31F796B1CBEFD65DE7445B99DE40F5 /* PFRESTAnalyticsCommand.m in Sources */, + 5037B6B609FEFCC92BAE0B8FD5CA4A7B /* PFRESTCloudCommand.m in Sources */, + 2E3C9AC03335B047D286B1C0B2109B5E /* PFRESTCommand.m in Sources */, + D59CCD92C8ACD36507363BF351D9ED55 /* PFRESTConfigCommand.m in Sources */, + 94461C9CD41815E16E390945FD4585AB /* PFRESTFileCommand.m in Sources */, + 25749EC991182C94C321768FBB9B53EB /* PFRESTObjectBatchCommand.m in Sources */, + F6F02BC2B7AB20AE3D39467BAC5E9641 /* PFRESTObjectCommand.m in Sources */, + D524D9C4B2BFEBA46D98587BB3E5EEDB /* PFRESTPushCommand.m in Sources */, + A060D301B393FD942E24357F70466F26 /* PFRESTQueryCommand.m in Sources */, + 48AEF11FE1BBC34BE7DEA36B71F44F67 /* PFRESTSessionCommand.m in Sources */, + EA0707AEC6E783EE7A3E59DBBA7BB159 /* PFRESTUserCommand.m in Sources */, + D52E42CF13845E973D80DA9764EC0A7E /* PFReachability.m in Sources */, + 87924B476947B8D5E7A25EBAE9AA8ECD /* PFRelation.m in Sources */, + A48A38259F9F93155AE30E6CB589A570 /* PFRelationState.m in Sources */, + 4CCB9CA05BFCAC3B4E68EACE11B55B96 /* PFRole.m in Sources */, + DB0217249C32224E91A48864E061B951 /* PFSQLiteDatabase.m in Sources */, + C471FC167AD7C220455BD11929E602CD /* PFSQLiteDatabaseController.m in Sources */, + D391C0EC06404C804F9DC76831D953C1 /* PFSQLiteDatabaseResult.m in Sources */, + F5AD0E2716B3CA4BC791A700B69F5DB6 /* PFSQLiteStatement.m in Sources */, + 2B356D314125B673E6B4BE8785997F38 /* PFSession.m in Sources */, + 61662C3A27D0EBF95593A438D87794D7 /* PFSessionController.m in Sources */, + D13043EC0E35764A23D65B7A0EC24D1F /* PFSessionUtilities.m in Sources */, + E864C1BEB7E4FBD4EAECF0A1AE90771D /* PFTaskQueue.m in Sources */, + 0D8A5FE22EE90B1E4008CB1A1D35C3DC /* PFThreadsafety.m in Sources */, + B532331FAF75B2B2F18967531F375723 /* PFURLConstructor.m in Sources */, + 3202BD5404C424FA2BEE387443DC609A /* PFURLSession.m in Sources */, + 1C7E9F19AACD66C3E6416D40D598E80D /* PFURLSessionCommandRunner.m in Sources */, + 4D7FE531EAE79470599DACF805128A77 /* PFURLSessionDataTaskDelegate.m in Sources */, + CA8743057D791E2A1B03B93CB8D3CC71 /* PFURLSessionFileDownloadTaskDelegate.m in Sources */, + 533DEC0963B064B1BF23E9C9D17332F7 /* PFURLSessionJSONDataTaskDelegate.m in Sources */, + 88E4A109BCBFD669EE458AEFD7B5ACB7 /* PFURLSessionUploadTaskDelegate.m in Sources */, + 339DB11BD1704A9C80E2460F411E466B /* PFUser.m in Sources */, + D5EB9EF641D6A9658B27111E36DEC1A8 /* PFUserAuthenticationController.m in Sources */, + 4709FE9CAEE7FE66848EF36AF7110562 /* PFUserConstants.m in Sources */, + F8F775667407AD2AC21065F263AFE374 /* PFUserController.m in Sources */, + D059BF84C427D0926F831091E8DC35A3 /* PFUserFileCodingLogic.m in Sources */, + 370E062806DD29730BCD4D5BE041C3D3 /* PFUserState.m in Sources */, + 5DA1870A15C061EA6DA75F6E52F5CA89 /* PFWeakValue.m in Sources */, + C8E6C301D27E180B527353C3EBE91D16 /* Parse-dummy.m in Sources */, + 2857BF1C458AEBC871960C05A6ECAB1D /* Parse.m in Sources */, + 01398DC1B86F80EB75EE0894DC440D6C /* ParseManager.m in Sources */, + F49C9802B02749EB2F6BD4FA65DEEF51 /* ParseModule.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 78CA8605EA5EA577DADE0EF033B6A17B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 92E52AF0F9AA58F1DDF4E39E1E419F1C /* BFCancellationToken.m in Sources */, + 1EFA6ACD4D8E3526529AF287165EE70F /* BFCancellationTokenRegistration.m in Sources */, + 3B52D2A91737D642D72373FD3CD09C75 /* BFCancellationTokenSource.m in Sources */, + 25EDAB4F90DCEEADF51B1260BC8638CB /* BFExecutor.m in Sources */, + 60D4C5AB361B8056D00CCF69DFCF3942 /* BFTask.m in Sources */, + 6562D01F079C8361582F11CF4824438F /* BFTaskCompletionSource.m in Sources */, + 5AF4F83832A8494203D0BFB1C3E4CEA1 /* Bolts-dummy.m in Sources */, + 575CC7827848B936ED7187F1C1C2216C /* Bolts.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BBB621C39EF13BC7899B6B6A8F15EF69 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AF286D38173EA5B941C10161C66B30F5 /* AFHTTPRequestOperation.m in Sources */, + 8FC8B2D947B8ED5DC54D650443DF9821 /* AFHTTPRequestOperationManager.m in Sources */, + 8CD9B40C2AF93448262EE57C1BE4DDE9 /* AFHTTPSessionManager.m in Sources */, + 2D70672D79B0F0334BF2EDEF1D42E928 /* AFNetworkActivityIndicatorManager.m in Sources */, + AFB0CD00E4EC4FECA54202A58CE7F2F2 /* AFNetworkReachabilityManager.m in Sources */, + 0BD38250D122B15783D258DE1977C0DD /* AFNetworking-dummy.m in Sources */, + 874F446E0B804BABFE522EA7F0FC1EB3 /* AFSecurityPolicy.m in Sources */, + 7F3E73EC4630EF8F1EB49AA991705DE4 /* AFURLConnectionOperation.m in Sources */, + 1B78465543A9687FB133F944D163583C /* AFURLRequestSerialization.m in Sources */, + 88B8A2037E08CA5071DC6DF6391EA8AA /* AFURLResponseSerialization.m in Sources */, + D6D849304265B47FE1FFD7983A5E6E9A /* AFURLSessionManager.m in Sources */, + 37937DB27B7EC6C78D2D8DCCC4205454 /* UIActivityIndicatorView+AFNetworking.m in Sources */, + 92FEFFF6E03231D715018E15EA02A414 /* UIAlertView+AFNetworking.m in Sources */, + 84154423F861A182F8C672CEF5AF2B99 /* UIButton+AFNetworking.m in Sources */, + 75F271B241FEF6E852ADB457839E3DA7 /* UIImageView+AFNetworking.m in Sources */, + E06CD9CAEA46B547013DC4CFA6D65EE7 /* UIProgressView+AFNetworking.m in Sources */, + 4344D005FD2539A8133664EA4BC836AE /* UIRefreshControl+AFNetworking.m in Sources */, + 2A22D38A97847D209CE7310C2D0D4381 /* UIWebView+AFNetworking.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BBC5CC087CBFE6A21AECBA3A47EE4932 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 698727C831749E84FA33523C00889B0A /* Pods-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C769E9D9EE5AE761EE8780669AF09495 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0E3E2B434F6BB10F2338532DC989A8C0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "LiquidFloatingActionButton-LiquidFloatingActionButton"; + target = 136FE3EDD694CDE21E3B556E3A8831E3 /* LiquidFloatingActionButton-LiquidFloatingActionButton */; + targetProxy = 18D533FE93435CFA12227234DD9C33D3 /* PBXContainerItemProxy */; + }; + 0E4C88DCFE8254DE82A89905FBDE4E78 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Parse; + target = 255B486D02599C8571584A81D3995B9C /* Parse */; + targetProxy = 8C9B01C63A85A8E466E8C7DB93AB8DBD /* PBXContainerItemProxy */; + }; + 16A41541C9FA1848B76E9F92D5418E26 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Bolts; + target = 4CFD6D06267A85EA321AFA80384E49C4 /* Bolts */; + targetProxy = 579026D29A26BCB5C81B7276872428CD /* PBXContainerItemProxy */; + }; + 55CBAB16A5819611D6193D8AABC78DEF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Bolts; + target = 4CFD6D06267A85EA321AFA80384E49C4 /* Bolts */; + targetProxy = 87438DD3727F78E05960BC9BEC835551 /* PBXContainerItemProxy */; + }; + 564B918261E3F62724E4551F74BA531F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Bolts; + target = 4CFD6D06267A85EA321AFA80384E49C4 /* Bolts */; + targetProxy = 81143337D0B99E5BB72D75FF2BFF94F1 /* PBXContainerItemProxy */; + }; + C2F36DE1C4F2E91313A31E15BAE1EDDB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Parse; + target = 255B486D02599C8571584A81D3995B9C /* Parse */; + targetProxy = 3157096E56F5CC8AAF21974C0E909770 /* PBXContainerItemProxy */; + }; + DE1BB65BA0D5D2AA7D5D19A5A2C41ACE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = AFNetworking; + target = 6F551023845F01D3B4326C1A426E12EF /* AFNetworking */; + targetProxy = 0EE8BEF01819C84E3669BF5A6E024DA5 /* PBXContainerItemProxy */; + }; + E0726DDC42AD783FB3E571B33B7C4003 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = LiquidFloatingActionButton; + target = EBB54F8CD09E891EC7CDEEBB645A5141 /* LiquidFloatingActionButton */; + targetProxy = 5AAF2A48D9FEACE1C8B0FAA4E0E14931 /* PBXContainerItemProxy */; + }; + FBBD1CC1BE1F33D6EB2477D6612659E5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ParseUI; + target = 702D990824EC762012B0DCFFCF50AE79 /* ParseUI */; + targetProxy = D9052FE3BEDCE9DE873F38DBB341EB30 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0DBCFB94FCE2C4D5BEB01F18F6A06393 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5296B4416AC12F2190F879868AC55E77 /* AFNetworking-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 2.6.1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 2; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/AFNetworking/AFNetworking-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/AFNetworking/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/AFNetworking/AFNetworking.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = AFNetworking; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 203208A3DE225600682B836408BF8964 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BE9223229E8325CB5F21B8ADDA6B9960 /* Bolts-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.3.0; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Bolts/Bolts-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Bolts/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Bolts/Bolts.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = Bolts; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2605A41B5B7D4838786A7D9A60480FB4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BE9223229E8325CB5F21B8ADDA6B9960 /* Bolts-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.3.0; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Bolts/Bolts-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Bolts/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Bolts/Bolts.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = Bolts; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 29AED804A8045AA6823FB7EB9C1B36DF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C57AA8424009C8745B20CAC511A50222 /* Pods.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "Target Support Files/Pods/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Pods/Pods.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = Pods; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 331F536E4495B239F6B73E76D52B4E65 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B82573787A30C373316857E73B756945 /* ParseUI-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.1.6; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/ParseUI/ParseUI-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/ParseUI/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/ParseUI/ParseUI.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = ParseUI; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 4575DAE69868711285A4E135CE373148 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5296B4416AC12F2190F879868AC55E77 /* AFNetworking-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 2.6.1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 2; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/AFNetworking/AFNetworking-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/AFNetworking/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/AFNetworking/AFNetworking.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = AFNetworking; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 4BBF750FD58578027892572B29F5A866 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4047A1EB9846648DAD467918C55ED03A /* Parse-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.9.0; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Parse/Parse-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Parse/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Parse/Parse.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = Parse; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6C7D1974279F3237880649BF08524999 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B82573787A30C373316857E73B756945 /* ParseUI-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.1.6; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/ParseUI/ParseUI-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/ParseUI/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/ParseUI/ParseUI.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = ParseUI; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 9A125EC790EC9D3E96B5DB41E1938EBB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BB7BF9759D7B78AB520BE410BA97011F /* Pods.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "Target Support Files/Pods/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Pods/Pods.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = Pods; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A2A3FCA6599E8034578E80B9179C76CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + ONLY_ACTIVE_ARCH = YES; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + A54F137775D2D103DD8B95C8653F4916 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4047A1EB9846648DAD467918C55ED03A /* Parse-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1.9.0; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/Parse/Parse-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Parse/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/Parse/Parse.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = Parse; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + AB66C978656E240C917407B296597F92 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B702EAF4CAAEB301409A0AD43E5FDEEE /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + PRODUCT_NAME = LiquidFloatingActionButton; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + BA7A4522A5C07570B3E06D5802FD36E4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0.1.1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 0.1.1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/LiquidFloatingActionButton/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = LiquidFloatingActionButton; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CA2DDFBA5A4DE587167A2B29F36C9115 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */; + buildSettings = { + ENABLE_STRICT_OBJC_MSGSEND = YES; + PRODUCT_NAME = LiquidFloatingActionButton; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + F7D337EF3A5387A7DFD1902CBB6CDF6C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 246A568668F7514B2B042CABE38B3FE6 /* LiquidFloatingActionButton-Private.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 0.1.1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 0.1.1; + DYLIB_CURRENT_VERSION = "$(CURRENT_PROJECT_VERSION)"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/LiquidFloatingActionButton/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = LiquidFloatingActionButton; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A2A3FCA6599E8034578E80B9179C76CD /* Debug */, + AB66C978656E240C917407B296597F92 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6194C4F5520D9DD6BA2194F8FC01F025 /* Build configuration list for PBXNativeTarget "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 29AED804A8045AA6823FB7EB9C1B36DF /* Debug */, + 9A125EC790EC9D3E96B5DB41E1938EBB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7FDF94920301B682907024096C818539 /* Build configuration list for PBXNativeTarget "Parse" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A54F137775D2D103DD8B95C8653F4916 /* Debug */, + 4BBF750FD58578027892572B29F5A866 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 81172DA6C72CCE384596B834A0DDC7E7 /* Build configuration list for PBXNativeTarget "Bolts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 203208A3DE225600682B836408BF8964 /* Debug */, + 2605A41B5B7D4838786A7D9A60480FB4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 88A92B1696C5DC161F34752E2A294DBE /* Build configuration list for PBXNativeTarget "AFNetworking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4575DAE69868711285A4E135CE373148 /* Debug */, + 0DBCFB94FCE2C4D5BEB01F18F6A06393 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D4EE60BA5ED84273CFB8978E385C2E4 /* Build configuration list for PBXNativeTarget "LiquidFloatingActionButton-LiquidFloatingActionButton" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B702EAF4CAAEB301409A0AD43E5FDEEE /* Debug */, + CA2DDFBA5A4DE587167A2B29F36C9115 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A07805171FD0C0ACCD779EB10A56EE0D /* Build configuration list for PBXNativeTarget "ParseUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331F536E4495B239F6B73E76D52B4E65 /* Debug */, + 6C7D1974279F3237880649BF08524999 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A1AA7F0D1642003497E360B27771EBBA /* Build configuration list for PBXNativeTarget "LiquidFloatingActionButton" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7D337EF3A5387A7DFD1902CBB6CDF6C /* Debug */, + BA7A4522A5C07570B3E06D5802FD36E4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-Private.xcconfig b/Pods/Target Support Files/AFNetworking/AFNetworking-Private.xcconfig new file mode 100644 index 0000000..f8ed288 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-Private.xcconfig @@ -0,0 +1,6 @@ +#include "AFNetworking.xcconfig" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/AFNetworking" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_LDFLAGS = ${AFNETWORKING_OTHER_LDFLAGS} +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m b/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m new file mode 100644 index 0000000..6a29cf8 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_AFNetworking : NSObject +@end +@implementation PodsDummy_AFNetworking +@end diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch b/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch new file mode 100644 index 0000000..1e116a3 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch @@ -0,0 +1,11 @@ +#ifdef __OBJC__ +#import +#endif + +#ifndef TARGET_OS_IOS + #define TARGET_OS_IOS TARGET_OS_IPHONE +#endif + +#ifndef TARGET_OS_WATCH + #define TARGET_OS_WATCH 0 +#endif diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking-umbrella.h b/Pods/Target Support Files/AFNetworking/AFNetworking-umbrella.h new file mode 100644 index 0000000..82a3f9d --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking-umbrella.h @@ -0,0 +1,26 @@ +#import + +#import "AFNetworking.h" +#import "AFURLConnectionOperation.h" +#import "AFHTTPRequestOperation.h" +#import "AFHTTPRequestOperationManager.h" +#import "AFHTTPSessionManager.h" +#import "AFURLSessionManager.h" +#import "AFNetworkReachabilityManager.h" +#import "AFSecurityPolicy.h" +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" +#import "AFNetworkActivityIndicatorManager.h" +#import "UIActivityIndicatorView+AFNetworking.h" +#import "UIAlertView+AFNetworking.h" +#import "UIButton+AFNetworking.h" +#import "UIImage+AFNetworking.h" +#import "UIImageView+AFNetworking.h" +#import "UIKit+AFNetworking.h" +#import "UIProgressView+AFNetworking.h" +#import "UIRefreshControl+AFNetworking.h" +#import "UIWebView+AFNetworking.h" + +FOUNDATION_EXPORT double AFNetworkingVersionNumber; +FOUNDATION_EXPORT const unsigned char AFNetworkingVersionString[]; + diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking.modulemap b/Pods/Target Support Files/AFNetworking/AFNetworking.modulemap new file mode 100644 index 0000000..5892cd3 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking.modulemap @@ -0,0 +1,6 @@ +framework module AFNetworking { + umbrella header "AFNetworking-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig b/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig new file mode 100644 index 0000000..b0b2d52 --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig @@ -0,0 +1 @@ +AFNETWORKING_OTHER_LDFLAGS = -framework "CoreGraphics" -framework "MobileCoreServices" -framework "Security" -framework "SystemConfiguration" \ No newline at end of file diff --git a/Pods/Target Support Files/AFNetworking/Info.plist b/Pods/Target Support Files/AFNetworking/Info.plist new file mode 100644 index 0000000..4e21c5f --- /dev/null +++ b/Pods/Target Support Files/AFNetworking/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.6.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Bolts/Bolts-Private.xcconfig b/Pods/Target Support Files/Bolts/Bolts-Private.xcconfig new file mode 100644 index 0000000..66a40e1 --- /dev/null +++ b/Pods/Target Support Files/Bolts/Bolts-Private.xcconfig @@ -0,0 +1,5 @@ +#include "Bolts.xcconfig" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Bolts" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/Bolts/Bolts-dummy.m b/Pods/Target Support Files/Bolts/Bolts-dummy.m new file mode 100644 index 0000000..aeabf61 --- /dev/null +++ b/Pods/Target Support Files/Bolts/Bolts-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Bolts : NSObject +@end +@implementation PodsDummy_Bolts +@end diff --git a/Pods/Target Support Files/Bolts/Bolts-prefix.pch b/Pods/Target Support Files/Bolts/Bolts-prefix.pch new file mode 100644 index 0000000..aa992a4 --- /dev/null +++ b/Pods/Target Support Files/Bolts/Bolts-prefix.pch @@ -0,0 +1,4 @@ +#ifdef __OBJC__ +#import +#endif + diff --git a/Pods/Target Support Files/Bolts/Bolts-umbrella.h b/Pods/Target Support Files/Bolts/Bolts-umbrella.h new file mode 100644 index 0000000..5bea17e --- /dev/null +++ b/Pods/Target Support Files/Bolts/Bolts-umbrella.h @@ -0,0 +1,15 @@ +#import + +#import "BFCancellationToken.h" +#import "BFCancellationTokenRegistration.h" +#import "BFCancellationTokenSource.h" +#import "BFDefines.h" +#import "BFExecutor.h" +#import "BFTask.h" +#import "BFTaskCompletionSource.h" +#import "Bolts.h" +#import "BoltsVersion.h" + +FOUNDATION_EXPORT double BoltsVersionNumber; +FOUNDATION_EXPORT const unsigned char BoltsVersionString[]; + diff --git a/Pods/Target Support Files/Bolts/Bolts.modulemap b/Pods/Target Support Files/Bolts/Bolts.modulemap new file mode 100644 index 0000000..712e2c8 --- /dev/null +++ b/Pods/Target Support Files/Bolts/Bolts.modulemap @@ -0,0 +1,6 @@ +framework module Bolts { + umbrella header "Bolts-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Bolts/Bolts.xcconfig b/Pods/Target Support Files/Bolts/Bolts.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/Pods/Target Support Files/Bolts/Info.plist b/Pods/Target Support Files/Bolts/Info.plist new file mode 100644 index 0000000..8c485de --- /dev/null +++ b/Pods/Target Support Files/Bolts/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.3.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/Info.plist b/Pods/Target Support Files/LiquidFloatingActionButton/Info.plist new file mode 100644 index 0000000..c3f6ee3 --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.1.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-Private.xcconfig b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-Private.xcconfig new file mode 100644 index 0000000..a951833 --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-Private.xcconfig @@ -0,0 +1,6 @@ +#include "LiquidFloatingActionButton.xcconfig" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/LiquidFloatingActionButton" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-dummy.m b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-dummy.m new file mode 100644 index 0000000..0de106e --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_LiquidFloatingActionButton : NSObject +@end +@implementation PodsDummy_LiquidFloatingActionButton +@end diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-prefix.pch b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-prefix.pch new file mode 100644 index 0000000..aa992a4 --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-prefix.pch @@ -0,0 +1,4 @@ +#ifdef __OBJC__ +#import +#endif + diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-umbrella.h b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-umbrella.h new file mode 100644 index 0000000..d403e75 --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton-umbrella.h @@ -0,0 +1,6 @@ +#import + + +FOUNDATION_EXPORT double LiquidFloatingActionButtonVersionNumber; +FOUNDATION_EXPORT const unsigned char LiquidFloatingActionButtonVersionString[]; + diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.modulemap b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.modulemap new file mode 100644 index 0000000..7a73f6b --- /dev/null +++ b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.modulemap @@ -0,0 +1,6 @@ +framework module LiquidFloatingActionButton { + umbrella header "LiquidFloatingActionButton-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.xcconfig b/Pods/Target Support Files/LiquidFloatingActionButton/LiquidFloatingActionButton.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/Pods/Target Support Files/Parse/Info.plist b/Pods/Target Support Files/Parse/Info.plist new file mode 100644 index 0000000..2f51fa5 --- /dev/null +++ b/Pods/Target Support Files/Parse/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.9.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Parse/Parse-Private.xcconfig b/Pods/Target Support Files/Parse/Parse-Private.xcconfig new file mode 100644 index 0000000..1408a6f --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse-Private.xcconfig @@ -0,0 +1,6 @@ +#include "Parse.xcconfig" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Parse" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_LDFLAGS = ${PARSE_OTHER_LDFLAGS} +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/Parse/Parse-dummy.m b/Pods/Target Support Files/Parse/Parse-dummy.m new file mode 100644 index 0000000..3f17a64 --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Parse : NSObject +@end +@implementation PodsDummy_Parse +@end diff --git a/Pods/Target Support Files/Parse/Parse-prefix.pch b/Pods/Target Support Files/Parse/Parse-prefix.pch new file mode 100644 index 0000000..aa992a4 --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse-prefix.pch @@ -0,0 +1,4 @@ +#ifdef __OBJC__ +#import +#endif + diff --git a/Pods/Target Support Files/Parse/Parse-umbrella.h b/Pods/Target Support Files/Parse/Parse-umbrella.h new file mode 100644 index 0000000..616d7c7 --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse-umbrella.h @@ -0,0 +1,30 @@ +#import + +#import "Parse.h" +#import "PFACL.h" +#import "PFAnalytics.h" +#import "PFAnonymousUtils.h" +#import "PFCloud.h" +#import "PFConfig.h" +#import "PFConstants.h" +#import "PFFile.h" +#import "PFGeoPoint.h" +#import "PFInstallation.h" +#import "PFNetworkActivityIndicatorManager.h" +#import "PFNullability.h" +#import "PFObject+Subclass.h" +#import "PFObject.h" +#import "PFProduct.h" +#import "PFPurchase.h" +#import "PFPush.h" +#import "PFQuery.h" +#import "PFRelation.h" +#import "PFRole.h" +#import "PFSession.h" +#import "PFSubclassing.h" +#import "PFUser.h" +#import "PFUserAuthenticationDelegate.h" + +FOUNDATION_EXPORT double ParseVersionNumber; +FOUNDATION_EXPORT const unsigned char ParseVersionString[]; + diff --git a/Pods/Target Support Files/Parse/Parse.modulemap b/Pods/Target Support Files/Parse/Parse.modulemap new file mode 100644 index 0000000..2499731 --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse.modulemap @@ -0,0 +1,6 @@ +framework module Parse { + umbrella header "Parse-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Parse/Parse.xcconfig b/Pods/Target Support Files/Parse/Parse.xcconfig new file mode 100644 index 0000000..4b7b805 --- /dev/null +++ b/Pods/Target Support Files/Parse/Parse.xcconfig @@ -0,0 +1 @@ +PARSE_OTHER_LDFLAGS = -l"sqlite3" -l"z" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreGraphics" -framework "CoreLocation" -framework "QuartzCore" -framework "Security" -framework "StoreKit" -framework "SystemConfiguration" -weak_framework "Accounts" -weak_framework "Social" \ No newline at end of file diff --git a/Pods/Target Support Files/ParseUI/Info.plist b/Pods/Target Support Files/ParseUI/Info.plist new file mode 100644 index 0000000..35ca206 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.1.6 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/ParseUI/ParseUI-Private.xcconfig b/Pods/Target Support Files/ParseUI/ParseUI-Private.xcconfig new file mode 100644 index 0000000..a931c1b --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI-Private.xcconfig @@ -0,0 +1,6 @@ +#include "ParseUI.xcconfig" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/ParseUI" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_LDFLAGS = ${PARSEUI_OTHER_LDFLAGS} +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/Pods/Target Support Files/ParseUI/ParseUI-dummy.m b/Pods/Target Support Files/ParseUI/ParseUI-dummy.m new file mode 100644 index 0000000..c204211 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_ParseUI : NSObject +@end +@implementation PodsDummy_ParseUI +@end diff --git a/Pods/Target Support Files/ParseUI/ParseUI-prefix.pch b/Pods/Target Support Files/ParseUI/ParseUI-prefix.pch new file mode 100644 index 0000000..aa992a4 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI-prefix.pch @@ -0,0 +1,4 @@ +#ifdef __OBJC__ +#import +#endif + diff --git a/Pods/Target Support Files/ParseUI/ParseUI-umbrella.h b/Pods/Target Support Files/ParseUI/ParseUI-umbrella.h new file mode 100644 index 0000000..81f5492 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI-umbrella.h @@ -0,0 +1,21 @@ +#import + +#import "PFLogInView.h" +#import "PFLogInView_Private.h" +#import "PFLogInViewController.h" +#import "PFSignUpView.h" +#import "PFSignUpViewController.h" +#import "PFQueryTableViewController.h" +#import "PFQueryCollectionViewController.h" +#import "PFProductTableViewController.h" +#import "PFImageView.h" +#import "PFTextField.h" +#import "PFCollectionViewCell.h" +#import "PFPurchaseTableViewCell.h" +#import "PFTableViewCell.h" +#import "ParseUI.h" +#import "ParseUIConstants.h" + +FOUNDATION_EXPORT double ParseUIVersionNumber; +FOUNDATION_EXPORT const unsigned char ParseUIVersionString[]; + diff --git a/Pods/Target Support Files/ParseUI/ParseUI.modulemap b/Pods/Target Support Files/ParseUI/ParseUI.modulemap new file mode 100644 index 0000000..96cd7f5 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI.modulemap @@ -0,0 +1,6 @@ +framework module ParseUI { + umbrella header "ParseUI-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/ParseUI/ParseUI.xcconfig b/Pods/Target Support Files/ParseUI/ParseUI.xcconfig new file mode 100644 index 0000000..d856d00 --- /dev/null +++ b/Pods/Target Support Files/ParseUI/ParseUI.xcconfig @@ -0,0 +1 @@ +PARSEUI_OTHER_LDFLAGS = -framework "CoreGraphics" -framework "Foundation" -framework "QuartzCore" -framework "UIKit" \ No newline at end of file diff --git a/Pods/Target Support Files/Pods/Info.plist b/Pods/Target Support Files/Pods/Info.plist new file mode 100644 index 0000000..6974542 --- /dev/null +++ b/Pods/Target Support Files/Pods/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown new file mode 100644 index 0000000..694a8a9 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown @@ -0,0 +1,118 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## AFNetworking + +Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## Bolts + +BSD License + +For Bolts software + +Copyright (c) 2013-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +## GoogleMaps + +If you use the Google Maps SDK for iOS in your application, you must +include the attribution text as part of a legal notices section in your +application. Including legal notices as an independent menu item, or as +part of an "About" menu item, is recommended. + +You can get the attribution text by making a call to +[GMSServices openSourceLicenseInfo]. + + +## LiquidFloatingActionButton + +Copyright (c) 2015 Takuma Yoshida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## Parse + +See https://www.parse.com/about/terms + +## ParseUI + +Copyright (c) 2014, Parse, LLC. All rights reserved. + +You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +copy, modify, and distribute this software in source code or binary form for use +in connection with the web services and APIs provided by Parse. + +As with any software that integrates with the Parse platform, your use of +this software is subject to the Parse Terms of Service +[https://www.parse.com/about/terms]. This copyright notice shall be +included in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Generated by CocoaPods - http://cocoapods.org diff --git a/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/Pods/Target Support Files/Pods/Pods-acknowledgements.plist new file mode 100644 index 0000000..4b52583 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-acknowledgements.plist @@ -0,0 +1,168 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + Title + AFNetworking + Type + PSGroupSpecifier + + + FooterText + BSD License + +For Bolts software + +Copyright (c) 2013-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Title + Bolts + Type + PSGroupSpecifier + + + FooterText + If you use the Google Maps SDK for iOS in your application, you must +include the attribution text as part of a legal notices section in your +application. Including legal notices as an independent menu item, or as +part of an "About" menu item, is recommended. + +You can get the attribution text by making a call to +[GMSServices openSourceLicenseInfo]. + + Title + GoogleMaps + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2015 Takuma Yoshida <yoa.jmpr.w@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + Title + LiquidFloatingActionButton + Type + PSGroupSpecifier + + + FooterText + See https://www.parse.com/about/terms + Title + Parse + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014, Parse, LLC. All rights reserved. + +You are hereby granted a non-exclusive, worldwide, royalty-free license to use, +copy, modify, and distribute this software in source code or binary form for use +in connection with the web services and APIs provided by Parse. + +As with any software that integrates with the Parse platform, your use of +this software is subject to the Parse Terms of Service +[https://www.parse.com/about/terms]. This copyright notice shall be +included in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Title + ParseUI + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - http://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods/Pods-dummy.m b/Pods/Target Support Files/Pods/Pods-dummy.m new file mode 100644 index 0000000..ade64bd --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods : NSObject +@end +@implementation PodsDummy_Pods +@end diff --git a/Pods/Target Support Files/Pods/Pods-frameworks.sh b/Pods/Target Support Files/Pods/Pods-frameworks.sh new file mode 100755 index 0000000..73792e3 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-frameworks.sh @@ -0,0 +1,67 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + else + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + fi + + local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries + local basename + basename="$(basename "$1" | sed -E s/\\..+// && exit ${PIPESTATUS[0]})" + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/${basename}.framework/${basename}" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" + /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" + fi +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework 'Pods/AFNetworking.framework' + install_framework 'Pods/Bolts.framework' + install_framework 'Pods/LiquidFloatingActionButton.framework' + install_framework 'Pods/Parse.framework' + install_framework 'Pods/ParseUI.framework' +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework 'Pods/AFNetworking.framework' + install_framework 'Pods/Bolts.framework' + install_framework 'Pods/LiquidFloatingActionButton.framework' + install_framework 'Pods/Parse.framework' + install_framework 'Pods/ParseUI.framework' +fi diff --git a/Pods/Target Support Files/Pods/Pods-resources.sh b/Pods/Target Support Files/Pods/Pods-resources.sh new file mode 100755 index 0000000..14dec4d --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-resources.sh @@ -0,0 +1,101 @@ +#!/bin/sh +set -e + +mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +realpath() { + DIRECTORY="$(cd "${1%/*}" && pwd)" + FILENAME="${1##*/}" + echo "$DIRECTORY/$FILENAME" +} + +install_resource() +{ + case $1 in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" + ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" + ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" + ;; + *.framework) + echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" + xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" + xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" + xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + /*) + echo "$1" + echo "$1" >> "$RESOURCES_TO_COPY" + ;; + *) + echo "${PODS_ROOT}/$1" + echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" + ;; + esac +} +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_resource "GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_resource "GoogleMaps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle" +fi + +mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; + esac + + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "`realpath $PODS_ROOT`*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/Pods/Target Support Files/Pods/Pods-umbrella.h b/Pods/Target Support Files/Pods/Pods-umbrella.h new file mode 100644 index 0000000..21dcfd2 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-umbrella.h @@ -0,0 +1,6 @@ +#import + + +FOUNDATION_EXPORT double PodsVersionNumber; +FOUNDATION_EXPORT const unsigned char PodsVersionString[]; + diff --git a/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/Pods/Target Support Files/Pods/Pods.debug.xcconfig new file mode 100644 index 0000000..4233567 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.debug.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = "$(PODS_ROOT)/GoogleMaps/Frameworks" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/AFNetworking.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Bolts.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/LiquidFloatingActionButton.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Parse.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/ParseUI.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/GoogleMaps" -isystem "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_LDFLAGS = $(inherited) -l"c++" -l"icucore" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "Bolts" -framework "CoreBluetooth" -framework "CoreData" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreText" -framework "GLKit" -framework "GoogleMaps" -framework "ImageIO" -framework "LiquidFloatingActionButton" -framework "OpenGLES" -framework "Parse" -framework "ParseUI" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods +PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Pods/Target Support Files/Pods/Pods.modulemap b/Pods/Target Support Files/Pods/Pods.modulemap new file mode 100644 index 0000000..8413413 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.modulemap @@ -0,0 +1,6 @@ +framework module Pods { + umbrella header "Pods-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods/Pods.release.xcconfig b/Pods/Target Support Files/Pods/Pods.release.xcconfig new file mode 100644 index 0000000..4233567 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.release.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = "$(PODS_ROOT)/GoogleMaps/Frameworks" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GoogleMaps" "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/AFNetworking.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Bolts.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/LiquidFloatingActionButton.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Parse.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/ParseUI.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/GoogleMaps" -isystem "${PODS_ROOT}/Headers/Public/GoogleMaps/GoogleMaps" +OTHER_LDFLAGS = $(inherited) -l"c++" -l"icucore" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "Bolts" -framework "CoreBluetooth" -framework "CoreData" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreText" -framework "GLKit" -framework "GoogleMaps" -framework "ImageIO" -framework "LiquidFloatingActionButton" -framework "OpenGLES" -framework "Parse" -framework "ParseUI" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods +PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file