diff --git a/.gitignore b/.gitignore index 0ef0d4a..29d7d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,12 @@ -# OSX files -.DS_Store - -# API & Secret files -*.sh -HackTX/ApiKeys.plist -GoogleService-Info.plist - # Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated build/ +DerivedData/ + +## Various settings *.pbxuser !default.pbxuser *.mode1v3 @@ -16,13 +15,52 @@ build/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -*.xcworkspace -!default.xcworkspace -xcuserdata -profile +xcuserdata/ + +## Other *.moved-aside -DerivedData -.idea/ +*.xcuserstate + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode -# Pods -Pods +iOSInjectionProject/ +HackTX/GoogleService-Info.plist +HackTX/HTXConstants.h +HackTX/HTXConstants.m +HackTX/APIKeys.plist diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 7df3410..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "Alamofire"] - path = Alamofire - url = https://github.com/Alamofire/Alamofire.git diff --git a/Bolts.framework/Bolts b/Bolts.framework/Bolts deleted file mode 100644 index acc8664..0000000 Binary files a/Bolts.framework/Bolts and /dev/null differ diff --git a/Bolts.framework/Headers/BFAppLink.h b/Bolts.framework/Headers/BFAppLink.h deleted file mode 100644 index aa89efc..0000000 --- a/Bolts.framework/Headers/BFAppLink.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 - -/*! The version of the App Link protocol that this library supports */ -FOUNDATION_EXPORT NSString *const BFAppLinkVersion; - -/*! - Contains App Link metadata relevant for navigation on this device - derived from the HTML at a given URL. - */ -@interface BFAppLink : NSObject - -/*! - Creates a BFAppLink with the given list of BFAppLinkTargets and target URL. - - Generally, this will only be used by implementers of the BFAppLinkResolving protocol, - as these implementers will produce App Link metadata for a given URL. - - @param sourceURL the URL from which this App Link is derived - @param targets an ordered list of BFAppLinkTargets for this platform derived - from App Link metadata. - @param webURL the fallback web URL, if any, for the app link. - */ -+ (instancetype)appLinkWithSourceURL:(NSURL *)sourceURL - targets:(NSArray *)targets - webURL:(NSURL *)webURL; - -/*! The URL from which this BFAppLink was derived */ -@property (nonatomic, strong, readonly) NSURL *sourceURL; - -/*! - The ordered list of targets applicable to this platform that will be used - for navigation. - */ -@property (nonatomic, copy, readonly) NSArray *targets; - -/*! The fallback web URL to use if no targets are installed on this device. */ -@property (nonatomic, strong, readonly) NSURL *webURL; - -@end diff --git a/Bolts.framework/Headers/BFAppLinkNavigation.h b/Bolts.framework/Headers/BFAppLinkNavigation.h deleted file mode 100644 index d459f72..0000000 --- a/Bolts.framework/Headers/BFAppLinkNavigation.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 - -/*! - The result of calling navigate on a BFAppLinkNavigation - */ -typedef NS_ENUM(NSInteger, BFAppLinkNavigationType) { - /*! Indicates that the navigation failed and no app was opened */ - BFAppLinkNavigationTypeFailure, - /*! Indicates that the navigation succeeded by opening the URL in the browser */ - BFAppLinkNavigationTypeBrowser, - /*! Indicates that the navigation succeeded by opening the URL in an app on the device */ - BFAppLinkNavigationTypeApp -}; - -@protocol BFAppLinkResolving; -@class BFTask; - -/*! - Represents a pending request to navigate to an App Link. Most developers will - simply use navigateToURLInBackground: to open a URL, but developers can build - custom requests with additional navigation and app data attached to them by - creating BFAppLinkNavigations themselves. - */ -@interface BFAppLinkNavigation : NSObject - -/*! - The extras for the AppLinkNavigation. This will generally contain application-specific - data that should be passed along with the request, such as advertiser or affiliate IDs or - other such metadata relevant on this device. - */ -@property (nonatomic, copy, readonly) NSDictionary *extras; - -/*! - The al_applink_data for the AppLinkNavigation. This will generally contain data common to - navigation attempts such as back-links, user agents, and other information that may be used - in routing and handling an App Link request. - */ -@property (nonatomic, copy, readonly) NSDictionary *appLinkData; - -/*! The AppLink to navigate to */ -@property (nonatomic, strong, readonly) BFAppLink *appLink; - -/*! Creates an AppLinkNavigation with the given link, extras, and App Link data */ -+ (instancetype)navigationWithAppLink:(BFAppLink *)appLink - extras:(NSDictionary *)extras - appLinkData:(NSDictionary *)appLinkData; - -/*! Performs the navigation */ -- (BFAppLinkNavigationType)navigate:(NSError **)error; - -/*! Returns a BFAppLink for the given URL */ -+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination; - -/*! Returns a BFAppLink for the given URL using the given App Link resolution strategy */ -+ (BFTask *)resolveAppLinkInBackground:(NSURL *)destination resolver:(id)resolver; - -/*! Navigates to a BFAppLink and returns whether it opened in-app or in-browser */ -+ (BFAppLinkNavigationType)navigateToAppLink:(BFAppLink *)link error:(NSError **)error; - -/*! Navigates to a URL (an asynchronous action) and returns a BFNavigationType */ -+ (BFTask *)navigateToURLInBackground:(NSURL *)destination; - -/*! - Navigates to a URL (an asynchronous action) using the given App Link resolution - strategy and returns a BFNavigationType - */ -+ (BFTask *)navigateToURLInBackground:(NSURL *)destination resolver:(id)resolver; - -/*! - Gets the default resolver to be used for App Link resolution. If the developer has not set one explicitly, - a basic, built-in resolver will be used. - */ -+ (id)defaultResolver; - -/*! - Sets the default resolver to be used for App Link resolution. Setting this to nil will revert the - default resolver to the basic, built-in resolver provided by Bolts. - */ -+ (void)setDefaultResolver:(id)resolver; - -@end diff --git a/Bolts.framework/Headers/BFAppLinkResolving.h b/Bolts.framework/Headers/BFAppLinkResolving.h deleted file mode 100644 index b67bdba..0000000 --- a/Bolts.framework/Headers/BFAppLinkResolving.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 BFTask; - -/*! - Implement this protocol to provide an alternate strategy for resolving - App Links that may include pre-fetching, caching, or querying for App Link - data from an index provided by a service provider. - */ -@protocol BFAppLinkResolving - -/*! - Asynchronously resolves App Link data for a given URL. - - @param url The URL to resolve into an App Link. - @returns A BFTask that will return a BFAppLink for the given URL. - */ -- (BFTask *)appLinkFromURLInBackground:(NSURL *)url; - -@end diff --git a/Bolts.framework/Headers/BFAppLinkReturnToRefererController.h b/Bolts.framework/Headers/BFAppLinkReturnToRefererController.h deleted file mode 100644 index d19465e..0000000 --- a/Bolts.framework/Headers/BFAppLinkReturnToRefererController.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 - -@class BFAppLink; -@class BFAppLinkReturnToRefererController; - -/*! - Protocol that a class can implement in order to be notified when the user has navigated back - to the referer of an App Link. - */ -@protocol BFAppLinkReturnToRefererControllerDelegate - -@optional - -/*! Called when the user has tapped to navigate, but before the navigation has been performed. */ -- (void)returnToRefererController:(BFAppLinkReturnToRefererController *)controller - willNavigateToAppLink:(BFAppLink *)appLink; - -/*! Called after the navigation has been attempted, with an indication of whether the referer - app link was successfully opened. */ -- (void)returnToRefererController:(BFAppLinkReturnToRefererController *)controller - didNavigateToAppLink:(BFAppLink *)url - type:(BFAppLinkNavigationType)type; - -@end - -/*! - A controller class that implements default behavior for a BFAppLinkReturnToRefererView, including - the ability to display the view above the navigation bar for navigation-based apps. - */ -@interface BFAppLinkReturnToRefererController : NSObject - -/*! - The delegate that will be notified when the user navigates back to the referer. - */ -@property (nonatomic, weak) id delegate; - -/*! - The BFAppLinkReturnToRefererView this controller is controlling. - */ -@property (nonatomic, strong) BFAppLinkReturnToRefererView *view; - -/*! - Initializes a controller suitable for controlling a BFAppLinkReturnToRefererView that is to be displayed - contained within another UIView (i.e., not displayed above the navigation bar). - */ -- (instancetype)init; - -/*! - Initializes a controller suitable for controlling a BFAppLinkReturnToRefererView that is to be displayed - displayed above the navigation bar. - */ -- (instancetype)initForDisplayAboveNavController:(UINavigationController *)navController; - -/*! - Removes the view entirely from the navigation controller it is currently displayed in. - */ -- (void)removeFromNavController; - -/*! - Shows the BFAppLinkReturnToRefererView with the specified referer information. If nil or missing data, - the view will not be displayed. */ -- (void)showViewForRefererAppLink:(BFAppLink *)refererAppLink; - -/*! - Shows the BFAppLinkReturnToRefererView with referer information extracted from the specified URL. - If nil or missing referer App Link data, the view will not be displayed. */ -- (void)showViewForRefererURL:(NSURL *)url; - -/*! - Closes the view, possibly animating it. - */ -- (void)closeViewAnimated:(BOOL)animated; - -@end diff --git a/Bolts.framework/Headers/BFAppLinkReturnToRefererView.h b/Bolts.framework/Headers/BFAppLinkReturnToRefererView.h deleted file mode 100644 index d20f73a..0000000 --- a/Bolts.framework/Headers/BFAppLinkReturnToRefererView.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 - -@class BFAppLinkReturnToRefererView; -@class BFURL; - -typedef NS_ENUM(NSUInteger, BFIncludeStatusBarInSize) { - BFIncludeStatusBarInSizeNever, - BFIncludeStatusBarInSizeIOS7AndLater, - BFIncludeStatusBarInSizeAlways, -}; - -/*! - Protocol that a class can implement in order to be notified when the user has navigated back - to the referer of an App Link. - */ -@protocol BFAppLinkReturnToRefererViewDelegate - -/*! - Called when the user has tapped inside the close button. - */ -- (void)returnToRefererViewDidTapInsideCloseButton:(BFAppLinkReturnToRefererView *)view; - -/*! - Called when the user has tapped inside the App Link portion of the view. - */ -- (void)returnToRefererViewDidTapInsideLink:(BFAppLinkReturnToRefererView *)view - link:(BFAppLink *)link; - -@end - -/*! - Provides a UIView that displays a button allowing users to navigate back to the - application that launched the App Link currently being handled, if the App Link - contained referer data. The user can also close the view by clicking a close button - rather than navigating away. If the view is provided an App Link that does not contain - referer data, it will have zero size and no UI will be displayed. - */ -@interface BFAppLinkReturnToRefererView : UIView - -/*! - The delegate that will be notified when the user navigates back to the referer. - */ -@property (nonatomic, weak) id delegate; - -/*! - The color of the text label and close button. - */ -@property (nonatomic, strong) UIColor *textColor; - -@property (nonatomic, strong) BFAppLink *refererAppLink; - -/*! - Indicates whether to extend the size of the view to include the current status bar - size, for use in scenarios where the view might extend under the status bar on iOS 7 and - above; this property has no effect on earlier versions of iOS. - */ -@property (nonatomic, assign) BFIncludeStatusBarInSize includeStatusBarInSize; - -/*! - Indicates whether the user has closed the view by clicking the close button. - */ -@property (nonatomic, assign) BOOL closed; - -@end diff --git a/Bolts.framework/Headers/BFAppLinkTarget.h b/Bolts.framework/Headers/BFAppLinkTarget.h deleted file mode 100644 index 6172126..0000000 --- a/Bolts.framework/Headers/BFAppLinkTarget.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 a target defined in App Link metadata, consisting of at least - a URL, and optionally an App Store ID and name. - */ -@interface BFAppLinkTarget : NSObject - -/*! Creates a BFAppLinkTarget with the given app site and target URL. */ -+ (instancetype)appLinkTargetWithURL:(NSURL *)url - appStoreId:(NSString *)appStoreId - appName:(NSString *)appName; - -/*! The URL prefix for this app link target */ -@property (nonatomic, strong, readonly) NSURL *URL; - -/*! The app ID for the app store */ -@property (nonatomic, copy, readonly) NSString *appStoreId; - -/*! The name of the app */ -@property (nonatomic, copy, readonly) NSString *appName; - -@end diff --git a/Bolts.framework/Headers/BFCancellationToken.h b/Bolts.framework/Headers/BFCancellationToken.h deleted file mode 100644 index 90a20d7..0000000 --- a/Bolts.framework/Headers/BFCancellationToken.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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/Bolts.framework/Headers/BFCancellationTokenRegistration.h b/Bolts.framework/Headers/BFCancellationTokenRegistration.h deleted file mode 100644 index 3e7b711..0000000 --- a/Bolts.framework/Headers/BFCancellationTokenRegistration.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/Bolts.framework/Headers/BFCancellationTokenSource.h b/Bolts.framework/Headers/BFCancellationTokenSource.h deleted file mode 100644 index bd6e7a1..0000000 --- a/Bolts.framework/Headers/BFCancellationTokenSource.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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/Bolts.framework/Headers/BFExecutor.h b/Bolts.framework/Headers/BFExecutor.h deleted file mode 100644 index 02af9ba..0000000 --- a/Bolts.framework/Headers/BFExecutor.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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/Bolts.framework/Headers/BFMeasurementEvent.h b/Bolts.framework/Headers/BFMeasurementEvent.h deleted file mode 100644 index b3173fc..0000000 --- a/Bolts.framework/Headers/BFMeasurementEvent.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - -/*! The name of the notification posted by BFMeasurementEvent */ -FOUNDATION_EXPORT NSString *const BFMeasurementEventNotificationName; - -/*! Defines keys in the userInfo object for the notification named BFMeasurementEventNotificationName */ -/*! The string field for the name of the event */ -FOUNDATION_EXPORT NSString *const BFMeasurementEventNameKey; -/*! The dictionary field for the arguments of the event */ -FOUNDATION_EXPORT NSString *const BFMeasurementEventArgsKey; - -/*! Bolts Events raised by BFMeasurementEvent for Applink */ -/*! - The name of the event posted when [BFURL URLWithURL:] is called successfully. This represents the successful parsing of an app link URL. - */ -FOUNDATION_EXPORT NSString *const BFAppLinkParseEventName; - -/*! - The name of the event posted when [BFURL URLWithInboundURL:] is called successfully. - This represents parsing an inbound app link URL from a different application - */ -FOUNDATION_EXPORT NSString *const BFAppLinkNavigateInEventName; - -/*! The event raised when the user navigates from your app to other apps */ -FOUNDATION_EXPORT NSString *const BFAppLinkNavigateOutEventName; - -/*! - The event raised when the user navigates out from your app and back to the referrer app. - e.g when the user leaves your app after tapping the back-to-referrer navigation bar - */ -FOUNDATION_EXPORT NSString *const BFAppLinkNavigateBackToReferrerEventName; - -@interface BFMeasurementEvent : NSObject - -@end diff --git a/Bolts.framework/Headers/BFTask.h b/Bolts.framework/Headers/BFTask.h deleted file mode 100644 index dee1137..0000000 --- a/Bolts.framework/Headers/BFTask.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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 - -/*! - 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; - -/*! - A block that can act as a continuation for a task. - */ -typedef id(^BFContinuationBlock)(BFTask *task); - -/*! - 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 : NSObject - -/*! - Creates a task that is already completed with the given result. - @param result The result for the task. - */ -+ (instancetype)taskWithResult:(id)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) id 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/Bolts.framework/Headers/BFTaskCompletionSource.h b/Bolts.framework/Headers/BFTaskCompletionSource.h deleted file mode 100644 index be2fbdb..0000000 --- a/Bolts.framework/Headers/BFTaskCompletionSource.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 BFTask; - -/*! - 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 : NSObject - -/*! - Creates a new unfinished task. - */ -+ (instancetype)taskCompletionSource; - -/*! - The task associated with this TaskCompletionSource. - */ -@property (nonatomic, strong, readonly) BFTask *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:(id)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:(id)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/Bolts.framework/Headers/BFURL.h b/Bolts.framework/Headers/BFURL.h deleted file mode 100644 index 924c91d..0000000 --- a/Bolts.framework/Headers/BFURL.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 BFAppLink; - -/*! - Provides a set of utilities for working with NSURLs, such as parsing of query parameters - and handling for App Link requests. - */ -@interface BFURL : NSObject - -/*! - Creates a link target from a raw URL. - On success, this posts the BFAppLinkParseEventName measurement event. If you are constructing the BFURL within your application delegate's - application:openURL:sourceApplication:annotation:, you should instead use URLWithInboundURL:sourceApplication: - to support better BFMeasurementEvent notifications - @param url The instance of `NSURL` to create BFURL from. - */ -+ (BFURL *)URLWithURL:(NSURL *)url; - -/*! - Creates a link target from a raw URL received from an external application. This is typically called from the app delegate's - application:openURL:sourceApplication:annotation: and will post the BFAppLinkNavigateInEventName measurement event. - @param url The instance of `NSURL` to create BFURL from. - @param sourceApplication the bundle ID of the app that is requesting your app to open the URL. The same sourceApplication in application:openURL:sourceApplication:annotation: - */ -+ (BFURL *)URLWithInboundURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication; - -/*! - Gets the target URL. If the link is an App Link, this is the target of the App Link. - Otherwise, it is the url that created the target. - */ -@property (nonatomic, strong, readonly) NSURL *targetURL; - -/*! - Gets the query parameters for the target, parsed into an NSDictionary. - */ -@property (nonatomic, strong, readonly) NSDictionary *targetQueryParameters; - -/*! - If this link target is an App Link, this is the data found in al_applink_data. - Otherwise, it is nil. - */ -@property (nonatomic, strong, readonly) NSDictionary *appLinkData; - -/*! - If this link target is an App Link, this is the data found in extras. - */ -@property (nonatomic, strong, readonly) NSDictionary *appLinkExtras; - -/*! - The App Link indicating how to navigate back to the referer app, if any. - */ -@property (nonatomic, strong, readonly) BFAppLink *appLinkReferer; - -/*! - The URL that was used to create this BFURL. - */ -@property (nonatomic, strong, readonly) NSURL *inputURL; - -/*! - The query parameters of the inputURL, parsed into an NSDictionary. - */ -@property (nonatomic, strong, readonly) NSDictionary *inputQueryParameters; - -@end diff --git a/Bolts.framework/Headers/BFWebViewAppLinkResolver.h b/Bolts.framework/Headers/BFWebViewAppLinkResolver.h deleted file mode 100644 index 3782ae2..0000000 --- a/Bolts.framework/Headers/BFWebViewAppLinkResolver.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 reference implementation for an App Link resolver that uses a hidden UIWebView - to parse the HTML containing App Link metadata. - */ -@interface BFWebViewAppLinkResolver : NSObject - -/*! - Gets the instance of a BFWebViewAppLinkResolver. - */ -+ (instancetype)sharedInstance; - -@end diff --git a/Bolts.framework/Headers/Bolts.h b/Bolts.framework/Headers/Bolts.h deleted file mode 100644 index ca48a16..0000000 --- a/Bolts.framework/Headers/Bolts.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 - -#if __has_include() && TARGET_OS_IPHONE -#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/Bolts.framework/Headers/BoltsVersion.h b/Bolts.framework/Headers/BoltsVersion.h deleted file mode 100644 index 6719301..0000000 --- a/Bolts.framework/Headers/BoltsVersion.h +++ /dev/null @@ -1 +0,0 @@ -#define BOLTS_VERSION @"1.2.1" diff --git a/Bolts.framework/Info.plist b/Bolts.framework/Info.plist deleted file mode 100644 index 007ef92..0000000 Binary files a/Bolts.framework/Info.plist and /dev/null differ diff --git a/Bolts.framework/Modules/module.modulemap b/Bolts.framework/Modules/module.modulemap deleted file mode 100644 index 3c92a17..0000000 --- a/Bolts.framework/Modules/module.modulemap +++ /dev/null @@ -1,15 +0,0 @@ -framework module Bolts { - umbrella header "Bolts.h" - - export * - module * { export * } - - explicit module BFAppLinkResolving { - header "BFAppLinkResolving.h" - export * - } - explicit module BFWebViewAppLinkResolver { - header "BFWebViewAppLinkResolver.h" - export * - } -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index fcaf027..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,21 +0,0 @@ -# How to contribute -This document will outline how to contribute to the further development of the HackTX iOS. While this app has been specifically designed for use at HackTX, it can easily be adapted for use by any hackathon, so we appreciate any contributions to help us further our support back to the community. We have a few guidelines that we need contributors from the community to follow in order to keep things running smoothly. - -## Getting Started - -Fork then clone the repo replacing `your_username` with your actual GitHub username - - git clone git@github.com:your_username/iOS-HackTX-2015.git - - -## Making Changes - -1. Create a branch with an appropriate name - 1. Branch off from where you want to base your work (usually the master branch) - 2. Please don't target release branches - 3. Do not work directly on master. If you submit a pull request directly on the master branch it will be closed. -2. Make your commits in logical units. There should not be tiny changes in all commits, nor should all your work be condensed in one commit. -3. Make sure you continue to follow the whitespace formatting. Remove extraneous whitespace using `git diff --check` - -## Submitting Changes -Once you have pushed all changes to your fork, you can submit a pull request to the HackTX repo. A member from the HackTX iOS team will review your changes. If it all looks good, we'll merge it, otherwise we'll make suggestions how to fix it. diff --git a/Fabric.framework/Fabric b/Fabric.framework/Fabric deleted file mode 100755 index 7ef9378..0000000 Binary files a/Fabric.framework/Fabric and /dev/null differ diff --git a/Fabric.framework/Headers/FABAttributes.h b/Fabric.framework/Headers/FABAttributes.h deleted file mode 100644 index 7200ea4..0000000 --- a/Fabric.framework/Headers/FABAttributes.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// FABAttributes.h -// Fabric -// -// Created by Priyanka Joshi on 3/3/15. -// Copyright (c) 2015 Twitter. All rights reserved. -// - -#pragma once - -#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x))) - -#if __has_feature(nullability) -#define FAB_NONNULL __nonnull -#define FAB_NULLABLE __nullable -#define FAB_START_NONNULL _Pragma("clang assume_nonnull begin") -#define FAB_END_NONNULL _Pragma("clang assume_nonnull end") -#else -#define FAB_NONNULL -#define FAB_NULLABLE -#define FAB_START_NONNULL -#define FAB_END_NONNULL -#endif diff --git a/Fabric.framework/Headers/Fabric.h b/Fabric.framework/Headers/Fabric.h deleted file mode 100644 index 7b1b31a..0000000 --- a/Fabric.framework/Headers/Fabric.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// Fabric.h -// -// Copyright (c) 2014 Twitter. All rights reserved. -// - -#import -#import "FABAttributes.h" - -FAB_START_NONNULL - -/** - * Fabric Base. Coordinates configuration and starts all provided kits. - */ -@interface Fabric : NSObject - -/** - * Initialize Fabric and all provided kits. Call this method within your App Delegate's - * `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. - * - * For example, in Objective-C: - * - * `[Fabric with:@[TwitterKit, CrashlyticsKit, MoPubKit]];` - * - * Swift: - * - * `Fabric.with([Twitter(), Crashlytics(), MoPub()])` - * - * Only the first call to this method is honored. Subsequent calls are no-ops. - * - * @param kits An array of kit instances. Kits may provide a macro such as CrashlyticsKit which can be passed in as array elements in objective-c. - * - * @return Returns the shared Fabric instance. In most cases this can be ignored. - */ -+ (instancetype)with:(NSArray *)kits; - -/** - * Returns the Fabric singleton object. - */ -+ (instancetype)sharedSDK; - -/** - * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. - */ -@property (nonatomic, assign) BOOL debug; - -/** - * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. - */ -- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); - -/** - * Returns Fabrics's instance of the specified kit. - * - * @param klass The class of the kit. - * - * @return The kit instance of class klass which was provided to with: or nil. - */ -- (id FAB_NULLABLE)kitForClass:(Class)klass; - -/** - * Returns a dictionary containing the kit configuration info for the provided kit. - * The configuration information is parsed from the application's Info.plist. This - * method is primarily intended to be used by kits to retrieve their configuration. - * - * @param kitInstance An instance of the kit whose configuration should be returned. - * - * @return A dictionary containing kit specific configuration information or nil if none exists. - */ -- (NSDictionary * FAB_NULLABLE)configurationDictionaryForKit:(id)kitInstance; - -@end - -FAB_END_NONNULL - diff --git a/Fabric.framework/Info.plist b/Fabric.framework/Info.plist deleted file mode 100644 index bf020d8..0000000 --- a/Fabric.framework/Info.plist +++ /dev/null @@ -1,55 +0,0 @@ - - - - - BuildMachineOSBuild - 13F34 - CFBundleDevelopmentRegion - en - CFBundleExecutable - Fabric - CFBundleIdentifier - io.fabric.sdk.ios - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Fabric - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.2.8 - CFBundleSignature - ???? - CFBundleSupportedPlatforms - - iPhoneOS - - CFBundleVersion - 20 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 12B411 - DTPlatformName - iphoneos - DTPlatformVersion - 8.1 - DTSDKBuild - 12B411 - DTSDKName - iphoneos8.1 - DTXcode - 0611 - DTXcodeBuild - 6A2008a - MinimumOSVersion - 5.0 - NSHumanReadableCopyright - Copyright © 2015 Twitter. All rights reserved. - UIDeviceFamily - - 1 - 2 - - - diff --git a/Fabric.framework/run b/Fabric.framework/run deleted file mode 100755 index b3ca4de..0000000 Binary files a/Fabric.framework/run and /dev/null differ diff --git a/HackTX-Bridging-Header.h b/HackTX-Bridging-Header.h deleted file mode 100644 index 2c0c617..0000000 --- a/HackTX-Bridging-Header.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// HackTX-Bridging-Header.h -// HackTX -// -// Created by Drew Romanyk on 9/5/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -#import diff --git a/HackTX.xcodeproj/project.pbxproj b/HackTX.xcodeproj/project.pbxproj index 4fdced1..2660cdc 100644 --- a/HackTX.xcodeproj/project.pbxproj +++ b/HackTX.xcodeproj/project.pbxproj @@ -7,804 +7,324 @@ objects = { /* Begin PBXBuildFile section */ - 0EEBD4D4183683D7099C95B8 /* Pods_HackTX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 715158D3DB02FD4CF82662A4 /* Pods_HackTX.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 3A192BA01B9EBE2C008AA3BA /* EventFeedbackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A192B9F1B9EBE2C008AA3BA /* EventFeedbackViewController.swift */; }; - 3A21566E1B8A430C00BCA241 /* PartnersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A21566D1B8A430C00BCA241 /* PartnersViewController.swift */; }; - 3A2156731B8A436100BCA241 /* PartnersViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2156711B8A436100BCA241 /* PartnersViewCell.swift */; }; - 3A2156741B8A436100BCA241 /* PartnersViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3A2156721B8A436100BCA241 /* PartnersViewCell.xib */; }; - 3A2156761B8A44CC00BCA241 /* Sponsor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2156751B8A44CC00BCA241 /* Sponsor.swift */; }; - 3A21567A1B8A577100BCA241 /* ScheduleDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2156791B8A577100BCA241 /* ScheduleDetailViewController.swift */; }; - 3A21567C1B8A5D3D00BCA241 /* ScheduleCluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A21567B1B8A5D3D00BCA241 /* ScheduleCluster.swift */; }; - 3A21567E1B8A5D5400BCA241 /* Day.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A21567D1B8A5D5400BCA241 /* Day.swift */; }; - 3A2156801B8A5D6A00BCA241 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A21567F1B8A5D6A00BCA241 /* Event.swift */; }; - 3A2156821B8A5D8900BCA241 /* Speaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2156811B8A5D8900BCA241 /* Speaker.swift */; }; - 3A3666581B9CD8350028BA91 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3666571B9CD8350028BA91 /* Router.swift */; }; - 3A806E291B9A895D00E29A0F /* CheckInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A806E281B9A895D00E29A0F /* CheckInViewController.swift */; }; - 3A806E511B9A9E4B00E29A0F /* UserPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A806E501B9A9E4B00E29A0F /* UserPrefs.swift */; }; - 3A806E5F1B9ACDE100E29A0F /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A806E5E1B9ACDE100E29A0F /* CoreData.framework */; }; - 3AEB82341B7F107F00224BC3 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEB82301B7F107F00224BC3 /* Fabric.framework */; }; - 3AEB82351B7F107F00224BC3 /* TwitterKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEB82311B7F107F00224BC3 /* TwitterKit.framework */; }; - 3AEB82361B7F107F00224BC3 /* TwitterKitResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3AEB82321B7F107F00224BC3 /* TwitterKitResources.bundle */; }; - 3AEB82371B7F107F00224BC3 /* TwitterCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEB82331B7F107F00224BC3 /* TwitterCore.framework */; }; - 3AEB82391B7F14E300224BC3 /* AnnouncementsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEB82381B7F14E300224BC3 /* AnnouncementsViewController.swift */; }; - 3AEB823B1B7F14F400224BC3 /* Announcement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEB823A1B7F14F400224BC3 /* Announcement.swift */; }; - F91C84341BAD2A57006E2D8F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F91C84331BAD2A57006E2D8F /* GoogleService-Info.plist */; settings = {ASSET_TAGS = (); }; }; - F91C84381BAD2A74006E2D8F /* ApiKeys.plist in Resources */ = {isa = PBXBuildFile; fileRef = F91C84371BAD2A74006E2D8F /* ApiKeys.plist */; settings = {ASSET_TAGS = (); }; }; - F91C843A1BAD2A81006E2D8F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F91C84391BAD2A81006E2D8F /* GoogleService-Info.plist */; settings = {ASSET_TAGS = (); }; }; - F91C843C1BAD2AED006E2D8F /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91C843B1BAD2AED006E2D8F /* Reachability.swift */; settings = {ASSET_TAGS = (); }; }; - F96AB7931B97D14B00394217 /* Parse.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7921B97D14B00394217 /* Parse.framework */; }; - F96AB7951B97D18F00394217 /* Bolts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7941B97D18F00394217 /* Bolts.framework */; }; - F96AB7A01B97D25C00394217 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7961B97D25C00394217 /* AudioToolbox.framework */; }; - F96AB7A11B97D25C00394217 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7971B97D25C00394217 /* CFNetwork.framework */; }; - F96AB7A21B97D25C00394217 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7981B97D25C00394217 /* CoreGraphics.framework */; }; - F96AB7A31B97D25C00394217 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB7991B97D25C00394217 /* CoreLocation.framework */; }; - F96AB7A41B97D25C00394217 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79A1B97D25C00394217 /* libsqlite3.dylib */; }; - F96AB7A51B97D25C00394217 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79B1B97D25C00394217 /* libz.dylib */; }; - F96AB7A61B97D25C00394217 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79C1B97D25C00394217 /* QuartzCore.framework */; }; - F96AB7A71B97D25C00394217 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79D1B97D25C00394217 /* Security.framework */; }; - F96AB7A81B97D25C00394217 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79E1B97D25C00394217 /* StoreKit.framework */; }; - F96AB7A91B97D25C00394217 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F96AB79F1B97D25C00394217 /* SystemConfiguration.framework */; }; - F96AB7AB1B97D40600394217 /* ApiKeys.plist in Resources */ = {isa = PBXBuildFile; fileRef = F96AB7AA1B97D40600394217 /* ApiKeys.plist */; }; - F96AB7AD1B97E01200394217 /* MapsPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96AB7AC1B97E01200394217 /* MapsPageViewController.swift */; }; - F9C42ACA1B979B7200A21CF8 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C42AC91B979B7200A21CF8 /* Location.swift */; }; - F9E990381B50EBBA00057823 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E990371B50EBBA00057823 /* AppDelegate.swift */; }; - F9E9903A1B50EBBA00057823 /* ScheduleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E990391B50EBBA00057823 /* ScheduleViewController.swift */; }; - F9E9903F1B50EBBA00057823 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F9E9903D1B50EBBA00057823 /* Main.storyboard */; }; - F9E990411B50EBBA00057823 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F9E990401B50EBBA00057823 /* Images.xcassets */; }; - F9E990441B50EBBA00057823 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9E990421B50EBBA00057823 /* LaunchScreen.xib */; }; - F9E990651B50FFFF00057823 /* TwitterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E990641B50FFFF00057823 /* TwitterViewController.swift */; }; - F9E990671B51001000057823 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E990661B51001000057823 /* MapViewController.swift */; }; - F9EE70221B912EE300B493A8 /* PageItemViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9EE70211B912EE300B493A8 /* PageItemViewController.swift */; }; + 4E5410941E35FEC803EA8E2D /* libPods-HackTX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B67A442663439B9AD415E5F6 /* libPods-HackTX.a */; }; + 8200AD711DB3577400D4EE11 /* AnnouncementTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8200AD6F1DB3577400D4EE11 /* AnnouncementTableViewCell.m */; }; + 8200AD721DB3577400D4EE11 /* AnnouncementTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8200AD701DB3577400D4EE11 /* AnnouncementTableViewCell.xib */; }; + 820B450C1D9624B1002A26BA /* SponsorTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 820B450A1D9624B1002A26BA /* SponsorTableViewCell.m */; }; + 820B450D1D9624B1002A26BA /* SponsorTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 820B450B1D9624B1002A26BA /* SponsorTableViewCell.xib */; }; + 8215AD7F1D8DBE3700377370 /* Sponsor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD7E1D8DBE3700377370 /* Sponsor.m */; }; + 8215AD831D8DC49F00377370 /* Announcement.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD821D8DC49F00377370 /* Announcement.m */; }; + 8215AD861D8DC53D00377370 /* Location.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD851D8DC53D00377370 /* Location.m */; }; + 8215AD891D8DC63F00377370 /* Event.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD881D8DC63F00377370 /* Event.m */; }; + 8215AD8C1D8DC70F00377370 /* Speaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD8B1D8DC70F00377370 /* Speaker.m */; }; + 8215AD8F1D8DC88100377370 /* HTXAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD8E1D8DC88100377370 /* HTXAPI.m */; }; + 8215AD921D8DD26C00377370 /* SponsorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215AD911D8DD26C00377370 /* SponsorViewController.m */; }; + 8215ADAF1D8E27F800377370 /* NSString+MD5.m in Sources */ = {isa = PBXBuildFile; fileRef = 8215ADAE1D8E27F800377370 /* NSString+MD5.m */; }; + 8221B2071DAEF9D900FB1BE1 /* ScheduleTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8221B2051DAEF9D900FB1BE1 /* ScheduleTableViewCell.m */; }; + 8221B2081DAEF9D900FB1BE1 /* ScheduleTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8221B2061DAEF9D900FB1BE1 /* ScheduleTableViewCell.xib */; }; + 8221B20B1DAF109B00FB1BE1 /* HTXAPIKeyStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8221B20A1DAF109B00FB1BE1 /* HTXAPIKeyStore.m */; }; + 822A4D981D779BCD00C65761 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 822A4D971D779BCD00C65761 /* main.m */; }; + 822A4D9B1D779BCD00C65761 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 822A4D9A1D779BCD00C65761 /* AppDelegate.m */; }; + 822A4DA41D779BCD00C65761 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 822A4DA21D779BCD00C65761 /* Main.storyboard */; }; + 822A4DA61D779BCD00C65761 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 822A4DA51D779BCD00C65761 /* Assets.xcassets */; }; + 822A4DA91D779BCD00C65761 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 822A4DA71D779BCD00C65761 /* LaunchScreen.storyboard */; }; + 822A4DB41D779DBD00C65761 /* ScheduleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 822A4DB31D779DBD00C65761 /* ScheduleViewController.m */; }; + 822A4DB71D779DD100C65761 /* AnnouncementsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 822A4DB61D779DD100C65761 /* AnnouncementsViewController.m */; }; + 822A4DC01D779DF100C65761 /* MapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 822A4DBF1D779DF100C65761 /* MapViewController.m */; }; + 822E95191D7FF4EE00C0402E /* HTXConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 822E95181D7FF4EE00C0402E /* HTXConstants.m */; }; + 82BA003A1D81492F006CA2C6 /* AutolayoutHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BA00391D81492F006CA2C6 /* AutolayoutHelper.m */; }; + 82BA003D1D8149B1006CA2C6 /* UIColor+Palette.m in Sources */ = {isa = PBXBuildFile; fileRef = 82BA003C1D8149B1006CA2C6 /* UIColor+Palette.m */; }; + 82BAC6D21D9D1FC000BF62C0 /* JosefinSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6C81D9D1FC000BF62C0 /* JosefinSans-Bold.ttf */; }; + 82BAC6D31D9D1FC000BF62C0 /* JosefinSans-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6C91D9D1FC000BF62C0 /* JosefinSans-BoldItalic.ttf */; }; + 82BAC6D41D9D1FC000BF62C0 /* JosefinSans-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CA1D9D1FC000BF62C0 /* JosefinSans-Italic.ttf */; }; + 82BAC6D51D9D1FC000BF62C0 /* JosefinSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CB1D9D1FC000BF62C0 /* JosefinSans-Light.ttf */; }; + 82BAC6D61D9D1FC000BF62C0 /* JosefinSans-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CC1D9D1FC000BF62C0 /* JosefinSans-LightItalic.ttf */; }; + 82BAC6D71D9D1FC000BF62C0 /* JosefinSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CD1D9D1FC000BF62C0 /* JosefinSans-Regular.ttf */; }; + 82BAC6D81D9D1FC000BF62C0 /* JosefinSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CE1D9D1FC000BF62C0 /* JosefinSans-SemiBold.ttf */; }; + 82BAC6D91D9D1FC000BF62C0 /* JosefinSans-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6CF1D9D1FC000BF62C0 /* JosefinSans-SemiBoldItalic.ttf */; }; + 82BAC6DA1D9D1FC000BF62C0 /* JosefinSans-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6D01D9D1FC000BF62C0 /* JosefinSans-Thin.ttf */; }; + 82BAC6DB1D9D1FC000BF62C0 /* JosefinSans-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 82BAC6D11D9D1FC000BF62C0 /* JosefinSans-ThinItalic.ttf */; }; + 82BFF6D71D9E662600DB2543 /* partners.json in Resources */ = {isa = PBXBuildFile; fileRef = 82BFF6D61D9E662600DB2543 /* partners.json */; }; + 82D303C91DB23BB2003C98ED /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82D303C81DB23BB2003C98ED /* PassKit.framework */; }; + 82D303D11DB24084003C98ED /* Hacker.m in Sources */ = {isa = PBXBuildFile; fileRef = 82D303D01DB24084003C98ED /* Hacker.m */; }; + 82EAFC871DB200D50061C449 /* CheckInViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 82EAFC851DB200D50061C449 /* CheckInViewController.m */; }; + 82EAFC881DB200D50061C449 /* CheckInViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82EAFC861DB200D50061C449 /* CheckInViewController.xib */; }; + 82F3C16B1DB71BAC004F96FC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 82F3C16A1DB71BAC004F96FC /* GoogleService-Info.plist */; }; + 83420EE61F15C75F002AA404 /* APIKeys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 83420EE51F15C75F002AA404 /* APIKeys.plist */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 3A806EF01B9AD92E00E29A0F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = C08F031FB69E7F1318E303AE8858C9AA; - remoteInfo = "Pods-HackTX"; - }; - 3ADDC0601B9CC2A500D0ECD2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 354BEE9D235DF8157E8AB34827ECD658; - remoteInfo = Alamofire; - }; - 3ADDC0621B9CC2A500D0ECD2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 1A677D45179CEC1F17E608D4F6CC0821; - remoteInfo = RSBarcodes_Swift; - }; - 3ADDC0641B9CC2A500D0ECD2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = EB4CB50B42FA0112BD3CB968CB68AC93; - remoteInfo = SwiftyJSON; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 3A806E2C1B9A8E6500E29A0F /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 7; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ - 163274BAD693C35C1B0222A5 /* Pods-HackTX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HackTX.release.xcconfig"; path = "Pods/Target Support Files/Pods-HackTX/Pods-HackTX.release.xcconfig"; sourceTree = ""; }; - 3A192B9F1B9EBE2C008AA3BA /* EventFeedbackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventFeedbackViewController.swift; sourceTree = ""; }; - 3A21566D1B8A430C00BCA241 /* PartnersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartnersViewController.swift; sourceTree = ""; }; - 3A2156711B8A436100BCA241 /* PartnersViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartnersViewCell.swift; sourceTree = ""; }; - 3A2156721B8A436100BCA241 /* PartnersViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PartnersViewCell.xib; sourceTree = ""; }; - 3A2156751B8A44CC00BCA241 /* Sponsor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sponsor.swift; sourceTree = ""; }; - 3A2156791B8A577100BCA241 /* ScheduleDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduleDetailViewController.swift; sourceTree = ""; }; - 3A21567B1B8A5D3D00BCA241 /* ScheduleCluster.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScheduleCluster.swift; sourceTree = ""; }; - 3A21567D1B8A5D5400BCA241 /* Day.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Day.swift; sourceTree = ""; }; - 3A21567F1B8A5D6A00BCA241 /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - 3A2156811B8A5D8900BCA241 /* Speaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Speaker.swift; sourceTree = ""; }; - 3A3666571B9CD8350028BA91 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; - 3A806E281B9A895D00E29A0F /* CheckInViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckInViewController.swift; sourceTree = ""; }; - 3A806E501B9A9E4B00E29A0F /* UserPrefs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPrefs.swift; sourceTree = ""; }; - 3A806E5E1B9ACDE100E29A0F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; - 3A806E801B9AD62800E29A0F /* HackTX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HackTX-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; - 3A806E861B9AD92E00E29A0F /* Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = ""; }; - 3A806E871B9AD92E00E29A0F /* GGLContext+Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GGLContext+Analytics.h"; sourceTree = ""; }; - 3A806E8A1B9AD92E00E29A0F /* Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Core.h; sourceTree = ""; }; - 3A806E8B1B9AD92E00E29A0F /* GGLConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLConfiguration.h; sourceTree = ""; }; - 3A806E8C1B9AD92E00E29A0F /* GGLContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLContext.h; sourceTree = ""; }; - 3A806E8D1B9AD92E00E29A0F /* GGLErrorCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLErrorCode.h; sourceTree = ""; }; - 3A806E8F1B9AD92E00E29A0F /* libGGLAnalytics.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGGLAnalytics.a; sourceTree = ""; }; - 3A806E901B9AD92E00E29A0F /* libGGLCore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGGLCore.a; sourceTree = ""; }; - 3A806E941B9AD92E00E29A0F /* GAI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAI.h; sourceTree = ""; }; - 3A806E951B9AD92E00E29A0F /* GAIDictionaryBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIDictionaryBuilder.h; sourceTree = ""; }; - 3A806E961B9AD92E00E29A0F /* GAIEcommerceFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceFields.h; sourceTree = ""; }; - 3A806E971B9AD92E00E29A0F /* GAIEcommerceProduct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProduct.h; sourceTree = ""; }; - 3A806E981B9AD92E00E29A0F /* GAIEcommerceProductAction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProductAction.h; sourceTree = ""; }; - 3A806E991B9AD92E00E29A0F /* GAIEcommercePromotion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommercePromotion.h; sourceTree = ""; }; - 3A806E9A1B9AD92E00E29A0F /* GAIFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIFields.h; sourceTree = ""; }; - 3A806E9B1B9AD92E00E29A0F /* GAILogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAILogger.h; sourceTree = ""; }; - 3A806E9C1B9AD92E00E29A0F /* GAITrackedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITrackedViewController.h; sourceTree = ""; }; - 3A806E9D1B9AD92E00E29A0F /* GAITracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITracker.h; sourceTree = ""; }; - 3A806E9F1B9AD92E00E29A0F /* libGoogleAnalytics.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGoogleAnalytics.a; sourceTree = ""; }; - 3A806EA21B9AD92E00E29A0F /* libGTMSessionFetcher_core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTMSessionFetcher_core.a; sourceTree = ""; }; - 3A806EA31B9AD92E00E29A0F /* libGTMSessionFetcher_full.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTMSessionFetcher_full.a; sourceTree = ""; }; - 3A806EA61B9AD92E00E29A0F /* libGSDK_Overload.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGSDK_Overload.a; sourceTree = ""; }; - 3A806EA91B9AD92E00E29A0F /* libGTM_AddressBook.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_AddressBook.a; sourceTree = ""; }; - 3A806EAA1B9AD92E00E29A0F /* libGTM_core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_core.a; sourceTree = ""; }; - 3A806EAB1B9AD92E00E29A0F /* libGTM_DebugUtils.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_DebugUtils.a; sourceTree = ""; }; - 3A806EAC1B9AD92E00E29A0F /* libGTM_GTMURLBuilder.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_GTMURLBuilder.a; sourceTree = ""; }; - 3A806EAD1B9AD92E00E29A0F /* libGTM_iPhone.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_iPhone.a; sourceTree = ""; }; - 3A806EAE1B9AD92E00E29A0F /* libGTM_KVO.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_KVO.a; sourceTree = ""; }; - 3A806EAF1B9AD92E00E29A0F /* libGTM_NSDictionary+URLArguments.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libGTM_NSDictionary+URLArguments.a"; sourceTree = ""; }; - 3A806EB01B9AD92E00E29A0F /* libGTM_NSScannerJSON.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_NSScannerJSON.a; sourceTree = ""; }; - 3A806EB11B9AD92E00E29A0F /* libGTM_NSStringHTML.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_NSStringHTML.a; sourceTree = ""; }; - 3A806EB21B9AD92E00E29A0F /* libGTM_NSStringXML.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_NSStringXML.a; sourceTree = ""; }; - 3A806EB31B9AD92E00E29A0F /* libGTM_Regex.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_Regex.a; sourceTree = ""; }; - 3A806EB41B9AD92E00E29A0F /* libGTM_RoundedRectPath.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_RoundedRectPath.a; sourceTree = ""; }; - 3A806EB51B9AD92E00E29A0F /* libGTM_StringEncoding.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_StringEncoding.a; sourceTree = ""; }; - 3A806EB61B9AD92E00E29A0F /* libGTM_SystemVersion.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTM_SystemVersion.a; sourceTree = ""; }; - 3A806EB71B9AD92E00E29A0F /* libGTM_UIFont+LineHeight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libGTM_UIFont+LineHeight.a"; sourceTree = ""; }; - 3A806EB81B9AD92E00E29A0F /* libGTMStackTrace.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libGTMStackTrace.a; sourceTree = ""; }; - 3A806EBD1B9AD92E00E29A0F /* Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = ""; }; - 3A806EBE1B9AD92E00E29A0F /* Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Core.h; sourceTree = ""; }; - 3A806EBF1B9AD92E00E29A0F /* GGLConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLConfiguration.h; sourceTree = ""; }; - 3A806EC01B9AD92E00E29A0F /* GGLContext+Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GGLContext+Analytics.h"; sourceTree = ""; }; - 3A806EC11B9AD92E00E29A0F /* GGLContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLContext.h; sourceTree = ""; }; - 3A806EC21B9AD92E00E29A0F /* GGLErrorCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLErrorCode.h; sourceTree = ""; }; - 3A806EC41B9AD92E00E29A0F /* GAI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAI.h; sourceTree = ""; }; - 3A806EC51B9AD92E00E29A0F /* GAIDictionaryBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIDictionaryBuilder.h; sourceTree = ""; }; - 3A806EC61B9AD92E00E29A0F /* GAIEcommerceFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceFields.h; sourceTree = ""; }; - 3A806EC71B9AD92E00E29A0F /* GAIEcommerceProduct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProduct.h; sourceTree = ""; }; - 3A806EC81B9AD92E00E29A0F /* GAIEcommerceProductAction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProductAction.h; sourceTree = ""; }; - 3A806EC91B9AD92E00E29A0F /* GAIEcommercePromotion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommercePromotion.h; sourceTree = ""; }; - 3A806ECA1B9AD92E00E29A0F /* GAIFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIFields.h; sourceTree = ""; }; - 3A806ECB1B9AD92E00E29A0F /* GAILogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAILogger.h; sourceTree = ""; }; - 3A806ECC1B9AD92E00E29A0F /* GAITrackedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITrackedViewController.h; sourceTree = ""; }; - 3A806ECD1B9AD92E00E29A0F /* GAITracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITracker.h; sourceTree = ""; }; - 3A806ED11B9AD92E00E29A0F /* Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = ""; }; - 3A806ED21B9AD92E00E29A0F /* Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Core.h; sourceTree = ""; }; - 3A806ED31B9AD92E00E29A0F /* GGLConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLConfiguration.h; sourceTree = ""; }; - 3A806ED41B9AD92E00E29A0F /* GGLContext+Analytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GGLContext+Analytics.h"; sourceTree = ""; }; - 3A806ED51B9AD92E00E29A0F /* GGLContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLContext.h; sourceTree = ""; }; - 3A806ED61B9AD92E00E29A0F /* GGLErrorCode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GGLErrorCode.h; sourceTree = ""; }; - 3A806ED81B9AD92E00E29A0F /* GAI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAI.h; sourceTree = ""; }; - 3A806ED91B9AD92E00E29A0F /* GAIDictionaryBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIDictionaryBuilder.h; sourceTree = ""; }; - 3A806EDA1B9AD92E00E29A0F /* GAIEcommerceFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceFields.h; sourceTree = ""; }; - 3A806EDB1B9AD92E00E29A0F /* GAIEcommerceProduct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProduct.h; sourceTree = ""; }; - 3A806EDC1B9AD92E00E29A0F /* GAIEcommerceProductAction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommerceProductAction.h; sourceTree = ""; }; - 3A806EDD1B9AD92E00E29A0F /* GAIEcommercePromotion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIEcommercePromotion.h; sourceTree = ""; }; - 3A806EDE1B9AD92E00E29A0F /* GAIFields.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAIFields.h; sourceTree = ""; }; - 3A806EDF1B9AD92E00E29A0F /* GAILogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAILogger.h; sourceTree = ""; }; - 3A806EE01B9AD92E00E29A0F /* GAITrackedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITrackedViewController.h; sourceTree = ""; }; - 3A806EE11B9AD92E00E29A0F /* GAITracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAITracker.h; sourceTree = ""; }; - 3A806EE31B9AD92E00E29A0F /* Manifest.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Manifest.lock; sourceTree = ""; }; - 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Pods.xcodeproj; sourceTree = ""; }; - 3A806EE91B9AD92E00E29A0F /* Pods-HackTX-acknowledgements.markdown */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Pods-HackTX-acknowledgements.markdown"; sourceTree = ""; }; - 3A806EEA1B9AD92E00E29A0F /* Pods-HackTX-acknowledgements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Pods-HackTX-acknowledgements.plist"; sourceTree = ""; }; - 3A806EEB1B9AD92E00E29A0F /* Pods-HackTX-dummy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Pods-HackTX-dummy.m"; sourceTree = ""; }; - 3A806EEC1B9AD92E00E29A0F /* Pods-HackTX-resources.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "Pods-HackTX-resources.sh"; sourceTree = ""; }; - 3A806EED1B9AD92E00E29A0F /* Pods-HackTX.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Pods-HackTX.debug.xcconfig"; sourceTree = ""; }; - 3A806EEE1B9AD92E00E29A0F /* Pods-HackTX.release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Pods-HackTX.release.xcconfig"; sourceTree = ""; }; - 3AEB82301B7F107F00224BC3 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = ""; }; - 3AEB82311B7F107F00224BC3 /* TwitterKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = TwitterKit.framework; sourceTree = ""; }; - 3AEB82321B7F107F00224BC3 /* TwitterKitResources.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = TwitterKitResources.bundle; path = TwitterKit.framework/Versions/A/Resources/TwitterKitResources.bundle; sourceTree = ""; }; - 3AEB82331B7F107F00224BC3 /* TwitterCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = TwitterCore.framework; sourceTree = ""; }; - 3AEB82381B7F14E300224BC3 /* AnnouncementsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnouncementsViewController.swift; sourceTree = ""; }; - 3AEB823A1B7F14F400224BC3 /* Announcement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Announcement.swift; sourceTree = ""; }; - 715158D3DB02FD4CF82662A4 /* Pods_HackTX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HackTX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 94E240E75C09DEDD03EA913F /* Pods-HackTX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HackTX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HackTX/Pods-HackTX.debug.xcconfig"; sourceTree = ""; }; - F91C84331BAD2A57006E2D8F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - F91C84371BAD2A74006E2D8F /* ApiKeys.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ApiKeys.plist; sourceTree = ""; }; - F91C84391BAD2A81006E2D8F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - F91C843B1BAD2AED006E2D8F /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; - F96AB7921B97D14B00394217 /* Parse.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Parse.framework; sourceTree = ""; }; - F96AB7941B97D18F00394217 /* Bolts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Bolts.framework; sourceTree = ""; }; - F96AB7961B97D25C00394217 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; - F96AB7971B97D25C00394217 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; - F96AB7981B97D25C00394217 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - F96AB7991B97D25C00394217 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; - F96AB79A1B97D25C00394217 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; - F96AB79B1B97D25C00394217 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; - F96AB79C1B97D25C00394217 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - F96AB79D1B97D25C00394217 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; - F96AB79E1B97D25C00394217 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; - F96AB79F1B97D25C00394217 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; - F96AB7AA1B97D40600394217 /* ApiKeys.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ApiKeys.plist; sourceTree = ""; }; - F96AB7AC1B97E01200394217 /* MapsPageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapsPageViewController.swift; sourceTree = ""; }; - F9C42AC91B979B7200A21CF8 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = ""; }; - F9E990321B50EBBA00057823 /* HackTX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HackTX.app; sourceTree = BUILT_PRODUCTS_DIR; }; - F9E990361B50EBBA00057823 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F9E990371B50EBBA00057823 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - F9E990391B50EBBA00057823 /* ScheduleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleViewController.swift; sourceTree = ""; }; - F9E9903E1B50EBBA00057823 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - F9E990401B50EBBA00057823 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - F9E990431B50EBBA00057823 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - F9E9904E1B50EBBA00057823 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F9E9904F1B50EBBA00057823 /* HackTXTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackTXTests.swift; sourceTree = ""; }; - F9E990641B50FFFF00057823 /* TwitterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterViewController.swift; sourceTree = ""; }; - F9E990661B51001000057823 /* MapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; - F9EE70211B912EE300B493A8 /* PageItemViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageItemViewController.swift; sourceTree = ""; }; + 099214E83B6739C941439E79 /* Pods-HackTX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HackTX.release.xcconfig"; path = "Pods/Target Support Files/Pods-HackTX/Pods-HackTX.release.xcconfig"; sourceTree = ""; }; + 8200AD6E1DB3577400D4EE11 /* AnnouncementTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementTableViewCell.h; sourceTree = ""; }; + 8200AD6F1DB3577400D4EE11 /* AnnouncementTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnnouncementTableViewCell.m; sourceTree = ""; }; + 8200AD701DB3577400D4EE11 /* AnnouncementTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AnnouncementTableViewCell.xib; sourceTree = ""; }; + 820B45091D9624B1002A26BA /* SponsorTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SponsorTableViewCell.h; sourceTree = ""; }; + 820B450A1D9624B1002A26BA /* SponsorTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SponsorTableViewCell.m; sourceTree = ""; }; + 820B450B1D9624B1002A26BA /* SponsorTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SponsorTableViewCell.xib; sourceTree = ""; }; + 8215AD7D1D8DBE3700377370 /* Sponsor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sponsor.h; sourceTree = ""; }; + 8215AD7E1D8DBE3700377370 /* Sponsor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Sponsor.m; sourceTree = ""; }; + 8215AD811D8DC49F00377370 /* Announcement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Announcement.h; sourceTree = ""; }; + 8215AD821D8DC49F00377370 /* Announcement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Announcement.m; sourceTree = ""; }; + 8215AD841D8DC53D00377370 /* Location.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Location.h; sourceTree = ""; }; + 8215AD851D8DC53D00377370 /* Location.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Location.m; sourceTree = ""; }; + 8215AD871D8DC63F00377370 /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Event.h; sourceTree = ""; }; + 8215AD881D8DC63F00377370 /* Event.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Event.m; sourceTree = ""; }; + 8215AD8A1D8DC70F00377370 /* Speaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Speaker.h; sourceTree = ""; }; + 8215AD8B1D8DC70F00377370 /* Speaker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Speaker.m; sourceTree = ""; }; + 8215AD8D1D8DC88100377370 /* HTXAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTXAPI.h; sourceTree = ""; }; + 8215AD8E1D8DC88100377370 /* HTXAPI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTXAPI.m; sourceTree = ""; }; + 8215AD901D8DD26C00377370 /* SponsorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SponsorViewController.h; sourceTree = ""; }; + 8215AD911D8DD26C00377370 /* SponsorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SponsorViewController.m; sourceTree = ""; }; + 8215ADAD1D8E27F700377370 /* NSString+MD5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MD5.h"; sourceTree = ""; }; + 8215ADAE1D8E27F800377370 /* NSString+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MD5.m"; sourceTree = ""; }; + 8221B2041DAEF9D900FB1BE1 /* ScheduleTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScheduleTableViewCell.h; sourceTree = ""; }; + 8221B2051DAEF9D900FB1BE1 /* ScheduleTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScheduleTableViewCell.m; sourceTree = ""; }; + 8221B2061DAEF9D900FB1BE1 /* ScheduleTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ScheduleTableViewCell.xib; sourceTree = ""; }; + 8221B2091DAF109B00FB1BE1 /* HTXAPIKeyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTXAPIKeyStore.h; sourceTree = ""; }; + 8221B20A1DAF109B00FB1BE1 /* HTXAPIKeyStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTXAPIKeyStore.m; sourceTree = ""; }; + 822A4D931D779BCD00C65761 /* HackTX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HackTX.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 822A4D971D779BCD00C65761 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 822A4D991D779BCD00C65761 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 822A4D9A1D779BCD00C65761 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 822A4DA31D779BCD00C65761 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 822A4DA51D779BCD00C65761 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 822A4DA81D779BCD00C65761 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 822A4DAA1D779BCD00C65761 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 822A4DB21D779DBD00C65761 /* ScheduleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScheduleViewController.h; sourceTree = ""; }; + 822A4DB31D779DBD00C65761 /* ScheduleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScheduleViewController.m; sourceTree = ""; }; + 822A4DB51D779DD100C65761 /* AnnouncementsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnnouncementsViewController.h; sourceTree = ""; }; + 822A4DB61D779DD100C65761 /* AnnouncementsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnnouncementsViewController.m; sourceTree = ""; }; + 822A4DBE1D779DF100C65761 /* MapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapViewController.h; sourceTree = ""; }; + 822A4DBF1D779DF100C65761 /* MapViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewController.m; sourceTree = ""; }; + 822E95171D7FF4EE00C0402E /* HTXConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTXConstants.h; sourceTree = ""; }; + 822E95181D7FF4EE00C0402E /* HTXConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTXConstants.m; sourceTree = ""; }; + 82BA00381D81492F006CA2C6 /* AutolayoutHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutolayoutHelper.h; sourceTree = ""; }; + 82BA00391D81492F006CA2C6 /* AutolayoutHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutolayoutHelper.m; sourceTree = ""; }; + 82BA003B1D8149B1006CA2C6 /* UIColor+Palette.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Palette.h"; sourceTree = ""; }; + 82BA003C1D8149B1006CA2C6 /* UIColor+Palette.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Palette.m"; sourceTree = ""; }; + 82BAC6C81D9D1FC000BF62C0 /* JosefinSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-Bold.ttf"; sourceTree = ""; }; + 82BAC6C91D9D1FC000BF62C0 /* JosefinSans-BoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-BoldItalic.ttf"; sourceTree = ""; }; + 82BAC6CA1D9D1FC000BF62C0 /* JosefinSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-Italic.ttf"; sourceTree = ""; }; + 82BAC6CB1D9D1FC000BF62C0 /* JosefinSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-Light.ttf"; sourceTree = ""; }; + 82BAC6CC1D9D1FC000BF62C0 /* JosefinSans-LightItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-LightItalic.ttf"; sourceTree = ""; }; + 82BAC6CD1D9D1FC000BF62C0 /* JosefinSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-Regular.ttf"; sourceTree = ""; }; + 82BAC6CE1D9D1FC000BF62C0 /* JosefinSans-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-SemiBold.ttf"; sourceTree = ""; }; + 82BAC6CF1D9D1FC000BF62C0 /* JosefinSans-SemiBoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-SemiBoldItalic.ttf"; sourceTree = ""; }; + 82BAC6D01D9D1FC000BF62C0 /* JosefinSans-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-Thin.ttf"; sourceTree = ""; }; + 82BAC6D11D9D1FC000BF62C0 /* JosefinSans-ThinItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "JosefinSans-ThinItalic.ttf"; sourceTree = ""; }; + 82BFF6D61D9E662600DB2543 /* partners.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = partners.json; sourceTree = ""; }; + 82D303C71DB23BB2003C98ED /* HackTX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HackTX.entitlements; sourceTree = ""; }; + 82D303C81DB23BB2003C98ED /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; + 82D303CF1DB24084003C98ED /* Hacker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hacker.h; sourceTree = ""; }; + 82D303D01DB24084003C98ED /* Hacker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Hacker.m; sourceTree = ""; }; + 82EAFC841DB200D50061C449 /* CheckInViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CheckInViewController.h; sourceTree = ""; }; + 82EAFC851DB200D50061C449 /* CheckInViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CheckInViewController.m; sourceTree = ""; }; + 82EAFC861DB200D50061C449 /* CheckInViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckInViewController.xib; sourceTree = ""; }; + 82F3C16A1DB71BAC004F96FC /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 83420EE51F15C75F002AA404 /* APIKeys.plist */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = APIKeys.plist; sourceTree = ""; }; + B67A442663439B9AD415E5F6 /* libPods-HackTX.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HackTX.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DDE9620564A4ACF2DB3BFB7E /* Pods-HackTX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HackTX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HackTX/Pods-HackTX.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - F9E9902F1B50EBBA00057823 /* Frameworks */ = { + 822A4D901D779BCD00C65761 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3A806E5F1B9ACDE100E29A0F /* CoreData.framework in Frameworks */, - F96AB7A01B97D25C00394217 /* AudioToolbox.framework in Frameworks */, - F96AB7A11B97D25C00394217 /* CFNetwork.framework in Frameworks */, - F96AB7A21B97D25C00394217 /* CoreGraphics.framework in Frameworks */, - F96AB7A31B97D25C00394217 /* CoreLocation.framework in Frameworks */, - F96AB7A41B97D25C00394217 /* libsqlite3.dylib in Frameworks */, - F96AB7A51B97D25C00394217 /* libz.dylib in Frameworks */, - F96AB7A61B97D25C00394217 /* QuartzCore.framework in Frameworks */, - F96AB7A71B97D25C00394217 /* Security.framework in Frameworks */, - F96AB7A81B97D25C00394217 /* StoreKit.framework in Frameworks */, - F96AB7A91B97D25C00394217 /* SystemConfiguration.framework in Frameworks */, - F96AB7951B97D18F00394217 /* Bolts.framework in Frameworks */, - 3AEB82341B7F107F00224BC3 /* Fabric.framework in Frameworks */, - 3AEB82371B7F107F00224BC3 /* TwitterCore.framework in Frameworks */, - F96AB7931B97D14B00394217 /* Parse.framework in Frameworks */, - 3AEB82351B7F107F00224BC3 /* TwitterKit.framework in Frameworks */, - 0EEBD4D4183683D7099C95B8 /* Pods_HackTX.framework in Frameworks */, + 82D303C91DB23BB2003C98ED /* PassKit.framework in Frameworks */, + 4E5410941E35FEC803EA8E2D /* libPods-HackTX.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 3A36664C1B9CCD3B0028BA91 /* Schedule */ = { - isa = PBXGroup; - children = ( - F9E990391B50EBBA00057823 /* ScheduleViewController.swift */, - 3A806E281B9A895D00E29A0F /* CheckInViewController.swift */, - 3A2156791B8A577100BCA241 /* ScheduleDetailViewController.swift */, - 3A192B9F1B9EBE2C008AA3BA /* EventFeedbackViewController.swift */, - ); - name = Schedule; - sourceTree = ""; - }; - 3A36664D1B9CCD480028BA91 /* Announcements */ = { - isa = PBXGroup; - children = ( - 3AEB82381B7F14E300224BC3 /* AnnouncementsViewController.swift */, - ); - name = Announcements; - sourceTree = ""; - }; - 3A36664E1B9CCD5C0028BA91 /* Maps */ = { + 4213B7E075F5C7663EDD6D3D /* Pods */ = { isa = PBXGroup; children = ( - F9E990661B51001000057823 /* MapViewController.swift */, - F96AB7AC1B97E01200394217 /* MapsPageViewController.swift */, - F9EE70211B912EE300B493A8 /* PageItemViewController.swift */, + DDE9620564A4ACF2DB3BFB7E /* Pods-HackTX.debug.xcconfig */, + 099214E83B6739C941439E79 /* Pods-HackTX.release.xcconfig */, ); - name = Maps; + name = Pods; sourceTree = ""; }; - 3A36664F1B9CCD720028BA91 /* Twitter */ = { + 71DC8291476C31865A92A66B /* Frameworks */ = { isa = PBXGroup; children = ( - F9E990641B50FFFF00057823 /* TwitterViewController.swift */, + 82D303C81DB23BB2003C98ED /* PassKit.framework */, + B67A442663439B9AD415E5F6 /* libPods-HackTX.a */, ); - name = Twitter; + name = Frameworks; sourceTree = ""; }; - 3A3666501B9CCD800028BA91 /* Partners */ = { + 8215AD801D8DBE4900377370 /* Models */ = { isa = PBXGroup; children = ( - 3A21566D1B8A430C00BCA241 /* PartnersViewController.swift */, + 8215AD7D1D8DBE3700377370 /* Sponsor.h */, + 8215AD7E1D8DBE3700377370 /* Sponsor.m */, + 8215AD811D8DC49F00377370 /* Announcement.h */, + 8215AD821D8DC49F00377370 /* Announcement.m */, + 8215AD841D8DC53D00377370 /* Location.h */, + 8215AD851D8DC53D00377370 /* Location.m */, + 8215AD871D8DC63F00377370 /* Event.h */, + 8215AD881D8DC63F00377370 /* Event.m */, + 8215AD8A1D8DC70F00377370 /* Speaker.h */, + 8215AD8B1D8DC70F00377370 /* Speaker.m */, + 82D303CF1DB24084003C98ED /* Hacker.h */, + 82D303D01DB24084003C98ED /* Hacker.m */, ); - name = Partners; + name = Models; sourceTree = ""; }; - 3A3666511B9CCDAE0028BA91 /* Schedule */ = { + 8215AD931D8DD2A900377370 /* Schedule */ = { isa = PBXGroup; children = ( - 3A21567D1B8A5D5400BCA241 /* Day.swift */, - 3A21567B1B8A5D3D00BCA241 /* ScheduleCluster.swift */, - 3A21567F1B8A5D6A00BCA241 /* Event.swift */, - F9C42AC91B979B7200A21CF8 /* Location.swift */, - 3A2156811B8A5D8900BCA241 /* Speaker.swift */, + 822A4DB21D779DBD00C65761 /* ScheduleViewController.h */, + 822A4DB31D779DBD00C65761 /* ScheduleViewController.m */, + 8221B2041DAEF9D900FB1BE1 /* ScheduleTableViewCell.h */, + 8221B2051DAEF9D900FB1BE1 /* ScheduleTableViewCell.m */, + 8221B2061DAEF9D900FB1BE1 /* ScheduleTableViewCell.xib */, ); name = Schedule; sourceTree = ""; }; - 3A806E4F1B9A9E3100E29A0F /* Utils */ = { - isa = PBXGroup; - children = ( - 3A806E501B9A9E4B00E29A0F /* UserPrefs.swift */, - 3A3666571B9CD8350028BA91 /* Router.swift */, - ); - name = Utils; - sourceTree = ""; - }; - 3A806E811B9AD92E00E29A0F /* Pods */ = { - isa = PBXGroup; - children = ( - 3A806E821B9AD92E00E29A0F /* Google */, - 3A806E911B9AD92E00E29A0F /* GoogleAnalytics */, - 3A806EA01B9AD92E00E29A0F /* GoogleNetworkingUtilities */, - 3A806EA41B9AD92E00E29A0F /* GoogleSymbolUtilities */, - 3A806EA71B9AD92E00E29A0F /* GoogleUtilities */, - 3A806EB91B9AD92E00E29A0F /* Headers */, - 3A806EE21B9AD92E00E29A0F /* Local Podspecs */, - 3A806EE31B9AD92E00E29A0F /* Manifest.lock */, - 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */, - 3A806EE71B9AD92E00E29A0F /* Target Support Files */, - ); - path = Pods; - sourceTree = ""; - }; - 3A806E821B9AD92E00E29A0F /* Google */ = { - isa = PBXGroup; - children = ( - 3A806E831B9AD92E00E29A0F /* Headers */, - 3A806E8E1B9AD92E00E29A0F /* Libraries */, - ); - path = Google; - sourceTree = ""; - }; - 3A806E831B9AD92E00E29A0F /* Headers */ = { - isa = PBXGroup; - children = ( - 3A806E841B9AD92E00E29A0F /* GGLAnalytics */, - 3A806E881B9AD92E00E29A0F /* GGLCore */, - ); - path = Headers; - sourceTree = ""; - }; - 3A806E841B9AD92E00E29A0F /* GGLAnalytics */ = { - isa = PBXGroup; - children = ( - 3A806E851B9AD92E00E29A0F /* Public */, - ); - path = GGLAnalytics; - sourceTree = ""; - }; - 3A806E851B9AD92E00E29A0F /* Public */ = { - isa = PBXGroup; - children = ( - 3A806E861B9AD92E00E29A0F /* Analytics.h */, - 3A806E871B9AD92E00E29A0F /* GGLContext+Analytics.h */, - ); - path = Public; - sourceTree = ""; - }; - 3A806E881B9AD92E00E29A0F /* GGLCore */ = { - isa = PBXGroup; - children = ( - 3A806E891B9AD92E00E29A0F /* Public */, - ); - path = GGLCore; - sourceTree = ""; - }; - 3A806E891B9AD92E00E29A0F /* Public */ = { - isa = PBXGroup; - children = ( - 3A806E8A1B9AD92E00E29A0F /* Core.h */, - 3A806E8B1B9AD92E00E29A0F /* GGLConfiguration.h */, - 3A806E8C1B9AD92E00E29A0F /* GGLContext.h */, - 3A806E8D1B9AD92E00E29A0F /* GGLErrorCode.h */, - ); - path = Public; - sourceTree = ""; - }; - 3A806E8E1B9AD92E00E29A0F /* Libraries */ = { - isa = PBXGroup; - children = ( - 3A806E8F1B9AD92E00E29A0F /* libGGLAnalytics.a */, - 3A806E901B9AD92E00E29A0F /* libGGLCore.a */, - ); - path = Libraries; - sourceTree = ""; - }; - 3A806E911B9AD92E00E29A0F /* GoogleAnalytics */ = { + 8215AD941D8DD2B300377370 /* Sponsor */ = { isa = PBXGroup; children = ( - 3A806E921B9AD92E00E29A0F /* Headers */, - 3A806E9E1B9AD92E00E29A0F /* Libraries */, + 8215AD901D8DD26C00377370 /* SponsorViewController.h */, + 8215AD911D8DD26C00377370 /* SponsorViewController.m */, + 820B45091D9624B1002A26BA /* SponsorTableViewCell.h */, + 820B450A1D9624B1002A26BA /* SponsorTableViewCell.m */, + 820B450B1D9624B1002A26BA /* SponsorTableViewCell.xib */, ); - path = GoogleAnalytics; + name = Sponsor; sourceTree = ""; }; - 3A806E921B9AD92E00E29A0F /* Headers */ = { + 8215AD951D8DD2BA00377370 /* Map */ = { isa = PBXGroup; children = ( - 3A806E931B9AD92E00E29A0F /* Public */, + 822A4DBE1D779DF100C65761 /* MapViewController.h */, + 822A4DBF1D779DF100C65761 /* MapViewController.m */, ); - path = Headers; + name = Map; sourceTree = ""; }; - 3A806E931B9AD92E00E29A0F /* Public */ = { + 8215AD961D8DD2D100377370 /* Announcement */ = { isa = PBXGroup; children = ( - 3A806E941B9AD92E00E29A0F /* GAI.h */, - 3A806E951B9AD92E00E29A0F /* GAIDictionaryBuilder.h */, - 3A806E961B9AD92E00E29A0F /* GAIEcommerceFields.h */, - 3A806E971B9AD92E00E29A0F /* GAIEcommerceProduct.h */, - 3A806E981B9AD92E00E29A0F /* GAIEcommerceProductAction.h */, - 3A806E991B9AD92E00E29A0F /* GAIEcommercePromotion.h */, - 3A806E9A1B9AD92E00E29A0F /* GAIFields.h */, - 3A806E9B1B9AD92E00E29A0F /* GAILogger.h */, - 3A806E9C1B9AD92E00E29A0F /* GAITrackedViewController.h */, - 3A806E9D1B9AD92E00E29A0F /* GAITracker.h */, + 822A4DB51D779DD100C65761 /* AnnouncementsViewController.h */, + 822A4DB61D779DD100C65761 /* AnnouncementsViewController.m */, + 8200AD6E1DB3577400D4EE11 /* AnnouncementTableViewCell.h */, + 8200AD6F1DB3577400D4EE11 /* AnnouncementTableViewCell.m */, + 8200AD701DB3577400D4EE11 /* AnnouncementTableViewCell.xib */, ); - path = Public; + name = Announcement; sourceTree = ""; }; - 3A806E9E1B9AD92E00E29A0F /* Libraries */ = { + 822A4D8A1D779BCD00C65761 = { isa = PBXGroup; children = ( - 3A806E9F1B9AD92E00E29A0F /* libGoogleAnalytics.a */, + 822A4D951D779BCD00C65761 /* HackTX */, + 822A4D941D779BCD00C65761 /* Products */, + 4213B7E075F5C7663EDD6D3D /* Pods */, + 71DC8291476C31865A92A66B /* Frameworks */, ); - path = Libraries; sourceTree = ""; }; - 3A806EA01B9AD92E00E29A0F /* GoogleNetworkingUtilities */ = { + 822A4D941D779BCD00C65761 /* Products */ = { isa = PBXGroup; children = ( - 3A806EA11B9AD92E00E29A0F /* Libraries */, - ); - path = GoogleNetworkingUtilities; - sourceTree = ""; - }; - 3A806EA11B9AD92E00E29A0F /* Libraries */ = { - isa = PBXGroup; - children = ( - 3A806EA21B9AD92E00E29A0F /* libGTMSessionFetcher_core.a */, - 3A806EA31B9AD92E00E29A0F /* libGTMSessionFetcher_full.a */, - ); - path = Libraries; - sourceTree = ""; - }; - 3A806EA41B9AD92E00E29A0F /* GoogleSymbolUtilities */ = { - isa = PBXGroup; - children = ( - 3A806EA51B9AD92E00E29A0F /* Libraries */, - ); - path = GoogleSymbolUtilities; - sourceTree = ""; - }; - 3A806EA51B9AD92E00E29A0F /* Libraries */ = { - isa = PBXGroup; - children = ( - 3A806EA61B9AD92E00E29A0F /* libGSDK_Overload.a */, - ); - path = Libraries; - sourceTree = ""; - }; - 3A806EA71B9AD92E00E29A0F /* GoogleUtilities */ = { - isa = PBXGroup; - children = ( - 3A806EA81B9AD92E00E29A0F /* Libraries */, - ); - path = GoogleUtilities; - sourceTree = ""; - }; - 3A806EA81B9AD92E00E29A0F /* Libraries */ = { - isa = PBXGroup; - children = ( - 3A806EA91B9AD92E00E29A0F /* libGTM_AddressBook.a */, - 3A806EAA1B9AD92E00E29A0F /* libGTM_core.a */, - 3A806EAB1B9AD92E00E29A0F /* libGTM_DebugUtils.a */, - 3A806EAC1B9AD92E00E29A0F /* libGTM_GTMURLBuilder.a */, - 3A806EAD1B9AD92E00E29A0F /* libGTM_iPhone.a */, - 3A806EAE1B9AD92E00E29A0F /* libGTM_KVO.a */, - 3A806EAF1B9AD92E00E29A0F /* libGTM_NSDictionary+URLArguments.a */, - 3A806EB01B9AD92E00E29A0F /* libGTM_NSScannerJSON.a */, - 3A806EB11B9AD92E00E29A0F /* libGTM_NSStringHTML.a */, - 3A806EB21B9AD92E00E29A0F /* libGTM_NSStringXML.a */, - 3A806EB31B9AD92E00E29A0F /* libGTM_Regex.a */, - 3A806EB41B9AD92E00E29A0F /* libGTM_RoundedRectPath.a */, - 3A806EB51B9AD92E00E29A0F /* libGTM_StringEncoding.a */, - 3A806EB61B9AD92E00E29A0F /* libGTM_SystemVersion.a */, - 3A806EB71B9AD92E00E29A0F /* libGTM_UIFont+LineHeight.a */, - 3A806EB81B9AD92E00E29A0F /* libGTMStackTrace.a */, - ); - path = Libraries; - sourceTree = ""; - }; - 3A806EB91B9AD92E00E29A0F /* Headers */ = { - isa = PBXGroup; - children = ( - 3A806EBA1B9AD92E00E29A0F /* Private */, - 3A806ECE1B9AD92E00E29A0F /* Public */, - ); - path = Headers; - sourceTree = ""; - }; - 3A806EBA1B9AD92E00E29A0F /* Private */ = { - isa = PBXGroup; - children = ( - 3A806EBB1B9AD92E00E29A0F /* Google */, - 3A806EC31B9AD92E00E29A0F /* GoogleAnalytics */, - ); - path = Private; - sourceTree = ""; - }; - 3A806EBB1B9AD92E00E29A0F /* Google */ = { - isa = PBXGroup; - children = ( - 3A806EBC1B9AD92E00E29A0F /* Google */, - ); - path = Google; - sourceTree = ""; - }; - 3A806EBC1B9AD92E00E29A0F /* Google */ = { - isa = PBXGroup; - children = ( - 3A806EBD1B9AD92E00E29A0F /* Analytics.h */, - 3A806EBE1B9AD92E00E29A0F /* Core.h */, - 3A806EBF1B9AD92E00E29A0F /* GGLConfiguration.h */, - 3A806EC01B9AD92E00E29A0F /* GGLContext+Analytics.h */, - 3A806EC11B9AD92E00E29A0F /* GGLContext.h */, - 3A806EC21B9AD92E00E29A0F /* GGLErrorCode.h */, - ); - path = Google; - sourceTree = ""; - }; - 3A806EC31B9AD92E00E29A0F /* GoogleAnalytics */ = { - isa = PBXGroup; - children = ( - 3A806EC41B9AD92E00E29A0F /* GAI.h */, - 3A806EC51B9AD92E00E29A0F /* GAIDictionaryBuilder.h */, - 3A806EC61B9AD92E00E29A0F /* GAIEcommerceFields.h */, - 3A806EC71B9AD92E00E29A0F /* GAIEcommerceProduct.h */, - 3A806EC81B9AD92E00E29A0F /* GAIEcommerceProductAction.h */, - 3A806EC91B9AD92E00E29A0F /* GAIEcommercePromotion.h */, - 3A806ECA1B9AD92E00E29A0F /* GAIFields.h */, - 3A806ECB1B9AD92E00E29A0F /* GAILogger.h */, - 3A806ECC1B9AD92E00E29A0F /* GAITrackedViewController.h */, - 3A806ECD1B9AD92E00E29A0F /* GAITracker.h */, - ); - path = GoogleAnalytics; - sourceTree = ""; - }; - 3A806ECE1B9AD92E00E29A0F /* Public */ = { - isa = PBXGroup; - children = ( - 3A806ECF1B9AD92E00E29A0F /* Google */, - 3A806ED71B9AD92E00E29A0F /* GoogleAnalytics */, - ); - path = Public; - sourceTree = ""; - }; - 3A806ECF1B9AD92E00E29A0F /* Google */ = { - isa = PBXGroup; - children = ( - 3A806ED01B9AD92E00E29A0F /* Google */, - ); - path = Google; - sourceTree = ""; - }; - 3A806ED01B9AD92E00E29A0F /* Google */ = { - isa = PBXGroup; - children = ( - 3A806ED11B9AD92E00E29A0F /* Analytics.h */, - 3A806ED21B9AD92E00E29A0F /* Core.h */, - 3A806ED31B9AD92E00E29A0F /* GGLConfiguration.h */, - 3A806ED41B9AD92E00E29A0F /* GGLContext+Analytics.h */, - 3A806ED51B9AD92E00E29A0F /* GGLContext.h */, - 3A806ED61B9AD92E00E29A0F /* GGLErrorCode.h */, - ); - path = Google; - sourceTree = ""; - }; - 3A806ED71B9AD92E00E29A0F /* GoogleAnalytics */ = { - isa = PBXGroup; - children = ( - 3A806ED81B9AD92E00E29A0F /* GAI.h */, - 3A806ED91B9AD92E00E29A0F /* GAIDictionaryBuilder.h */, - 3A806EDA1B9AD92E00E29A0F /* GAIEcommerceFields.h */, - 3A806EDB1B9AD92E00E29A0F /* GAIEcommerceProduct.h */, - 3A806EDC1B9AD92E00E29A0F /* GAIEcommerceProductAction.h */, - 3A806EDD1B9AD92E00E29A0F /* GAIEcommercePromotion.h */, - 3A806EDE1B9AD92E00E29A0F /* GAIFields.h */, - 3A806EDF1B9AD92E00E29A0F /* GAILogger.h */, - 3A806EE01B9AD92E00E29A0F /* GAITrackedViewController.h */, - 3A806EE11B9AD92E00E29A0F /* GAITracker.h */, - ); - path = GoogleAnalytics; - sourceTree = ""; - }; - 3A806EE21B9AD92E00E29A0F /* Local Podspecs */ = { - isa = PBXGroup; - children = ( - ); - path = "Local Podspecs"; - sourceTree = ""; - }; - 3A806EE51B9AD92E00E29A0F /* Products */ = { - isa = PBXGroup; - children = ( - 3ADDC0611B9CC2A500D0ECD2 /* Alamofire.framework */, - 3A806EF11B9AD92E00E29A0F /* Pods_HackTX.framework */, - 3ADDC0631B9CC2A500D0ECD2 /* RSBarcodes_Swift.framework */, - 3ADDC0651B9CC2A500D0ECD2 /* SwiftyJSON.framework */, + 822A4D931D779BCD00C65761 /* HackTX.app */, ); name = Products; sourceTree = ""; }; - 3A806EE71B9AD92E00E29A0F /* Target Support Files */ = { - isa = PBXGroup; - children = ( - 3A806EE81B9AD92E00E29A0F /* Pods-HackTX */, - ); - path = "Target Support Files"; - sourceTree = ""; - }; - 3A806EE81B9AD92E00E29A0F /* Pods-HackTX */ = { - isa = PBXGroup; - children = ( - 3A806EE91B9AD92E00E29A0F /* Pods-HackTX-acknowledgements.markdown */, - 3A806EEA1B9AD92E00E29A0F /* Pods-HackTX-acknowledgements.plist */, - 3A806EEB1B9AD92E00E29A0F /* Pods-HackTX-dummy.m */, - 3A806EEC1B9AD92E00E29A0F /* Pods-HackTX-resources.sh */, - 3A806EED1B9AD92E00E29A0F /* Pods-HackTX.debug.xcconfig */, - 3A806EEE1B9AD92E00E29A0F /* Pods-HackTX.release.xcconfig */, - ); - path = "Pods-HackTX"; - sourceTree = ""; - }; - 59EAB6531DF07EF91F85AACE /* Frameworks */ = { - isa = PBXGroup; - children = ( - F91C84331BAD2A57006E2D8F /* GoogleService-Info.plist */, - 3A806E5E1B9ACDE100E29A0F /* CoreData.framework */, - F96AB7961B97D25C00394217 /* AudioToolbox.framework */, - F96AB7971B97D25C00394217 /* CFNetwork.framework */, - F96AB7981B97D25C00394217 /* CoreGraphics.framework */, - F96AB7991B97D25C00394217 /* CoreLocation.framework */, - F96AB79A1B97D25C00394217 /* libsqlite3.dylib */, - F96AB79B1B97D25C00394217 /* libz.dylib */, - F96AB79C1B97D25C00394217 /* QuartzCore.framework */, - F96AB79D1B97D25C00394217 /* Security.framework */, - F96AB79E1B97D25C00394217 /* StoreKit.framework */, - F96AB79F1B97D25C00394217 /* SystemConfiguration.framework */, - F96AB7941B97D18F00394217 /* Bolts.framework */, - F96AB7921B97D14B00394217 /* Parse.framework */, - 3AEB82301B7F107F00224BC3 /* Fabric.framework */, - 3AEB82311B7F107F00224BC3 /* TwitterKit.framework */, - 3AEB82321B7F107F00224BC3 /* TwitterKitResources.bundle */, - 3AEB82331B7F107F00224BC3 /* TwitterCore.framework */, - 3A806E811B9AD92E00E29A0F /* Pods */, - 715158D3DB02FD4CF82662A4 /* Pods_HackTX.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 5FC63154830407E9274D0DC2 /* Pods */ = { - isa = PBXGroup; - children = ( - 94E240E75C09DEDD03EA913F /* Pods-HackTX.debug.xcconfig */, - 163274BAD693C35C1B0222A5 /* Pods-HackTX.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - F956D0711B535067001393DE /* Model */ = { - isa = PBXGroup; - children = ( - F91C843B1BAD2AED006E2D8F /* Reachability.swift */, - 3A3666511B9CCDAE0028BA91 /* Schedule */, - 3AEB823A1B7F14F400224BC3 /* Announcement.swift */, - 3A2156751B8A44CC00BCA241 /* Sponsor.swift */, - ); - name = Model; - sourceTree = ""; - }; - F956D0731B53507D001393DE /* View */ = { - isa = PBXGroup; - children = ( - 3A2156711B8A436100BCA241 /* PartnersViewCell.swift */, - 3A2156721B8A436100BCA241 /* PartnersViewCell.xib */, - F9E990421B50EBBA00057823 /* LaunchScreen.xib */, - ); - name = View; - sourceTree = ""; - }; - F956D0741B535081001393DE /* Controller */ = { - isa = PBXGroup; - children = ( - 3A36664C1B9CCD3B0028BA91 /* Schedule */, - 3A36664D1B9CCD480028BA91 /* Announcements */, - 3A36664F1B9CCD720028BA91 /* Twitter */, - 3A36664E1B9CCD5C0028BA91 /* Maps */, - 3A3666501B9CCD800028BA91 /* Partners */, - ); - name = Controller; - sourceTree = ""; - }; - F9E990291B50EBBA00057823 = { - isa = PBXGroup; - children = ( - F9E990341B50EBBA00057823 /* HackTX */, - F9E9904C1B50EBBA00057823 /* HackTXTests */, - F9E990331B50EBBA00057823 /* Products */, - 5FC63154830407E9274D0DC2 /* Pods */, - 59EAB6531DF07EF91F85AACE /* Frameworks */, - ); - sourceTree = ""; - }; - F9E990331B50EBBA00057823 /* Products */ = { - isa = PBXGroup; - children = ( - F9E990321B50EBBA00057823 /* HackTX.app */, - ); - name = Products; - sourceTree = ""; - }; - F9E990341B50EBBA00057823 /* HackTX */ = { - isa = PBXGroup; - children = ( - 3A806E4F1B9A9E3100E29A0F /* Utils */, - F956D0711B535067001393DE /* Model */, - F956D0741B535081001393DE /* Controller */, - F956D0731B53507D001393DE /* View */, - F9E990371B50EBBA00057823 /* AppDelegate.swift */, - F9E9903D1B50EBBA00057823 /* Main.storyboard */, - F91C84391BAD2A81006E2D8F /* GoogleService-Info.plist */, - F91C84371BAD2A74006E2D8F /* ApiKeys.plist */, - F9E990401B50EBBA00057823 /* Images.xcassets */, - F9E990351B50EBBA00057823 /* Supporting Files */, + 822A4D951D779BCD00C65761 /* HackTX */ = { + isa = PBXGroup; + children = ( + 822A4D961D779BCD00C65761 /* Supporting Files */, + 82BAC6DC1D9D1FC400BF62C0 /* Fonts */, + 82D303C71DB23BB2003C98ED /* HackTX.entitlements */, + 82BFF6D61D9E662600DB2543 /* partners.json */, + 8215AD8D1D8DC88100377370 /* HTXAPI.h */, + 8215AD8E1D8DC88100377370 /* HTXAPI.m */, + 8221B2091DAF109B00FB1BE1 /* HTXAPIKeyStore.h */, + 8221B20A1DAF109B00FB1BE1 /* HTXAPIKeyStore.m */, + 8215ADAD1D8E27F700377370 /* NSString+MD5.h */, + 8215ADAE1D8E27F800377370 /* NSString+MD5.m */, + 822A4D991D779BCD00C65761 /* AppDelegate.h */, + 822A4D9A1D779BCD00C65761 /* AppDelegate.m */, + 8215AD801D8DBE4900377370 /* Models */, + 8215AD931D8DD2A900377370 /* Schedule */, + 8215AD941D8DD2B300377370 /* Sponsor */, + 8215AD951D8DD2BA00377370 /* Map */, + 8215AD961D8DD2D100377370 /* Announcement */, + 82EAFC891DB200DB0061C449 /* Check In */, + 82BA003B1D8149B1006CA2C6 /* UIColor+Palette.h */, + 82BA003C1D8149B1006CA2C6 /* UIColor+Palette.m */, + 822A4DA21D779BCD00C65761 /* Main.storyboard */, + 822A4DA51D779BCD00C65761 /* Assets.xcassets */, + 822A4DA71D779BCD00C65761 /* LaunchScreen.storyboard */, + 822A4DAA1D779BCD00C65761 /* Info.plist */, + 82F3C16A1DB71BAC004F96FC /* GoogleService-Info.plist */, ); path = HackTX; sourceTree = ""; }; - F9E990351B50EBBA00057823 /* Supporting Files */ = { + 822A4D961D779BCD00C65761 /* Supporting Files */ = { isa = PBXGroup; children = ( - 3A806E801B9AD62800E29A0F /* HackTX-Bridging-Header.h */, - F9E990361B50EBBA00057823 /* Info.plist */, - F96AB7AA1B97D40600394217 /* ApiKeys.plist */, + 83420EE51F15C75F002AA404 /* APIKeys.plist */, + 822A4D971D779BCD00C65761 /* main.m */, + 82BA00381D81492F006CA2C6 /* AutolayoutHelper.h */, + 82BA00391D81492F006CA2C6 /* AutolayoutHelper.m */, + 822E95171D7FF4EE00C0402E /* HTXConstants.h */, + 822E95181D7FF4EE00C0402E /* HTXConstants.m */, ); name = "Supporting Files"; sourceTree = ""; }; - F9E9904C1B50EBBA00057823 /* HackTXTests */ = { + 82BAC6DC1D9D1FC400BF62C0 /* Fonts */ = { isa = PBXGroup; children = ( - F9E9904F1B50EBBA00057823 /* HackTXTests.swift */, - F9E9904D1B50EBBA00057823 /* Supporting Files */, + 82BAC6C81D9D1FC000BF62C0 /* JosefinSans-Bold.ttf */, + 82BAC6C91D9D1FC000BF62C0 /* JosefinSans-BoldItalic.ttf */, + 82BAC6CA1D9D1FC000BF62C0 /* JosefinSans-Italic.ttf */, + 82BAC6CB1D9D1FC000BF62C0 /* JosefinSans-Light.ttf */, + 82BAC6CC1D9D1FC000BF62C0 /* JosefinSans-LightItalic.ttf */, + 82BAC6CD1D9D1FC000BF62C0 /* JosefinSans-Regular.ttf */, + 82BAC6CE1D9D1FC000BF62C0 /* JosefinSans-SemiBold.ttf */, + 82BAC6CF1D9D1FC000BF62C0 /* JosefinSans-SemiBoldItalic.ttf */, + 82BAC6D01D9D1FC000BF62C0 /* JosefinSans-Thin.ttf */, + 82BAC6D11D9D1FC000BF62C0 /* JosefinSans-ThinItalic.ttf */, ); - path = HackTXTests; + name = Fonts; sourceTree = ""; }; - F9E9904D1B50EBBA00057823 /* Supporting Files */ = { + 82EAFC891DB200DB0061C449 /* Check In */ = { isa = PBXGroup; children = ( - F9E9904E1B50EBBA00057823 /* Info.plist */, + 82EAFC841DB200D50061C449 /* CheckInViewController.h */, + 82EAFC851DB200D50061C449 /* CheckInViewController.m */, + 82EAFC861DB200D50061C449 /* CheckInViewController.xib */, ); - name = "Supporting Files"; + name = "Check In"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - F9E990311B50EBBA00057823 /* HackTX */ = { + 822A4D921D779BCD00C65761 /* HackTX */ = { isa = PBXNativeTarget; - buildConfigurationList = F9E990531B50EBBA00057823 /* Build configuration list for PBXNativeTarget "HackTX" */; + buildConfigurationList = 822A4DAD1D779BCD00C65761 /* Build configuration list for PBXNativeTarget "HackTX" */; buildPhases = ( - 738EE24A6DC85B59682A9D4C /* Check Pods Manifest.lock */, - F9E9902E1B50EBBA00057823 /* Sources */, - F9E9902F1B50EBBA00057823 /* Frameworks */, - F9E990301B50EBBA00057823 /* Resources */, - 3AEB822F1B7F104A00224BC3 /* Run Script */, - 3A806E2C1B9A8E6500E29A0F /* CopyFiles */, - 67D896DC6BB7CCEDB6AA1407 /* Copy Pods Resources */, - 4C6724898F618A996523C194 /* Embed Pods Frameworks */, + 548772046E64AF676BEB4EBB /* [CP] Check Pods Manifest.lock */, + 822A4D8F1D779BCD00C65761 /* Sources */, + 822A4D901D779BCD00C65761 /* Frameworks */, + 822A4D911D779BCD00C65761 /* Resources */, + 78E6BCED18D9A22BCA539775 /* [CP] Embed Pods Frameworks */, + C7B6E33EEDB5E5FFC7EE5294 /* [CP] Copy Pods Resources */, + 8221B2101DAF157700FB1BE1 /* Fabric - Run Script */, ); buildRules = ( ); @@ -812,27 +332,34 @@ ); name = HackTX; productName = HackTX; - productReference = F9E990321B50EBBA00057823 /* HackTX.app */; + productReference = 822A4D931D779BCD00C65761 /* HackTX.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - F9E9902A1B50EBBA00057823 /* Project object */ = { + 822A4D8B1D779BCD00C65761 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0640; + LastUpgradeCheck = 0830; ORGANIZATIONNAME = HackTX; TargetAttributes = { - F9E990311B50EBBA00057823 = { - CreatedOnToolsVersion = 6.4; - DevelopmentTeam = W2F3CF3EX5; + 822A4D921D779BCD00C65761 = { + CreatedOnToolsVersion = 8.0; + DevelopmentTeam = 245X4K2U9R; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Push = { + enabled = 1; + }; + com.apple.Wallet = { + enabled = 1; + }; + }; }; }; }; - buildConfigurationList = F9E9902D1B50EBBA00057823 /* Build configuration list for PBXProject "HackTX" */; + buildConfigurationList = 822A4D8E1D779BCD00C65761 /* Build configuration list for PBXProject "HackTX" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -840,95 +367,70 @@ en, Base, ); - mainGroup = F9E990291B50EBBA00057823; - productRefGroup = F9E990331B50EBBA00057823 /* Products */; + mainGroup = 822A4D8A1D779BCD00C65761; + productRefGroup = 822A4D941D779BCD00C65761 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 3A806EE51B9AD92E00E29A0F /* Products */; - ProjectRef = 3A806EE41B9AD92E00E29A0F /* Pods.xcodeproj */; - }, - ); projectRoot = ""; targets = ( - F9E990311B50EBBA00057823 /* HackTX */, + 822A4D921D779BCD00C65761 /* HackTX */, ); }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 3A806EF11B9AD92E00E29A0F /* Pods_HackTX.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Pods_HackTX.framework; - remoteRef = 3A806EF01B9AD92E00E29A0F /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3ADDC0611B9CC2A500D0ECD2 /* Alamofire.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Alamofire.framework; - remoteRef = 3ADDC0601B9CC2A500D0ECD2 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3ADDC0631B9CC2A500D0ECD2 /* RSBarcodes_Swift.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = RSBarcodes_Swift.framework; - remoteRef = 3ADDC0621B9CC2A500D0ECD2 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 3ADDC0651B9CC2A500D0ECD2 /* SwiftyJSON.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = SwiftyJSON.framework; - remoteRef = 3ADDC0641B9CC2A500D0ECD2 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ - F9E990301B50EBBA00057823 /* Resources */ = { + 822A4D911D779BCD00C65761 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F9E9903F1B50EBBA00057823 /* Main.storyboard in Resources */, - 3A2156741B8A436100BCA241 /* PartnersViewCell.xib in Resources */, - F91C843A1BAD2A81006E2D8F /* GoogleService-Info.plist in Resources */, - F96AB7AB1B97D40600394217 /* ApiKeys.plist in Resources */, - F9E990441B50EBBA00057823 /* LaunchScreen.xib in Resources */, - F91C84381BAD2A74006E2D8F /* ApiKeys.plist in Resources */, - F9E990411B50EBBA00057823 /* Images.xcassets in Resources */, - 3AEB82361B7F107F00224BC3 /* TwitterKitResources.bundle in Resources */, - F91C84341BAD2A57006E2D8F /* GoogleService-Info.plist in Resources */, + 820B450D1D9624B1002A26BA /* SponsorTableViewCell.xib in Resources */, + 83420EE61F15C75F002AA404 /* APIKeys.plist in Resources */, + 82BAC6D71D9D1FC000BF62C0 /* JosefinSans-Regular.ttf in Resources */, + 822A4DA91D779BCD00C65761 /* LaunchScreen.storyboard in Resources */, + 82BAC6D51D9D1FC000BF62C0 /* JosefinSans-Light.ttf in Resources */, + 82EAFC881DB200D50061C449 /* CheckInViewController.xib in Resources */, + 822A4DA61D779BCD00C65761 /* Assets.xcassets in Resources */, + 8200AD721DB3577400D4EE11 /* AnnouncementTableViewCell.xib in Resources */, + 82BAC6D31D9D1FC000BF62C0 /* JosefinSans-BoldItalic.ttf in Resources */, + 82BAC6D61D9D1FC000BF62C0 /* JosefinSans-LightItalic.ttf in Resources */, + 82BFF6D71D9E662600DB2543 /* partners.json in Resources */, + 82BAC6D81D9D1FC000BF62C0 /* JosefinSans-SemiBold.ttf in Resources */, + 822A4DA41D779BCD00C65761 /* Main.storyboard in Resources */, + 82BAC6D21D9D1FC000BF62C0 /* JosefinSans-Bold.ttf in Resources */, + 82BAC6D41D9D1FC000BF62C0 /* JosefinSans-Italic.ttf in Resources */, + 82BAC6D91D9D1FC000BF62C0 /* JosefinSans-SemiBoldItalic.ttf in Resources */, + 82BAC6DA1D9D1FC000BF62C0 /* JosefinSans-Thin.ttf in Resources */, + 8221B2081DAEF9D900FB1BE1 /* ScheduleTableViewCell.xib in Resources */, + 82BAC6DB1D9D1FC000BF62C0 /* JosefinSans-ThinItalic.ttf in Resources */, + 82F3C16B1DB71BAC004F96FC /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3AEB822F1B7F104A00224BC3 /* Run Script */ = { + 548772046E64AF676BEB4EBB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Run Script"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = ""; + 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; }; - 4C6724898F618A996523C194 /* Embed Pods Frameworks */ = { + 78E6BCED18D9A22BCA539775 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -936,94 +438,94 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HackTX/Pods-HackTX-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 67D896DC6BB7CCEDB6AA1407 /* Copy Pods Resources */ = { + 8221B2101DAF157700FB1BE1 /* Fabric - Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "Fabric - Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HackTX/Pods-HackTX-resources.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "\"${PODS_ROOT}/Fabric/run\" 867b9bd547cc88317b3ba6c1d23b68632020aca5 c3d584fedb368d508fbc34feb97124a81493de480a85efba4a07dea65eb9b477"; }; - 738EE24A6DC85B59682A9D4C /* Check Pods Manifest.lock */ = { + C7B6E33EEDB5E5FFC7EE5294 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; 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"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HackTX/Pods-HackTX-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - F9E9902E1B50EBBA00057823 /* Sources */ = { + 822A4D8F1D779BCD00C65761 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3A2156761B8A44CC00BCA241 /* Sponsor.swift in Sources */, - 3AEB82391B7F14E300224BC3 /* AnnouncementsViewController.swift in Sources */, - F9E990651B50FFFF00057823 /* TwitterViewController.swift in Sources */, - F9E990381B50EBBA00057823 /* AppDelegate.swift in Sources */, - 3A21567A1B8A577100BCA241 /* ScheduleDetailViewController.swift in Sources */, - 3A2156821B8A5D8900BCA241 /* Speaker.swift in Sources */, - 3A21567E1B8A5D5400BCA241 /* Day.swift in Sources */, - 3A806E511B9A9E4B00E29A0F /* UserPrefs.swift in Sources */, - F9C42ACA1B979B7200A21CF8 /* Location.swift in Sources */, - 3A3666581B9CD8350028BA91 /* Router.swift in Sources */, - F9E990671B51001000057823 /* MapViewController.swift in Sources */, - 3A192BA01B9EBE2C008AA3BA /* EventFeedbackViewController.swift in Sources */, - 3A2156731B8A436100BCA241 /* PartnersViewCell.swift in Sources */, - 3A806E291B9A895D00E29A0F /* CheckInViewController.swift in Sources */, - 3A21566E1B8A430C00BCA241 /* PartnersViewController.swift in Sources */, - 3A2156801B8A5D6A00BCA241 /* Event.swift in Sources */, - 3AEB823B1B7F14F400224BC3 /* Announcement.swift in Sources */, - F9E9903A1B50EBBA00057823 /* ScheduleViewController.swift in Sources */, - F91C843C1BAD2AED006E2D8F /* Reachability.swift in Sources */, - F9EE70221B912EE300B493A8 /* PageItemViewController.swift in Sources */, - F96AB7AD1B97E01200394217 /* MapsPageViewController.swift in Sources */, - 3A21567C1B8A5D3D00BCA241 /* ScheduleCluster.swift in Sources */, + 822A4D9B1D779BCD00C65761 /* AppDelegate.m in Sources */, + 822A4DB71D779DD100C65761 /* AnnouncementsViewController.m in Sources */, + 822E95191D7FF4EE00C0402E /* HTXConstants.m in Sources */, + 82BA003A1D81492F006CA2C6 /* AutolayoutHelper.m in Sources */, + 8215AD7F1D8DBE3700377370 /* Sponsor.m in Sources */, + 822A4DB41D779DBD00C65761 /* ScheduleViewController.m in Sources */, + 822A4D981D779BCD00C65761 /* main.m in Sources */, + 8215ADAF1D8E27F800377370 /* NSString+MD5.m in Sources */, + 8215AD861D8DC53D00377370 /* Location.m in Sources */, + 8215AD831D8DC49F00377370 /* Announcement.m in Sources */, + 820B450C1D9624B1002A26BA /* SponsorTableViewCell.m in Sources */, + 8221B20B1DAF109B00FB1BE1 /* HTXAPIKeyStore.m in Sources */, + 8200AD711DB3577400D4EE11 /* AnnouncementTableViewCell.m in Sources */, + 8221B2071DAEF9D900FB1BE1 /* ScheduleTableViewCell.m in Sources */, + 8215AD8F1D8DC88100377370 /* HTXAPI.m in Sources */, + 8215AD891D8DC63F00377370 /* Event.m in Sources */, + 822A4DC01D779DF100C65761 /* MapViewController.m in Sources */, + 82D303D11DB24084003C98ED /* Hacker.m in Sources */, + 82BA003D1D8149B1006CA2C6 /* UIColor+Palette.m in Sources */, + 8215AD8C1D8DC70F00377370 /* Speaker.m in Sources */, + 8215AD921D8DD26C00377370 /* SponsorViewController.m in Sources */, + 82EAFC871DB200D50061C449 /* CheckInViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - F9E9903D1B50EBBA00057823 /* Main.storyboard */ = { + 822A4DA21D779BCD00C65761 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - F9E9903E1B50EBBA00057823 /* Base */, + 822A4DA31D779BCD00C65761 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; - F9E990421B50EBBA00057823 /* LaunchScreen.xib */ = { + 822A4DA71D779BCD00C65761 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( - F9E990431B50EBBA00057823 /* Base */, + 822A4DA81D779BCD00C65761 /* Base */, ); - name = LaunchScreen.xib; + name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - F9E990511B50EBBA00057823 /* Debug */ = { + 822A4DAB1D779BCD00C65761 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -1031,17 +533,21 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + 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; @@ -1050,26 +556,25 @@ "DEBUG=1", "$(inherited)", ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; 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.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - F9E990521B50EBBA00057823 /* Release */ = { + 822A4DAC1D779BCD00C65761 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -1077,14 +582,17 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -1097,7 +605,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1105,78 +613,56 @@ }; name = Release; }; - F9E990541B50EBBA00057823 /* Debug */ = { + 822A4DAE1D779BCD00C65761 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 94E240E75C09DEDD03EA913F /* Pods-HackTX.debug.xcconfig */; + baseConfigurationReference = DDE9620564A4ACF2DB3BFB7E /* Pods-HackTX.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); + CODE_SIGN_ENTITLEMENTS = HackTX/HackTX.entitlements; + DEVELOPMENT_TEAM = 245X4K2U9R; INFOPLIST_FILE = HackTX/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(PROJECT_DIR)/**", - "$(inherited)/**", - ); + PRODUCT_BUNDLE_IDENTIFIER = com.HackTX; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "8cf0982d-0702-413c-a1c7-755a4ddf84a9"; - SWIFT_OBJC_BRIDGING_HEADER = "HackTX-Bridging-Header.h"; }; name = Debug; }; - F9E990551B50EBBA00057823 /* Release */ = { + 822A4DAF1D779BCD00C65761 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 163274BAD693C35C1B0222A5 /* Pods-HackTX.release.xcconfig */; + baseConfigurationReference = 099214E83B6739C941439E79 /* Pods-HackTX.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Rohit Datta (7LU2FJSKBU)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); + CODE_SIGN_ENTITLEMENTS = HackTX/HackTX.entitlements; + DEVELOPMENT_TEAM = 245X4K2U9R; INFOPLIST_FILE = HackTX/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(PROJECT_DIR)/**", - "$(inherited)/**", - ); + PRODUCT_BUNDLE_IDENTIFIER = com.HackTX; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "8cf0982d-0702-413c-a1c7-755a4ddf84a9"; - SWIFT_OBJC_BRIDGING_HEADER = "HackTX-Bridging-Header.h"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - F9E9902D1B50EBBA00057823 /* Build configuration list for PBXProject "HackTX" */ = { + 822A4D8E1D779BCD00C65761 /* Build configuration list for PBXProject "HackTX" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9E990511B50EBBA00057823 /* Debug */, - F9E990521B50EBBA00057823 /* Release */, + 822A4DAB1D779BCD00C65761 /* Debug */, + 822A4DAC1D779BCD00C65761 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F9E990531B50EBBA00057823 /* Build configuration list for PBXNativeTarget "HackTX" */ = { + 822A4DAD1D779BCD00C65761 /* Build configuration list for PBXNativeTarget "HackTX" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9E990541B50EBBA00057823 /* Debug */, - F9E990551B50EBBA00057823 /* Release */, + 822A4DAE1D779BCD00C65761 /* Debug */, + 822A4DAF1D779BCD00C65761 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = F9E9902A1B50EBBA00057823 /* Project object */; + rootObject = 822A4D8B1D779BCD00C65761 /* Project object */; } diff --git a/HackTX.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/HackTX.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..f1f674c --- /dev/null +++ b/HackTX.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/HackTX.xcworkspace/contents.xcworkspacedata b/HackTX.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9f0e426 --- /dev/null +++ b/HackTX.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/HackTX/Announcement.h b/HackTX/Announcement.h new file mode 100644 index 0000000..1171544 --- /dev/null +++ b/HackTX/Announcement.h @@ -0,0 +1,21 @@ +// +// Announcement.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface Announcement : RLMObject + +@property NSString *serverID; +@property NSString *text; +@property NSDate *timestamp; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Announcement) diff --git a/HackTX/Announcement.m b/HackTX/Announcement.m new file mode 100644 index 0000000..395f93f --- /dev/null +++ b/HackTX/Announcement.m @@ -0,0 +1,31 @@ +// +// Announcement.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Announcement.h" + +@implementation Announcement + ++ (NSString *)primaryKey { + return @"serverID"; +} + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Announcement.swift b/HackTX/Announcement.swift deleted file mode 100644 index 8769203..0000000 --- a/HackTX/Announcement.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Announcement.swift -// HackTX -// -// Created by Drew Romanyk on 8/15/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Announcement: NSObject { - var text = "" - var ts = "" - - init(text: String, ts: String) { - self.text = text - self.ts = ts - } - - func getEnglishTs() -> String { - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - - let jsonDate = dateFormatter.dateFromString(self.ts) - dateFormatter.dateFormat = "MMM d, hh:mm a" - return dateFormatter.stringFromDate(jsonDate!) - } - - func getTsDate() -> NSDate { - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - - return dateFormatter.dateFromString(self.ts)! - } -} diff --git a/HackTX/AnnouncementTableViewCell.h b/HackTX/AnnouncementTableViewCell.h new file mode 100644 index 0000000..ca9ce87 --- /dev/null +++ b/HackTX/AnnouncementTableViewCell.h @@ -0,0 +1,17 @@ +// +// AnnouncementTableViewCell.h +// HackTX +// +// Created by Jose Bethancourt on 10/16/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface AnnouncementTableViewCell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UIView *cardView; +@property (weak, nonatomic) IBOutlet UILabel *text; +@property (weak, nonatomic) IBOutlet UILabel *time; + +@end diff --git a/HackTX/AnnouncementTableViewCell.m b/HackTX/AnnouncementTableViewCell.m new file mode 100644 index 0000000..9dcc62d --- /dev/null +++ b/HackTX/AnnouncementTableViewCell.m @@ -0,0 +1,59 @@ +// +// AnnouncementTableViewCell.m +// HackTX +// +// Created by Jose Bethancourt on 10/16/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "AnnouncementTableViewCell.h" + +#import "UIColor+Palette.h" + +@implementation AnnouncementTableViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + + self.time.textColor = [UIColor htx_red]; + + self.backgroundColor = [UIColor clearColor]; + + self.cardView.backgroundColor = [UIColor whiteColor]; + self.cardView.layer.cornerRadius = 2.5; + self.cardView.layer.masksToBounds = false; + self.cardView.layer.shadowColor = [UIColor blackColor].CGColor; + self.cardView.layer.shadowOffset = CGSizeMake(0.0, .25); + self.cardView.layer.shadowRadius = 1.0; + self.cardView.layer.shadowOpacity = 0.2; + + +} + +//- (void)layoutSubviews { +// [super layoutSubviews]; +// +// self.text.preferredMaxLayoutWidth = self.text.frame.size.width; +// +//} +// +//- (void)viewDidLayoutSubviews { +// dispatch_async(dispatch_get_main_queue(), ^{ +// self.text.preferredMaxLayoutWidth = self.text.frame.size.width; +// }); +//} + +- (void)setFrame:(CGRect)frame { + frame.origin.x += 7.5; + frame.origin.y += 4; + + frame.size.width -= 2 * 7.5; + frame.size.height -= 2 * 4; + [super setFrame:frame]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; +} + +@end diff --git a/HackTX/AnnouncementTableViewCell.xib b/HackTX/AnnouncementTableViewCell.xib new file mode 100644 index 0000000..71a1c2f --- /dev/null +++ b/HackTX/AnnouncementTableViewCell.xib @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackTX/AnnouncementsViewController.h b/HackTX/AnnouncementsViewController.h new file mode 100644 index 0000000..f1ba944 --- /dev/null +++ b/HackTX/AnnouncementsViewController.h @@ -0,0 +1,13 @@ +// +// AnnouncementsViewController.h +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface AnnouncementsViewController : UIViewController + +@end diff --git a/HackTX/AnnouncementsViewController.m b/HackTX/AnnouncementsViewController.m new file mode 100644 index 0000000..bc04ba3 --- /dev/null +++ b/HackTX/AnnouncementsViewController.m @@ -0,0 +1,168 @@ +// +// AnnouncementsViewController.m +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "AnnouncementsViewController.h" +#import "AnnouncementTableViewCell.h" + +#import "AutolayoutHelper.h" +#import "UIColor+Palette.h" +#import "SVProgressHUD.h" +#import "Announcement.h" +#import "FCAlertView.h" +#import "HTXAPI.h" + +@interface AnnouncementsViewController () + +@property (nonatomic, strong) UITableView *tableView; +@property (nonatomic, strong) UIRefreshControl *refreshControl; +@property (nonatomic, strong) RLMResults *announcements; + +@end + +static NSString *reuseIdentifier = @"com.HackTX.announcement"; + +@implementation AnnouncementsViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView = [[UITableView alloc] init]; + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.estimatedRowHeight = 85; + self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.tableView.tableFooterView = [UIView new]; + self.tableView.allowsSelection = NO; + self.tableView.backgroundColor = [UIColor htx_white]; + + self.refreshControl = [[UIRefreshControl alloc] init]; + [self.tableView setRefreshControl:self.refreshControl]; + [self.refreshControl addTarget:self action:@selector(hardRefresh) forControlEvents:UIControlEventValueChanged]; + + self.edgesForExtendedLayout = UIRectEdgeAll; + self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f); + + UINib *nib = [UINib nibWithNibName:@"AnnouncementTableViewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:reuseIdentifier]; + + [AutolayoutHelper configureView:self.view fillWithSubView:self.tableView]; + + [self initData]; + [self.tableView layoutIfNeeded]; + [self.tableView reloadData]; + +} + +- (void)hardRefresh { + + [HTXAPI refreshAnnouncements:^(BOOL success) { + if (success) { + [self.refreshControl endRefreshing]; + [self refreshData]; + } else { + [self.refreshControl endRefreshing]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the announcements, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Announcements refresh failed"); + } + }]; +} + +- (void)refresh { + [HTXAPI refreshAnnouncements:^(BOOL success) { + if (success) { + [self initData]; + } else { + [self.refreshControl endRefreshing]; + [SVProgressHUD dismiss]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the announcements, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Schedule refresh failed"); + } + }]; +} + +- (void)initData { + self.announcements = [[Announcement allObjects] sortedResultsUsingProperty:@"timestamp" ascending:NO]; + [SVProgressHUD show]; + + if (self.announcements.count > 0) { + self.announcements = [[Announcement allObjects] sortedResultsUsingProperty:@"timestamp" ascending:NO]; + + [SVProgressHUD dismiss]; + [self.tableView reloadData]; + + [HTXAPI refreshAnnouncements:^(BOOL success) { + if (success) { + [self refreshData]; + } + }]; + + } else { + [self refresh]; + } +} + +- (void)refreshData { + self.announcements = [[Announcement allObjects] sortedResultsUsingProperty:@"timestamp" ascending:NO]; + [self.tableView reloadData]; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + UIView *headerView = [[UIView alloc] init]; + headerView.backgroundColor = [UIColor clearColor]; + return headerView; +} +- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section { + return 40; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + AnnouncementTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; + + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.timeStyle = NSDateFormatterShortStyle; + formatter.dateStyle = NSDateFormatterNoStyle; + + cell.text.text = self.announcements[indexPath.row].text; + cell.time.text = [formatter stringFromDate:self.announcements[indexPath.row].timestamp]; + + [cell updateConstraintsIfNeeded]; + cell.text.preferredMaxLayoutWidth = cell.text.frame.size.width; + + return cell; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.announcements.count; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +@end diff --git a/HackTX/AnnouncementsViewController.swift b/HackTX/AnnouncementsViewController.swift deleted file mode 100644 index bbbe6bd..0000000 --- a/HackTX/AnnouncementsViewController.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// AnnouncementsViewController.swift -// HackTX -// -// Created by Drew Romanyk on 8/15/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import Alamofire -import SwiftyJSON - -class AnnouncementsViewController: UITableViewController { - - var announcementList = [Announcement]() - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.estimatedRowHeight = 44 - tableView.rowHeight = UITableViewAutomaticDimension - NSNotificationCenter.defaultCenter().addObserver(self, selector: "reloadTable:", name: "reloadTheTable", object: nil) - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getAnnouncementData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - } - - func reloadTable(notification: NSNotification) { - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getAnnouncementData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - } - - - - // Collect announcement data from the api - func getAnnouncementData() { - - Alamofire.request(Router.Announcements()) - .responseJSON{ (request, response, data) in - if data.isFailure { - let errorAlert = UIAlertView() - if errorAlert.title == "" { - errorAlert.title = "Error" - errorAlert.message = "Oops! Looks like there was a problem trying to get the announcements" - errorAlert.addButtonWithTitle("Ok") - errorAlert.show() - } - } else if let data: AnyObject = data.value { - let json = JSON(data) - self.announcementList.removeAll(keepCapacity: true) - - for (_, subJson): (String, JSON) in json { - self.announcementList.insert(Announcement(text: subJson["text"].stringValue, ts: subJson["ts"].stringValue), atIndex: 0) - } - self.tableView.reloadData() - } - - } - } - - // Sort announcement messages by newest to oldest - func sortAnnouncements(this: Announcement, that: Announcement) -> Bool { - return this.getTsDate().compare(that.getTsDate()) == NSComparisonResult.OrderedDescending - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "Announcements") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - - /* - * TABLE VIEW METHODS - */ - - // Refresh the tableview data - @IBAction func refresh(sender: UIRefreshControl) { - getAnnouncementData() - sender.endRefreshing() - } - - override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 1 - } - - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return announcementList.count - } - - - override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCellWithIdentifier("AnnouncementCell", forIndexPath: indexPath) - let announcement = announcementList[indexPath.row] - - cell.textLabel!.text = announcement.text - cell.detailTextLabel!.text = announcement.getEnglishTs() - cell.selectionStyle = UITableViewCellSelectionStyle.None - return cell - } -} diff --git a/HackTX/ApiKeys.plist b/HackTX/ApiKeys.plist deleted file mode 100644 index 1d448e0..0000000 --- a/HackTX/ApiKeys.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - ClientID - vb13BWJVxT9kPTHNazEf2KN9ev28mk7F0ve7Fvga - ApplicationID - zNIuPOtKnxNAVHekB1yCkgTBlABXiG5OqB4Cv4C5 - - diff --git a/HackTX/AppDelegate.h b/HackTX/AppDelegate.h new file mode 100644 index 0000000..312093c --- /dev/null +++ b/HackTX/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/HackTX/AppDelegate.m b/HackTX/AppDelegate.m new file mode 100644 index 0000000..b8e62ce --- /dev/null +++ b/HackTX/AppDelegate.m @@ -0,0 +1,186 @@ +// +// AppDelegate.m +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "AppDelegate.h" + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +@import UserNotifications; +#endif + +@import Firebase; +@import FirebaseInstanceID; +@import FirebaseMessaging; +@import GoogleMaps; + +#import "AnnouncementsViewController.h" +#import "CheckInViewController.h" +#import "MapViewController.h" +#import "ScheduleViewController.h" +#import "SponsorViewController.h" +#import "HTXAPIKeyStore.h" +#import "UIColor+Palette.h" + +#import +#import + + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +@interface AppDelegate () +#endif + +@property (nonatomic, strong) UITabBarController *tabBarController; + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + + [Fabric with:@[[Crashlytics class]]]; + [GMSServices provideAPIKey:[[HTXAPIKeyStore sharedHTXAPIKeyStore] getGMSKey]]; + + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + UIUserNotificationType allNotificationTypes = + (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + } else { + // iOS 10 or later + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNAuthorizationOptions authOptions = + UNAuthorizationOptionAlert + | UNAuthorizationOptionSound + | UNAuthorizationOptionBadge; + [[UNUserNotificationCenter currentNotificationCenter] + requestAuthorizationWithOptions:authOptions + completionHandler:^(BOOL granted, NSError * _Nullable error) { + } + ]; + + // For iOS 10 display notification (sent via APNS) + [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; +// [[FIRMessaging messaging] setRemoteMessageDelegate:self]; + #endif + } + + [FIRApp configure]; + + + [[UIApplication sharedApplication] registerForRemoteNotifications]; + + ScheduleViewController *vc1 = [[ScheduleViewController alloc] init]; + AnnouncementsViewController *vc2 = [[AnnouncementsViewController alloc] init]; + CheckInViewController *vc3 = [[CheckInViewController alloc] init]; + MapViewController *vc4 = [[MapViewController alloc] init]; + SponsorViewController *vc5 = [[SponsorViewController alloc] init]; + + _tabBarController = [[UITabBarController alloc] init]; + _tabBarController.tabBar.translucent = NO; + + _tabBarController.viewControllers = @[vc1, vc2, vc3, vc4, vc5]; + + vc1.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Schedule" image:[UIImage imageNamed:@"icon_calendar"] selectedImage:nil]; + vc2.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Updates" image:[UIImage imageNamed:@"icon_bell"] selectedImage:nil]; + vc3.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Check-In" image:[UIImage imageNamed:@"icon_profile"] selectedImage:nil]; + vc4.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Map" image:[UIImage imageNamed:@"icon_map"] selectedImage:nil]; + vc5.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Partners" image:[UIImage imageNamed:@"icon_heart"] selectedImage:nil]; + + [[UITabBar appearance] setTintColor:[UIColor htx_red]]; + + _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + UINavigationController *navController = [[UINavigationController alloc]initWithRootViewController:_tabBarController]; + + UIImageView *headerImage = [[UIImageView alloc] init]; + headerImage.contentMode = UIViewContentModeScaleAspectFit; + headerImage.frame = CGRectMake(0, 0, 38, 38); + headerImage.image = [UIImage imageNamed:@"htx_logo"]; + + navController.navigationBar.barTintColor = [UIColor htx_lightBlue]; + navController.navigationBar.topItem.titleView = headerImage; + navController.navigationBar.translucent = NO; + + _window.rootViewController = navController; + [_window makeKeyAndVisible]; + + if (launchOptions != nil) { + // Launched from push notification + NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; + if ([notification count] != 0) { + [self.tabBarController setSelectedIndex:1]; + } + } + + return YES; +} + + +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + //[AVOSCloud handleRemoteNotificationsWithDeviceToken:deviceToken]; + dispatch_queue_t myQueue = dispatch_queue_create("firebase_topics", NULL); + + dispatch_async(myQueue, ^{ + NSArray *topics = @[@"/topics/announcements", @"/topics/hacktx", @"/topics/ios", @"/topics/debug"]; + + for (NSString *topic in topics) { + [[FIRMessaging messaging] subscribeToTopic:topic]; + [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]; + } + + }); + +} +- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { + dispatch_queue_t myQueue = dispatch_queue_create("firebase_topics", NULL); + + dispatch_async(myQueue, ^{ + NSArray *topics = @[@"/topics/announcements", @"/topics/hacktx", @"/topics/ios", @"/topics/debug"]; + + for (NSString *topic in topics) { + [[FIRMessaging messaging] subscribeToTopic:topic]; + [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]]; + } + + }); + +} + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler +{ +} + +- (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 invalidate graphics rendering callbacks. 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 active 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/HackTX/AppDelegate.swift b/HackTX/AppDelegate.swift deleted file mode 100644 index edc1cf6..0000000 --- a/HackTX/AppDelegate.swift +++ /dev/null @@ -1,225 +0,0 @@ -// -// AppDelegate.swift -// HackTX -// -// Created by Rohit Datta on 7/11/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import Fabric -import TwitterKit -import Parse - - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - - UserPrefs.shared().registerDefaults() - Fabric.with([Twitter()]) - setNavTabBarLayout() - - setupGoogleAnalytics() - initParse(application, launchOptions: launchOptions) - UIApplication.sharedApplication().applicationIconBadgeNumber = 0 - - return true - } - - func setupGoogleAnalytics() { - // Configure tracker from GoogleService-Info.plist. - var configureError:NSError? - GGLContext.sharedInstance().configureWithError(&configureError) - assert(configureError == nil, "Error configuring Google services: \(configureError)") - - // Optional: configure GAI options. - let gai = GAI.sharedInstance() - gai.trackUncaughtExceptions = true // report uncaught exceptions - } - - func initParse(application: UIApplication, launchOptions: [NSObject: AnyObject]?) { - - - let parseAppID = getParseAppID() - let parseClientID = getParseClientID() - Parse.setApplicationId(parseAppID, - clientKey: parseClientID) - - if application.applicationState != UIApplicationState.Background { - // Track an app open here if we launch with a push, unless - // "content_available" was used to trigger a background push (introduced in iOS 7). - // In that case, we skip tracking here to avoid double counting the app-open. - - let preBackgroundPush = !application.respondsToSelector("backgroundRefreshStatus") - let oldPushHandlerOnly = !self.respondsToSelector("application:didReceiveRemoteNotification:fetchCompletionHandler:") - var pushPayload = false - if let options = launchOptions { - pushPayload = options[UIApplicationLaunchOptionsRemoteNotificationKey] != nil - } - if (preBackgroundPush || oldPushHandlerOnly || pushPayload) { - PFAnalytics.trackAppOpenedWithLaunchOptions(launchOptions) - } - } - if application.respondsToSelector("registerUserNotificationSettings:") { - let userNotificationTypes: UIUserNotificationType = [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound] - let settings = UIUserNotificationSettings(forTypes: userNotificationTypes, categories: nil) - application.registerUserNotificationSettings(settings) - application.registerForRemoteNotifications() - } else { - application.registerForRemoteNotifications() - } - - let currentInstallation = PFInstallation.currentInstallation() - currentInstallation.addUniqueObject("announcements", forKey: "channels") - currentInstallation.saveInBackground() - - registerCheckInReminder() - } - - func registerCheckInReminder() { - let fireDate = NSDate(timeIntervalSince1970: 1443274200) - - //Remind attendees to check in to the event - if (!UserPrefs.shared().isRegisteredForCheckInNotif() && !eventPassed(fireDate)) { - let notification = UILocalNotification() - notification.alertBody = "Remember to check in for HackTX in the app!" - notification.alertAction = "check in" - notification.fireDate = fireDate - notification.soundName = UILocalNotificationDefaultSoundName - notification.userInfo = ["uid": "check-in-reminder"] - UIApplication.sharedApplication().scheduleLocalNotification(notification) - UserPrefs.shared().setRegisterForCheckInNotif(true) - // let val = UserPrefs.shared().isRegisteredForCheckInNotif() - } - } - - func eventPassed(eventDate: NSDate) -> Bool { - let currentDate = NSDate() - return currentDate.timeIntervalSince1970 > eventDate.timeIntervalSince1970 - } - - func getParseKeyDict() -> NSDictionary { - var parseDict : NSDictionary? - if let path = NSBundle.mainBundle().pathForResource("ApiKeys", ofType: "plist") { - parseDict = NSDictionary(contentsOfFile: path) - } - return parseDict! - } - - func getParseAppID() -> String { - let parseDict = getParseKeyDict() - return parseDict["ApplicationID"] as! String - } - - func getParseClientID() -> String { - let parseDict = getParseKeyDict() - return parseDict["ClientID"] as! String - } - - func setNavTabBarLayout() { -// UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true) -// UINavigationBar.appearance().barTintColor = UIColor(red: 10/255.0, green: 166/255.0, blue: 182/255.0, alpha: 1.0) -// UINavigationBar.appearance().tintColor = UIColor.whiteColor() -// UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.whiteColor()] -// -// UITabBar.appearance().barTintColor = UIColor(red: 10/255.0, green: 166/255.0, blue: 182/255.0, alpha: 1.0) - UITabBar.appearance().tintColor = UIColor(red: 125/255.0, green: 211/255.0, blue: 244/255.0, alpha: 1.0) //UIColor.whiteColor() - - - } - - func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) { - let installation = PFInstallation.currentInstallation() - installation.setDeviceTokenFromData(deviceToken) - installation.saveInBackground() - } - - func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) { - if error.code == 3010 { - print("Push notifications are not supported in the iOS Simulator.") - } else { - print("application:didFailToRegisterForRemoteNotificationsWithError: %@", error) - } - } - - func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) { - PFPush.handlePush(userInfo) - if application.applicationState == UIApplicationState.Inactive { - PFAnalytics.trackAppOpenedWithRemoteNotificationPayload(userInfo) - } - - self.window?.makeKeyAndVisible() - let rootController = window?.rootViewController as! UITabBarController - rootController.selectedIndex = 1; - NSNotificationCenter.defaultCenter().postNotificationName("reloadTheTable", object: nil) - - } - - @available(iOS 9.0, *) - func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) { - print("Application performActionForShortcutItem") - completionHandler(handleShortcut(shortcutItem)) - } - - @available(iOS 9.0, *) - func handleShortcut(shortcutItem: UIApplicationShortcutItem) -> Bool { - print("handling shortcut") - var succeeded = false - - self.window?.makeKeyAndVisible() - let rootController = window?.rootViewController as! UITabBarController - if (shortcutItem.type == "schedule") { - //Debugging code - print("- Handling \(shortcutItem.type)") - - rootController.selectedIndex = 0 - succeeded = true - - } else if (shortcutItem.type == "announcements") { - print("- Handling \(shortcutItem.type)") - - rootController.selectedIndex = 1; - succeeded = true - - } else if (shortcutItem.type == "maps") { - print("- Handling \(shortcutItem.type)") - - rootController.selectedIndex = 3 - succeeded = true - } - - return succeeded - } - - func applicationWillResignActive(application: UIApplication) { - // 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. - } - - func applicationDidEnterBackground(application: UIApplication) { - // 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. - } - - func applicationWillEnterForeground(application: UIApplication) { - // 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. - UIApplication.sharedApplication().applicationIconBadgeNumber = 0 - - } - - func applicationDidBecomeActive(application: UIApplication) { - // 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. - } - - func applicationWillTerminate(application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - -} - diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Contents.json b/HackTX/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..7a79b23 --- /dev/null +++ b/HackTX/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,110 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-Spotlight-42.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-60.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Spotlight-40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Spotlight-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-20.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-Spotlight-41.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x-1.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Spotlight-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Spotlight-40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-iPadPro@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-20.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-20.png new file mode 100644 index 0000000..f9b71b6 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-20.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60.png new file mode 100644 index 0000000..669d420 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000..71b161b Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000..d58c271 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000..8325c19 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000..25fcbd6 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000..cbe6f0b Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png new file mode 100644 index 0000000..b32bcbe Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000..b32bcbe Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000..014e156 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png new file mode 100644 index 0000000..ba5665a Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png new file mode 100644 index 0000000..689f7d4 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png new file mode 100644 index 0000000..689f7d4 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png new file mode 100644 index 0000000..99dfb82 Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png new file mode 100644 index 0000000..ba5665a Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-41.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png new file mode 100644 index 0000000..ba5665a Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-42.png differ diff --git a/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png new file mode 100644 index 0000000..987fbae Binary files /dev/null and b/HackTX/Assets.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png differ diff --git a/HackTX/Assets.xcassets/Contents.json b/HackTX/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/HackTX/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/htx_logo.imageset/Contents.json b/HackTX/Assets.xcassets/htx_logo.imageset/Contents.json new file mode 100644 index 0000000..e970d54 --- /dev/null +++ b/HackTX/Assets.xcassets/htx_logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "htx_logo.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/htx_logo.imageset/htx_logo.pdf b/HackTX/Assets.xcassets/htx_logo.imageset/htx_logo.pdf new file mode 100644 index 0000000..72ec370 Binary files /dev/null and b/HackTX/Assets.xcassets/htx_logo.imageset/htx_logo.pdf differ diff --git a/HackTX/Assets.xcassets/icon_bell.imageset/Bell.pdf b/HackTX/Assets.xcassets/icon_bell.imageset/Bell.pdf new file mode 100644 index 0000000..fd7c68e Binary files /dev/null and b/HackTX/Assets.xcassets/icon_bell.imageset/Bell.pdf differ diff --git a/HackTX/Assets.xcassets/icon_bell.imageset/Contents.json b/HackTX/Assets.xcassets/icon_bell.imageset/Contents.json new file mode 100644 index 0000000..ee3fff4 --- /dev/null +++ b/HackTX/Assets.xcassets/icon_bell.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Bell.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_calendar.imageset/Calendar.pdf b/HackTX/Assets.xcassets/icon_calendar.imageset/Calendar.pdf new file mode 100644 index 0000000..f607715 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_calendar.imageset/Calendar.pdf differ diff --git a/HackTX/Assets.xcassets/icon_calendar.imageset/Contents.json b/HackTX/Assets.xcassets/icon_calendar.imageset/Contents.json new file mode 100644 index 0000000..4685f1f --- /dev/null +++ b/HackTX/Assets.xcassets/icon_calendar.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Calendar.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_heart.imageset/Contents.json b/HackTX/Assets.xcassets/icon_heart.imageset/Contents.json new file mode 100644 index 0000000..cf38a9c --- /dev/null +++ b/HackTX/Assets.xcassets/icon_heart.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Heart2.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_heart.imageset/Heart2.pdf b/HackTX/Assets.xcassets/icon_heart.imageset/Heart2.pdf new file mode 100644 index 0000000..82b3729 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_heart.imageset/Heart2.pdf differ diff --git a/HackTX/Assets.xcassets/icon_htx.imageset/Contents.json b/HackTX/Assets.xcassets/icon_htx.imageset/Contents.json new file mode 100644 index 0000000..aa6a47b --- /dev/null +++ b/HackTX/Assets.xcassets/icon_htx.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "app_icon-2.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "app_icon-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "app_icon.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-1.png b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-1.png new file mode 100644 index 0000000..5da01c6 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-1.png differ diff --git a/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-2.png b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-2.png new file mode 100644 index 0000000..5da01c6 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon-2.png differ diff --git a/HackTX/Assets.xcassets/icon_htx.imageset/app_icon.png b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon.png new file mode 100644 index 0000000..5da01c6 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx.imageset/app_icon.png differ diff --git a/HackTX/Assets.xcassets/icon_htx_not.imageset/Contents.json b/HackTX/Assets.xcassets/icon_htx_not.imageset/Contents.json new file mode 100644 index 0000000..57feea7 --- /dev/null +++ b/HackTX/Assets.xcassets/icon_htx_not.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_launcher-1.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_launcher.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_launcher-2.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-1.png b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-1.png new file mode 100644 index 0000000..6652839 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-1.png differ diff --git a/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-2.png b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-2.png new file mode 100644 index 0000000..6652839 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher-2.png differ diff --git a/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher.png b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher.png new file mode 100644 index 0000000..6652839 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_htx_not.imageset/ic_launcher.png differ diff --git a/HackTX/Assets.xcassets/icon_map.imageset/Contents.json b/HackTX/Assets.xcassets/icon_map.imageset/Contents.json new file mode 100644 index 0000000..9a481b8 --- /dev/null +++ b/HackTX/Assets.xcassets/icon_map.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Map.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_map.imageset/Map.pdf b/HackTX/Assets.xcassets/icon_map.imageset/Map.pdf new file mode 100644 index 0000000..e2eed45 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_map.imageset/Map.pdf differ diff --git a/HackTX/Assets.xcassets/icon_profile.imageset/Contents.json b/HackTX/Assets.xcassets/icon_profile.imageset/Contents.json new file mode 100644 index 0000000..2db34cc --- /dev/null +++ b/HackTX/Assets.xcassets/icon_profile.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "profile.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HackTX/Assets.xcassets/icon_profile.imageset/profile.pdf b/HackTX/Assets.xcassets/icon_profile.imageset/profile.pdf new file mode 100644 index 0000000..ad1ec33 Binary files /dev/null and b/HackTX/Assets.xcassets/icon_profile.imageset/profile.pdf differ diff --git a/HackTX/AutolayoutHelper.h b/HackTX/AutolayoutHelper.h new file mode 100755 index 0000000..e77b464 --- /dev/null +++ b/HackTX/AutolayoutHelper.h @@ -0,0 +1,98 @@ + +// Helper to configure auto layout constraints +// Supports VFL constraints and more extended constraints (see README.md) +// +// Based on this tutorial: http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/ +// Also inspired by this: http://stackoverflow.com/a/18066138/1121497 + +#import + +#define VarBindings NSDictionaryOfVariableBindings + +#define PRIORITY_DEFAULT -1 +#define XT_CONSTRAINT_SYMBOL @"X" + + +@interface AutolayoutHelper : NSObject + + +@property(nonatomic, weak) UIView* view; // The view where the subviews and constraints will be added +@property(nonatomic, strong) NSMutableDictionary* subViews; // Dictionary of key:subview (subviews to be added to the view) +@property(nonatomic, strong) NSDictionary* metrics; // Metrics to be used for the constraints + + +// When set to true, subviews will be painted with random semi-transparent backgrounds +// just to better visualize the position and sizes of the subviews. ++ (void)setDisplayBackgroundColorsForDebugging:(BOOL)displayColor; + + +// Convenience class methods +// Arguments: +// subViews: NSDictionary* of (NSString*)key:(UIView*)view with subviews to add and their keys to be referred in constraints +// subViewLayers: NSArray* of NSDictionary* subViews, useful when some views should be added before others +// metrics: NSDictionary* of (NSString*)key:(NSNumber*)value with metrics to be referred in constraints +// constraints: NSArray* of NSString* with VFL constraints (and also other extended constraints; see README.md) + +// Adds subViews and VFL constraints to view ++ (AutolayoutHelper*)configureView:(UIView*)view subViews:(NSDictionary*)subViews constraints:(NSArray*)constraints; + +// Adds subViews and VFL constraints (with given metrics) to view ++ (AutolayoutHelper*)configureView:(UIView*)view subViews:(NSDictionary*)subViews metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints; + +// Adds subViewLayers and VFL constraints (with given metrics) to view ++ (AutolayoutHelper*)configureView:(UIView*)view subViewLayers:(NSArray*)subViewLayers metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints; + +// Adds the subview to the view so it fills it (aligns to edges) ++ (AutolayoutHelper*)configureView:(UIView*)view fillWithSubView:(UIView*)subview; + +// Adds the subview to the controller.view so it fills it (aligns to edges) ++ (AutolayoutHelper*)configureViewController:(UIViewController*)controller fillWithSubView:(UIView*)subview; + +// Adds the subViews and VFL constraints to a new UIView ++ (AutolayoutHelper*)subViews:(NSDictionary*)subViews constraints:(NSArray*)constraints; + +// Adds the subViews and VFL constraints (with given metrics) to a new UIView ++ (AutolayoutHelper*)subViews:(NSDictionary*)subViews metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints; + +// Configures a "vertical" scroll view (see README.md) ++ (void)configureScrollView:(UIScrollView*)scrollView contentView:(UIView*)contentView mainView:(UIView*)mainView; + + +// Initializes this helper to configure subviews of the given view +- (id)initWithView:(UIView*)view; + + +// Adds subViews to self.view +- (void)addViews:(NSDictionary*)subViews; + +// Adds subView to self.view, and uses subViewKey for constraints +- (void)addView:(UIView*)subView withKey:(NSString*)subViewKey; + +// Adds subViews and constraints to self.view +- (void)addViews:(NSDictionary*)subViews constraints:(NSArray*)constraints; + +// Removes subViews from self.view (actually, it uses only the dictionary keys) +- (void)removeViews:(NSDictionary*)subViews; + +// Removes subViews with given viewKeys from self.view +- (void)removeViewsWithKeys:(NSArray*)viewKeys; + +// Adds one constraint and returns an array of the generated NSLayoutConstraint +- (NSArray*)addConstraint:(NSString*)constraint; + +// Adds one constraint with given priority and returns an array of the generated NSLayoutConstraint +- (NSArray*)addConstraint:(NSString*)constraint priority:(UILayoutPriority)priority; + +// Adds constraints +- (void)addConstraints:(NSArray*)constraints; + +// Adds constraints with given priority +- (void)addConstraints:(NSArray*)constraints priority:(UILayoutPriority)priority; + +// Adds constraints, replacing constraints that were added before with the same key +- (void)setConstraints:(NSArray*)constraints forKey:(NSString*)key; + +// Adds constraints with given priority, replacing constraints that were added before with the same key +- (void)setConstraints:(NSArray*)constraints priority:(UILayoutPriority)priority forKey:(NSString*)key; + +@end \ No newline at end of file diff --git a/HackTX/AutolayoutHelper.m b/HackTX/AutolayoutHelper.m new file mode 100755 index 0000000..8472656 --- /dev/null +++ b/HackTX/AutolayoutHelper.m @@ -0,0 +1,462 @@ + +#import "AutolayoutHelper.h" + +#define XT_CONSTRAINT_ERROR @"Invalid extended constraint" + + +@interface AutolayoutHelper () + +@property(nonatomic, strong) NSMutableDictionary* temporalConstraints; +@property(nonatomic, strong) UIView* strongPointerToView; + +@end + + +@implementation AutolayoutHelper + +BOOL displayBackgroundColorsForDebugging = NO; + +NSRegularExpression * xtConstraintRegex; +NSDictionary* attributes; +NSDictionary* relations; + + ++ (void) initialize +{ + [self initializeXtConstraintLogic]; +} + ++ (void)setDisplayBackgroundColorsForDebugging:(BOOL)displayColors { + displayBackgroundColorsForDebugging = displayColors; +} + + ++ (AutolayoutHelper*)configureView:(UIView*)view subViews:(NSDictionary*)subViews constraints:(NSArray*)constraints +{ + return [AutolayoutHelper configureView:view subViews:subViews metrics:nil constraints:constraints]; +} + ++ (AutolayoutHelper*)configureView:(UIView*)view subViews:(NSDictionary*)subViews metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints +{ + return [self configureView:view subViews:subViews metrics:metrics constraints:constraints keepViewStrongly:NO]; +} + ++ (AutolayoutHelper*)configureView:(UIView*)view subViewLayers:(NSArray*)subViewLayers metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints +{ + return [AutolayoutHelper configureView:view subViewLayers:subViewLayers metrics:metrics constraints:constraints keepViewStrongly:NO]; +} + ++ (AutolayoutHelper*)configureView:(UIView*)view fillWithSubView:(UIView*)v +{ + return [self configureView:view subViews:VarBindings(v) constraints:@[@"H:|[v]|", @"V:|[v]|"]]; +} + ++ (AutolayoutHelper*)configureViewController:(UIViewController*)controller fillWithSubView:(UIView*)v +{ + id top = controller.topLayoutGuide; + id bottom = controller.bottomLayoutGuide; + + return [self configureView:controller.view + subViews:VarBindings(top, v, bottom) + constraints:@[@"H:|[v]|", @"V:[top][v][bottom]"]]; +} + ++ (AutolayoutHelper*)subViews:(NSDictionary*)subViews constraints:(NSArray*)constraints +{ + return [AutolayoutHelper subViews:subViews metrics:nil constraints:constraints]; +} + ++ (AutolayoutHelper*)subViews:(NSDictionary*)subViews metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints +{ + // Here we want to keep the view strongly, because the AutolayoutHelper.view property is weak + return [self configureView:[[UIView alloc] init] subViews:subViews metrics:metrics constraints:constraints keepViewStrongly:YES]; +} + ++ (AutolayoutHelper*)configureView:(UIView*)view subViews:(NSDictionary*)subViews metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints keepViewStrongly:(BOOL)keepViewStrongly +{ + return [AutolayoutHelper configureView:view subViewLayers:@[subViews] metrics:metrics constraints:constraints keepViewStrongly:keepViewStrongly]; +} + ++ (void)configureScrollView:(UIScrollView*)scrollView contentView:(UIView*)contentView mainView:(UIView*)mainView { + + [AutolayoutHelper configureView:scrollView fillWithSubView:contentView]; + + [mainView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:[contentView(==mainView)]" + options:(NSLayoutFormatOptions)0 + metrics:nil + views:VarBindings(contentView, mainView)]]; +} + +/** +* The methods above end up calling this one +*/ ++ (AutolayoutHelper*)configureView:(UIView*)view subViewLayers:(NSArray*)subViewLayers metrics:(NSDictionary*)metrics constraints:(NSArray*)constraints keepViewStrongly:(BOOL)keepViewStrongly +{ + AutolayoutHelper* helper = [[AutolayoutHelper alloc] initWithView:view]; + + if (keepViewStrongly) { + helper.strongPointerToView = view; + } + + helper.metrics = metrics; + + for (NSDictionary* subViews in subViewLayers) { + [helper addViews:subViews]; + } + + [helper addConstraints:constraints]; + + return helper; +} + + +- (id)initWithView:(UIView*)view +{ + self = [super init]; + + self.view = view; + self.subViews = [[NSMutableDictionary alloc] init]; + self.temporalConstraints = [[NSMutableDictionary alloc] init]; + + return self; +} + +- (void)addViews:(NSDictionary*)subViews { + + for (NSString* subViewKey in subViews.allKeys) { + + UIView* subView = subViews[subViewKey]; + [self addView:subView withKey:subViewKey]; + } +} + +- (void)addView:(UIView*)subView withKey:(NSString *)subViewKey +{ + self.subViews[subViewKey] = subView; + + // Ignore layout guides, just keep then in subViews dictionary + if ([subView conformsToProtocol:@protocol(UILayoutSupport)]) { + return; + } + + subView.translatesAutoresizingMaskIntoConstraints = NO; + + [self.view addSubview:subView]; + + if (displayBackgroundColorsForDebugging) { + subView.backgroundColor = [AutolayoutHelper getRandomColorWithAlpha:0.4]; + } +} + ++ (UIColor*)getRandomColorWithAlpha:(CGFloat)alpha +{ + u_int32_t red = arc4random_uniform(256); + u_int32_t green = arc4random_uniform(256); + u_int32_t blue = arc4random_uniform(256); + + return [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha]; +} + +- (void)addViews:(NSDictionary*)subViews constraints:(NSArray*)constraints { + + [self addViews:subViews]; + + [self addConstraints:constraints]; +} + +- (void)removeViews:(NSDictionary*)subViews { + + [self removeViewsWithKeys:subViews.allKeys]; +} + +- (void)removeViewsWithKeys:(NSArray*)viewKeys { + + for (NSString* viewKey in viewKeys) { + UIView* subView = self.subViews[viewKey]; + [subView removeFromSuperview]; + [self.subViews removeObjectForKey:viewKey]; + } +} + +- (void)addConstraints:(NSArray*)constraints +{ + [self addConstraints:constraints priority:PRIORITY_DEFAULT]; +} + +- (void)addConstraints:(NSArray*)constraints priority:(UILayoutPriority)priority +{ + for (NSString* constraint in constraints) { + [self addConstraint:constraint priority:priority]; + } +} + +- (NSArray*)addConstraint:(NSString*)constraint +{ + return [self addConstraint:constraint priority:PRIORITY_DEFAULT]; +} + +- (NSArray*)addConstraint:(NSString*)constraint priority:(UILayoutPriority)priority +{ + NSArray *constraints = [self parseConstraint:constraint]; + + if (priority != PRIORITY_DEFAULT) { + for (NSLayoutConstraint* c in constraints) { + c.priority = priority; + } + } + + [self.view addConstraints:constraints]; + + return constraints; +} + +- (NSArray*)parseConstraint:(NSString *)constraint +{ + if ([constraint hasPrefix:XT_CONSTRAINT_SYMBOL]) + { + return [self parseXtConstraint:constraint]; + + } else { + // Normal VFL constraint + return [NSLayoutConstraint constraintsWithVisualFormat:constraint + options:(NSLayoutFormatOptions) 0 + metrics:self.metrics + views:self.subViews]; + } +} + +- (void)setConstraints:(NSArray*)constraints forKey:(NSString*)key { + [self setConstraints:constraints priority:PRIORITY_DEFAULT forKey:key]; +} + +- (void)setConstraints:(NSArray*)constraints priority:(UILayoutPriority)priority forKey:(NSString*)key { + + // Remove previously added constraints for that key + + NSArray* constraintsToRemove = self.temporalConstraints[key]; + + if (constraintsToRemove.count > 0) { + [self.view removeConstraints:constraintsToRemove]; + [self.temporalConstraints removeObjectForKey:key]; + } + + // Add new constraints and store them + + NSMutableArray* addedConstraints = [[NSMutableArray alloc] init]; + + for (NSString* constraint in constraints) { + NSArray* resultingConstraints = [self addConstraint:constraint priority:priority]; + [addedConstraints addObjectsFromArray:resultingConstraints]; + } + + self.temporalConstraints[key] = addedConstraints; +} + + +#pragma mark - xt constaints + ++ (void)initializeXtConstraintLogic +{ + [self initializeXtConstraintRegex]; + + attributes = @{ + @"left" : @(NSLayoutAttributeLeft), + @"right" : @(NSLayoutAttributeRight), + @"top" : @(NSLayoutAttributeTop), + @"bottom" : @(NSLayoutAttributeBottom), + @"leading" : @(NSLayoutAttributeLeading), + @"trailing" : @(NSLayoutAttributeTrailing), + @"width" : @(NSLayoutAttributeWidth), + @"height" : @(NSLayoutAttributeHeight), + @"centerX" : @(NSLayoutAttributeCenterX), + @"centerY" : @(NSLayoutAttributeCenterY), + @"baseline" : @(NSLayoutAttributeBaseline) + }; + + relations = @{ + @"==" : @(NSLayoutRelationEqual), + @">=" : @(NSLayoutRelationGreaterThanOrEqual), + @"<=" : @(NSLayoutRelationLessThanOrEqual) + }; +} + ++ (void)initializeXtConstraintRegex +{ + NSError* error = nil; + // C identifier + NSString* identifier = @"[_a-zA-Z][_a-zA-Z0-9]{0,30}"; + // VIEW_KEY.ATTR or (use "superview" as VIEW_KEY to refer to superview) + NSString* attr = [NSString stringWithFormat:@"(%@)\\.(%@)", identifier, identifier]; + // Relations taken from NSLayoutRelation + NSString* relation = @"([=><]+)"; + // float number e.g. "12", "12.", "2.56" + NSString* number = @"\\d+\\.?\\d*"; + // Value (indentifier or number) + NSString* value = [NSString stringWithFormat:@"(?:(?:%@)|(?:%@))", identifier, number]; + // e.g. "*5" or "/ 27.3" or "* 200" + NSString* multiplier = [NSString stringWithFormat:@"([*/]) *(%@)", value]; + // e.g. "+ 2." or "- 56" or "-7.5" + NSString* constant = [NSString stringWithFormat:@"([+-]) *(%@)", value]; + + NSString* pattern = [NSString stringWithFormat:@"^%@: *%@ *%@ *%@ *(?:%@)? *(?:%@)?$", + XT_CONSTRAINT_SYMBOL, attr, relation, attr, multiplier, constant]; + + xtConstraintRegex = [NSRegularExpression + regularExpressionWithPattern:pattern + options:NSRegularExpressionCaseInsensitive + error:&error]; +} + +- (NSArray*)parseXtConstraint:(NSString*)constraint +{ + NSArray* results = [xtConstraintRegex matchesInString:constraint + options:0 + range:NSMakeRange(0, constraint.length)]; + + if (results.count != 1) { + [self throwInvalidConstraint:constraint]; + } + + NSTextCheckingResult* match = results[0]; + + // I think this won't happen if the regex is right, but check for debugging + if (match.numberOfRanges != 10) { + [self dumpMatch:match forString:constraint]; + [self throwInvalidConstraint:constraint]; + } + + // item1.attr1 relation item2.attr2 factor constant + // e.g. v1.leading == v2.centerX / 2 + 10 + NSString* item1Key = [constraint substringWithRange:[match rangeAtIndex:1]]; + NSString* attr1Str = [constraint substringWithRange:[match rangeAtIndex:2]]; + NSString* relationStr = [constraint substringWithRange:[match rangeAtIndex:3]]; + NSString* item2Key = [constraint substringWithRange:[match rangeAtIndex:4]]; + NSString* attr2Str = [constraint substringWithRange:[match rangeAtIndex:5]]; + + id item1 = [self findViewFromKey:item1Key]; + id item2 = [self findViewFromKey:item2Key]; + + NSLayoutAttribute attr1 = [self parseAttribute:attr1Str]; + NSLayoutAttribute attr2 = [self parseAttribute:attr2Str]; + + NSLayoutRelation relation = [self parseRelation:relationStr]; + + // Default multiplier is 1 + CGFloat multiplier = 1; + if ([match rangeAtIndex:6].location != NSNotFound) + { + NSString* operation = [constraint substringWithRange:[match rangeAtIndex:6]]; + NSString* multiplierValue = [constraint substringWithRange:[match rangeAtIndex:7]]; + multiplier = [self getFloatFromValue:multiplierValue]; + // If division, invert factor + if ([operation isEqualToString:@"/"]) { + multiplier = 1/multiplier; + } + } + + // Default constant is 0 + CGFloat constant = 0; + if ([match rangeAtIndex:8].location != NSNotFound) + { + NSString* operation = [constraint substringWithRange:[match rangeAtIndex:8]]; + NSString* constantValue = [constraint substringWithRange:[match rangeAtIndex:9]]; + constant = [self getFloatFromValue:constantValue]; + // If subtraction, negate constant + if ([operation isEqualToString:@"-"]) { + constant = -constant; + } + } + + NSLayoutConstraint* c = + [NSLayoutConstraint constraintWithItem:item1 + attribute:attr1 + relatedBy:relation + toItem:item2 + attribute:attr2 + multiplier:multiplier + constant:constant]; + + return @[c]; +} + +- (float)getFloatFromValue:(NSString*)value +{ + if ([self stringStartsWithAlphaOrUnderscore:value]) { // if so, must be a metric identifier + NSNumber* metric = self.metrics[value]; + if (metric) { + return [metric floatValue]; + } else { + NSString* reason = [NSString stringWithFormat:@"Metric `%@` was not provided", value]; + @throw([NSException exceptionWithName:XT_CONSTRAINT_ERROR reason:reason userInfo:nil]); + } + } else { + return [value floatValue]; + } +} + +- (BOOL)stringStartsWithAlphaOrUnderscore:(NSString*)value +{ + unichar c = [value characterAtIndex:0]; + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; +} + +- (id)findViewFromKey:(NSString*)key +{ + if ([key isEqualToString:@"superview"]) { + return self.view; + } else { + id view = self.subViews[key]; + if (view) { + return view; + } else { + NSString* reason = [NSString stringWithFormat:@"No view was added with key `%@`", key]; + @throw([NSException exceptionWithName:XT_CONSTRAINT_ERROR reason:reason userInfo:nil]); + } + } +} + +- (NSLayoutAttribute)parseAttribute:(NSString*)attrStr +{ + NSNumber* value = attributes[attrStr]; + + if (value) { + return (NSLayoutAttribute) [value intValue]; + } else { + NSString* reason = [NSString stringWithFormat:@"Attribute `%@` is not valid. Use one of: %@", attrStr, [attributes allKeys]]; + @throw([NSException exceptionWithName:XT_CONSTRAINT_ERROR reason:reason userInfo:nil]); + } +} + +- (NSLayoutRelation)parseRelation:(NSString*)relationStr +{ + NSNumber* value = relations[relationStr]; + + if (value) { + return (NSLayoutRelation) [value intValue]; + } else { + // This won't happen since the regex only matches if the relation is right + NSString* reason = [NSString stringWithFormat:@"Relation `%@` is not valid. Use one of: %@", relationStr, [relations allKeys]]; + @throw([NSException exceptionWithName:XT_CONSTRAINT_ERROR reason:reason userInfo:nil]); + } +} + +- (void)throwInvalidConstraint:(NSString*)constraint +{ + [NSException raise:XT_CONSTRAINT_ERROR + format:@"%@: %@", XT_CONSTRAINT_ERROR, constraint]; +} + +-(void)dumpMatch:(NSTextCheckingResult*)match forString:(NSString*)str +{ + for (NSUInteger i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackTX/Base.lproj/LaunchScreen.xib b/HackTX/Base.lproj/LaunchScreen.xib deleted file mode 100644 index d54c321..0000000 --- a/HackTX/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/HackTX/Base.lproj/Main.storyboard b/HackTX/Base.lproj/Main.storyboard index a739b64..b56c13e 100644 --- a/HackTX/Base.lproj/Main.storyboard +++ b/HackTX/Base.lproj/Main.storyboard @@ -1,975 +1,8 @@ - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/HackTX/CheckInViewController.h b/HackTX/CheckInViewController.h new file mode 100644 index 0000000..7e3e2ef --- /dev/null +++ b/HackTX/CheckInViewController.h @@ -0,0 +1,20 @@ +// +// CheckInViewController.h +// HackTX +// +// Created by Jose Bethancourt on 10/15/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface CheckInViewController : UIViewController + +@property (weak, nonatomic) IBOutlet UITextField *emailInput; +@property (weak, nonatomic) IBOutlet UILabel *message; +@property (weak, nonatomic) IBOutlet UILabel *header; + +@property (weak, nonatomic) IBOutlet UIButton *addToWallet; +@property (nonatomic, weak) IBOutlet UIImageView *qrCodeImageView; + +@end diff --git a/HackTX/CheckInViewController.m b/HackTX/CheckInViewController.m new file mode 100644 index 0000000..07134d3 --- /dev/null +++ b/HackTX/CheckInViewController.m @@ -0,0 +1,229 @@ +// +// CheckInViewController.m +// HackTX +// +// Created by Jose Bethancourt on 10/15/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "CheckInViewController.h" +#import "UIColor+Palette.h" +#import "AutolayoutHelper.h" +#import "SVProgressHUD.h" +#import "FCAlertView.h" +#import "Hacker.h" +#import "HTXAPI.h" + +#import + + +@import PassKit; + +@interface CheckInViewController () + +@property (nonatomic, retain) CAGradientLayer *gradient; +@property (nonatomic, retain) NSString *hackerEmail; +@property (nonatomic, retain) Hacker *currentHacker; +@property (nonatomic, retain) UITapGestureRecognizer *tap; + +@end + +@implementation CheckInViewController + +- (void)viewDidLoad { + + [super viewDidLoad]; + + [self.navigationController.navigationBar setShadowImage:[[UIImage alloc] init]]; + [self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault]; + + self.emailInput.delegate = self; + [self setupView]; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self dismissKeyboard]; + [SVProgressHUD show]; + self.hackerEmail = textField.text; + RLMResults *hackers = [Hacker allObjects]; + +// [HTXAPI fetchHacker:self.hackerEmail withCompletion:^(NSDictionary *response) { +// if ([response[@"data"][@"email"] isEqualToString:self.hackerEmail]) { + + [Answers logCustomEventWithName:@"Checked In" customAttributes:@{}]; + + Hacker *newHacker = [[Hacker alloc] init]; +// newHacker.name = response[@"data"][@"name"]; + newHacker.email = self.hackerEmail; +// newHacker.school = response[@"data"][@"school"]; +// + RLMRealm *realm = [RLMRealm defaultRealm]; + [realm beginWriteTransaction]; + [realm deleteObjects:hackers]; + [realm commitWriteTransaction]; + + if(![self.hackerEmail isEqual: @""]){ + [realm beginWriteTransaction]; + [realm addObject:newHacker]; + [realm commitWriteTransaction]; + } + + [SVProgressHUD dismiss]; + + [self updateView]; +// } else if (![response[@"response"] boolValue]) { +// +// [SVProgressHUD dismiss]; +// +// FCAlertView *alert = [[FCAlertView alloc] init]; +// +// [alert showAlertInView:self +// withTitle:@"Sorry" +// withSubtitle:@"We could not find your email. If you believe this is an error, ask a friendly volunteer. 🙂" +// withCustomImage:nil +// withDoneButtonTitle:@"Okay" +// andButtons:nil]; +// [alert makeAlertTypeCaution]; + +// } else { +// [SVProgressHUD dismiss]; +// +// FCAlertView *alert = [[FCAlertView alloc] init]; +// +// [alert showAlertInView:self +// withTitle:@"Network error" +// withSubtitle:@"There was an error checking you in, please try again later. 😥" +// withCustomImage:nil +// withDoneButtonTitle:@"Okay" +// andButtons:nil]; +// [alert makeAlertTypeCaution]; +// } +// }]; + + return YES; +} + + +- (void)setQRCode:(NSString *)qrValue { + CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; + + [filter setDefaults]; + + NSData *data = [qrValue dataUsingEncoding:NSUTF8StringEncoding]; + [filter setValue:data forKey:@"inputMessage"]; + + CIImage *outputImage = [filter outputImage]; + + CIContext *context = [CIContext contextWithOptions:nil]; + CGImageRef cgImage = [context createCGImage:outputImage + fromRect:[outputImage extent]]; + + UIImage *image = [UIImage imageWithCGImage:cgImage + scale:1. + orientation:UIImageOrientationUp]; + + // Resize without interpolating + UIImage *resized = [self resizeImage:image + withQuality:kCGInterpolationNone + rate:5.0]; + + self.qrCodeImageView.image = resized; + + CGImageRelease(cgImage); +} + +- (UIImage *)resizeImage:(UIImage *)image + withQuality:(CGInterpolationQuality)quality + rate:(CGFloat)rate +{ + UIImage *resized = nil; + CGFloat width = image.size.width * rate; + CGFloat height = image.size.height * rate; + + UIGraphicsBeginImageContext(CGSizeMake(width, height)); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetInterpolationQuality(context, quality); + [image drawInRect:CGRectMake(0, 0, width, height)]; + resized = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return resized; +} + +- (void)updateView { + RLMResults *hackers = [Hacker allObjects]; + + if (hackers.count > 0) { + self.hackerEmail = hackers[0].email; + self.emailInput.text = self.hackerEmail; + [self setQRCode:self.hackerEmail]; + + self.header.text = [NSString stringWithFormat:@"You are all set!"]; + self.message.text = @"Show this QR Code to a volunteer when checking in."; + self.addToWallet.hidden = YES; + self.emailInput.hidden = NO; + self.qrCodeImageView.hidden = NO; + + //[self.addToWallet addTarget:self action:@selector(showPass) forControlEvents:UIControlEventTouchUpInside]; + + [self.view removeGestureRecognizer:self.tap]; + + + } else { + self.header.text = @"Welcome to HackTX!"; + self.message.text = @"Enter the email you used during registration to fetch your ticket."; + + self.message.hidden = NO; + self.emailInput.hidden = NO; + self.addToWallet.hidden = YES; + self.qrCodeImageView.hidden = YES; + + self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)]; + + [self.tap setCancelsTouchesInView:NO]; + [self.view addGestureRecognizer:self.tap]; + + + } +} + +- (void)setupView { + self.gradient = [CAGradientLayer layer]; + self.gradient.frame = self.view.bounds; + self.gradient.colors = [NSArray arrayWithObjects:(id)[UIColor htx_lightBlue].CGColor, (id)[UIColor htx_lighterBlue].CGColor, nil]; + + [self.view.layer insertSublayer:self.gradient atIndex:0]; + + self.message.textColor = [UIColor htx_white]; + self.header.textColor = [UIColor htx_white]; + self.emailInput.tintColor = [UIColor htx_white]; + self.emailInput.textColor = [UIColor htx_white]; + + [self updateView]; +} + +- (void)dismissKeyboard { + [self.emailInput resignFirstResponder]; +} + +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + self.gradient.frame = self.view.bounds; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#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/HackTX/CheckInViewController.swift b/HackTX/CheckInViewController.swift deleted file mode 100644 index 5e511b9..0000000 --- a/HackTX/CheckInViewController.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// CheckInViewController.swift -// HackTX -// -// Created by Drew Romanyk on 9/4/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import AVFoundation -import RSBarcodes_Swift - -class CheckInViewController: UITableViewController { - - var emailField : UITextField? = nil - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.estimatedRowHeight = 500 - tableView.rowHeight = UITableViewAutomaticDimension - } - - override func viewDidAppear(animated: Bool) { - super.viewDidAppear(animated) - tableView.reloadData() - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "CheckIn") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - - override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 1 - } - - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 2 - } - - - override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - var cell = tableView.dequeueReusableCellWithIdentifier("TitleCell", forIndexPath: indexPath) - - switch (indexPath.row) { - case 0: - cell = tableView.dequeueReusableCellWithIdentifier("TitleCell", forIndexPath: indexPath) - let desc = cell.viewWithTag(1001) as! UILabel - desc.text = "" - desc.text = "Lets get through the line quick." - desc.preferredMaxLayoutWidth = CGRectGetWidth(desc.frame) - case 1: - if(!UserPrefs.shared().isCheckedIn()){ - cell = tableView.dequeueReusableCellWithIdentifier("AboutCell", forIndexPath: indexPath) - emailField = cell.viewWithTag(1000) as? UITextField - emailField?.keyboardType = UIKeyboardType.EmailAddress - let enterButton = cell.viewWithTag(1001) as! UIButton - enterButton.addTarget(self, action: "enterEmailToQr:", forControlEvents: UIControlEvents.TouchUpInside) - } else { - cell = tableView.dequeueReusableCellWithIdentifier("CheckInCell", forIndexPath: indexPath) - - // Setup email text - let desc = cell.viewWithTag(1000) as! UILabel - let emailLabel = cell.viewWithTag(1001) as! UILabel - let emailPrefix = "Your email is " - let email = "\(UserPrefs.shared().getCheckedEmail())" - - //bold the email - let attributedString = NSMutableAttributedString(string:emailPrefix) - let attrs = [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)] - let boldString = NSMutableAttributedString(string:email, attributes:attrs) - attributedString.appendAttributedString(boldString) - - - emailLabel.attributedText = attributedString - desc.preferredMaxLayoutWidth = CGRectGetWidth(desc.frame) - emailLabel.preferredMaxLayoutWidth = CGRectGetWidth(emailLabel.frame) - - // Setup QR Image - let qrImageView = cell.viewWithTag(1002) as! UIImageView - // RSAbstractCodeGenerator.generateCode(contents, filterName: RSAbstractCodeGenerator.filterName(machineReadableCodeObjectType)) - //let qrr = RSUnifiedCodeGenerator().generateCode(UserPrefs.shared().getCheckedEmail(), machineReadableCodeObjectType: AVMetadataObjectTypeQRCode) - let qrr = RSAbstractCodeGenerator.generateCode(UserPrefs.shared().getCheckedEmail(), filterName: RSAbstractCodeGenerator.filterName(AVMetadataObjectTypeQRCode)) - let qrScale = RSAbstractCodeGenerator.resizeImage(qrr!, scale: 10) - qrImageView.image = qrScale - - // Setup reset button - let resetButton = cell.viewWithTag(1003) as! UIButton - resetButton.addTarget(self, action: "resetQrAccount:", forControlEvents: UIControlEvents.TouchUpInside) - } - default: - cell = tableView.dequeueReusableCellWithIdentifier("TitleCell", forIndexPath: indexPath) - } - - return cell - } - - func enterEmailToQr(sender: UIButton!) { - if (emailField != nil) { - let emailStr = emailField?.text - if ((emailStr!).characters.count != 0 && isValidEmail(emailStr!)) { - UserPrefs.shared().setIsCheckedIn(true) - UserPrefs.shared().setCheckedEmail(emailStr!) - tableView.reloadData() - - //delete the check in reminder notification - let app = UIApplication.sharedApplication() - let uidOfCheckIn = "check-in-reminder" - for event in app.scheduledLocalNotifications! { - let notification = event as UILocalNotification - let notificationInfo = notification.userInfo! as! [String:String] - let uid = notificationInfo["uid"]! - if uid == uidOfCheckIn { - app.cancelLocalNotification(notification) - break; - } - } - - } else { - let error = UIAlertView() - error.title = "Cannot Validate Email" - error.message = "\(emailStr!) does seem to be a valid email. Perhaps you should try again?" - error.addButtonWithTitle("OK") - error.show() - } - } - } - - func isValidEmail(testStr:String) -> Bool { - // println("validate calendar: \(testStr)") - let emailRegEx = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - - let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx) - return emailTest.evaluateWithObject(testStr) - } - - func resetQrAccount(sender: UIButton!) { - UserPrefs.shared().setIsCheckedIn(false) - UserPrefs.shared().setCheckedEmail("") - - //re-register the check in reminder - UserPrefs.shared().setRegisterForCheckInNotif(false) - let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate - appDelegate.registerCheckInReminder() - tableView.reloadData() - } - -} diff --git a/HackTX/CheckInViewController.xib b/HackTX/CheckInViewController.xib new file mode 100644 index 0000000..b2f9076 --- /dev/null +++ b/HackTX/CheckInViewController.xib @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackTX/Day.swift b/HackTX/Day.swift deleted file mode 100644 index a0cb49e..0000000 --- a/HackTX/Day.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Day.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Day { - var clusterList = [ScheduleCluster]() -} \ No newline at end of file diff --git a/HackTX/Event.h b/HackTX/Event.h new file mode 100644 index 0000000..62fe3dc --- /dev/null +++ b/HackTX/Event.h @@ -0,0 +1,29 @@ +// +// Event.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import +#import "Location.h" +#import "Speaker.h" + +@interface Event : RLMObject + +@property NSString *serverID; +@property NSString *name; +@property NSString *desc; +@property NSString *imageURL; +// Swith this to NSDate +@property NSDate *startDate; +@property NSDate *endDate; +@property Location *location; +@property RLMArray *speakers; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Event) diff --git a/HackTX/Event.m b/HackTX/Event.m new file mode 100644 index 0000000..cc6d3d1 --- /dev/null +++ b/HackTX/Event.m @@ -0,0 +1,31 @@ +// +// Event.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Event.h" + +@implementation Event + ++ (NSString *)primaryKey { + return @"serverID"; +} + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Event.swift b/HackTX/Event.swift deleted file mode 100644 index 4417816..0000000 --- a/HackTX/Event.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Event.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Event { - let dateFormatStr = "yyyy-MM-dd HH:mm:ss" - - var id : Int? = 0 - var name: String? = "" - var type: String? = "" - var imageUrl: String? = "" - var startDate: NSDate? = NSDate() - var endDate: NSDate? = NSDate() - var startDateStr: String? = "" - var endDateStr: String? = "" - var location: Location? - var description: String? = "" - var speakerList: [Speaker]? - - func convertDateStrToDates() { - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = dateFormatStr - - self.startDate = dateFormatter.dateFromString(startDateStr!) - self.endDate = dateFormatter.dateFromString(endDateStr!) - } -} \ No newline at end of file diff --git a/HackTX/EventFeedbackViewController.swift b/HackTX/EventFeedbackViewController.swift deleted file mode 100644 index 4f5e1eb..0000000 --- a/HackTX/EventFeedbackViewController.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// EventFeedbackViewController.swift -// HackTX -// -// Created by Drew Romanyk on 9/8/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import Alamofire - -class EventFeedbackViewController: UIViewController { - - @IBAction func cancelButton(sender: UIBarButtonItem) { - - dismissViewControllerAnimated(true, completion: nil) - } - @IBOutlet weak var star1: UIButton! - @IBOutlet weak var star2: UIButton! - @IBOutlet weak var star3: UIButton! - @IBOutlet weak var star4: UIButton! - @IBOutlet weak var star5: UIButton! - - @IBOutlet weak var submit: UIButton! - - var ratingScore = 5 - var scheduleEvent = Event() - - override func viewDidLoad() { - super.viewDidLoad() - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - - @IBAction func star1Clicked(sender: UIButton) { - ratingScore = 1 - star2.imageView?.image = UIImage(named: "partners") - star3.imageView?.image = UIImage(named: "partners") - star4.imageView?.image = UIImage(named: "partners") - star5.imageView?.image = UIImage(named: "partners") - } - @IBAction func star2Clicked(sender: UIButton) { - ratingScore = 2 - star2.imageView?.image = UIImage(named: "partners-filled") - star3.imageView?.image = UIImage(named: "partners") - star4.imageView?.image = UIImage(named: "partners") - star5.imageView?.image = UIImage(named: "partners") - } - @IBAction func star3Clicked(sender: UIButton) { - ratingScore = 3 - star2.imageView?.image = UIImage(named: "partners-filled") - star3.imageView?.image = UIImage(named: "partners-filled") - star4.imageView?.image = UIImage(named: "partners") - star5.imageView?.image = UIImage(named: "partners") - } - @IBAction func star4Clicked(sender: UIButton) { - ratingScore = 4 - star2.imageView?.image = UIImage(named: "partners-filled") - star3.imageView?.image = UIImage(named: "partners-filled") - star4.imageView?.image = UIImage(named: "partners-filled") - star5.imageView?.image = UIImage(named: "partners") - } - @IBAction func star5Clicked(sender: UIButton) { - ratingScore = 5 - star2.imageView?.image = UIImage(named: "partners-filled") - star3.imageView?.image = UIImage(named: "partners-filled") - star4.imageView?.image = UIImage(named: "partners-filled") - star5.imageView?.image = UIImage(named: "partners-filled") - } - - @IBAction func submitClicked(sender: UIButton) { - let parameters = ["id": scheduleEvent.id!, - "rating": ratingScore] - - Alamofire.request(Router.Feedback((parameters))) - .responseJSON{ (request, response, data) in - if data.isFailure { - let errorAlert = UIAlertView() - if errorAlert.title == "" { - errorAlert.title = "Error" - errorAlert.message = "Oops! Looks like there was a problem trying to send your feedback." - errorAlert.addButtonWithTitle("Ok") - errorAlert.show() - } - } else if let _: AnyObject = data.value { - UserPrefs.shared().setFeedbackEventDone(self.scheduleEvent.id!) - self.dismissViewControllerAnimated(true, completion: nil) - } - } - } -} \ No newline at end of file diff --git a/HackTX/GoogleService-Info.plist b/HackTX/GoogleService-Info.plist deleted file mode 100644 index 3765d4c..0000000 --- a/HackTX/GoogleService-Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - TRACKING_ID - UA-65953016-4 - PLIST_VERSION - 1 - BUNDLE_ID - com.HackTX.HackTX - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - - \ No newline at end of file diff --git a/HackTX/HTXAPI.h b/HackTX/HTXAPI.h new file mode 100644 index 0000000..fffc9a9 --- /dev/null +++ b/HackTX/HTXAPI.h @@ -0,0 +1,24 @@ +// +// HTXAPI.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface HTXAPI : NSObject + ++ (void)fetchPass:(NSString *)email withPassData:(void(^)(NSData *data))passData; ++ (void)fetchHacker:(NSString *)email withCompletion:(void(^)(NSDictionary *data))completion; ++ (void)refreshAnnouncements:(void(^)(BOOL success))completion; ++ (void)refreshEvents:(void(^)(BOOL success))completion; ++ (void)refreshSponsors:(void(^)(BOOL success))completion; + ++ (void)sendRequest:(NSDictionary *)request + toEndpoint:(NSString *)endpoint + withType:(NSString *)type + withCompletion:(void(^)(NSDictionary *response))completion; + +@end diff --git a/HackTX/HTXAPI.m b/HackTX/HTXAPI.m new file mode 100644 index 0000000..814ff98 --- /dev/null +++ b/HackTX/HTXAPI.m @@ -0,0 +1,210 @@ +// +// HTXAPI.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "HTXAPI.h" +#import "HTXConstants.h" +#import "AFNetworking.h" +#import "NSString+MD5.h" +#import "Announcement.h" +#import "Sponsor.h" +#import "Event.h" + + +@implementation HTXAPI + +- (id)init { + self = [super init]; + if (self) { + + } + return self; +} + ++ (void)fetchPass:(NSString *)email withPassData:(void(^)(NSData *data))passData { + return [[[HTXAPI alloc] init] fetchPass:email withPassData:passData]; +} + ++ (void)fetchHacker:(NSString *)email withCompletion:(void(^)(NSDictionary *data))completion { + return [[[HTXAPI alloc] init] fetchHacker:email withCompletion:completion]; +} + ++ (void)refreshAnnouncements:(void(^)(BOOL success))completion { + return [[[HTXAPI alloc] init] refreshAnnouncements:completion]; +} + ++ (void)refreshEvents:(void(^)(BOOL success))completion { + return [[[HTXAPI alloc] init] refreshEvents:completion]; +} + ++ (void)refreshSponsors:(void(^)(BOOL success))completion { + return [[[HTXAPI alloc] init] refreshSponsors:completion]; +} + ++ (void)sendRequest:(NSDictionary *)request + toEndpoint:(NSString *)endpoint + withType:(NSString *)type + withCompletion:(void(^)(NSDictionary *response))completion { + return [[[HTXAPI alloc] init] sendRequest:request toEndpoint:endpoint withType:type withCompletion:completion]; +} + +- (void)fetchPass:(NSString *)email + withPassData:(void(^)(NSData *data))passData { + + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + + [manager GET:@"https://hacktx.joseb.me/pass" parameters:@{@"email": email} progress:nil success:^(NSURLSessionTask *task, NSData *responseObject) { + passData(responseObject); + } failure:^(NSURLSessionTask *operation, NSError *error) { + NSLog(@"%% %@", error); + }]; + +} + +- (void)fetchHacker:(NSString *)email + withCompletion:(void(^)(NSDictionary *data))completion { + + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.responseSerializer = [AFJSONResponseSerializer serializer]; + + [manager GET:@"https://hacktx.joseb.me/checkin" parameters:@{@"email": email} progress:nil success:^(NSURLSessionTask *task, NSData *responseObject) { + completion(@{@"success": @YES, @"data": responseObject}); + } failure:^(NSURLSessionTask *operation, NSError *error) { + completion(@{@"success": @NO, @"error": error}); + }]; + +} + +- (void)refreshAnnouncements:(void(^)(BOOL success))completion { + [self sendRequest:nil toEndpoint:@"announcements" withType:@"GET" withCompletion:^(NSDictionary *response) { + + RLMRealm *realm = [RLMRealm defaultRealm]; + NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; + [dateFormat setDateFormat:@"yyyy-MM-dd kk:mm:ss"]; + + if ([response[@"success"] boolValue]) { + for (id object in response[@"data"]) { + Announcement *newAnnouncement = [[Announcement alloc] init]; + + newAnnouncement.serverID = [NSString stringWithFormat:@"%@%@", [object[@"text"] MD5], [object[@"ts"] MD5]]; + newAnnouncement.text = object[@"text"]; + newAnnouncement.timestamp = [dateFormat dateFromString:object[@"ts"]]; + + [realm beginWriteTransaction]; + [realm addOrUpdateObject:newAnnouncement]; + [realm commitWriteTransaction]; + } + completion(YES); + } else { + completion(NO); + } + }]; +} + +- (void)refreshSponsors:(void(^)(BOOL success))completion { + + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.responseSerializer = [AFJSONResponseSerializer serializer]; + + [manager GET:@"https://hacktx.joseb.me/sponsors" parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) { + + RLMRealm *realm = [RLMRealm defaultRealm]; + + for (id object in responseObject) { + Sponsor *newSponsor = [[Sponsor alloc] init]; + + newSponsor.serverID = [NSString stringWithFormat:@"%@%@", [object[@"website"] MD5], [object[@"name"] MD5]]; + newSponsor.name = object[@"name"]; + newSponsor.logoImage = object[@"logoImage"]; + newSponsor.website = object[@"website"]; + newSponsor.level = [object[@"level"] integerValue]; + + [realm beginWriteTransaction]; + [realm addOrUpdateObject:newSponsor]; + [realm commitWriteTransaction]; + } + completion(YES); + + } failure:^(NSURLSessionTask *operation, NSError *error) { + completion(NO); + }]; +} + +- (void)refreshEvents:(void(^)(BOOL success))completion { + [self sendRequest:nil toEndpoint:@"schedule" withType:@"GET" withCompletion:^(NSDictionary *response) { + + RLMRealm *realm = [RLMRealm defaultRealm]; + NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; + [dateFormat setDateFormat:@"yyyy-MM-dd kk:mm:ss"]; + + if ([response[@"success"] boolValue]) { + for (id object in response[@"data"]) { + + for (id eventObject in object[@"eventsList"]) { + Event *newEvent = [[Event alloc] init]; + newEvent.serverID = [eventObject[@"id"] stringValue]; + newEvent.name = eventObject[@"name"]; + newEvent.desc = eventObject[@"description"]; + newEvent.imageURL = eventObject[@"imageUrl"]; + newEvent.startDate = [dateFormat dateFromString:eventObject[@"startDate"]]; + newEvent.endDate = [dateFormat dateFromString:eventObject[@"endDate"]]; + + Location *newLocation = [[Location alloc] init]; + newLocation.building = eventObject[@"location"][@"building"]; + newLocation.level = eventObject[@"location"][@"level"]; + newLocation.room = eventObject[@"location"][@"room"]; + + newEvent.location = newLocation; + + [realm beginWriteTransaction]; + [Event createOrUpdateInRealm:realm withValue:newEvent]; + [realm commitWriteTransaction]; + } + } + completion(YES); + } else { + completion(NO); + } + + }]; +} + +- (void)sendRequest:(NSDictionary *)request + toEndpoint:(NSString *)endpoint + withType:(NSString *)type + withCompletion:(void(^)(NSDictionary *response))completion { + + NSString *requestURL = [kHTXBaseURL stringByAppendingString:endpoint]; + + if ([type isEqualToString:@"GET"]) { + + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.requestSerializer = [AFJSONRequestSerializer serializer]; + + [manager GET:requestURL parameters:request progress:nil success:^(NSURLSessionTask *task, id responseObject) { + completion(@{@"success": @YES, @"data": responseObject}); + } failure:^(NSURLSessionTask *operation, NSError *error) { + completion(@{@"success": @NO, @"error": error}); + }]; + + } else if ([type isEqualToString:@"POST"]) { + + AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; + manager.requestSerializer = [AFJSONRequestSerializer serializer]; + + [manager POST:requestURL parameters:request progress:nil success:^(NSURLSessionDataTask *task, id responseObject) { + completion(@{@"success": @YES, @"data": responseObject}); + } failure:^(NSURLSessionDataTask *task, NSError *error) { + completion(@{@"success": @NO, @"error": error}); + }]; + + } + +} + +@end diff --git a/HackTX/HTXAPIKeyStore.h b/HackTX/HTXAPIKeyStore.h new file mode 100644 index 0000000..a84f51c --- /dev/null +++ b/HackTX/HTXAPIKeyStore.h @@ -0,0 +1,17 @@ +// +// HTXAPIKeyStore.h +// HackTX +// +// Created by Jose Bethancourt on 10/12/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface HTXAPIKeyStore : NSObject + ++ (instancetype)sharedHTXAPIKeyStore; + +- (NSString *)getGMSKey; + +@end diff --git a/HackTX/HTXAPIKeyStore.m b/HackTX/HTXAPIKeyStore.m new file mode 100644 index 0000000..c3b17ff --- /dev/null +++ b/HackTX/HTXAPIKeyStore.m @@ -0,0 +1,42 @@ +// +// HTXAPIKeyStore.m +// HackTX +// +// Created by Jose Bethancourt on 10/12/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "HTXAPIKeyStore.h" + +@interface HTXAPIKeyStore() + +@property (nonatomic, strong) NSDictionary *apiKeys; + +@end + +@implementation HTXAPIKeyStore + ++ (instancetype)sharedHTXAPIKeyStore { + static dispatch_once_t once; + static HTXAPIKeyStore *_sharedInstance; + dispatch_once(&once, ^{ + _sharedInstance = [[self alloc] init]; + }); + + return _sharedInstance; +} + +- (instancetype)init { + self = [super init]; + + NSString *filePath = [[NSBundle mainBundle] pathForResource:@"APIKeys" ofType: @"plist"]; + self.apiKeys = [NSDictionary dictionaryWithContentsOfFile:filePath]; + + return self; +} + +- (NSString *)getGMSKey { + return self.apiKeys[@"gms_key"]; +} + +@end diff --git a/HackTX/HTXConstants.h b/HackTX/HTXConstants.h new file mode 100644 index 0000000..eddefbf --- /dev/null +++ b/HackTX/HTXConstants.h @@ -0,0 +1,14 @@ +// +// HTXConstants.h +// HackTX +// +// Created by Jose Bethancourt on 9/7/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +FOUNDATION_EXPORT NSString *const kMLHAuthURL; +FOUNDATION_EXPORT NSString *const kMLHClientId; +FOUNDATION_EXPORT NSString *const kMLHAuthRedirect; +FOUNDATION_EXPORT NSString *const kHTXBaseURL; diff --git a/HackTX/HTXConstants.m b/HackTX/HTXConstants.m new file mode 100644 index 0000000..3fa0d3b --- /dev/null +++ b/HackTX/HTXConstants.m @@ -0,0 +1,16 @@ +// +// HTXConstants.m +// HackTX +// +// Created by Jose Bethancourt on 9/7/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "HTXConstants.h" + +NSString *const kMLHAuthURL = @"https://my.mlh.io/oauth/authorize?client_id=%@&redirect_uri=%@&response_type=code"; +NSString *const kMLHClientId = @"52d076854f7bec4ef067bd54316fd4580509149f8e71a35bf5898bec59497916"; +NSString *const kMLHAuthRedirect = @"https://hacktx.com/test"; +//NSString *const kHTXBaseURL = @"https://my.hacktx.com/api/"; +NSString *const kHTXBaseURL = @"https://pepper-prod.herokuapp.com/api/"; +NSString *const kHTXWalletURL = @"https://hacktx.joseb.me/"; diff --git a/HackTX/HackTX.entitlements b/HackTX/HackTX.entitlements new file mode 100644 index 0000000..4fd55be --- /dev/null +++ b/HackTX/HackTX.entitlements @@ -0,0 +1,12 @@ + + + + + aps-environment + development + com.apple.developer.pass-type-identifiers + + $(TeamIdentifierPrefix)* + + + diff --git a/HackTX/Hacker.h b/HackTX/Hacker.h new file mode 100644 index 0000000..d04b713 --- /dev/null +++ b/HackTX/Hacker.h @@ -0,0 +1,21 @@ +// +// Hacker.h +// HackTX +// +// Created by Jose Bethancourt on 10/15/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface Hacker : RLMObject + +@property NSString *name; +@property NSString *email; +@property NSString *school; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Hacker) diff --git a/HackTX/Hacker.m b/HackTX/Hacker.m new file mode 100644 index 0000000..39aea8f --- /dev/null +++ b/HackTX/Hacker.m @@ -0,0 +1,27 @@ +// +// Hacker.m +// HackTX +// +// Created by Jose Bethancourt on 10/15/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Hacker.h" + +@implementation Hacker + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Contents.json b/HackTX/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 72875c4..0000000 --- a/HackTX/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "images" : [ - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-Small@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-Small@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-60@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-Small.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-Small@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-40.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-76.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-76@2x.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40.png deleted file mode 100644 index d2f8385..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png deleted file mode 100644 index 99ed8d7..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png deleted file mode 100644 index 4574c57..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png deleted file mode 100644 index 4574c57..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png deleted file mode 100644 index b42e55f..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76.png deleted file mode 100644 index 68ea69a..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png deleted file mode 100644 index d6abdd2..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small.png deleted file mode 100644 index 8b83078..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png deleted file mode 100644 index dcbf043..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png deleted file mode 100644 index 3df37e3..0000000 Binary files a/HackTX/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/LaunchScreen.imageset/Contents.json b/HackTX/Images.xcassets/LaunchScreen.imageset/Contents.json deleted file mode 100644 index 75626bb..0000000 --- a/HackTX/Images.xcassets/LaunchScreen.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal" - }, - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Default-667h@2x.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/LaunchScreen.imageset/Default-667h@2x.png b/HackTX/Images.xcassets/LaunchScreen.imageset/Default-667h@2x.png deleted file mode 100644 index 19c9736..0000000 Binary files a/HackTX/Images.xcassets/LaunchScreen.imageset/Default-667h@2x.png and /dev/null differ diff --git a/HackTX/Images.xcassets/Maps/cla1.imageset/Contents.json b/HackTX/Images.xcassets/Maps/cla1.imageset/Contents.json deleted file mode 100644 index 8945f00..0000000 --- a/HackTX/Images.xcassets/Maps/cla1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "cla_first_level.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/Maps/cla1.imageset/cla_first_level.png b/HackTX/Images.xcassets/Maps/cla1.imageset/cla_first_level.png deleted file mode 100644 index 657125a..0000000 Binary files a/HackTX/Images.xcassets/Maps/cla1.imageset/cla_first_level.png and /dev/null differ diff --git a/HackTX/Images.xcassets/Maps/sac1.imageset/Contents.json b/HackTX/Images.xcassets/Maps/sac1.imageset/Contents.json deleted file mode 100644 index c3a7de0..0000000 --- a/HackTX/Images.xcassets/Maps/sac1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "SAC-map-first-level rotated-1.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/Maps/sac1.imageset/SAC-map-first-level rotated-1.png b/HackTX/Images.xcassets/Maps/sac1.imageset/SAC-map-first-level rotated-1.png deleted file mode 100644 index 8f06517..0000000 Binary files a/HackTX/Images.xcassets/Maps/sac1.imageset/SAC-map-first-level rotated-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/Maps/sac2.imageset/Contents.json b/HackTX/Images.xcassets/Maps/sac2.imageset/Contents.json deleted file mode 100644 index 29cbb3a..0000000 --- a/HackTX/Images.xcassets/Maps/sac2.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "sac_second_level.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/Maps/sac2.imageset/sac_second_level.png b/HackTX/Images.xcassets/Maps/sac2.imageset/sac_second_level.png deleted file mode 100644 index 692116f..0000000 Binary files a/HackTX/Images.xcassets/Maps/sac2.imageset/sac_second_level.png and /dev/null differ diff --git a/HackTX/Images.xcassets/Maps/sac3.imageset/Contents.json b/HackTX/Images.xcassets/Maps/sac3.imageset/Contents.json deleted file mode 100644 index 677948e..0000000 --- a/HackTX/Images.xcassets/Maps/sac3.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "sac_third_level.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/Maps/sac3.imageset/sac_third_level.png b/HackTX/Images.xcassets/Maps/sac3.imageset/sac_third_level.png deleted file mode 100644 index 725c122..0000000 Binary files a/HackTX/Images.xcassets/Maps/sac3.imageset/sac_third_level.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements-filled.imageset/Contents.json b/HackTX/Images.xcassets/announcements-filled.imageset/Contents.json deleted file mode 100644 index cf637b4..0000000 --- a/HackTX/Images.xcassets/announcements-filled.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Megaphone Filled-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Megaphone Filled-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Megaphone Filled-64-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-1.png b/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-1.png deleted file mode 100644 index 5583af6..0000000 Binary files a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-2.png b/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-2.png deleted file mode 100644 index 5583af6..0000000 Binary files a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2.png b/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2.png deleted file mode 100644 index 0cad10c..0000000 Binary files a/HackTX/Images.xcassets/announcements-filled.imageset/Megaphone Filled-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements.imageset/Contents.json b/HackTX/Images.xcassets/announcements.imageset/Contents.json deleted file mode 100644 index 52ac82e..0000000 --- a/HackTX/Images.xcassets/announcements.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Megaphone-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Megaphone-64-2.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Megaphone-64-3.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2-1.png b/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2-1.png deleted file mode 100644 index 84ee11e..0000000 Binary files a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2.png b/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2.png deleted file mode 100644 index 84ee11e..0000000 Binary files a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-3.png b/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-3.png deleted file mode 100644 index df72588..0000000 Binary files a/HackTX/Images.xcassets/announcements.imageset/Megaphone-64-3.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-1.png b/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-1.png deleted file mode 100644 index fbfef73..0000000 Binary files a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-2.png b/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-2.png deleted file mode 100644 index fbfef73..0000000 Binary files a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2.png b/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2.png deleted file mode 100644 index fbfef73..0000000 Binary files a/HackTX/Images.xcassets/event_bus.imageset/Bus Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_bus.imageset/Contents.json b/HackTX/Images.xcassets/event_bus.imageset/Contents.json deleted file mode 100644 index 293cd7a..0000000 --- a/HackTX/Images.xcassets/event_bus.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Bus Filled-100-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Bus Filled-100-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Bus Filled-100-2-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-1.png b/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-1.png deleted file mode 100644 index 3d654e9..0000000 Binary files a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-2.png b/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-2.png deleted file mode 100644 index 3d654e9..0000000 Binary files a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100.png b/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100.png deleted file mode 100644 index 3d654e9..0000000 Binary files a/HackTX/Images.xcassets/event_default.imageset/Calendar Filled-100.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_default.imageset/Contents.json b/HackTX/Images.xcassets/event_default.imageset/Contents.json deleted file mode 100644 index 851c108..0000000 --- a/HackTX/Images.xcassets/event_default.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Calendar Filled-100.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Calendar Filled-100-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Calendar Filled-100-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-1.png b/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-1.png deleted file mode 100644 index cc050f7..0000000 Binary files a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-2.png b/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-2.png deleted file mode 100644 index cc050f7..0000000 Binary files a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100.png b/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100.png deleted file mode 100644 index cc050f7..0000000 Binary files a/HackTX/Images.xcassets/event_dev.imageset/Code Filled-100.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_dev.imageset/Contents.json b/HackTX/Images.xcassets/event_dev.imageset/Contents.json deleted file mode 100644 index e9e162a..0000000 --- a/HackTX/Images.xcassets/event_dev.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Code Filled-100.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Code Filled-100-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Code Filled-100-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_education.imageset/Contents.json b/HackTX/Images.xcassets/event_education.imageset/Contents.json deleted file mode 100644 index 5fba58a..0000000 --- a/HackTX/Images.xcassets/event_education.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Literature Filled-100.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Literature Filled-100-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Literature Filled-100-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-1.png b/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-1.png deleted file mode 100644 index 5025428..0000000 Binary files a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-2.png b/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-2.png deleted file mode 100644 index 5025428..0000000 Binary files a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100.png b/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100.png deleted file mode 100644 index 5025428..0000000 Binary files a/HackTX/Images.xcassets/event_education.imageset/Literature Filled-100.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_food.imageset/Contents.json b/HackTX/Images.xcassets/event_food.imageset/Contents.json deleted file mode 100644 index b0f4b04..0000000 --- a/HackTX/Images.xcassets/event_food.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Dining Room Filled-100.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Dining Room Filled-100-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Dining Room Filled-100-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-1.png b/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-1.png deleted file mode 100644 index 071e7a5..0000000 Binary files a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-2.png b/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-2.png deleted file mode 100644 index 071e7a5..0000000 Binary files a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100.png b/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100.png deleted file mode 100644 index 071e7a5..0000000 Binary files a/HackTX/Images.xcassets/event_food.imageset/Dining Room Filled-100.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_talk.imageset/Contents.json b/HackTX/Images.xcassets/event_talk.imageset/Contents.json deleted file mode 100644 index 8e0a14a..0000000 --- a/HackTX/Images.xcassets/event_talk.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Talk Filled-100.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Talk Filled-100-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Talk Filled-100-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-1.png b/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-1.png deleted file mode 100644 index b2c8f50..0000000 Binary files a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-2.png b/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-2.png deleted file mode 100644 index b2c8f50..0000000 Binary files a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100.png b/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100.png deleted file mode 100644 index b2c8f50..0000000 Binary files a/HackTX/Images.xcassets/event_talk.imageset/Talk Filled-100.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map-filled.imageset/Contents.json b/HackTX/Images.xcassets/map-filled.imageset/Contents.json deleted file mode 100644 index f5c19f2..0000000 --- a/HackTX/Images.xcassets/map-filled.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Map Filled-64-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Map Filled-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Map Filled-64-2-1.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-1.png b/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-1.png deleted file mode 100644 index d059faa..0000000 Binary files a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-2.png b/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-2.png deleted file mode 100644 index 537206a..0000000 Binary files a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2.png b/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2.png deleted file mode 100644 index 537206a..0000000 Binary files a/HackTX/Images.xcassets/map-filled.imageset/Map Filled-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map.imageset/Contents.json b/HackTX/Images.xcassets/map.imageset/Contents.json deleted file mode 100644 index c9d8580..0000000 --- a/HackTX/Images.xcassets/map.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Map-64-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Map-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Map-64-2-1.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/map.imageset/Map-64-2-1.png b/HackTX/Images.xcassets/map.imageset/Map-64-2-1.png deleted file mode 100644 index be4e52d..0000000 Binary files a/HackTX/Images.xcassets/map.imageset/Map-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map.imageset/Map-64-2-2.png b/HackTX/Images.xcassets/map.imageset/Map-64-2-2.png deleted file mode 100644 index 98a88f2..0000000 Binary files a/HackTX/Images.xcassets/map.imageset/Map-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/map.imageset/Map-64-2.png b/HackTX/Images.xcassets/map.imageset/Map-64-2.png deleted file mode 100644 index 98a88f2..0000000 Binary files a/HackTX/Images.xcassets/map.imageset/Map-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners-filled.imageset/Contents.json b/HackTX/Images.xcassets/partners-filled.imageset/Contents.json deleted file mode 100644 index edb60fe..0000000 --- a/HackTX/Images.xcassets/partners-filled.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Star Filled-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Star Filled-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Star Filled-64-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-1.png b/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-1.png deleted file mode 100644 index 23212da..0000000 Binary files a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-2.png b/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-2.png deleted file mode 100644 index 23212da..0000000 Binary files a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2.png b/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2.png deleted file mode 100644 index 46220de..0000000 Binary files a/HackTX/Images.xcassets/partners-filled.imageset/Star Filled-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners.imageset/Contents.json b/HackTX/Images.xcassets/partners.imageset/Contents.json deleted file mode 100644 index 8b0e158..0000000 --- a/HackTX/Images.xcassets/partners.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Star-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Star-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Star-64-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/partners.imageset/Star-64-2-1.png b/HackTX/Images.xcassets/partners.imageset/Star-64-2-1.png deleted file mode 100644 index 6b6ef5b..0000000 Binary files a/HackTX/Images.xcassets/partners.imageset/Star-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners.imageset/Star-64-2-2.png b/HackTX/Images.xcassets/partners.imageset/Star-64-2-2.png deleted file mode 100644 index 6b6ef5b..0000000 Binary files a/HackTX/Images.xcassets/partners.imageset/Star-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/partners.imageset/Star-64-2.png b/HackTX/Images.xcassets/partners.imageset/Star-64-2.png deleted file mode 100644 index fa25176..0000000 Binary files a/HackTX/Images.xcassets/partners.imageset/Star-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2-1.png b/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2-1.png deleted file mode 100644 index af7de2d..0000000 Binary files a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2.png b/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2.png deleted file mode 100644 index 4f8bf57..0000000 Binary files a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64.png b/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64.png deleted file mode 100644 index 4f8bf57..0000000 Binary files a/HackTX/Images.xcassets/schedule-filled.imageset/Calendar 26 Filled-64.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule-filled.imageset/Contents.json b/HackTX/Images.xcassets/schedule-filled.imageset/Contents.json deleted file mode 100644 index 6647b1e..0000000 --- a/HackTX/Images.xcassets/schedule-filled.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Calendar 26 Filled-64-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Calendar 26 Filled-64.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Calendar 26 Filled-64-2-1.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-1.png b/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-1.png deleted file mode 100644 index b118796..0000000 Binary files a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-2.png b/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-2.png deleted file mode 100644 index b118796..0000000 Binary files a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64.png b/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64.png deleted file mode 100644 index 4160fd3..0000000 Binary files a/HackTX/Images.xcassets/schedule.imageset/Calendar 26-64.png and /dev/null differ diff --git a/HackTX/Images.xcassets/schedule.imageset/Contents.json b/HackTX/Images.xcassets/schedule.imageset/Contents.json deleted file mode 100644 index fe72b91..0000000 --- a/HackTX/Images.xcassets/schedule.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Calendar 26-64-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Calendar 26-64-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Calendar 26-64.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/twitter-filled.imageset/Contents.json b/HackTX/Images.xcassets/twitter-filled.imageset/Contents.json deleted file mode 100644 index 455361e..0000000 --- a/HackTX/Images.xcassets/twitter-filled.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Twitter Filled-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Twitter Filled-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Twitter Filled-64-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-1.png b/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-1.png deleted file mode 100644 index 95a24ae..0000000 Binary files a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-2.png b/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-2.png deleted file mode 100644 index 95a24ae..0000000 Binary files a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2.png b/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2.png deleted file mode 100644 index 1b01eda..0000000 Binary files a/HackTX/Images.xcassets/twitter-filled.imageset/Twitter Filled-64-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/twitter.imageset/Contents.json b/HackTX/Images.xcassets/twitter.imageset/Contents.json deleted file mode 100644 index 966f5e2..0000000 --- a/HackTX/Images.xcassets/twitter.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Twitter-64-2-2.png" - }, - { - "idiom" : "universal", - "scale" : "2x", - "filename" : "Twitter-64-2-1.png" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "Twitter-64-2.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-1.png b/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-1.png deleted file mode 100644 index ec3ca6e..0000000 Binary files a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-1.png and /dev/null differ diff --git a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-2.png b/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-2.png deleted file mode 100644 index ec3ca6e..0000000 Binary files a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2-2.png and /dev/null differ diff --git a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2.png b/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2.png deleted file mode 100644 index 5b8a643..0000000 Binary files a/HackTX/Images.xcassets/twitter.imageset/Twitter-64-2.png and /dev/null differ diff --git a/HackTX/Info.plist b/HackTX/Info.plist index 4d59634..fd54571 100644 --- a/HackTX/Info.plist +++ b/HackTX/Info.plist @@ -7,58 +7,33 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.HackTX.HackTX + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - HackTX + $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString - 1.1 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLName - com.HackTX.HackTX - CFBundleURLSchemes - - hacktx - - - + 1.1.1 CFBundleVersion - 8 - UIApplicationShortcutItems - - - UIApplicationShortcutItemIconFile - schedule-filled - UIApplicationShortcutItemTitle - Schedule - UIApplicationShortcutItemType - schedule - - - UIApplicationShortcutItemIconFile - announcements-filled - UIApplicationShortcutItemTitle - Announcements - UIApplicationShortcutItemType - announcements - - - UIApplicationShortcutItemIconFile - map-filled - UIApplicationShortcutItemTitle - Maps - UIApplicationShortcutItemType - maps - - - + 1 + Fabric + + APIKey + 867b9bd547cc88317b3ba6c1d23b68632020aca5 + Kits + + + KitInfo + + KitName + Crashlytics + + + + FirebaseAppDelegateProxyEnabled + LSRequiresIPhoneOS NSAppTransportSecurity @@ -66,6 +41,21 @@ NSAllowsArbitraryLoads + NSPhotoLibraryUsageDescription + HackTX or one of it's dependencies needs access to your Photos + UIAppFonts + + JosefinSans-Italic.ttf + JosefinSans-Bold.ttf + JosefinSans-BoldItalic.ttf + JosefinSans-Light.ttf + JosefinSans-LightItalic.ttf + JosefinSans-Regular.ttf + JosefinSans-SemiBold.ttf + JosefinSans-SemiBoldItalic.ttf + JosefinSans-Thin.ttf + JosefinSans-ThinItalic.ttf + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -74,6 +64,8 @@ armv7 + UIStatusBarStyle + UIStatusBarStyleLightContent UIStatusBarTintParameters UINavigationBar @@ -97,5 +89,7 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIViewControllerBasedStatusBarAppearance + diff --git a/HackTX/JosefinSans-Bold.ttf b/HackTX/JosefinSans-Bold.ttf new file mode 100755 index 0000000..7541bf0 Binary files /dev/null and b/HackTX/JosefinSans-Bold.ttf differ diff --git a/HackTX/JosefinSans-BoldItalic.ttf b/HackTX/JosefinSans-BoldItalic.ttf new file mode 100755 index 0000000..6682405 Binary files /dev/null and b/HackTX/JosefinSans-BoldItalic.ttf differ diff --git a/HackTX/JosefinSans-Italic.ttf b/HackTX/JosefinSans-Italic.ttf new file mode 100755 index 0000000..9374b33 Binary files /dev/null and b/HackTX/JosefinSans-Italic.ttf differ diff --git a/HackTX/JosefinSans-Light.ttf b/HackTX/JosefinSans-Light.ttf new file mode 100755 index 0000000..2205afe Binary files /dev/null and b/HackTX/JosefinSans-Light.ttf differ diff --git a/HackTX/JosefinSans-LightItalic.ttf b/HackTX/JosefinSans-LightItalic.ttf new file mode 100755 index 0000000..938aaf3 Binary files /dev/null and b/HackTX/JosefinSans-LightItalic.ttf differ diff --git a/HackTX/JosefinSans-Regular.ttf b/HackTX/JosefinSans-Regular.ttf new file mode 100755 index 0000000..d234c43 Binary files /dev/null and b/HackTX/JosefinSans-Regular.ttf differ diff --git a/HackTX/JosefinSans-SemiBold.ttf b/HackTX/JosefinSans-SemiBold.ttf new file mode 100755 index 0000000..03bfb70 Binary files /dev/null and b/HackTX/JosefinSans-SemiBold.ttf differ diff --git a/HackTX/JosefinSans-SemiBoldItalic.ttf b/HackTX/JosefinSans-SemiBoldItalic.ttf new file mode 100755 index 0000000..8145dd8 Binary files /dev/null and b/HackTX/JosefinSans-SemiBoldItalic.ttf differ diff --git a/HackTX/JosefinSans-Thin.ttf b/HackTX/JosefinSans-Thin.ttf new file mode 100755 index 0000000..275b028 Binary files /dev/null and b/HackTX/JosefinSans-Thin.ttf differ diff --git a/HackTX/JosefinSans-ThinItalic.ttf b/HackTX/JosefinSans-ThinItalic.ttf new file mode 100755 index 0000000..486c156 Binary files /dev/null and b/HackTX/JosefinSans-ThinItalic.ttf differ diff --git a/HackTX/Location.h b/HackTX/Location.h new file mode 100644 index 0000000..7775fbe --- /dev/null +++ b/HackTX/Location.h @@ -0,0 +1,21 @@ +// +// Location.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface Location : RLMObject + +@property NSString *building; +@property NSString *level; +@property NSString *room; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Location) diff --git a/HackTX/Location.m b/HackTX/Location.m new file mode 100644 index 0000000..4b8ed7f --- /dev/null +++ b/HackTX/Location.m @@ -0,0 +1,27 @@ +// +// Location.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Location.h" + +@implementation Location + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Location.swift b/HackTX/Location.swift deleted file mode 100644 index bf5401b..0000000 --- a/HackTX/Location.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Location.swift -// HackTX -// -// Created by Rohit Datta on 9/2/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Location { - var building: String? = "" - var level: String? = "" - var room: String? = "" - - init(building: String, level: String, room: String) { - self.building = building - self.level = level - self.room = room - } - - func description() -> String{ - return building! + " " + level! + " - " + room! - } - -} \ No newline at end of file diff --git a/HackTX/MapViewController.h b/HackTX/MapViewController.h new file mode 100644 index 0000000..9e64056 --- /dev/null +++ b/HackTX/MapViewController.h @@ -0,0 +1,13 @@ +// +// MapViewController.h +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface MapViewController : UIViewController + +@end diff --git a/HackTX/MapViewController.m b/HackTX/MapViewController.m new file mode 100644 index 0000000..a4dbf97 --- /dev/null +++ b/HackTX/MapViewController.m @@ -0,0 +1,54 @@ +// +// MapViewController.m +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "MapViewController.h" +#import +#import "HTXAPI.h" + +@interface MapViewController () + +@end + +@implementation MapViewController + +- (void)loadView { + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:30.268915 + longitude:-97.740378 + zoom:19]; + GMSMapView *mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + mapView.settings.rotateGestures = NO; + self.view = mapView; + + // Creates a marker in the center of the map. + GMSMarker *marker = [[GMSMarker alloc] init]; + marker.position = CLLocationCoordinate2DMake(30.268915, -97.740378); + marker.title = @"HackTX 2016"; + marker.map = mapView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#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/HackTX/MapViewController.swift b/HackTX/MapViewController.swift deleted file mode 100644 index 3063986..0000000 --- a/HackTX/MapViewController.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// MapViewController.swift -// HackTX -// -// Created by Rohit Datta on 7/11/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation -import UIKit - -class MapViewController: UIViewController, UIPageViewControllerDataSource { - - let mapImages = [["sac1", "sac2", "sac3"], ["cla1"]]; - var mapIndex = 0 - var pageViewController: UIPageViewController? - - override func viewDidLoad() { - super.viewDidLoad() - - let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as! UIPageViewController - pageController.dataSource = self - - if mapImages[mapIndex].count > 0 { - let startingViewControllers: NSArray = [getItemController(0)!] - pageController.setViewControllers(startingViewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil) - } - - pageViewController = pageController - addChildViewController(pageViewController!) - pageViewController!.view.frame = CGRectMake(15, -50, pageViewController!.view.frame.size.width-30, pageViewController!.view.frame.size.height) - self.view.addSubview(pageViewController!.view) - pageViewController!.didMoveToParentViewController(self) - - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "Maps") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - - func updatePageView() { - if mapImages[mapIndex].count > 0 { - let startingViewControllers: NSArray = [getItemController(0)!] - self.pageViewController?.setViewControllers(startingViewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil) - } - } - - private func getItemController(itemIndex: Int) -> PageItemViewController? { - if itemIndex < mapImages[mapIndex].count && mapImages[mapIndex].count > 0 { - let pageItemController = self.storyboard?.instantiateViewControllerWithIdentifier("ItemController") as! PageItemViewController - pageItemController.itemIndex = itemIndex - pageItemController.imageName = mapImages[mapIndex][itemIndex] - return pageItemController - } - - return nil - } - - func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? { - - let itemController = viewController as! PageItemViewController - - if itemController.itemIndex > 0 { - return getItemController(itemController.itemIndex-1) - } - - return nil - } - - func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? { - - let itemController = viewController as! PageItemViewController - - if itemController.itemIndex + 1 < mapImages[mapIndex].count { - return getItemController(itemController.itemIndex+1) - } - - return nil - } - - - func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { - return mapImages[mapIndex].count - } - - func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { - return 0 - } - - @IBAction func updateMap(sender: AnyObject) { - if mapIndex == 0 { - mapIndex = 1 - } else { - mapIndex = 0 - } - updatePageView() - } -} diff --git a/HackTX/MapsPageViewController.swift b/HackTX/MapsPageViewController.swift deleted file mode 100644 index 33e6279..0000000 --- a/HackTX/MapsPageViewController.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// MapsPageViewController.swift -// HackTX -// -// Created by Gilad Oved on 9/2/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit - -class MapsPageViewController: UIPageViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - let pageControl = UIPageControl.appearance() - pageControl.pageIndicatorTintColor = UIColor.grayColor() - pageControl.currentPageIndicatorTintColor = UIColor.blackColor() - } - -} diff --git a/HackTX/NSString+MD5.h b/HackTX/NSString+MD5.h new file mode 100644 index 0000000..89c0450 --- /dev/null +++ b/HackTX/NSString+MD5.h @@ -0,0 +1,15 @@ +// +// MD5.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface NSString (MD5) + +- (NSString *)MD5; + +@end diff --git a/HackTX/NSString+MD5.m b/HackTX/NSString+MD5.m new file mode 100644 index 0000000..b5dc6ec --- /dev/null +++ b/HackTX/NSString+MD5.m @@ -0,0 +1,28 @@ +// +// MD5.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "NSString+MD5.h" +#import + +@implementation NSString (MD5) + +- (NSString *)MD5 { + + const char * pointer = [self UTF8String]; + unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH]; + + CC_MD5(pointer, (CC_LONG)strlen(pointer), md5Buffer); + + NSMutableString *string = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) + [string appendFormat:@"%02x",md5Buffer[i]]; + + return string; +} + +@end diff --git a/HackTX/PageItemViewController.swift b/HackTX/PageItemViewController.swift deleted file mode 100644 index 8b9a055..0000000 --- a/HackTX/PageItemViewController.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// PageItemViewController.swift -// HackTX -// -// Created by Gilad Oved on 8/27/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit - -class PageItemViewController: UIViewController { - - var itemIndex: Int = 0 - var imageName: String = "" { - didSet { - if let imageView = mapImageView { - imageView.image = UIImage(named: imageName) - } - } - } - @IBOutlet weak var mapImageView: UIImageView! - @IBOutlet weak var levelLabel: UILabel! - - override func viewDidLoad() { - super.viewDidLoad() - mapImageView!.image = UIImage(named: imageName) - levelLabel.text = "Level \(itemIndex + 1)" - } - -} diff --git a/HackTX/PartnersViewCell.swift b/HackTX/PartnersViewCell.swift deleted file mode 100644 index 982a7a4..0000000 --- a/HackTX/PartnersViewCell.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// SponsorViewCell.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit - -class PartnersViewCell: UICollectionViewCell { - - @IBOutlet weak var sponsorImage: UIImageView! - - override func awakeFromNib() { - super.awakeFromNib() - // Initialization code - } - -} diff --git a/HackTX/PartnersViewCell.xib b/HackTX/PartnersViewCell.xib deleted file mode 100644 index 4f312ef..0000000 --- a/HackTX/PartnersViewCell.xib +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/HackTX/PartnersViewController.swift b/HackTX/PartnersViewController.swift deleted file mode 100644 index 48ad826..0000000 --- a/HackTX/PartnersViewController.swift +++ /dev/null @@ -1,195 +0,0 @@ -// -// CollectionViewController.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import Alamofire -import SwiftyJSON - -class PartnersViewController: UICollectionViewController { - - let reuseIdCell = "PartnersCell" - - var sponsorList = [Sponsor]() - var imageCache = [String:UIImage]() - var refreshControl: UIRefreshControl! - - override func viewDidLoad() { - super.viewDidLoad() - - collectionView?.registerNib(UINib(nibName: "PartnersViewCell", bundle: nil), forCellWithReuseIdentifier: reuseIdCell) - - - self.refreshControl = UIRefreshControl() - self.refreshControl.tintColor = UIColor(red: 125/255.0, green: 211/255.0, blue: 244/255.0, alpha: 1.0) - self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged) - self.collectionView!.addSubview(refreshControl) - - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getPartnersData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - - } - - func refresh(sender: AnyObject) { - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getPartnersData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - self.refreshControl.endRefreshing() - } - - func getPartnersData() { - Alamofire.request(Router.Partners()) - .responseJSON{ (request, response, data) in - if data.isFailure { - let errorAlert = UIAlertView() - if errorAlert.title == "" { - errorAlert.title = "Error" - errorAlert.message = "Oops! Looks like there was a problem trying to get the announcements" - errorAlert.addButtonWithTitle("Ok") - errorAlert.show() - } - } else if let data: AnyObject = data.value { - print("\n\nJSON HERE:\n\(data)") - let json = JSON(data) - self.sponsorList.removeAll(keepCapacity: true) - - for (_, subJson): (String, JSON) in json { - self.sponsorList.append(Sponsor(name: subJson["name"].stringValue, logoImage: subJson["logoImage"].stringValue, website: subJson["website"].stringValue, level: subJson["level"].intValue)) - } - - self.collectionView!.reloadData() - } - - } - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "Sponsors") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. - } - */ - - // MARK: UICollectionViewDataSource - - override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { - //#warning Incomplete method implementation -- Return the number of sections - return 1 - } - - - override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - //#warning Incomplete method implementation -- Return the number of items in the section - return sponsorList.count - } - - override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdCell, forIndexPath: indexPath) as! PartnersViewCell - let sponsor = sponsorList[indexPath.row] - -// cell.backgroundImage.image = UIImage(named: "transparent") - if let img = imageCache[sponsor.logoImage] { - cell.sponsorImage.image = img - } else { - let image_link_url = NSURL(string: sponsor.logoImage) - // The image isn't cached, download the img data - // We should perform this in a background thread - let request: NSURLRequest = NSURLRequest(URL: image_link_url!) - let mainQueue = NSOperationQueue.mainQueue() - NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in - if error == nil { - // Convert the downloaded data in to a UIImage object - let image = UIImage(data: data!) - // Store the image in to our cache - self.imageCache[sponsor.logoImage] = image - // Update the cell - dispatch_async(dispatch_get_main_queue(), { - if let _ = self.collectionView!.cellForItemAtIndexPath(indexPath) { - cell.sponsorImage.image = self.imageCache[sponsor.logoImage] - } - }) - } - else { - print("Error: \(error!.localizedDescription)") - } - }) - } - - return cell - } - - func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { - let currentSponsor = sponsorList[indexPath.item] - let level = currentSponsor.level - - var size : CGSize - - switch level{ - case 0: - size = CGSize(width: view.frame.width, height: 100) - case 1: - size = CGSize(width: view.frame.width, height: 100) - case 2: - size = CGSize(width: (view.frame.width/2)-10, height: 100) - default: - size = CGSize(width: (view.frame.width/2)-10, height: 100) - - } - return size - } - - override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - let sponsor = sponsorList[indexPath.row] - - if (UIApplication.sharedApplication().canOpenURL(NSURL(string:"googlechrome-x-callback://")!)) { - openInChrome(sponsor.website) - } else { - UIApplication.sharedApplication().openURL(NSURL(string: sponsor.website)!) - } - } - - func openInChrome(url: String) { - var urlString = url - if(!urlString.hasPrefix("http")) { - urlString = "http://" + urlString - } - - UIApplication.sharedApplication().openURL(NSURL(string: - "googlechrome-x-callback://x-callback-url/open/?x-source=HackTX&x-success=hacktx%3A%2F%2F&url=" + urlString + "&create-new-tab")!) - } - -} diff --git a/HackTX/Reachability.swift b/HackTX/Reachability.swift deleted file mode 100644 index 98894e7..0000000 --- a/HackTX/Reachability.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// Reachability.swift -// HackTX -// -// Created by Rohit Datta on 9/20/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import SystemConfiguration - -public class Reachability { - class func isConnectedToNetwork() -> Bool { - var zeroAddress = sockaddr_in() - zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) - zeroAddress.sin_family = sa_family_t(AF_INET) - let defaultRouteReachability = withUnsafePointer(&zeroAddress) { - SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) - } - var flags = SCNetworkReachabilityFlags.ConnectionAutomatic - if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) { - return false - } - let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0 - let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0 - return (isReachable && !needsConnection) - } -} \ No newline at end of file diff --git a/HackTX/Router.swift b/HackTX/Router.swift deleted file mode 100644 index 2dd704d..0000000 --- a/HackTX/Router.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Router.swift -// HackTX -// -// Created by Drew Romanyk on 9/6/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation -import Alamofire - -enum Router: URLRequestConvertible { - static let baseURLString = "https://my.hacktx.com/api" - - case Schedule(String) - case Announcements() - case Partners() - case Feedback([String: Int]) - - var method: Alamofire.Method { - switch self { - case .Schedule: - return .GET - case .Announcements: - return .GET - case .Partners: - return .GET - case .Feedback: - return .POST - } - } - - var path: String { - switch self { - case .Schedule(let dayId): - return "/schedule/\(dayId)" - case .Announcements(): - return "/announcements" - case .Partners(): - return "/sponsors" - case .Feedback: - return "/feedback" - } - } - - // MARK: URLRequestConvertible - - var URLRequest: NSMutableURLRequest { - let URL = NSURL(string: Router.baseURLString)! - let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path)) - mutableURLRequest.HTTPMethod = method.rawValue - - switch self { - case .Feedback(let parameters): - return ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0 - default: - return mutableURLRequest - } - } -} diff --git a/HackTX/ScheduleCluster.swift b/HackTX/ScheduleCluster.swift deleted file mode 100644 index fe4f082..0000000 --- a/HackTX/ScheduleCluster.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ScheduleCluster.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class ScheduleCluster { - var id : Int? = 0 - var name: String? = "" - var eventsList: [Event]? -} \ No newline at end of file diff --git a/HackTX/ScheduleDetailViewController.swift b/HackTX/ScheduleDetailViewController.swift deleted file mode 100644 index 9530ae1..0000000 --- a/HackTX/ScheduleDetailViewController.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// ScheduleDetailViewController.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit - -class ScheduleDetailViewController: UITableViewController { - - @IBOutlet weak var feedbackButton: UIBarButtonItem! - - var scheduleEvent = Event() - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.estimatedRowHeight = 215 - tableView.rowHeight = UITableViewAutomaticDimension - - title = scheduleEvent.name - - } - - func enableFeedback() { - let todaysDate = NSDate() - feedbackButton.enabled = true && !UserPrefs.shared().isFeedbackEventDone(scheduleEvent.id!) && todaysDate.compare(scheduleEvent.endDate!) == .OrderedDescending - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "ScheduleDetails-\(scheduleEvent.id)") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - - enableFeedback() - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - // MARK: - Table view data source - - override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - // #warning Potentially incomplete method implementation. - // Return the number of sections. - return 1 - } - - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // #warning Incomplete method implementation. - // Return the number of rows in the section. - return 2 - } - - - override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - var cell = UITableViewCell() - - switch indexPath.row { - case 0: - cell = tableView.dequeueReusableCellWithIdentifier("ImageCell", forIndexPath: indexPath) - - let imageUI = cell.viewWithTag(1000) as! UIImageView - - let image_link_url = NSURL(string: scheduleEvent.imageUrl!) - // The image isn't cached, download the img data - // We should perform this in a background thread - let request: NSURLRequest = NSURLRequest(URL: image_link_url!) - let mainQueue = NSOperationQueue.mainQueue() - NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in - if error == nil { - // Convert the downloaded data in to a UIImage object - let image = UIImage(data: data!) - // Store the image in to our cache - let loadedImage = image - // Update the cell - dispatch_async(dispatch_get_main_queue(), { - imageUI.image = loadedImage - }) - } - else { - print("Error: \(error!.localizedDescription)") - } - }) - - case 1: - cell = tableView.dequeueReusableCellWithIdentifier("InfoCell", forIndexPath: indexPath) - - let eventLocation = cell.viewWithTag(1000) as! UILabel - let eventTime = cell.viewWithTag(1001) as! UILabel - let eventDesc = cell.viewWithTag(1002) as! UITextView - let speakerTagName = cell.viewWithTag(999) as! UILabel - - let eventSpeaker = scheduleEvent.speakerList!.first - if eventSpeaker == nil { - speakerTagName.text = "" - } - - if let speakerNameCell = cell.viewWithTag(1005) as? UILabel { - let speakerDescriptionCell = cell.viewWithTag(1007) as! UILabel - let speakerImageCell = cell.viewWithTag(1008) as! UIImageView - speakerNameCell.text = eventSpeaker?.name - speakerDescriptionCell.text = eventSpeaker?.description - if eventSpeaker != nil { - - let image_link_url = NSURL(string: eventSpeaker!.imageUrl) - // The image isn't cached, download the img data - // We should perform this in a background thread - let request: NSURLRequest = NSURLRequest(URL: image_link_url!) - let mainQueue = NSOperationQueue.mainQueue() - NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in - if error == nil { - // Convert the downloaded data in to a UIImage object - let image = UIImage(data: data!) - // Store the image in to our cache - let loadedImage = image - // Update the cell - dispatch_async(dispatch_get_main_queue(), { - speakerImageCell.image = loadedImage - }) - } - else { - print("Error: \(error!.localizedDescription)") - } - }) - - speakerImageCell.layer.cornerRadius = speakerImageCell.frame.size.width / 2 - speakerImageCell.clipsToBounds = true - } - } - - - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = "h:mm a" - let startTime = dateFormatter.stringFromDate(scheduleEvent.startDate!) - let endTime = dateFormatter.stringFromDate(scheduleEvent.endDate!) - - eventLocation.text = scheduleEvent.location!.description() - eventTime.text = "\(startTime) - \(endTime)" - eventDesc.text = scheduleEvent.description - - default: - cell = UITableViewCell() - } - - return cell - } - - @IBAction func showFeedback(sender: UIBarButtonItem) { - performSegueWithIdentifier("showFeedback", sender: scheduleEvent) - } - - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { - if segue.identifier == "showFeedback" { - let navController = segue.destinationViewController as! UINavigationController - let controller = navController.topViewController as! EventFeedbackViewController - controller.scheduleEvent = sender as! Event - } - } - -} diff --git a/HackTX/ScheduleTableViewCell.h b/HackTX/ScheduleTableViewCell.h new file mode 100644 index 0000000..bd0a8af --- /dev/null +++ b/HackTX/ScheduleTableViewCell.h @@ -0,0 +1,19 @@ +// +// ScheduleTableViewCell.h +// HackTX +// +// Created by Jose Bethancourt on 10/12/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface ScheduleTableViewCell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UILabel *name; +@property (weak, nonatomic) IBOutlet UILabel *time; +@property (weak, nonatomic) IBOutlet UILabel *location; +@property (weak, nonatomic) IBOutlet UILabel *desc; +@property (weak, nonatomic) IBOutlet UIView *cardView; + +@end diff --git a/HackTX/ScheduleTableViewCell.m b/HackTX/ScheduleTableViewCell.m new file mode 100644 index 0000000..18321f6 --- /dev/null +++ b/HackTX/ScheduleTableViewCell.m @@ -0,0 +1,59 @@ +// +// ScheduleTableViewCell.m +// HackTX +// +// Created by Jose Bethancourt on 10/12/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "ScheduleTableViewCell.h" + +#import "UIColor+Palette.h" + +@implementation ScheduleTableViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + + self.time.textColor = [UIColor htx_red]; + self.location.textColor = [UIColor htx_lightBlue]; + + self.backgroundColor = [UIColor clearColor]; + + self.cardView.backgroundColor = [UIColor whiteColor]; + self.cardView.layer.cornerRadius = 2.5; + self.cardView.layer.masksToBounds = false; + self.cardView.layer.shadowColor = [UIColor blackColor].CGColor; + self.cardView.layer.shadowOffset = CGSizeMake(0.0, .25); + self.cardView.layer.shadowRadius = 1.0; + self.cardView.layer.shadowOpacity = 0.2; + +} + + +//- (void)layoutSubviews { +// [super layoutSubviews]; +// +// self.desc.preferredMaxLayoutWidth = self.desc.frame.size.width; +//} +// +//- (void)viewDidLayoutSubviews { +// dispatch_async(dispatch_get_main_queue(), ^{ +// self.desc.preferredMaxLayoutWidth = self.desc.frame.size.width; +// }); +//} + +- (void)setFrame:(CGRect)frame { + frame.origin.x += 7.5; + frame.origin.y += 4; + + frame.size.width -= 2 * 7.5; + frame.size.height -= 2 * 4; + [super setFrame:frame]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; +} + +@end diff --git a/HackTX/ScheduleTableViewCell.xib b/HackTX/ScheduleTableViewCell.xib new file mode 100644 index 0000000..ee99f8c --- /dev/null +++ b/HackTX/ScheduleTableViewCell.xib @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackTX/ScheduleViewController.h b/HackTX/ScheduleViewController.h new file mode 100644 index 0000000..eee6ffb --- /dev/null +++ b/HackTX/ScheduleViewController.h @@ -0,0 +1,13 @@ +// +// ScheduleViewController.h +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface ScheduleViewController : UIViewController + +@end diff --git a/HackTX/ScheduleViewController.m b/HackTX/ScheduleViewController.m new file mode 100644 index 0000000..0909e04 --- /dev/null +++ b/HackTX/ScheduleViewController.m @@ -0,0 +1,221 @@ +// +// ScheduleViewController.m +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "ScheduleViewController.h" +#import "ScheduleTableViewCell.h" + +#import "AutolayoutHelper.h" +#import "UIColor+Palette.h" +#import "SVProgressHUD.h" +#import "FCAlertView.h" +#import "HTXAPI.h" +#import "Event.h" + +@interface ScheduleViewController () + +@property (nonatomic, strong) UITableView *tableView; +@property (nonatomic, strong) UIRefreshControl *refreshControl; +@property (nonatomic, strong) NSArray *> *events; + +@end + +static NSString *reuseIdentifier = @"com.HackTX.schedule"; + +@implementation ScheduleViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView = [[UITableView alloc] init]; + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.estimatedRowHeight = 100; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.tableView.tableFooterView = [UIView new]; + self.tableView.allowsSelection = NO; + self.tableView.backgroundColor = [UIColor htx_white]; + + self.refreshControl = [[UIRefreshControl alloc] init]; + [self.tableView setRefreshControl:self.refreshControl]; + [self.refreshControl addTarget:self action:@selector(hardRefresh) forControlEvents:UIControlEventValueChanged]; + + self.edgesForExtendedLayout = UIRectEdgeAll; + self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f); + + UINib *nib = [UINib nibWithNibName:@"ScheduleTableViewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:reuseIdentifier]; + + [AutolayoutHelper configureView:self.view fillWithSubView:self.tableView]; + + [self initData]; + [self.tableView layoutIfNeeded]; + [self.tableView reloadData]; + +} + +- (void)hardRefresh { + + [HTXAPI refreshEvents:^(BOOL success) { + if (success) { + [self.refreshControl endRefreshing]; + [self refreshData]; + } else { + [self.refreshControl endRefreshing]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the schedule, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Schedule refresh failed"); + } + }]; + +} + +- (void)refresh { + [HTXAPI refreshEvents:^(BOOL success) { + if (success) { + [self initData]; + } else { + [SVProgressHUD dismiss]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the schedule, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Schedule refresh failed"); + } + }]; +} + +- (void)initData { + RLMResults *eventResult = [[Event allObjects] sortedResultsUsingProperty:@"startDate" ascending:YES]; + [SVProgressHUD show]; + + if (eventResult.count > 0) { + RLMResults *eventResult = [[Event allObjects] sortedResultsUsingProperty:@"startDate" ascending:YES]; + + [self transformRLMArray:eventResult]; + + [SVProgressHUD dismiss]; + [self.tableView reloadData]; + + [HTXAPI refreshSponsors:^(BOOL success) { + if (success) { + [self refreshData]; + } + }]; + + } else { + [self refresh]; + } +} + +- (void)refreshData { + RLMResults *eventResult = [[Event allObjects] sortedResultsUsingProperty:@"startDate" ascending:YES]; + [self transformRLMArray:eventResult]; + [self.tableView reloadData]; +} + + +- (void)transformRLMArray:(RLMResults *)eventData { + NSMutableArray *> *eventArray = [[NSMutableArray alloc] init]; + NSMutableArray *innerArray = [[NSMutableArray alloc] init]; + + NSInteger currentDay = [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:eventData[0].startDate]; + + for (Event *event in eventData) { + NSInteger testDay = [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:event.startDate]; + + if (currentDay == testDay) { + [innerArray addObject:event]; + } else { + currentDay = testDay; + + [eventArray addObject:innerArray]; + innerArray = [[NSMutableArray alloc] init]; + [innerArray addObject:event]; + } + } + [eventArray addObject:innerArray]; + + self.events = eventArray; +} + +- (NSInteger)getYear:(NSDate *)date { + NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date]; + + NSInteger day = [components day]; + + return day; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + UIView *headerView = [[UIView alloc] init]; + headerView.backgroundColor = [UIColor clearColor]; + return headerView; +} +- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section { + return 40; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + ScheduleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; + + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.timeStyle = NSDateFormatterShortStyle; + formatter.dateStyle = NSDateFormatterNoStyle; + + NSString *startTS = [formatter stringFromDate:self.events[indexPath.section][indexPath.row].startDate]; + NSString *endTS =[formatter stringFromDate:self.events[indexPath.section][indexPath.row].endDate]; + + NSString *description; + + if ([self.events[indexPath.section][indexPath.row].desc length] == 0) { + description = [NSString stringWithFormat:@"%@", self.events[indexPath.section][indexPath.row].name]; + } else { + description = [NSString stringWithFormat:@"%@ | %@", self.events[indexPath.section][indexPath.row].name, self.events[indexPath.section][indexPath.row].desc]; + } + + cell.desc.text = description; + cell.location.text = self.events[indexPath.section][indexPath.row].location.room; + + if ([startTS isEqualToString:endTS]) { + cell.time.text = [NSString stringWithFormat:@"%@", startTS]; + } else { + cell.time.text = [NSString stringWithFormat:@"%@ - %@", startTS, endTS]; + } + + [cell updateConstraintsIfNeeded]; + cell.desc.preferredMaxLayoutWidth = cell.desc.frame.size.width; + + return cell; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return self.events.count; +} +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.events[section].count; +} +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +@end diff --git a/HackTX/ScheduleViewController.swift b/HackTX/ScheduleViewController.swift deleted file mode 100644 index bb76ede..0000000 --- a/HackTX/ScheduleViewController.swift +++ /dev/null @@ -1,212 +0,0 @@ -// -// FirstViewController.swift -// HackTX -// -// Created by Gilad Oved on 7/11/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import Alamofire -import SwiftyJSON - -class ScheduleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { - - @IBOutlet weak var tableView: UITableView! - @IBOutlet weak var chooseDaySegmentControl: UISegmentedControl! - - let numberOfDays = 2 - var dayDict = [Int:Day]() - var refreshControl: UIRefreshControl! - - override func viewDidLoad() { - super.viewDidLoad() - self.refreshControl = UIRefreshControl() - self.refreshControl.tintColor = UIColor(red: 125/255.0, green: 211/255.0, blue: 244/255.0, alpha: 1.0) - self.refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged) - self.tableView.addSubview(refreshControl) - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getScheduleData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - - } - - func refresh(sender: AnyObject) { - if Reachability.isConnectedToNetwork() { - print("Internet connection OK") - getScheduleData() - } else { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - self.refreshControl.endRefreshing() - } - - func getScheduleData() { - for i in 0.. Bool { - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - - let date1 = dateFormatter.dateFromString(this.clusterList[0].eventsList![0].startDateStr!) - let date2 = dateFormatter.dateFromString(that.clusterList[0].eventsList![0].startDateStr!) - - return date1!.compare(date2!) == NSComparisonResult.OrderedAscending - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "Schedule") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - - func numberOfSectionsInTableView(tableView: UITableView) -> Int { - // #warning Potentially incomplete method implementation. - // Return the number of sections. - if(dayDict.indexForKey(chooseDaySegmentControl.selectedSegmentIndex) == nil) { - return 0 - } - - return dayDict[chooseDaySegmentControl.selectedSegmentIndex]!.clusterList.count - } - - func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - let scheduleCluster = dayDict[chooseDaySegmentControl.selectedSegmentIndex]!.clusterList[section] - - return scheduleCluster.eventsList!.count - } - - func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - let scheduleCluster = dayDict[chooseDaySegmentControl.selectedSegmentIndex]!.clusterList[section] - - return scheduleCluster.name - } - - func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "ScheduleCell") - let scheduleCluster = dayDict[chooseDaySegmentControl.selectedSegmentIndex]!.clusterList[indexPath.section] as ScheduleCluster - let event = scheduleCluster.eventsList![indexPath.row] as Event - - cell.textLabel!.text = event.name - - let dateFormatter = NSDateFormatter() - dateFormatter.dateFormat = "h:mm a" - let startTime = dateFormatter.stringFromDate(event.startDate!) - let endTime = dateFormatter.stringFromDate(event.endDate!) - - cell.detailTextLabel!.text = "\(startTime) - \(endTime) | \(event.location!.description())" - switch event.type! { - case "talk": - cell.imageView?.image = UIImage(named: "event_talk") - case "education": - cell.imageView?.image = UIImage(named: "event_school") - case "bus": - cell.imageView?.image = UIImage(named: "event_bus") - case "food": - cell.imageView?.image = UIImage(named: "event_food") - case "dev": - cell.imageView?.image = UIImage(named: "event_dev") - default: - cell.imageView?.image = UIImage(named: "event_default") - } - return cell - } - - func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - let scheduleCluster = dayDict[chooseDaySegmentControl.selectedSegmentIndex]!.clusterList[indexPath.section] - let event = scheduleCluster.eventsList![indexPath.row] - performSegueWithIdentifier("ShowEvent", sender: event) - tableView.deselectRowAtIndexPath(tableView.indexPathForSelectedRow!, animated: false) - } - - @IBAction func choseDifferentDay(sender: AnyObject) { - tableView.reloadData() - } - - override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { - if segue.identifier == "ShowEvent" { - let controller = segue.destinationViewController as! ScheduleDetailViewController - controller.scheduleEvent = sender as! Event - } - } - -} - diff --git a/HackTX/Speaker.h b/HackTX/Speaker.h new file mode 100644 index 0000000..5181a49 --- /dev/null +++ b/HackTX/Speaker.h @@ -0,0 +1,23 @@ +// +// Speaker.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface Speaker : RLMObject + +@property NSString *serverID; +@property NSString *name; +@property NSString *organization; +@property NSString *desc; +@property NSString *imageURL; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Speaker) diff --git a/HackTX/Speaker.m b/HackTX/Speaker.m new file mode 100644 index 0000000..55ad783 --- /dev/null +++ b/HackTX/Speaker.m @@ -0,0 +1,31 @@ +// +// Speaker.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Speaker.h" + +@implementation Speaker + ++ (NSString *)primaryKey { + return @"serverID"; +} + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Speaker.swift b/HackTX/Speaker.swift deleted file mode 100644 index ac2b90e..0000000 --- a/HackTX/Speaker.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Speaker.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Speaker { - var id: Int? = 0 - var name: String = "" - var organization: String = "" - var description: String = "" - var imageUrl: String = "" -} \ No newline at end of file diff --git a/HackTX/Sponsor.h b/HackTX/Sponsor.h new file mode 100644 index 0000000..5039c9f --- /dev/null +++ b/HackTX/Sponsor.h @@ -0,0 +1,23 @@ +// +// Sponsors.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface Sponsor : RLMObject + +@property NSString *serverID; +@property NSString *name; +@property NSString *logoImage; +@property NSString *website; +@property NSInteger level; + +@end + +// This protocol enables typed collections. i.e.: +// RLMArray +RLM_ARRAY_TYPE(Sponsor) diff --git a/HackTX/Sponsor.m b/HackTX/Sponsor.m new file mode 100644 index 0000000..e099e58 --- /dev/null +++ b/HackTX/Sponsor.m @@ -0,0 +1,31 @@ +// +// Sponsors.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "Sponsor.h" + +@implementation Sponsor + ++ (NSString *)primaryKey { + return @"serverID"; +} + +// Specify default values for properties + +//+ (NSDictionary *)defaultPropertyValues +//{ +// return @{}; +//} + +// Specify properties to ignore (Realm won't persist these) + +//+ (NSArray *)ignoredProperties +//{ +// return @[]; +//} + +@end diff --git a/HackTX/Sponsor.swift b/HackTX/Sponsor.swift deleted file mode 100644 index d259bb1..0000000 --- a/HackTX/Sponsor.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Sponsor.swift -// HackTX -// -// Created by Drew Romanyk on 8/23/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -class Sponsor: NSObject { - var name = "" - var logoImage = "" - var website = "" - var level = 0 - - init(name: String, logoImage: String, website: String, level: Int) { - self.name = name - self.logoImage = logoImage - self.website = website - self.level = level - } -} \ No newline at end of file diff --git a/HackTX/SponsorTableViewCell.h b/HackTX/SponsorTableViewCell.h new file mode 100644 index 0000000..0d868e0 --- /dev/null +++ b/HackTX/SponsorTableViewCell.h @@ -0,0 +1,18 @@ +// +// SponsorTableViewCell.h +// HackTX +// +// Created by Jose Bethancourt on 9/23/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface SponsorTableViewCell : UITableViewCell + +@property (weak, nonatomic) IBOutlet UIImageView *image; +@property (weak, nonatomic) IBOutlet UILabel *name; +@property (weak, nonatomic) IBOutlet UILabel *url; +@property (weak, nonatomic) IBOutlet UIView *cardView; + +@end diff --git a/HackTX/SponsorTableViewCell.m b/HackTX/SponsorTableViewCell.m new file mode 100644 index 0000000..12c88bb --- /dev/null +++ b/HackTX/SponsorTableViewCell.m @@ -0,0 +1,52 @@ +// +// SponsorTableViewCell.m +// HackTX +// +// Created by Jose Bethancourt on 9/23/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "SponsorTableViewCell.h" + +#import "UIColor+Palette.h" + +@implementation SponsorTableViewCell + +- (void)awakeFromNib { + [super awakeFromNib]; + + self.url.textColor = [UIColor htx_lightBlue]; + + self.backgroundColor = [UIColor clearColor]; + + self.cardView.backgroundColor = [UIColor whiteColor]; + self.cardView.layer.cornerRadius = 2.5; + self.cardView.layer.masksToBounds = NO; + self.cardView.layer.shadowColor = [UIColor blackColor].CGColor; + self.cardView.layer.shadowOffset = CGSizeMake(0.0, .25); + self.cardView.layer.shadowRadius = 1.0; + self.cardView.layer.shadowOpacity = 0.2; + + self.image.layer.cornerRadius = 2.5; + self.image.clipsToBounds = YES; + + self.url.userInteractionEnabled = YES; + + self.selectionStyle = UITableViewCellSelectionStyleNone; +} + + +- (void)setFrame:(CGRect)frame { + frame.origin.x += 7.5; + frame.origin.y += 4; + + frame.size.width -= 2 * 7.5; + frame.size.height -= 2 * 4; + [super setFrame:frame]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; +} + +@end diff --git a/HackTX/SponsorTableViewCell.xib b/HackTX/SponsorTableViewCell.xib new file mode 100644 index 0000000..36f28d6 --- /dev/null +++ b/HackTX/SponsorTableViewCell.xib @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HackTX/SponsorViewController.h b/HackTX/SponsorViewController.h new file mode 100644 index 0000000..5b4a260 --- /dev/null +++ b/HackTX/SponsorViewController.h @@ -0,0 +1,13 @@ +// +// SponsorViewController.h +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface SponsorViewController : UIViewController + +@end diff --git a/HackTX/SponsorViewController.m b/HackTX/SponsorViewController.m new file mode 100644 index 0000000..8bd5cee --- /dev/null +++ b/HackTX/SponsorViewController.m @@ -0,0 +1,212 @@ +// +// SponsorViewController.m +// HackTX +// +// Created by Jose Bethancourt on 9/17/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "SponsorViewController.h" +#import "SponsorTableViewCell.h" + +#import "UIImageView+AFNetworking.h" +#import "AutolayoutHelper.h" +#import "UIColor+Palette.h" +#import "SVProgressHUD.h" +#import "FCAlertView.h" +#import "Sponsor.h" +#import "HTXAPI.h" + +#import + +@interface SponsorViewController () + +@property (nonatomic, strong) UITableView *tableView; +@property (nonatomic, strong) UIRefreshControl *refreshControl; +@property (nonatomic, strong) NSArray *> *sponsors; + +@end + +static NSString *reuseIdentifier = @"com.HackTX.sponsor"; + +@implementation SponsorViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.tableView = [[UITableView alloc] init]; + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.rowHeight = 70; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.tableView.tableFooterView = [UIView new]; + self.tableView.allowsSelection = YES; + self.tableView.backgroundColor = [UIColor htx_white]; + + self.refreshControl = [[UIRefreshControl alloc] init]; + [self.tableView setRefreshControl:self.refreshControl]; + [self.refreshControl addTarget:self action:@selector(hardRefresh) forControlEvents:UIControlEventValueChanged]; + + self.edgesForExtendedLayout = UIRectEdgeAll; + self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f); + + UINib *nib = [UINib nibWithNibName:@"SponsorTableViewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:reuseIdentifier]; + + [AutolayoutHelper configureView:self.view fillWithSubView:self.tableView]; + + [self initData]; +} + +- (void)hardRefresh { + + [HTXAPI refreshSponsors:^(BOOL success) { + if (success) { + [self.refreshControl endRefreshing]; + [self refreshData]; + } else { + [self.refreshControl endRefreshing]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the sponsors, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Sponsor refresh failed"); + } + }]; +} + +- (void)refresh { + [HTXAPI refreshSponsors:^(BOOL success) { + if (success) { + [self initData]; + } else { + [SVProgressHUD dismiss]; + FCAlertView *alert = [[FCAlertView alloc] init]; + + [alert showAlertInView:self + withTitle:@"Network error" + withSubtitle:@"There was an error fetching the sponsors, please try again later. 😥" + withCustomImage:nil + withDoneButtonTitle:@"Okay" + andButtons:nil]; + [alert makeAlertTypeCaution]; + + NSLog(@"[HTX] Sponsor refresh failed"); + + } + }]; +} + +- (void)initData { + RLMResults *sponsorResult = [[Sponsor allObjects] sortedResultsUsingProperty:@"level" ascending:YES]; + [SVProgressHUD show]; + + if (sponsorResult.count > 0) { + RLMResults *sponsorResult = [[Sponsor allObjects] sortedResultsUsingProperty:@"level" ascending:YES]; + [self transformRLMArray:sponsorResult]; + + [SVProgressHUD dismiss]; + [self.tableView reloadData]; + + [HTXAPI refreshSponsors:^(BOOL success) { + if (success) { + [self refreshData]; + } + }]; + + } else { + [self refresh]; + } + +} + +- (void)refreshData { + RLMResults *sponsorResult = [[Sponsor allObjects] sortedResultsUsingProperty:@"level" ascending:YES]; + [self transformRLMArray:sponsorResult]; + [self.tableView reloadData]; +} + +- (void)transformRLMArray:(RLMResults *)eventData { + NSMutableArray *> *sponsorArray = [[NSMutableArray alloc] init]; + NSMutableArray *innerArray = [[NSMutableArray alloc] init]; + + NSInteger i = eventData[0].level; + + for (Sponsor *sponsor in eventData) { + + if (i == [sponsor[@"level"] integerValue]) { + [innerArray addObject:sponsor]; + } else { + i = [sponsor[@"level"] integerValue]; + + [sponsorArray addObject:innerArray]; + innerArray = [[NSMutableArray alloc] init]; + [innerArray addObject:sponsor]; + } + } + [sponsorArray addObject:innerArray]; + + self.sponsors = sponsorArray; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + UIView *headerView = [[UIView alloc] init]; + headerView.backgroundColor = [UIColor clearColor]; + return headerView; +} +- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section { + return 40; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + NSURL *sponsorURL = [NSURL URLWithString:self.sponsors[indexPath.section][indexPath.row].website]; + + [Answers logCustomEventWithName:@"Partner Click" + customAttributes:@{@"partner": self.sponsors[indexPath.section][indexPath.row].name}]; + + [[UIApplication sharedApplication] openURL:sponsorURL]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + SponsorTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; + + cell.name.text = self.sponsors[indexPath.section][indexPath.row].name; + cell.url.text = self.sponsors[indexPath.section][indexPath.row].website; + UIImage *placeholderImage = [UIImage imageNamed:@"icon_htx"]; + + __weak SponsorTableViewCell *weakCell = cell; + NSURL *url = [NSURL URLWithString:self.sponsors[indexPath.section][indexPath.row].logoImage]; + + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + + [cell.image setImageWithURLRequest:request + placeholderImage:placeholderImage + success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { + + weakCell.image.image = image; + + } failure:nil]; + + return cell; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return self.sponsors.count; +} +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.sponsors[section].count; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + + +@end diff --git a/HackTX/SwiftyJSON.swift b/HackTX/SwiftyJSON.swift deleted file mode 100755 index 5407e9a..0000000 --- a/HackTX/SwiftyJSON.swift +++ /dev/null @@ -1,1347 +0,0 @@ -// SwiftyJSON.swift -// -// Copyright (c) 2014 Ruoyu Fu, Pinglin Tang -// -// 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 Foundation - -// MARK: - Error - -///Error domain -public let ErrorDomain: String! = "SwiftyJSONErrorDomain" - -///Error code -public let ErrorUnsupportedType: Int! = 999 -public let ErrorIndexOutOfBounds: Int! = 900 -public let ErrorWrongType: Int! = 901 -public let ErrorNotExist: Int! = 500 - -// MARK: - JSON Type - -/** -JSON's type definitions. - -See http://tools.ietf.org/html/rfc7231#section-4.3 -*/ -public enum Type :Int{ - - case Number - case String - case Bool - case Array - case Dictionary - case Null - case Unknown -} - -// MARK: - JSON Base - -public struct JSON { - - /** - Creates a JSON using the data. - - :param: data The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary - :param: opt The JSON serialization reading options. `.AllowFragments` by default. - :param: error error The NSErrorPointer used to return the error. `nil` by default. - - :returns: The created JSON - */ - public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) { - if let object: AnyObject = NSJSONSerialization.JSONObjectWithData(data, options: opt, error: error) { - self.init(object) - } else { - self.init(NSNull()) - } - } - - /** - Creates a JSON using the object. - - :param: object The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. - - :returns: The created JSON - */ - public init(_ object: AnyObject) { - self.object = object - } - - /** - Creates a JSON from a [JSON] - - :param: jsonArray A Swift array of JSON objects - - :returns: The created JSON - */ - public init(_ jsonArray:[JSON]) { - self.init(jsonArray.map { $0.object }) - } - - /// Private object - private var _object: AnyObject = NSNull() - /// Private type - private var _type: Type = .Null - /// prviate error - private var _error: NSError? - - /// Object in JSON - public var object: AnyObject { - get { - return _object - } - set { - _object = newValue - switch newValue { - case let number as NSNumber: - if number.isBool { - _type = .Bool - } else { - _type = .Number - } - case let string as NSString: - _type = .String - case let null as NSNull: - _type = .Null - case let array as [AnyObject]: - _type = .Array - case let dictionary as [String : AnyObject]: - _type = .Dictionary - default: - _type = .Unknown - _object = NSNull() - _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"]) - } - } - } - - /// json type - public var type: Type { get { return _type } } - - /// Error in JSON - public var error: NSError? { get { return self._error } } - - /// The static null json - public static var nullJSON: JSON { get { return JSON(NSNull()) } } - -} - -// MARK: - SequenceType -extension JSON : Swift.SequenceType { - - /// If `type` is `.Array` or `.Dictionary`, return `array.empty` or `dictonary.empty` otherwise return `false`. - public var isEmpty: Bool { - get { - switch self.type { - case .Array: - return (self.object as! [AnyObject]).isEmpty - case .Dictionary: - return (self.object as! [String : AnyObject]).isEmpty - default: - return false - } - } - } - - /// If `type` is `.Array` or `.Dictionary`, return `array.count` or `dictonary.count` otherwise return `0`. - public var count: Int { - get { - switch self.type { - case .Array: - return self.arrayValue.count - case .Dictionary: - return self.dictionaryValue.count - default: - return 0 - } - } - } - - /** - If `type` is `.Array` or `.Dictionary`, return a generator over the elements like `Array` or `Dictionary`, otherwise return a generator over empty. - - :returns: Return a *generator* over the elements of this *sequence*. - */ - public func generate() -> GeneratorOf <(String, JSON)> { - switch self.type { - case .Array: - let array_ = object as! [AnyObject] - var generate_ = array_.generate() - var index_: Int = 0 - return GeneratorOf<(String, JSON)> { - if let element_: AnyObject = generate_.next() { - return ("\(index_++)", JSON(element_)) - } else { - return nil - } - } - case .Dictionary: - let dictionary_ = object as! [String : AnyObject] - var generate_ = dictionary_.generate() - return GeneratorOf<(String, JSON)> { - if let (key_: String, value_: AnyObject) = generate_.next() { - return (key_, JSON(value_)) - } else { - return nil - } - } - default: - return GeneratorOf<(String, JSON)> { - return nil - } - } - } -} - -// MARK: - Subscript - -/** -* To mark both String and Int can be used in subscript. -*/ -public protocol SubscriptType {} - -extension Int: SubscriptType {} - -extension String: SubscriptType {} - -extension JSON { - - /// If `type` is `.Array`, return json which's object is `array[index]`, otherwise return null json with error. - private subscript(#index: Int) -> JSON { - get { - - if self.type != .Array { - var errorResult_ = JSON.nullJSON - errorResult_._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) - return errorResult_ - } - - let array_ = self.object as! [AnyObject] - - if index >= 0 && index < array_.count { - return JSON(array_[index]) - } - - var errorResult_ = JSON.nullJSON - errorResult_._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"]) - return errorResult_ - } - set { - if self.type == .Array { - var array_ = self.object as! [AnyObject] - if array_.count > index { - array_[index] = newValue.object - self.object = array_ - } - } - } - } - - /// If `type` is `.Dictionary`, return json which's object is `dictionary[key]` , otherwise return null json with error. - private subscript(#key: String) -> JSON { - get { - var returnJSON = JSON.nullJSON - if self.type == .Dictionary { - let dictionary_ = self.object as! [String : AnyObject] - if let object_: AnyObject = dictionary_[key] { - returnJSON = JSON(object_) - } else { - returnJSON._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"]) - } - } else { - returnJSON._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"]) - } - return returnJSON - } - set { - if self.type == .Dictionary { - var dictionary_ = self.object as! [String : AnyObject] - dictionary_[key] = newValue.object - self.object = dictionary_ - } - } - } - - /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. - private subscript(#sub: SubscriptType) -> JSON { - get { - if sub is String { - return self[key:sub as! String] - } else { - return self[index:sub as! Int] - } - } - set { - if sub is String { - self[key:sub as! String] = newValue - } else { - self[index:sub as! Int] = newValue - } - } - } - - /** - Find a json in the complex data structuresby using the Int/String's array. - - :param: path The target json's path. Example: - - let json = JSON[data] - let path = [9,"list","person","name"] - let name = json[path] - - The same as: let name = json[9]["list"]["person"]["name"] - - :returns: Return a json found by the path or a null json with error - */ - public subscript(path: [SubscriptType]) -> JSON { - get { - if path.count == 0 { - return JSON.nullJSON - } - - var next = self - for sub in path { - next = next[sub:sub] - } - return next - } - set { - - switch path.count { - case 0: return - case 1: self[sub:path[0]] = newValue - default: - var last = newValue - var newPath = path - newPath.removeLast() - for sub in path.reverse() { - var previousLast = self[newPath] - previousLast[sub:sub] = last - last = previousLast - if newPath.count <= 1 { - break - } - newPath.removeLast() - } - self[sub:newPath[0]] = last - } - } - } - - /** - Find a json in the complex data structuresby using the Int/String's array. - - :param: path The target json's path. Example: - - let name = json[9,"list","person","name"] - - The same as: let name = json[9]["list"]["person"]["name"] - - :returns: Return a json found by the path or a null json with error - */ - public subscript(path: SubscriptType...) -> JSON { - get { - return self[path] - } - set { - self[path] = newValue - } - } -} - -// MARK: - LiteralConvertible - -extension JSON: Swift.StringLiteralConvertible { - - public init(stringLiteral value: StringLiteralType) { - self.init(value) - } - - public init(extendedGraphemeClusterLiteral value: StringLiteralType) { - self.init(value) - } - - public init(unicodeScalarLiteral value: StringLiteralType) { - self.init(value) - } -} - -extension JSON: Swift.IntegerLiteralConvertible { - - public init(integerLiteral value: IntegerLiteralType) { - self.init(value) - } -} - -extension JSON: Swift.BooleanLiteralConvertible { - - public init(booleanLiteral value: BooleanLiteralType) { - self.init(value) - } -} - -extension JSON: Swift.FloatLiteralConvertible { - - public init(floatLiteral value: FloatLiteralType) { - self.init(value) - } -} - -extension JSON: Swift.DictionaryLiteralConvertible { - - public init(dictionaryLiteral elements: (String, AnyObject)...) { - var dictionary_ = [String : AnyObject]() - for (key_, value) in elements { - dictionary_[key_] = value - } - self.init(dictionary_) - } -} - -extension JSON: Swift.ArrayLiteralConvertible { - - public init(arrayLiteral elements: AnyObject...) { - self.init(elements) - } -} - -extension JSON: Swift.NilLiteralConvertible { - - public init(nilLiteral: ()) { - self.init(NSNull()) - } -} - -// MARK: - Raw - -extension JSON: Swift.RawRepresentable { - - public init?(rawValue: AnyObject) { - if JSON(rawValue).type == .Unknown { - return nil - } else { - self.init(rawValue) - } - } - - public var rawValue: AnyObject { - return self.object - } - - public func rawData(options opt: NSJSONWritingOptions = NSJSONWritingOptions(0), error: NSErrorPointer = nil) -> NSData? { - return NSJSONSerialization.dataWithJSONObject(self.object, options: opt, error:error) - } - - public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? { - switch self.type { - case .Array, .Dictionary: - if let data = self.rawData(options: opt) { - return NSString(data: data, encoding: encoding) as? String - } else { - return nil - } - case .String: - return (self.object as! String) - case .Number: - return (self.object as! NSNumber).stringValue - case .Bool: - return (self.object as! Bool).description - case .Null: - return "null" - default: - return nil - } - } -} - -// MARK: - Printable, DebugPrintable - -extension JSON: Swift.Printable, Swift.DebugPrintable { - - public var description: String { - if let string = self.rawString(options:.PrettyPrinted) { - return string - } else { - return "unknown" - } - } - - public var debugDescription: String { - return description - } -} - -// MARK: - Array - -extension JSON { - - //Optional [JSON] - public var array: [JSON]? { - get { - if self.type == .Array { - return map(self.object as! [AnyObject]){ JSON($0) } - } else { - return nil - } - } - } - - //Non-optional [JSON] - public var arrayValue: [JSON] { - get { - return self.array ?? [] - } - } - - //Optional [AnyObject] - public var arrayObject: [AnyObject]? { - get { - switch self.type { - case .Array: - return self.object as? [AnyObject] - default: - return nil - } - } - set { - if newValue != nil { - self.object = NSMutableArray(array: newValue!, copyItems: true) - } else { - self.object = NSNull() - } - } - } -} - -// MARK: - Dictionary - -extension JSON { - - private func _map(source: [Key: Value], transform: Value -> NewValue) -> [Key: NewValue] { - var result = [Key: NewValue](minimumCapacity:source.count) - for (key,value) in source { - result[key] = transform(value) - } - return result - } - - //Optional [String : JSON] - public var dictionary: [String : JSON]? { - get { - if self.type == .Dictionary { - return _map(self.object as! [String : AnyObject]){ JSON($0) } - } else { - return nil - } - } - } - - //Non-optional [String : JSON] - public var dictionaryValue: [String : JSON] { - get { - return self.dictionary ?? [:] - } - } - - //Optional [String : AnyObject] - public var dictionaryObject: [String : AnyObject]? { - get { - switch self.type { - case .Dictionary: - return self.object as? [String : AnyObject] - default: - return nil - } - } - set { - if newValue != nil { - self.object = NSMutableDictionary(dictionary: newValue!, copyItems: true) - } else { - self.object = NSNull() - } - } - } -} - -// MARK: - Bool - -extension JSON: Swift.BooleanType { - - //Optional bool - public var bool: Bool? { - get { - switch self.type { - case .Bool: - return self.object.boolValue - default: - return nil - } - } - set { - if newValue != nil { - self.object = NSNumber(bool: newValue!) - } else { - self.object = NSNull() - } - } - } - - //Non-optional bool - public var boolValue: Bool { - get { - switch self.type { - case .Bool, .Number, .String: - return self.object.boolValue - default: - return false - } - } - set { - self.object = NSNumber(bool: newValue) - } - } -} - -// MARK: - String - -extension JSON { - - //Optional string - public var string: String? { - get { - switch self.type { - case .String: - return self.object as? String - default: - return nil - } - } - set { - if newValue != nil { - self.object = NSString(string:newValue!) - } else { - self.object = NSNull() - } - } - } - - //Non-optional string - public var stringValue: String { - get { - switch self.type { - case .String: - return self.object as! String - case .Number: - return self.object.stringValue - case .Bool: - return (self.object as! Bool).description - default: - return "" - } - } - set { - self.object = NSString(string:newValue) - } - } -} - -// MARK: - Number -extension JSON { - - //Optional number - public var number: NSNumber? { - get { - switch self.type { - case .Number, .Bool: - return self.object as? NSNumber - default: - return nil - } - } - set { - self.object = newValue?.copy() ?? NSNull() - } - } - - //Non-optional number - public var numberValue: NSNumber { - get { - switch self.type { - case .String: - let scanner = NSScanner(string: self.object as! String) - if scanner.scanDouble(nil){ - if (scanner.atEnd) { - return NSNumber(double:(self.object as! NSString).doubleValue) - } - } - return NSNumber(double: 0.0) - case .Number, .Bool: - return self.object as! NSNumber - default: - return NSNumber(double: 0.0) - } - } - set { - self.object = newValue.copy() - } - } -} - -//MARK: - Null -extension JSON { - - public var null: NSNull? { - get { - switch self.type { - case .Null: - return NSNull() - default: - return nil - } - } - set { - self.object = NSNull() - } - } -} - -//MARK: - URL -extension JSON { - - //Optional URL - public var URL: NSURL? { - get { - switch self.type { - case .String: - if let encodedString_ = self.object.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) { - return NSURL(string: encodedString_) - } else { - return nil - } - default: - return nil - } - } - set { - self.object = newValue?.absoluteString ?? NSNull() - } - } -} - -// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 - -extension JSON { - - public var double: Double? { - get { - return self.number?.doubleValue - } - set { - if newValue != nil { - self.object = NSNumber(double: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var doubleValue: Double { - get { - return self.numberValue.doubleValue - } - set { - self.object = NSNumber(double: newValue) - } - } - - public var float: Float? { - get { - return self.number?.floatValue - } - set { - if newValue != nil { - self.object = NSNumber(float: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var floatValue: Float { - get { - return self.numberValue.floatValue - } - set { - self.object = NSNumber(float: newValue) - } - } - - public var int: Int? { - get { - return self.number?.longValue - } - set { - if newValue != nil { - self.object = NSNumber(integer: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var intValue: Int { - get { - return self.numberValue.integerValue - } - set { - self.object = NSNumber(integer: newValue) - } - } - - public var uInt: UInt? { - get { - return self.number?.unsignedLongValue - } - set { - if newValue != nil { - self.object = NSNumber(unsignedLong: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var uIntValue: UInt { - get { - return self.numberValue.unsignedLongValue - } - set { - self.object = NSNumber(unsignedLong: newValue) - } - } - - public var int8: Int8? { - get { - return self.number?.charValue - } - set { - if newValue != nil { - self.object = NSNumber(char: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var int8Value: Int8 { - get { - return self.numberValue.charValue - } - set { - self.object = NSNumber(char: newValue) - } - } - - public var uInt8: UInt8? { - get { - return self.number?.unsignedCharValue - } - set { - if newValue != nil { - self.object = NSNumber(unsignedChar: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var uInt8Value: UInt8 { - get { - return self.numberValue.unsignedCharValue - } - set { - self.object = NSNumber(unsignedChar: newValue) - } - } - - public var int16: Int16? { - get { - return self.number?.shortValue - } - set { - if newValue != nil { - self.object = NSNumber(short: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var int16Value: Int16 { - get { - return self.numberValue.shortValue - } - set { - self.object = NSNumber(short: newValue) - } - } - - public var uInt16: UInt16? { - get { - return self.number?.unsignedShortValue - } - set { - if newValue != nil { - self.object = NSNumber(unsignedShort: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var uInt16Value: UInt16 { - get { - return self.numberValue.unsignedShortValue - } - set { - self.object = NSNumber(unsignedShort: newValue) - } - } - - public var int32: Int32? { - get { - return self.number?.intValue - } - set { - if newValue != nil { - self.object = NSNumber(int: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var int32Value: Int32 { - get { - return self.numberValue.intValue - } - set { - self.object = NSNumber(int: newValue) - } - } - - public var uInt32: UInt32? { - get { - return self.number?.unsignedIntValue - } - set { - if newValue != nil { - self.object = NSNumber(unsignedInt: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var uInt32Value: UInt32 { - get { - return self.numberValue.unsignedIntValue - } - set { - self.object = NSNumber(unsignedInt: newValue) - } - } - - public var int64: Int64? { - get { - return self.number?.longLongValue - } - set { - if newValue != nil { - self.object = NSNumber(longLong: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var int64Value: Int64 { - get { - return self.numberValue.longLongValue - } - set { - self.object = NSNumber(longLong: newValue) - } - } - - public var uInt64: UInt64? { - get { - return self.number?.unsignedLongLongValue - } - set { - if newValue != nil { - self.object = NSNumber(unsignedLongLong: newValue!) - } else { - self.object = NSNull() - } - } - } - - public var uInt64Value: UInt64 { - get { - return self.numberValue.unsignedLongLongValue - } - set { - self.object = NSNumber(unsignedLongLong: newValue) - } - } -} - -//MARK: - Comparable -extension JSON: Swift.Comparable {} - -public func ==(lhs: JSON, rhs: JSON) -> Bool { - - switch (lhs.type, rhs.type) { - case (.Number, .Number): - return (lhs.object as! NSNumber) == (rhs.object as! NSNumber) - case (.String, .String): - return (lhs.object as! String) == (rhs.object as! String) - case (.Bool, .Bool): - return (lhs.object as! Bool) == (rhs.object as! Bool) - case (.Array, .Array): - return (lhs.object as! NSArray) == (rhs.object as! NSArray) - case (.Dictionary, .Dictionary): - return (lhs.object as! NSDictionary) == (rhs.object as! NSDictionary) - case (.Null, .Null): - return true - default: - return false - } -} - -public func <=(lhs: JSON, rhs: JSON) -> Bool { - - switch (lhs.type, rhs.type) { - case (.Number, .Number): - return (lhs.object as! NSNumber) <= (rhs.object as! NSNumber) - case (.String, .String): - return (lhs.object as! String) <= (rhs.object as! String) - case (.Bool, .Bool): - return (lhs.object as! Bool) == (rhs.object as! Bool) - case (.Array, .Array): - return (lhs.object as! NSArray) == (rhs.object as! NSArray) - case (.Dictionary, .Dictionary): - return (lhs.object as! NSDictionary) == (rhs.object as! NSDictionary) - case (.Null, .Null): - return true - default: - return false - } -} - -public func >=(lhs: JSON, rhs: JSON) -> Bool { - - switch (lhs.type, rhs.type) { - case (.Number, .Number): - return (lhs.object as! NSNumber) >= (rhs.object as! NSNumber) - case (.String, .String): - return (lhs.object as! String) >= (rhs.object as! String) - case (.Bool, .Bool): - return (lhs.object as! Bool) == (rhs.object as! Bool) - case (.Array, .Array): - return (lhs.object as! NSArray) == (rhs.object as! NSArray) - case (.Dictionary, .Dictionary): - return (lhs.object as! NSDictionary) == (rhs.object as! NSDictionary) - case (.Null, .Null): - return true - default: - return false - } -} - -public func >(lhs: JSON, rhs: JSON) -> Bool { - - switch (lhs.type, rhs.type) { - case (.Number, .Number): - return (lhs.object as! NSNumber) > (rhs.object as! NSNumber) - case (.String, .String): - return (lhs.object as! String) > (rhs.object as! String) - default: - return false - } -} - -public func <(lhs: JSON, rhs: JSON) -> Bool { - - switch (lhs.type, rhs.type) { - case (.Number, .Number): - return (lhs.object as! NSNumber) < (rhs.object as! NSNumber) - case (.String, .String): - return (lhs.object as! String) < (rhs.object as! String) - default: - return false - } -} - -private let trueNumber = NSNumber(bool: true) -private let falseNumber = NSNumber(bool: false) -private let trueObjCType = String.fromCString(trueNumber.objCType) -private let falseObjCType = String.fromCString(falseNumber.objCType) - -// MARK: - NSNumber: Comparable - -extension NSNumber: Swift.Comparable { - var isBool:Bool { - get { - let objCType = String.fromCString(self.objCType) - if (self.compare(trueNumber) == NSComparisonResult.OrderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == NSComparisonResult.OrderedSame && objCType == falseObjCType){ - return true - } else { - return false - } - } - } -} - -public func ==(lhs: NSNumber, rhs: NSNumber) -> Bool { - switch (lhs.isBool, rhs.isBool) { - case (false, true): - return false - case (true, false): - return false - default: - return lhs.compare(rhs) == NSComparisonResult.OrderedSame - } -} - -public func !=(lhs: NSNumber, rhs: NSNumber) -> Bool { - return !(lhs == rhs) -} - -public func <(lhs: NSNumber, rhs: NSNumber) -> Bool { - - switch (lhs.isBool, rhs.isBool) { - case (false, true): - return false - case (true, false): - return false - default: - return lhs.compare(rhs) == NSComparisonResult.OrderedAscending - } -} - -public func >(lhs: NSNumber, rhs: NSNumber) -> Bool { - - switch (lhs.isBool, rhs.isBool) { - case (false, true): - return false - case (true, false): - return false - default: - return lhs.compare(rhs) == NSComparisonResult.OrderedDescending - } -} - -public func <=(lhs: NSNumber, rhs: NSNumber) -> Bool { - - switch (lhs.isBool, rhs.isBool) { - case (false, true): - return false - case (true, false): - return false - default: - return lhs.compare(rhs) != NSComparisonResult.OrderedDescending - } -} - -public func >=(lhs: NSNumber, rhs: NSNumber) -> Bool { - - switch (lhs.isBool, rhs.isBool) { - case (false, true): - return false - case (true, false): - return false - default: - return lhs.compare(rhs) != NSComparisonResult.OrderedAscending - } -} - -//MARK:- Unavailable - -@availability(*, unavailable, renamed="JSON") -public typealias JSONValue = JSON - -extension JSON { - - @availability(*, unavailable, message="use 'init(_ object:AnyObject)' instead") - public init(object: AnyObject) { - self = JSON(object) - } - - @availability(*, unavailable, renamed="dictionaryObject") - public var dictionaryObjects: [String : AnyObject]? { - get { return self.dictionaryObject } - } - - @availability(*, unavailable, renamed="arrayObject") - public var arrayObjects: [AnyObject]? { - get { return self.arrayObject } - } - - @availability(*, unavailable, renamed="int8") - public var char: Int8? { - get { - return self.number?.charValue - } - } - - @availability(*, unavailable, renamed="int8Value") - public var charValue: Int8 { - get { - return self.numberValue.charValue - } - } - - @availability(*, unavailable, renamed="uInt8") - public var unsignedChar: UInt8? { - get{ - return self.number?.unsignedCharValue - } - } - - @availability(*, unavailable, renamed="uInt8Value") - public var unsignedCharValue: UInt8 { - get{ - return self.numberValue.unsignedCharValue - } - } - - @availability(*, unavailable, renamed="int16") - public var short: Int16? { - get{ - return self.number?.shortValue - } - } - - @availability(*, unavailable, renamed="int16Value") - public var shortValue: Int16 { - get{ - return self.numberValue.shortValue - } - } - - @availability(*, unavailable, renamed="uInt16") - public var unsignedShort: UInt16? { - get{ - return self.number?.unsignedShortValue - } - } - - @availability(*, unavailable, renamed="uInt16Value") - public var unsignedShortValue: UInt16 { - get{ - return self.numberValue.unsignedShortValue - } - } - - @availability(*, unavailable, renamed="int") - public var long: Int? { - get{ - return self.number?.longValue - } - } - - @availability(*, unavailable, renamed="intValue") - public var longValue: Int { - get{ - return self.numberValue.longValue - } - } - - @availability(*, unavailable, renamed="uInt") - public var unsignedLong: UInt? { - get{ - return self.number?.unsignedLongValue - } - } - - @availability(*, unavailable, renamed="uIntValue") - public var unsignedLongValue: UInt { - get{ - return self.numberValue.unsignedLongValue - } - } - - @availability(*, unavailable, renamed="int64") - public var longLong: Int64? { - get{ - return self.number?.longLongValue - } - } - - @availability(*, unavailable, renamed="int64Value") - public var longLongValue: Int64 { - get{ - return self.numberValue.longLongValue - } - } - - @availability(*, unavailable, renamed="uInt64") - public var unsignedLongLong: UInt64? { - get{ - return self.number?.unsignedLongLongValue - } - } - - @availability(*, unavailable, renamed="uInt64Value") - public var unsignedLongLongValue: UInt64 { - get{ - return self.numberValue.unsignedLongLongValue - } - } - - @availability(*, unavailable, renamed="int") - public var integer: Int? { - get { - return self.number?.integerValue - } - } - - @availability(*, unavailable, renamed="intValue") - public var integerValue: Int { - get { - return self.numberValue.integerValue - } - } - - @availability(*, unavailable, renamed="uInt") - public var unsignedInteger: Int? { - get { - return self.number?.unsignedIntegerValue - } - } - - @availability(*, unavailable, renamed="uIntValue") - public var unsignedIntegerValue: Int { - get { - return self.numberValue.unsignedIntegerValue - } - } -} diff --git a/HackTX/TwitterViewController.swift b/HackTX/TwitterViewController.swift deleted file mode 100644 index d12146d..0000000 --- a/HackTX/TwitterViewController.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// TwitterViewController.swift -// HackTX -// -// Created by Rohit Datta on 7/11/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation -import UIKit -import TwitterKit - -class TwitterViewController: TWTRTimelineViewController { - -// let reachability = Reachability.reachabilityForInternetConnection() - - override func viewDidLoad() { - super.viewDidLoad() - -// let topLayoutGuide = CGFloat(60) -// tableView.contentInset = UIEdgeInsetsMake(topLayoutGuide, 0, 0, 0) - if !Reachability.isConnectedToNetwork() { - print("Internet connection FAILED") - let alert = UIAlertView(title: "No Internet Connection", message: "The HackTX app requires an internet connection to work. Talk to a volunteer about getting Internet access.", delegate: nil, cancelButtonTitle: "OK") - alert.show() - } - Twitter.sharedInstance().logInGuestWithCompletion { session, error in - if let _ = session { - let client = Twitter.sharedInstance().APIClient - self.dataSource = TWTRUserTimelineDataSource(screenName: "hacktx", APIClient: client) - } else { - print("error: \(error.localizedDescription)") - } - } - } - - // Setup Google Analytics for the controller - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - - let tracker = GAI.sharedInstance().defaultTracker - tracker.set(kGAIScreenName, value: "Twitter") - - let builder = GAIDictionaryBuilder.createScreenView() - tracker.send(builder.build() as [NSObject : AnyObject]) - } - -} \ No newline at end of file diff --git a/HackTX/UIColor+Palette.h b/HackTX/UIColor+Palette.h new file mode 100644 index 0000000..31ef538 --- /dev/null +++ b/HackTX/UIColor+Palette.h @@ -0,0 +1,18 @@ +// +// UIColor+Palette.h +// HackTX +// +// Created by Jose Bethancourt on 9/8/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import + +@interface UIColor(Palette) + ++ (instancetype) htx_lightBlue; ++ (instancetype) htx_red; ++ (instancetype) htx_white; ++ (instancetype) htx_lighterBlue; + +@end diff --git a/HackTX/UIColor+Palette.m b/HackTX/UIColor+Palette.m new file mode 100644 index 0000000..6036687 --- /dev/null +++ b/HackTX/UIColor+Palette.m @@ -0,0 +1,49 @@ +// +// UIColor+Palette.m +// HackTX +// +// Created by Jose Bethancourt on 9/8/16. +// Copyright © 2016 HackTX. All rights reserved. +// + +#import "UIColor+Palette.h" + +@implementation UIColor(Palette) + +// Light Blue #5983A0 -> RGB (89,131,160) + ++ (instancetype) htx_lightBlue { + return [self colorWithRed:89.f/255.f + green:131.f/255.f + blue:160.f/255.f + alpha:1]; +} + +// Red #cb0922 -> rgb(203,9,34) + ++ (instancetype) htx_red { + return [self colorWithRed:203.f/255.f + green:9.f/255.f + blue:34.f/255.f + alpha:1]; +} + +// White #EDF2F5 -> rgb (237,242,245) + ++ (instancetype) htx_white { + return [self colorWithRed:237.f/255.f + green:242.f/255.f + blue:245.f/255.f + alpha:1]; +} + +// Lighter Blue #8AA8BC -> rgb (138,168,188) + ++ (instancetype) htx_lighterBlue { + return [self colorWithRed:138.f/255.f + green:168.f/255.f + blue:188.f/255.f + alpha:1]; +} + +@end diff --git a/HackTX/UserPrefs.swift b/HackTX/UserPrefs.swift deleted file mode 100644 index 25d45a5..0000000 --- a/HackTX/UserPrefs.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// UserPref.swift -// HackTX -// -// Created by Drew Romanyk on 9/4/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import Foundation - -import Foundation -import UIKit - -class UserPrefs { - - static var instance: UserPrefs! - - class func shared() -> UserPrefs { - self.instance = (self.instance ?? UserPrefs()) - return self.instance - } - - func getUserDefaults() -> NSUserDefaults { - return NSUserDefaults.standardUserDefaults() - } - - func registerDefaults() { - let dictionary = ["checkedIn": false, - "checkInEmail": "", "registeredCheckIn": false] - getUserDefaults().registerDefaults(dictionary) - } - - func isCheckedIn() -> Bool { - return getUserDefaults().boolForKey("checkedIn") - } - - func setIsCheckedIn(checkedIn: Bool) { - getUserDefaults().setBool(checkedIn, forKey: "checkedIn") - } - - func getCheckedEmail() -> String { - return getUserDefaults().valueForKey("checkInEmail") as! String - } - - func setCheckedEmail(checkedEmail: String) { - getUserDefaults().setValue(checkedEmail, forKey: "checkInEmail") - } - - func isRegisteredForCheckInNotif() -> Bool { - return getUserDefaults().boolForKey("registeredCheckIn") - } - - func setRegisterForCheckInNotif(registered: Bool) { - getUserDefaults().setBool(registered, forKey: "registeredCheckIn") - } - - func isFeedbackEventDone(id: Int) -> Bool { - return getUserDefaults().boolForKey("feedbackEvent=\(id)") - } - - func setFeedbackEventDone(id: Int) { - getUserDefaults().setBool(true, forKey: "feedbackEvent=\(id)") - } -} \ No newline at end of file diff --git a/HackTX/main.m b/HackTX/main.m new file mode 100644 index 0000000..5de538c --- /dev/null +++ b/HackTX/main.m @@ -0,0 +1,16 @@ +// +// main.m +// HackTX +// +// Created by Jose Bethancourt on 8/31/16. +// Copyright © 2016 HackTX. 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/HackTX/partners.json b/HackTX/partners.json new file mode 100644 index 0000000..f13045f --- /dev/null +++ b/HackTX/partners.json @@ -0,0 +1,98 @@ +[ + { + "name": "HackTX", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 0 + }, + { + "name": "HackTX 1", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 1 + }, + { + "name": "HackTX 2", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 1 + }, + { + "name": "HackTX 3", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 1 + }, + { + "name": "HackTX 4", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 1 + }, + { + "name": "HackTX 5", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 6", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 7", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 8", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 9", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 10", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 11", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 2 + }, + { + "name": "HackTX 12", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 3 + }, + { + "name": "HackTX 13", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 3 + }, + { + "name": "HackTX 14", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 3 + }, + { + "name": "HackTX 15", + "logoImage": "http://placehold.it/500x500", + "website": "http://hacktx.com/", + "level": 3 + } +] diff --git a/HackTXTests/HackTXTests.swift b/HackTXTests/HackTXTests.swift deleted file mode 100644 index b41c291..0000000 --- a/HackTXTests/HackTXTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// HackTXTests.swift -// HackTXTests -// -// Created by Rohit Datta on 7/11/15. -// Copyright (c) 2015 HackTX. All rights reserved. -// - -import UIKit -import XCTest - -class HackTXTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - XCTAssert(true, "Pass") - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/HackTXTests/Info.plist b/HackTXTests/Info.plist deleted file mode 100644 index 8b70776..0000000 --- a/HackTXTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.HackTX.$(PRODUCT_NAME:rfc1034identifier) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 20cc18d..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 HackTX - -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/Parse.framework/Headers/PFACL.h b/Parse.framework/Headers/PFACL.h deleted file mode 100644 index a952585..0000000 --- a/Parse.framework/Headers/PFACL.h +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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/Parse.framework/Headers/PFAnalytics.h b/Parse.framework/Headers/PFAnalytics.h deleted file mode 100644 index 97a74c3..0000000 --- a/Parse.framework/Headers/PFAnalytics.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 *)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 *)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 *)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 *)trackEvent:(NSString *)name dimensions:(PF_NULLABLE NSDictionary *)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 *)dimensions - block:(PF_NULLABLE PFBooleanResultBlock)block; - -@end - -PF_ASSUME_NONNULL_END diff --git a/Parse.framework/Headers/PFAnonymousUtils.h b/Parse.framework/Headers/PFAnonymousUtils.h deleted file mode 100644 index 498d838..0000000 --- a/Parse.framework/Headers/PFAnonymousUtils.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 *)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/Parse.framework/Headers/PFCloud.h b/Parse.framework/Headers/PFCloud.h deleted file mode 100644 index a4a93bb..0000000 --- a/Parse.framework/Headers/PFCloud.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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; - -/*! - @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 *)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/Parse.framework/Headers/PFConfig.h b/Parse.framework/Headers/PFConfig.h deleted file mode 100644 index c3d8eba..0000000 --- a/Parse.framework/Headers/PFConfig.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 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; - -/*! - @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 *)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/Parse.framework/Headers/PFConstants.h b/Parse.framework/Headers/PFConstants.h deleted file mode 100644 index 6e3251d..0000000 --- a/Parse.framework/Headers/PFConstants.h +++ /dev/null @@ -1,422 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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.8.2" - -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 (^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 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 diff --git a/Parse.framework/Headers/PFFile.h b/Parse.framework/Headers/PFFile.h deleted file mode 100644 index 6c8147a..0000000 --- a/Parse.framework/Headers/PFFile.h +++ /dev/null @@ -1,388 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 -///-------------------------------------- - -/*! - @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`. - */ -+ (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. - */ -+ (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. - */ -+ (instancetype)fileWithName:(PF_NULLABLE NSString *)name contentsAtPath:(NSString *)path; - -/*! - @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. - */ -+ (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. - */ -+ (instancetype)fileWithName:(PF_NULLABLE NSString *)name - data:(NSData *)data - contentType:(PF_NULLABLE NSString *)contentType; - -/*! - @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. - */ -+ (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; - -/*! - @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; - -///-------------------------------------- -/// @name Storing Data with Parse -///-------------------------------------- - -/*! - @abstract Whether the file has been uploaded for the first time. - */ -@property (nonatomic, assign, readonly) BOOL isDirty; - -/*! - @abstract Saves the file *synchronously*. - - @returns Returns whether the save succeeded. - */ -- (BOOL)save; - -/*! - @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 *)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 *)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; - -/*! - @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; - -/*! - @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 *)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 *)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 *)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 *)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 *)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 *)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; - -///-------------------------------------- -/// @name Interrupting a Transfer -///-------------------------------------- - -/*! - @abstract Cancels the current request (upload or download of file). - */ -- (void)cancel; - -@end - -PF_ASSUME_NONNULL_END diff --git a/Parse.framework/Headers/PFGeoPoint.h b/Parse.framework/Headers/PFGeoPoint.h deleted file mode 100644 index 37d3bb0..0000000 --- a/Parse.framework/Headers/PFGeoPoint.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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/Parse.framework/Headers/PFInstallation.h b/Parse.framework/Headers/PFInstallation.h deleted file mode 100644 index 7907a75..0000000 --- a/Parse.framework/Headers/PFInstallation.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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. - */ - -@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/Parse.framework/Headers/PFNetworkActivityIndicatorManager.h b/Parse.framework/Headers/PFNetworkActivityIndicatorManager.h deleted file mode 100644 index d5b376a..0000000 --- a/Parse.framework/Headers/PFNetworkActivityIndicatorManager.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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/Parse.framework/Headers/PFNullability.h b/Parse.framework/Headers/PFNullability.h deleted file mode 100644 index 8c1b958..0000000 --- a/Parse.framework/Headers/PFNullability.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in 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/Parse.framework/Headers/PFObject+Subclass.h b/Parse.framework/Headers/PFObject+Subclass.h deleted file mode 100644 index bda5dad..0000000 --- a/Parse.framework/Headers/PFObject+Subclass.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 PFQuery; - -/*! - ### 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/Parse.framework/Headers/PFObject.h b/Parse.framework/Headers/PFObject.h deleted file mode 100644 index ba829e0..0000000 --- a/Parse.framework/Headers/PFObject.h +++ /dev/null @@ -1,1429 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 *)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 *)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:(PF_NULLABLE_S 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; - -/*! - @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 *)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 *)saveEventually; - -/*! - @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; - -///-------------------------------------- -/// @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 *)objects; - -/*! - @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 *)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 *)saveAllInBackground:(PF_NULLABLE NSArray *)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 *)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 *)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 *)objects; - -/*! - @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 *)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 *)deleteAllInBackground:(PF_NULLABLE NSArray *)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 *)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 *)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. - */ -- (void)refresh 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. - */ -- (void)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. - */ -- (void)fetch; -/*! - @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. - */ -- (void)fetch:(NSError **)error; - -/*! - @abstract *Synchronously* fetches the `PFObject` data from the server if is `NO`. - */ -- (PF_NULLABLE PFObject *)fetchIfNeeded; - -/*! - @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 PFObject *)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 *)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 *)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. - */ -+ (void)fetchAll:(PF_NULLABLE NSArray *)objects; - -/*! - @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. - */ -+ (void)fetchAll:(PF_NULLABLE NSArray *)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. - */ -+ (void)fetchAllIfNeeded:(PF_NULLABLE NSArray *)objects; - -/*! - @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. - */ -+ (void)fetchAllIfNeeded:(PF_NULLABLE NSArray *)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 *)fetchAllInBackground:(PF_NULLABLE NSArray *)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 *)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 *)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 *)fetchAllIfNeededInBackground:(PF_NULLABLE NSArray *)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 *)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 *)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. - */ -- (void)fetchFromLocalDatastore; - -/*! - @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. - */ -- (void)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 *)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; - -/*! - @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 *)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 *)deleteEventually; - -///-------------------------------------- -/// @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; - -/*! - @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; - -/*! - @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 *)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 *)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 *)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. - @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 *)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 *)objects withName:(NSString *)name; - -/*! - @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 *)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 *)pinAllInBackground:(PF_NULLABLE NSArray *)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 *)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 *)pinAllInBackground:(PF_NULLABLE NSArray *)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 *)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; - -/*! - @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; - -/*! - @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 *)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 *)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; - -/*! - @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; - -/*! - @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 *)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 *)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 *)objects; - -/*! - @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 *)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 *)objects withName:(NSString *)name; - -/*! - @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 *)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 *)unpinAllInBackground:(PF_NULLABLE NSArray *)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 *)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 *)unpinAllInBackground:(PF_NULLABLE NSArray *)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 *)objects - withName:(NSString *)name - block:(PF_NULLABLE PFBooleanResultBlock)block; - -@end - -PF_ASSUME_NONNULL_END diff --git a/Parse.framework/Headers/PFProduct.h b/Parse.framework/Headers/PFProduct.h deleted file mode 100644 index 8bdf1ae..0000000 --- a/Parse.framework/Headers/PFProduct.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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. - */ -@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/Parse.framework/Headers/PFPurchase.h b/Parse.framework/Headers/PFPurchase.h deleted file mode 100644 index ee81874..0000000 --- a/Parse.framework/Headers/PFPurchase.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 - -/*! - `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:(void(^)(SKPaymentTransaction *transaction))block; - -/*! - @abstract *Asynchronously* initiates the purchase for the product. - - @param productIdentifier the product identifier - @param block the completion block. - */ -+ (void)buyProduct:(NSString *)productIdentifier block:(void(^)(NSError *error))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:(void(^)(NSString *filePath, NSError *error))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:(void(^)(NSString *filePath, NSError *error))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/Parse.framework/Headers/PFPush.h b/Parse.framework/Headers/PFPush.h deleted file mode 100644 index afdca65..0000000 --- a/Parse.framework/Headers/PFPush.h +++ /dev/null @@ -1,531 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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_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 *)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 *)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 *)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 *)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 *)sendPushMessageToQueryInBackground:(PFQuery *)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 *)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 *)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 *)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 *)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 *)sendPushDataToQueryInBackground:(PFQuery *)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 *)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 *)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 *)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 *)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/Parse.framework/Headers/PFQuery.h b/Parse.framework/Headers/PFQuery.h deleted file mode 100644 index e7fab6e..0000000 --- a/Parse.framework/Headers/PFQuery.h +++ /dev/null @@ -1,886 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 : NSObject - -///-------------------------------------- -/// @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 *)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 *)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 *)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 PFObject *)getObjectOfClass:(NSString *)objectClass objectId:(NSString *)objectId; - -/*! - @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 PFObject *)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 PFObject *)getObjectWithId:(NSString *)objectId; - -/*! - @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 PFObject *)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 *)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 PFObjectResultBlock)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; - -/*! - 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 *)findObjects; - -/*! - @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 *)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 *)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 PFArrayResultBlock)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 PFObject *)getFirstObject; - -/*! - @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 PFObject *)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 *)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 PFObjectResultBlock)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; - -/*! - @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 *)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/Parse.framework/Headers/PFRelation.h b/Parse.framework/Headers/PFRelation.h deleted file mode 100644 index af64ff9..0000000 --- a/Parse.framework/Headers/PFRelation.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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/Parse.framework/Headers/PFRole.h b/Parse.framework/Headers/PFRole.h deleted file mode 100644 index 18d21c9..0000000 --- a/Parse.framework/Headers/PFRole.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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/Parse.framework/Headers/PFSession.h b/Parse.framework/Headers/PFSession.h deleted file mode 100644 index ebbe6ce..0000000 --- a/Parse.framework/Headers/PFSession.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 *)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/Parse.framework/Headers/PFSubclassing.h b/Parse.framework/Headers/PFSubclassing.h deleted file mode 100644 index 5e40687..0000000 --- a/Parse.framework/Headers/PFSubclassing.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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_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/Parse.framework/Headers/PFUser.h b/Parse.framework/Headers/PFUser.h deleted file mode 100644 index 542de5a..0000000 --- a/Parse.framework/Headers/PFUser.h +++ /dev/null @@ -1,455 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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; - -/*! - 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. - */ -+ (PFUser *)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; - -/*! - @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 *)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; - -/*! - @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 *)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; - -/*! - @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 *)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; - -/*! - @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 *)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; - -@end - -PF_ASSUME_NONNULL_END diff --git a/Parse.framework/Headers/Parse.h b/Parse.framework/Headers/Parse.h deleted file mode 100644 index d3b98dc..0000000 --- a/Parse.framework/Headers/Parse.h +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) 2015-present, Parse, LLC. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the 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 -#import - -#if TARGET_OS_IPHONE - -#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:`."); - -/*! - @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; - -/*! - @abstract Application Group Identifier for Data Sharing - - @returns `NSString` value if data sharing is enabled, otherwise `nil`. - */ -+ (NSString *)applicationGroupIdentifierForDataSharing; - -/*! - @abstract Containing application bundle identifier. - - @returns `NSString` value if data sharing is enabled, otherwise `nil`. - */ -+ (NSString *)containingApplicationBundleIdentifierForDataSharing; - -#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/Parse.framework/Info.plist b/Parse.framework/Info.plist deleted file mode 100644 index dd4c63b..0000000 Binary files a/Parse.framework/Info.plist and /dev/null differ diff --git a/Parse.framework/Modules/module.modulemap b/Parse.framework/Modules/module.modulemap deleted file mode 100644 index 32a75e9..0000000 --- a/Parse.framework/Modules/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Parse { - umbrella header "Parse.h" - - export * - module * { export * } -} diff --git a/Parse.framework/Parse b/Parse.framework/Parse deleted file mode 100644 index 84353ff..0000000 Binary files a/Parse.framework/Parse and /dev/null differ diff --git a/Parse.framework/en.lproj/Parse.strings b/Parse.framework/en.lproj/Parse.strings deleted file mode 100644 index 815195a..0000000 Binary files a/Parse.framework/en.lproj/Parse.strings and /dev/null differ diff --git a/Parse.framework/third_party_licenses.txt b/Parse.framework/third_party_licenses.txt deleted file mode 100644 index a49d8d1..0000000 --- a/Parse.framework/third_party_licenses.txt +++ /dev/null @@ -1,68 +0,0 @@ -THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE PARSE PRODUCT. - ------ - -The following software may be included in this product: OAuthCore. This software contains the following license and notice below: - -Copyright (C) 2012 Loren Brichter - -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. - ------ - -The following software may be included in this product: google-breakpad. This software contains the following license and notice below: - -Copyright (c) 2006, Google 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 of Google Inc. 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 -OWNER 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. - --------------------------------------------------------------------- - -Copyright 2001-2004 Unicode, Inc. - -Disclaimer - -This source code is provided as is by Unicode, Inc. No claims are -made as to fitness for any particular purpose. No warranties of any -kind are expressed or implied. The recipient agrees to determine -applicability of information provided. If this file has been -purchased on magnetic or optical media from Unicode, Inc., the -sole remedy for any claim will be exchange of defective media -within 90 days of receipt. - -Limitations on Rights to Redistribute This Code - -Unicode, Inc. hereby grants the right to freely use the information -supplied in this file in the creation of products supporting the -Unicode Standard, and to make copies of this file in any form -for internal or external distribution as long as this notice -remains attached. diff --git a/Podfile b/Podfile index 6cd247f..89de289 100644 --- a/Podfile +++ b/Podfile @@ -1,15 +1,21 @@ # Uncomment this line to define a global platform for your project -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -use_frameworks! +platform :ios, '9.0' target 'HackTX' do - pod 'Google/Analytics', '~> 1.0.0' - pod 'Alamofire', '~> 2.0' - pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git' - pod 'RSBarcodes_Swift', :git => 'https://github.com/yeahdongcn/RSBarcodes_Swift.git', :branch => 'Swift2.0' + # Uncomment this line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + # Pods for HackTX + pod 'Firebase/Core' + pod 'Firebase/Messaging' + pod 'MBProgressHUD', '~> 1.0.0' + pod 'Realm' + pod 'AFNetworking' + pod 'FCAlertView' + pod 'ChameleonFramework' + pod 'GoogleMaps' + pod 'GooglePlaces' + pod 'Fabric' + pod 'Crashlytics' + pod 'SVProgressHUD' end - - - diff --git a/Podfile.lock b/Podfile.lock index d509dd7..b02ddeb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,51 +1,103 @@ PODS: - - Alamofire (2.0.1) - - Google/Analytics (1.0.7): - - Google/Core - - GoogleAnalytics (~> 3.12) - - Google/Core (1.0.7): - - GoogleNetworkingUtilities (~> 1.0) + - AFNetworking (3.1.0): + - AFNetworking/NSURLSession (= 3.1.0) + - AFNetworking/Reachability (= 3.1.0) + - AFNetworking/Security (= 3.1.0) + - AFNetworking/Serialization (= 3.1.0) + - AFNetworking/UIKit (= 3.1.0) + - AFNetworking/NSURLSession (3.1.0): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (3.1.0) + - AFNetworking/Security (3.1.0) + - AFNetworking/Serialization (3.1.0) + - AFNetworking/UIKit (3.1.0): + - AFNetworking/NSURLSession + - ChameleonFramework (2.1.0): + - ChameleonFramework/Default (= 2.1.0) + - ChameleonFramework/Default (2.1.0) + - Crashlytics (3.8.3): + - Fabric (~> 1.6.3) + - Fabric (1.6.10) + - FCAlertView (1.1.2) + - Firebase/Core (3.7.1): + - FirebaseAnalytics (= 3.4.4) + - FirebaseCore (= 3.4.3) + - Firebase/Messaging (3.7.1): + - Firebase/Core + - FirebaseMessaging (= 1.2.0) + - FirebaseAnalytics (3.4.4): + - FirebaseCore (~> 3.4) + - FirebaseInstanceID (~> 1.0) + - GoogleInterchangeUtilities (~> 1.2) + - GoogleSymbolUtilities (~> 1.1) + - FirebaseCore (3.4.3): + - GoogleInterchangeUtilities (~> 1.2) + - GoogleUtilities (~> 1.2) + - FirebaseInstanceID (1.0.8) + - FirebaseMessaging (1.2.0): + - FirebaseAnalytics (~> 3.3) + - FirebaseInstanceID (~> 1.0) + - GoogleInterchangeUtilities (~> 1.2) + - GoogleIPhoneUtilities (~> 1.2) + - GoogleSymbolUtilities (~> 1.1) + - GoogleInterchangeUtilities (1.2.2): + - GoogleSymbolUtilities (~> 1.1) + - GoogleIPhoneUtilities (1.2.1): - GoogleSymbolUtilities (~> 1.0) - GoogleUtilities (~> 1.0) - - GoogleAnalytics (3.13.0) - - GoogleNetworkingUtilities (1.0.0): - - GoogleSymbolUtilities (~> 1.0) - - GoogleSymbolUtilities (1.0.0) - - GoogleUtilities (1.0.1): - - GoogleSymbolUtilities (~> 1.0) - - RSBarcodes_Swift (0.0.9) - - SwiftyJSON (2.3.0) + - GoogleMaps (2.1.0): + - GoogleMaps/Maps (= 2.1.0) + - GoogleMaps/Base (2.1.0) + - GoogleMaps/Maps (2.1.0): + - GoogleMaps/Base (= 2.1.0) + - GooglePlaces (2.1.0): + - GoogleMaps/Base (= 2.1.0) + - GoogleSymbolUtilities (1.1.2) + - GoogleUtilities (1.3.2): + - GoogleSymbolUtilities (~> 1.1) + - MBProgressHUD (1.0.0) + - Realm (2.0.2): + - Realm/Headers (= 2.0.2) + - Realm/Headers (2.0.2) + - SVProgressHUD (2.0.3) DEPENDENCIES: - - Alamofire (~> 2.0) - - Google/Analytics (~> 1.0.0) - - RSBarcodes_Swift (from `https://github.com/yeahdongcn/RSBarcodes_Swift.git`, branch - `Swift2.0`) - - SwiftyJSON (from `https://github.com/SwiftyJSON/SwiftyJSON.git`) - -EXTERNAL SOURCES: - RSBarcodes_Swift: - :branch: Swift2.0 - :git: https://github.com/yeahdongcn/RSBarcodes_Swift.git - SwiftyJSON: - :git: https://github.com/SwiftyJSON/SwiftyJSON.git - -CHECKOUT OPTIONS: - RSBarcodes_Swift: - :commit: d3c89b0231976afa5be74a424952dae4934b1246 - :git: https://github.com/yeahdongcn/RSBarcodes_Swift.git - SwiftyJSON: - :commit: 3b5d0d4e0c19173b1bf0dc823bde25ad870f70c0 - :git: https://github.com/SwiftyJSON/SwiftyJSON.git + - AFNetworking + - ChameleonFramework + - Crashlytics + - Fabric + - FCAlertView + - Firebase/Core + - Firebase/Messaging + - GoogleMaps + - GooglePlaces + - MBProgressHUD (~> 1.0.0) + - Realm + - SVProgressHUD SPEC CHECKSUMS: - Alamofire: 1d8e208d616fbbfd2391b15eae766d07c96cdc49 - Google: 875f5a8ade86dd02e36586e4a54814843938c7f6 - GoogleAnalytics: fcf1f1cad57ae1a9a53520018f0f680ceeeb8b49 - GoogleNetworkingUtilities: 292992ab31b7b4ba32809a0e8b40e324cdfb2c0c - GoogleSymbolUtilities: a7b0feed1fe667c8d5e647177938d0ecd62d6948 - GoogleUtilities: e3475cd4d45134221d2f3f3626095a1c49b62041 - RSBarcodes_Swift: dff2e4b0e2328d0a4fe3e92801e45749ba16ecb0 - SwiftyJSON: 8d6b61a70277ef2a5d710d372e06e7e2d87fb9e4 + AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 + ChameleonFramework: d21a3cc247abfe5e37609a283a8238b03575cf64 + Crashlytics: 2b6dbe138a42395577cfa73dfa1aa7248cadf39e + Fabric: c73f371ee543e3f0b80608f2674750e4910d1669 + FCAlertView: 8c777212f654a8970b9b192904bf8b84d32582a2 + Firebase: be473484f0d515e72ffd04acf22f981773c23e58 + FirebaseAnalytics: 0533a00b681e08fd0b2cd0444b7ddf0ef9fd7a1a + FirebaseCore: 5a885548bbc5f0c410b04f8e9ac9d73ff1221907 + FirebaseInstanceID: ba1e640935235e5fac39dfa816fe7660e72e1a8a + FirebaseMessaging: 789d23fd796594dfb55dcf36cd325541df887c22 + GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7 + GoogleIPhoneUtilities: 63f25e93a3ddcb66884d182aab3a660d98f1479b + GoogleMaps: 06589b9a38097bce0cd6e90f0fd9b5e4b4a9344c + GooglePlaces: 16e96266483b8010e9f275399403647978523c86 + GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96 + GoogleUtilities: 8bbc733218aad26306f9d4a253823986110e3358 + MBProgressHUD: 4890f671c94e8a0f3cf959aa731e9de2f036d71a + Realm: 0f5a194af0b7e6158b83db41b4ee1a00be7ddf1a + SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4 + +PODFILE CHECKSUM: 5e97ee0504e715c4312054d413cb726e8ffdd610 -COCOAPODS: 0.38.2 +COCOAPODS: 1.0.1 diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h new file mode 100644 index 0000000..5ce279a --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1,295 @@ +// AFHTTPSessionManager.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV +#import +#else +#import +#endif + +#import "AFURLSessionManager.h" + +/** + `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. + */ + +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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; + + +/** + 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 downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @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:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(nullable id)parameters + progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable 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 * _Nullable 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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; + +/** + 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 uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @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:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable 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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE; + +/** + 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 uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @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:uploadProgress:downloadProgress:completionHandler: + */ +- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + constructingBodyWithBlock:(nullable void (^)(id formData))block + progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress + success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable 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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable 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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable 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 _Nullable responseObject))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m new file mode 100644 index 0000000..2591070 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m @@ -0,0 +1,361 @@ +// AFHTTPSessionManager.m +// Copyright (c) 2011–2016 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" + +#import "AFURLRequestSerialization.h" +#import "AFURLResponseSerialization.h" + +#import +#import +#import + +#import +#import +#import +#import +#import + +#if TARGET_OS_IOS || TARGET_OS_TV +#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 - + +- (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 +{ + + return [self GET:URLString parameters:parameters progress:nil success:success failure:failure]; +} + +- (NSURLSessionDataTask *)GET:(NSString *)URLString + parameters:(id)parameters + progress:(void (^)(NSProgress * _Nonnull))downloadProgress + success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success + failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" + URLString:URLString + parameters:parameters + uploadProgress:nil + downloadProgress:downloadProgress + 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 uploadProgress:nil downloadProgress:nil 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 +{ + return [self POST:URLString parameters:parameters progress:nil success:success failure:failure]; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + progress:(void (^)(NSProgress * _Nonnull))uploadProgress + success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success + failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(nullable id)parameters + constructingBodyWithBlock:(nullable void (^)(id _Nonnull))block + success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success + failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure +{ + return [self POST:URLString parameters:parameters constructingBodyWithBlock:block progress:nil success:success failure:failure]; +} + +- (NSURLSessionDataTask *)POST:(NSString *)URLString + parameters:(id)parameters + constructingBodyWithBlock:(void (^)(id formData))block + progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress + 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:uploadProgress 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 uploadProgress:nil downloadProgress:nil 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 uploadProgress:nil downloadProgress:nil 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 uploadProgress:nil downloadProgress:nil success:success failure:failure]; + + [dataTask resume]; + + return dataTask; +} + +- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(id)parameters + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress + 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 + uploadProgress:uploadProgress + downloadProgress:downloadProgress + 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; +} + +- (instancetype)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))]; + AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))]; + if (decodedPolicy) { + self.securityPolicy = decodedPolicy; + } + + 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))]; + [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))]; +} + +#pragma mark - NSCopying + +- (instancetype)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]; + HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone]; + return HTTPClient; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h new file mode 100644 index 0000000..0feb18d --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1,206 @@ +// AFNetworkReachabilityManager.h +// Copyright (c) 2011–2016 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 + +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 with the default socket address. + + @return An initialized network reachability manager, actively monitoring the default socket address. + */ ++ (instancetype)manager; + +/** + 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_in6`) 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..d458364 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m @@ -0,0 +1,263 @@ +// AFNetworkReachabilityManager.m +// Copyright (c) 2011–2016 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); + +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; +} + +/** + * Queue a status change notification for the main thread. + * + * This is done to ensure that the notifications are received in the same order + * as they are sent. If notifications are sent directly, it is possible that + * a queued notification (for an earlier status condition) is processed after + * the later update, resulting in the listener being left in the wrong state. + */ +static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + dispatch_async(dispatch_get_main_queue(), ^{ + if (block) { + block(status); + } + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; + [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; + }); +} + +static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { + AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); +} + + +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 (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability; +@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, ^{ + _sharedManager = [self manager]; + }); + + return _sharedManager; +} + ++ (instancetype)managerForDomain:(NSString *)domain { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); + + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + CFRelease(reachability); + + return manager; +} + ++ (instancetype)managerForAddress:(const void *)address { + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); + AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; + + CFRelease(reachability); + + return manager; +} + ++ (instancetype)manager +{ +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + struct sockaddr_in6 address; + bzero(&address, sizeof(address)); + address.sin6_len = sizeof(address); + address.sin6_family = AF_INET6; +#else + struct sockaddr_in address; + bzero(&address, sizeof(address)); + address.sin_len = sizeof(address); + address.sin_family = AF_INET; +#endif + return [self managerForAddress:&address]; +} + +- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { + self = [super init]; + if (!self) { + return nil; + } + + _networkReachability = CFRetain(reachability); + self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + + return self; +} + +- (instancetype)init NS_UNAVAILABLE +{ + return nil; +} + +- (void)dealloc { + [self stopMonitoring]; + + if (_networkReachability != NULL) { + CFRelease(_networkReachability); + } +} + +#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); + } + + }; + + SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { + AFPostReachabilityStatusChange(flags, callback); + } + }); +} + +- (void)stopMonitoring { + if (!self.networkReachability) { + return; + } + + SCNetworkReachabilityUnscheduleFromRunLoop(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..e2fb2f4 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFNetworking.h @@ -0,0 +1,41 @@ +// 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 +#import + +#ifndef _AFNETWORKING_ + #define _AFNETWORKING_ + + #import "AFURLRequestSerialization.h" + #import "AFURLResponseSerialization.h" + #import "AFSecurityPolicy.h" + +#if !TARGET_OS_WATCH + #import "AFNetworkReachabilityManager.h" +#endif + + #import "AFURLSessionManager.h" + #import "AFHTTPSessionManager.h" + +#endif /* _AFNETWORKING_ */ diff --git a/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h new file mode 100644 index 0000000..c005efa --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1,154 @@ +// AFSecurityPolicy.h +// Copyright (c) 2011–2016 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 target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`. + + Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches. + */ +@property (nonatomic, strong, nullable) NSSet *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 Certificates from the Bundle +///----------------------------------------- + +/** + Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`. + + @return The certificates included in the given bundle. + */ ++ (NSSet *)certificatesInBundle:(NSBundle *)bundle; + +///----------------------------------------- +/// @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; + +/** + Creates and returns a security policy with the specified pinning mode. + + @param pinningMode The SSL pinning mode. + @param pinnedCertificates The certificates to pin against. + + @return A new security policy. + */ ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates; + +///------------------------------ +/// @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. + @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..ec81d37 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m @@ -0,0 +1,353 @@ +// AFSecurityPolicy.m +// Copyright (c) 2011–2016 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 && !TARGET_OS_TV +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 || TARGET_OS_TV + 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) NSSet *pinnedPublicKeys; +@end + +@implementation AFSecurityPolicy + ++ (NSSet *)certificatesInBundle:(NSBundle *)bundle { + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + return [NSSet setWithSet:certificates]; +} + ++ (NSSet *)defaultPinnedCertificates { + static NSSet *_defaultPinnedCertificates = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + _defaultPinnedCertificates = [self certificatesInBundle:bundle]; + }); + + return _defaultPinnedCertificates; +} + ++ (instancetype)defaultPolicy { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = AFSSLPinningModeNone; + + return securityPolicy; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { + return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; +} + ++ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { + AFSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = pinningMode; + + [securityPolicy setPinnedCertificates:pinnedCertificates]; + + return securityPolicy; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.validatesDomainName = YES; + + return self; +} + +- (void)setPinnedCertificates:(NSSet *)pinnedCertificates { + _pinnedCertificates = pinnedCertificates; + + if (self.pinnedCertificates) { + NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; + for (NSData *certificate in self.pinnedCertificates) { + id publicKey = AFPublicKeyForCertificate(certificate); + if (!publicKey) { + continue; + } + [mutablePinnedPublicKeys addObject:publicKey]; + } + self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; + } else { + self.pinnedPublicKeys = nil; + } +} + +#pragma mark - + +- (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) { + return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); + } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + return NO; + } + + 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; + } + + // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA) + NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); + + for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { + if ([self.pinnedCertificates containsObject:trustChainCertificate]) { + return YES; + } + } + + return NO; + } + 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"]; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + + self = [self init]; + if (!self) { + return nil; + } + + self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue]; + self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; + self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))]; + self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))]; + [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; + [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))]; + [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init]; + securityPolicy.SSLPinningMode = self.SSLPinningMode; + securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates; + securityPolicy.validatesDomainName = self.validatesDomainName; + securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; + + return securityPolicy; +} + +@end diff --git a/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h new file mode 100644 index 0000000..694696b --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1,479 @@ +// AFURLRequestSerialization.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV +#import +#elif TARGET_OS_WATCH +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + 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. + + @param string The string to be percent-escaped. + + @return The percent-escaped string. + */ +FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string); + +/** + A helper method to generate encoded url query parameters for appending to the end of a URL. + + @param parameters A dictionary of key/values to be encoded. + + @return A url encoded query string + */ +FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters); + +/** + 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 NS_SWIFT_NOTHROW; + +@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; + +/** + 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 +///------------------------------- + +/** + 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; + +/** + 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..9a2ac98 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m @@ -0,0 +1,1376 @@ +// AFURLRequestSerialization.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV +#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); + +/** + 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. + */ +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; + + NSUInteger index = 0; + NSMutableString *escaped = @"".mutableCopy; + + while (index < string.length) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu" + NSUInteger length = MIN(string.length - index, batchSize); +#pragma GCC diagnostic pop + 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; + +- (instancetype)initWithField:(id)field value:(id)value; + +- (NSString *)URLEncodedStringValue; +@end + +@implementation AFQueryStringPair + +- (instancetype)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); + +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 +{ + NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; + [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] 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 + 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 + 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 && query.length > 0) { + 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; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [self init]; + if (!self) { + return nil; + } + + self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; + self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[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 + +- (instancetype)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) { + 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; + } +} + +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; + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 = MIN(length, self.numberOfBytesInPacket) - (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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = (NSPropertyListFormat)[[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 + +- (instancetype)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..a9430ad --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1,311 @@ +// AFURLResponseSerialization.h +// Copyright (c) 2011–2016 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 NS_SWIFT_NOTHROW; + +@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 TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +/** + 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 100755 index 0000000..5e46799 --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m @@ -0,0 +1,805 @@ +// AFURLResponseSerialization.m +// Copyright (c) 2011–2016 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" + +#import + +#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]] && + !([response MIMEType] == nil && [data length] == 0)) { + + 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; +} + +- (instancetype)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 + +- (instancetype)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; + } + } + + id responseObject = nil; + NSError *serializationError = 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 + BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]]; + if (data.length > 0 && !isSpace) { + responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError]; + } else { + return nil; + } + + if (self.removesKeysWithNullValues && responseObject) { + responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions); + } + + if (error) { + *error = AFErrorWithUnderlyingError(serializationError, *error); + } + + return responseObject; +} + +#pragma mark - NSSecureCoding + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)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 + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + + self.format = (NSPropertyListFormat)[[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 + +- (instancetype)copyWithZone:(NSZone *)zone { + AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + serializer.format = self.format; + serializer.readOptions = self.readOptions; + + return serializer; +} + +@end + +#pragma mark - + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +#import +#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 || TARGET_OS_TV + 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 TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + if (self.automaticallyInflatesResponseImage) { + return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale); + } else { + return AFImageWithDataAtScale(data, self.imageScale); + } +#else + // 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 + +- (instancetype)initWithCoder:(NSCoder *)decoder { + self = [super initWithCoder:decoder]; + if (!self) { + return nil; + } + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + 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 TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + [coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))]; + [coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))]; +#endif +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone { + AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init]; + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH + 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 + +- (instancetype)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 + +- (instancetype)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..89909fe --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h @@ -0,0 +1,500 @@ +// AFURLSessionManager.h +// Copyright (c) 2011–2016 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 + +/** + `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:needNewBodyStream:` + - `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 + +@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 session 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. + */ +@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue; + +/** + The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used. + */ +@property (nonatomic, strong, nullable) dispatch_group_t completionGroup; + +///--------------------------------- +/// @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; + +/** + Creates an `NSURLSessionDataTask` with the specified request. + + @param request The HTTP request for the request. + @param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @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 + uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + 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 uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @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:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + 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 uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @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:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + 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 uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue. + @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:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock + 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 downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @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:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + 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 downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue. + @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:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock + 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 task The session 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:(NSURLSessionTask *)task; + +/** + Returns the download progress of the specified task. + + @param task The session 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:(NSURLSessionTask *)task; + +///----------------------------------------- +/// @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 * _Nullable (^)(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 + +///-------------------- +/// @name Notifications +///-------------------- + +/** + 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. + */ +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 `AFNetworkingTaskDidCompleteNotification` 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 `AFNetworkingTaskDidCompleteNotification` 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 `AFNetworkingTaskDidCompleteNotification` 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 `AFNetworkingTaskDidCompleteNotification` 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 `AFNetworkingTaskDidCompleteNotification` 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..ef2108c --- /dev/null +++ b/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m @@ -0,0 +1,1244 @@ +// AFURLSessionManager.m +// Copyright (c) 2011–2016 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 + +#ifndef NSFoundationVersionNumber_iOS_8_0 +#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11 +#else +#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0 +#endif + +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 void url_session_manager_create_task_safely(dispatch_block_t block) { + if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) { + // Fix of bug + // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8) + // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093 + dispatch_sync(url_session_manager_creation_queue(), block); + } else { + block(); + } +} + +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 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"; + +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 (^AFURLSessionTaskProgressBlock)(NSProgress *); + +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 *uploadProgress; +@property (nonatomic, strong) NSProgress *downloadProgress; +@property (nonatomic, copy) NSURL *downloadFileURL; +@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; +@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; +@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; +@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; +@end + +@implementation AFURLSessionManagerTaskDelegate + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.mutableData = [NSMutableData data]; + self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + self.uploadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; + + self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil]; + self.downloadProgress.totalUnitCount = NSURLSessionTransferSizeUnknown; + return self; +} + +#pragma mark - NSProgress Tracking + +- (void)setupProgressForTask:(NSURLSessionTask *)task { + __weak __typeof__(task) weakTask = task; + + self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; + self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive; + [self.uploadProgress setCancellable:YES]; + [self.uploadProgress setCancellationHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask cancel]; + }]; + [self.uploadProgress setPausable:YES]; + [self.uploadProgress setPausingHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask suspend]; + }]; + if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) { + [self.uploadProgress setResumingHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask resume]; + }]; + } + + [self.downloadProgress setCancellable:YES]; + [self.downloadProgress setCancellationHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask cancel]; + }]; + [self.downloadProgress setPausable:YES]; + [self.downloadProgress setPausingHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask suspend]; + }]; + + if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) { + [self.downloadProgress setResumingHandler:^{ + __typeof__(weakTask) strongTask = weakTask; + [strongTask resume]; + }]; + } + + [task addObserver:self + forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) + options:NSKeyValueObservingOptionNew + context:NULL]; + [task addObserver:self + forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) + options:NSKeyValueObservingOptionNew + context:NULL]; + + [task addObserver:self + forKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) + options:NSKeyValueObservingOptionNew + context:NULL]; + [task addObserver:self + forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) + options:NSKeyValueObservingOptionNew + context:NULL]; + + [self.downloadProgress addObserver:self + forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) + options:NSKeyValueObservingOptionNew + context:NULL]; + [self.uploadProgress addObserver:self + forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) + options:NSKeyValueObservingOptionNew + context:NULL]; +} + +- (void)cleanUpProgressForTask:(NSURLSessionTask *)task { + [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; + [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]; + [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; + [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]; + [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; + [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) { + if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { + self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) { + self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { + self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; + } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) { + self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; + } + } + else if ([object isEqual:self.downloadProgress]) { + if (self.downloadProgressBlock) { + self.downloadProgressBlock(object); + } + } + else if ([object isEqual:self.uploadProgress]) { + if (self.uploadProgressBlock) { + self.uploadProgressBlock(object); + } + } +} + +#pragma mark - NSURLSessionTaskDelegate + +- (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]; + } + } + } +} + +@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 theClass, SEL originalSelector, SEL swizzledSelector) { + Method originalMethod = class_getInstanceMethod(theClass, originalSelector); + Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) { + return class_addMethod(theClass, 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 + */ + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + NSURLSessionDataTask *localDataTask = [session 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]; + [session finishTasksAndInvalidate]; + } +} + ++ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass { + Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume)); + Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend)); + + if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) { + af_swizzleSelector(theClass, @selector(resume), @selector(af_resume)); + } + + if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) { + af_swizzleSelector(theClass, @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 uploadProgress:nil downloadProgress:nil 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]; + } + }]; + + 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; + [delegate setupProgressForTask:task]; + [self addNotificationObserverForTask:task]; + [self.lock unlock]; +} + +- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock + 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]; + + delegate.uploadProgressBlock = uploadProgressBlock; + delegate.downloadProgressBlock = downloadProgressBlock; +} + +- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; + delegate.manager = self; + delegate.completionHandler = completionHandler; + + uploadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:uploadTask]; + + delegate.uploadProgressBlock = uploadProgressBlock; +} + +- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + 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); + }; + } + + downloadTask.taskDescription = self.taskDescriptionForSessionTasks; + + [self setDelegate:delegate forTask:downloadTask]; + + delegate.downloadProgressBlock = downloadProgressBlock; +} + +- (void)removeDelegateForTask:(NSURLSessionTask *)task { + NSParameterAssert(task); + + AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; + [self.lock lock]; + [delegate cleanUpProgressForTask:task]; + [self removeNotificationObserverForTask:task]; + [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; + [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 - +- (void)addNotificationObserverForTask:(NSURLSessionTask *)task { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task]; +} + +- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task { + [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidSuspendNotification object:task]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNSURLSessionTaskDidResumeNotification object:task]; +} + +#pragma mark - + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler]; +} + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock + downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock + completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { + + __block NSURLSessionDataTask *dataTask = nil; + url_session_manager_create_task_safely(^{ + dataTask = [self.session dataTaskWithRequest:request]; + }); + + [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; + + return dataTask; +} + +#pragma mark - + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + url_session_manager_create_task_safely(^{ + 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:uploadProgressBlock completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromData:(NSData *)bodyData + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + url_session_manager_create_task_safely(^{ + uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; + }); + + [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; + + return uploadTask; +} + +- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request + progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock + completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler +{ + __block NSURLSessionUploadTask *uploadTask = nil; + url_session_manager_create_task_safely(^{ + uploadTask = [self.session uploadTaskWithStreamedRequest:request]; + }); + + [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler]; + + return uploadTask; +} + +#pragma mark - + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + url_session_manager_create_task_safely(^{ + downloadTask = [self.session downloadTaskWithRequest:request]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData + progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock + destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination + completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler +{ + __block NSURLSessionDownloadTask *downloadTask = nil; + url_session_manager_create_task_safely(^{ + downloadTask = [self.session downloadTaskWithResumeData:resumeData]; + }); + + [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler]; + + return downloadTask; +} + +#pragma mark - +- (NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task { + return [[self delegateForTask:task] uploadProgress]; +} + +- (NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task { + return [[self delegateForTask:task] downloadProgress]; +} + +#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); + } + + [[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]; + } + } + + 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 +{ + 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 +{ + if (self.downloadTaskDidResume) { + self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes); + } +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)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 + +- (instancetype)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration]; +} + +@end diff --git a/Pods/AFNetworking/LICENSE b/Pods/AFNetworking/LICENSE new file mode 100644 index 0000000..3fbc2c9 --- /dev/null +++ b/Pods/AFNetworking/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011–2016 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..53cb202 --- /dev/null +++ b/Pods/AFNetworking/README.md @@ -0,0 +1,320 @@ +

+ AFNetworking +

+ +[![Build Status](https://travis-ci.org/AFNetworking/AFNetworking.svg)](https://travis-ci.org/AFNetworking/AFNetworking) +[![codecov.io](https://codecov.io/github/AFNetworking/AFNetworking/coverage.svg?branch=master)](https://codecov.io/github/AFNetworking/AFNetworking?branch=master) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/AFNetworking.svg)](https://img.shields.io/cocoapods/v/AFNetworking.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Platform](https://img.shields.io/cocoapods/p/AFNetworking.svg?style=flat)](http://cocoadocs.org/docsets/AFNetworking) +[![Twitter](https://img.shields.io/badge/twitter-@AFNetworking-blue.svg?style=flat)](http://twitter.com/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 3.0 Migration Guide](https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-3.0-Migration-Guide) for an overview of the architectural changes from 2.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 +AFNetworking supports multiple methods for installing the library in a project. + +## 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). You can install it with the following command: + +```bash +$ gem install cocoapods +``` + +> CocoaPods 0.39.0+ is required to build AFNetworking 3.0.0+. + +#### Podfile + +To integrate AFNetworking into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +pod 'AFNetworking', '~> 3.0' +``` + +Then, run the following command: + +```bash +$ pod install +``` + +### Installation with Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate AFNetworking into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "AFNetworking/AFNetworking" ~> 3.0 +``` + +Run `carthage` to build the framework and drag the built `AFNetworking.framework` into your Xcode project. + +## Requirements + +| AFNetworking Version | Minimum iOS Target | Minimum OS X Target | Minimum watchOS Target | Minimum tvOS Target | Notes | +|:--------------------:|:---------------------------:|:----------------------------:|:----------------------------:|:----------------------------:|:-------------------------------------------------------------------------:| +| 3.x | iOS 7 | OS X 10.9 | watchOS 2.0 | tvOS 9.0 | Xcode 7+ is required. `NSURLConnectionOperation` support has been removed. | +| 2.6 -> 2.6.3 | iOS 7 | OS X 10.9 | watchOS 2.0 | n/a | Xcode 7+ is required. | +| 2.0 -> 2.5.4 | iOS 6 | OS X 10.8 | n/a | n/a | Xcode 5+ is required. `NSURLSession` subspec requires iOS 7 or OS X 10.9. | +| 1.x | iOS 5 | Mac OS X 10.7 | n/a | n/a | +| 0.10.x | iOS 4 | Mac OS X 10.6 | n/a | n/a | + +(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 + +### NSURLSession + +- `AFURLSessionManager` +- `AFHTTPSessionManager` + +### Serialization + +* `` + - `AFHTTPRequestSerializer` + - `AFJSONRequestSerializer` + - `AFPropertyListRequestSerializer` +* `` + - `AFHTTPResponseSerializer` + - `AFJSONResponseSerializer` + - `AFXMLParserResponseSerializer` + - `AFXMLDocumentResponseSerializer` _(Mac OS X)_ + - `AFPropertyListResponseSerializer` + - `AFImageResponseSerializer` + - `AFCompoundResponseSerializer` + +### Additional Functionality + +- `AFSecurityPolicy` +- `AFNetworkReachabilityManager` + +## Usage + +### 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]]; + +NSURLSessionUploadTask *uploadTask; +uploadTask = [manager + uploadTaskWithStreamedRequest:request + progress:^(NSProgress * _Nonnull uploadProgress) { + // This is not called back on the main queue. + // You are responsible for dispatching to the main queue for UI updates + dispatch_async(dispatch_get_main_queue(), ^{ + //Update the progress view + [progressView setProgress:uploadProgress.fractionCompleted]; + }); + } + completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable 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://httpbin.org/get"]; +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 error:nil]; +``` + + 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 error:nil]; +``` + + 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/play/wwdc2012-706/). + +#### Shared Network Reachability + +```objective-c +[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { + NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); +}]; + +[[AFNetworkReachabilityManager sharedManager] 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 +AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; +manager.securityPolicy.allowInvalidCertificates = YES; // not recommended for production +``` + +--- + +## Unit Tests + +AFNetworking includes a suite of unit tests within the Tests subdirectory. These tests can be run simply be executed the test action on the platform framework you would like to 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/AFAutoPurgingImageCache.h b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h new file mode 100644 index 0000000..9bdc15c --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1,149 @@ +// AFAutoPurgingImageCache.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + The `AFImageCache` protocol defines a set of APIs for adding, removing and fetching images from a cache synchronously. + */ +@protocol AFImageCache + +/** + Adds the image to the cache with the given identifier. + + @param image The image to cache. + @param identifier The unique identifier for the image in the cache. + */ +- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier; + +/** + Removes the image from the cache matching the given identifier. + + @param identifier The unique identifier for the image in the cache. + + @return A BOOL indicating whether or not the image was removed from the cache. + */ +- (BOOL)removeImageWithIdentifier:(NSString *)identifier; + +/** + Removes all images from the cache. + + @return A BOOL indicating whether or not all images were removed from the cache. + */ +- (BOOL)removeAllImages; + +/** + Returns the image in the cache associated with the given identifier. + + @param identifier The unique identifier for the image in the cache. + + @return An image for the matching identifier, or nil. + */ +- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier; +@end + + +/** + The `ImageRequestCache` protocol extends the `ImageCache` protocol by adding methods for adding, removing and fetching images from a cache given an `NSURLRequest` and additional identifier. + */ +@protocol AFImageRequestCache + +/** + Adds the image to the cache using an identifier created from the request and additional identifier. + + @param image The image to cache. + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + */ +- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +/** + Removes the image from the cache using an identifier created from the request and additional identifier. + + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + + @return A BOOL indicating whether or not all images were removed from the cache. + */ +- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +/** + Returns the image from the cache associated with an identifier created from the request and additional identifier. + + @param request The unique URL request identifing the image asset. + @param identifier The additional identifier to apply to the URL request to identify the image. + + @return An image for the matching request and identifier, or nil. + */ +- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier; + +@end + +/** + The `AutoPurgingImageCache` in an in-memory image cache used to store images up to a given memory capacity. When the memory capacity is reached, the image cache is sorted by last access date, then the oldest image is continuously purged until the preferred memory usage after purge is met. Each time an image is accessed through the cache, the internal access date of the image is updated. + */ +@interface AFAutoPurgingImageCache : NSObject + +/** + The total memory capacity of the cache in bytes. + */ +@property (nonatomic, assign) UInt64 memoryCapacity; + +/** + The preferred memory usage after purge in bytes. During a purge, images will be purged until the memory capacity drops below this limit. + */ +@property (nonatomic, assign) UInt64 preferredMemoryUsageAfterPurge; + +/** + The current total memory usage in bytes of all images stored within the cache. + */ +@property (nonatomic, assign, readonly) UInt64 memoryUsage; + +/** + Initialies the `AutoPurgingImageCache` instance with default values for memory capacity and preferred memory usage after purge limit. `memoryCapcity` defaults to `100 MB`. `preferredMemoryUsageAfterPurge` defaults to `60 MB`. + + @return The new `AutoPurgingImageCache` instance. + */ +- (instancetype)init; + +/** + Initialies the `AutoPurgingImageCache` instance with the given memory capacity and preferred memory usage + after purge limit. + + @param memoryCapacity The total memory capacity of the cache in bytes. + @param preferredMemoryCapacity The preferred memory usage after purge in bytes. + + @return The new `AutoPurgingImageCache` instance. + */ +- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity; + +@end + +NS_ASSUME_NONNULL_END + +#endif + diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m new file mode 100644 index 0000000..1f40715 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m @@ -0,0 +1,201 @@ +// AFAutoPurgingImageCache.m +// Copyright (c) 2011–2016 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 || TARGET_OS_TV + +#import "AFAutoPurgingImageCache.h" + +@interface AFCachedImage : NSObject + +@property (nonatomic, strong) UIImage *image; +@property (nonatomic, strong) NSString *identifier; +@property (nonatomic, assign) UInt64 totalBytes; +@property (nonatomic, strong) NSDate *lastAccessDate; +@property (nonatomic, assign) UInt64 currentMemoryUsage; + +@end + +@implementation AFCachedImage + +-(instancetype)initWithImage:(UIImage *)image identifier:(NSString *)identifier { + if (self = [self init]) { + self.image = image; + self.identifier = identifier; + + CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale); + CGFloat bytesPerPixel = 4.0; + CGFloat bytesPerSize = imageSize.width * imageSize.height; + self.totalBytes = (UInt64)bytesPerPixel * (UInt64)bytesPerSize; + self.lastAccessDate = [NSDate date]; + } + return self; +} + +- (UIImage*)accessImage { + self.lastAccessDate = [NSDate date]; + return self.image; +} + +- (NSString *)description { + NSString *descriptionString = [NSString stringWithFormat:@"Idenfitier: %@ lastAccessDate: %@ ", self.identifier, self.lastAccessDate]; + return descriptionString; + +} + +@end + +@interface AFAutoPurgingImageCache () +@property (nonatomic, strong) NSMutableDictionary *cachedImages; +@property (nonatomic, assign) UInt64 currentMemoryUsage; +@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; +@end + +@implementation AFAutoPurgingImageCache + +- (instancetype)init { + return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024]; +} + +- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity { + if (self = [super init]) { + self.memoryCapacity = memoryCapacity; + self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity; + self.cachedImages = [[NSMutableDictionary alloc] init]; + + NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]]; + self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(removeAllImages) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (UInt64)memoryUsage { + __block UInt64 result = 0; + dispatch_sync(self.synchronizationQueue, ^{ + result = self.currentMemoryUsage; + }); + return result; +} + +- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { + dispatch_barrier_async(self.synchronizationQueue, ^{ + AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier]; + + AFCachedImage *previousCachedImage = self.cachedImages[identifier]; + if (previousCachedImage != nil) { + self.currentMemoryUsage -= previousCachedImage.totalBytes; + } + + self.cachedImages[identifier] = cacheImage; + self.currentMemoryUsage += cacheImage.totalBytes; + }); + + dispatch_barrier_async(self.synchronizationQueue, ^{ + if (self.currentMemoryUsage > self.memoryCapacity) { + UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge; + NSMutableArray *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues]; + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" + ascending:YES]; + [sortedImages sortUsingDescriptors:@[sortDescriptor]]; + + UInt64 bytesPurged = 0; + + for (AFCachedImage *cachedImage in sortedImages) { + [self.cachedImages removeObjectForKey:cachedImage.identifier]; + bytesPurged += cachedImage.totalBytes; + if (bytesPurged >= bytesToPurge) { + break ; + } + } + self.currentMemoryUsage -= bytesPurged; + } + }); +} + +- (BOOL)removeImageWithIdentifier:(NSString *)identifier { + __block BOOL removed = NO; + dispatch_barrier_sync(self.synchronizationQueue, ^{ + AFCachedImage *cachedImage = self.cachedImages[identifier]; + if (cachedImage != nil) { + [self.cachedImages removeObjectForKey:identifier]; + self.currentMemoryUsage -= cachedImage.totalBytes; + removed = YES; + } + }); + return removed; +} + +- (BOOL)removeAllImages { + __block BOOL removed = NO; + dispatch_barrier_sync(self.synchronizationQueue, ^{ + if (self.cachedImages.count > 0) { + [self.cachedImages removeAllObjects]; + self.currentMemoryUsage = 0; + removed = YES; + } + }); + return removed; +} + +- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier { + __block UIImage *image = nil; + dispatch_sync(self.synchronizationQueue, ^{ + AFCachedImage *cachedImage = self.cachedImages[identifier]; + image = [cachedImage accessImage]; + }); + return image; +} + +- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + [self addImage:image withIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (BOOL)removeImageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + return [self removeImageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier { + return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]]; +} + +- (NSString *)imageCacheKeyFromURLRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)additionalIdentifier { + NSString *key = request.URL.absoluteString; + if (additionalIdentifier != nil) { + key = [key stringByAppendingString:additionalIdentifier]; + } + return key; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h new file mode 100644 index 0000000..3903eec --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h @@ -0,0 +1,157 @@ +// AFImageDownloader.h +// Copyright (c) 2011–2016 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 || TARGET_OS_TV + +#import +#import "AFAutoPurgingImageCache.h" +#import "AFHTTPSessionManager.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) { + AFImageDownloadPrioritizationFIFO, + AFImageDownloadPrioritizationLIFO +}; + +/** + The `AFImageDownloadReceipt` is an object vended by the `AFImageDownloader` when starting a data task. It can be used to cancel active tasks running on the `AFImageDownloader` session. As a general rule, image data tasks should be cancelled using the `AFImageDownloadReceipt` instead of calling `cancel` directly on the `task` itself. The `AFImageDownloader` is optimized to handle duplicate task scenarios as well as pending versus active downloads. + */ +@interface AFImageDownloadReceipt : NSObject + +/** + The data task created by the `AFImageDownloader`. +*/ +@property (nonatomic, strong) NSURLSessionDataTask *task; + +/** + The unique identifier for the success and failure blocks when duplicate requests are made. + */ +@property (nonatomic, strong) NSUUID *receiptID; +@end + +/** The `AFImageDownloader` class is responsible for downloading images in parallel on a prioritized queue. Incoming downloads are added to the front or back of the queue depending on the download prioritization. Each downloaded image is cached in the underlying `NSURLCache` as well as the in-memory image cache. By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation. + */ +@interface AFImageDownloader : NSObject + +/** + The image cache used to store all downloaded images in. `AFAutoPurgingImageCache` by default. + */ +@property (nonatomic, strong, nullable) id imageCache; + +/** + The `AFHTTPSessionManager` used to download images. By default, this is configured with an `AFImageResponseSerializer`, and a shared `NSURLCache` for all image downloads. + */ +@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; + +/** + Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default. + */ +@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton; + +/** + The shared default instance of `AFImageDownloader` initialized with default values. + */ ++ (instancetype)defaultInstance; + +/** + Creates a default `NSURLCache` with common usage parameter values. + + @returns The default `NSURLCache` instance. + */ ++ (NSURLCache *)defaultURLCache; + +/** + Default initializer + + @return An instance of `AFImageDownloader` initialized with default values. + */ +- (instancetype)init; + +/** + Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache. + + @param sessionManager The session manager to use to download images. + @param downloadPrioritization The download prioritization of the download queue. + @param maximumActiveDownloads The maximum number of active downloads allowed at any given time. Recommend `4`. + @param imageCache The image cache used to store all downloaded images in. + + @return The new `AFImageDownloader` instance. + */ +- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager + downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization + maximumActiveDownloads:(NSInteger)maximumActiveDownloads + imageCache:(nullable id )imageCache; + +/** + Creates a data task using the `sessionManager` instance for the specified URL request. + + If the same data task is already in the queue or currently being downloaded, the success and failure blocks are + appended to the already existing task. Once the task completes, all success or failure blocks attached to the + task are executed in the order they were added. + + @param request The URL request. + @param success A block to be executed when the image data task 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 data task 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. + + @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. + cache and the URL request cache policy allows the cache to be used. + */ +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + +/** + Creates a data task using the `sessionManager` instance for the specified URL request. + + If the same data task is already in the queue or currently being downloaded, the success and failure blocks are + appended to the already existing task. Once the task completes, all success or failure blocks attached to the + task are executed in the order they were added. + + @param request The URL request. + @param receiptID The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request. + @param success A block to be executed when the image data task 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 data task 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. + + @return The image download receipt for the data task if available. `nil` if the image is stored in the cache. + cache and the URL request cache policy allows the cache to be used. + */ +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + withReceiptID:(NSUUID *)receiptID + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure; + +/** + Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary. + + If the data task is pending in the queue, it will be cancelled if no other success and failure blocks are registered with the data task. If the data task is currently executing or is already completed, the success and failure blocks are removed and will not be called when the task finishes. + + @param imageDownloadReceipt The image download receipt to cancel. + */ +- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m new file mode 100644 index 0000000..78477bf --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m @@ -0,0 +1,391 @@ +// AFImageDownloader.m +// Copyright (c) 2011–2016 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 || TARGET_OS_TV + +#import "AFImageDownloader.h" +#import "AFHTTPSessionManager.h" + +@interface AFImageDownloaderResponseHandler : NSObject +@property (nonatomic, strong) NSUUID *uuid; +@property (nonatomic, copy) void (^successBlock)(NSURLRequest*, NSHTTPURLResponse*, UIImage*); +@property (nonatomic, copy) void (^failureBlock)(NSURLRequest*, NSHTTPURLResponse*, NSError*); +@end + +@implementation AFImageDownloaderResponseHandler + +- (instancetype)initWithUUID:(NSUUID *)uuid + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { + if (self = [self init]) { + self.uuid = uuid; + self.successBlock = success; + self.failureBlock = failure; + } + return self; +} + +- (NSString *)description { + return [NSString stringWithFormat: @"UUID: %@", [self.uuid UUIDString]]; +} + +@end + +@interface AFImageDownloaderMergedTask : NSObject +@property (nonatomic, strong) NSString *URLIdentifier; +@property (nonatomic, strong) NSUUID *identifier; +@property (nonatomic, strong) NSURLSessionDataTask *task; +@property (nonatomic, strong) NSMutableArray *responseHandlers; + +@end + +@implementation AFImageDownloaderMergedTask + +- (instancetype)initWithURLIdentifier:(NSString *)URLIdentifier identifier:(NSUUID *)identifier task:(NSURLSessionDataTask *)task { + if (self = [self init]) { + self.URLIdentifier = URLIdentifier; + self.task = task; + self.identifier = identifier; + self.responseHandlers = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)addResponseHandler:(AFImageDownloaderResponseHandler*)handler { + [self.responseHandlers addObject:handler]; +} + +- (void)removeResponseHandler:(AFImageDownloaderResponseHandler*)handler { + [self.responseHandlers removeObject:handler]; +} + +@end + +@implementation AFImageDownloadReceipt + +- (instancetype)initWithReceiptID:(NSUUID *)receiptID task:(NSURLSessionDataTask *)task { + if (self = [self init]) { + self.receiptID = receiptID; + self.task = task; + } + return self; +} + +@end + +@interface AFImageDownloader () + +@property (nonatomic, strong) dispatch_queue_t synchronizationQueue; +@property (nonatomic, strong) dispatch_queue_t responseQueue; + +@property (nonatomic, assign) NSInteger maximumActiveDownloads; +@property (nonatomic, assign) NSInteger activeRequestCount; + +@property (nonatomic, strong) NSMutableArray *queuedMergedTasks; +@property (nonatomic, strong) NSMutableDictionary *mergedTasks; + +@end + + +@implementation AFImageDownloader + ++ (NSURLCache *)defaultURLCache { + return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024 + diskCapacity:150 * 1024 * 1024 + diskPath:@"com.alamofire.imagedownloader"]; +} + ++ (NSURLSessionConfiguration *)defaultURLSessionConfiguration { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + + //TODO set the default HTTP headers + + configuration.HTTPShouldSetCookies = YES; + configuration.HTTPShouldUsePipelining = NO; + + configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy; + configuration.allowsCellularAccess = YES; + configuration.timeoutIntervalForRequest = 60.0; + configuration.URLCache = [AFImageDownloader defaultURLCache]; + + return configuration; +} + +- (instancetype)init { + NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration]; + AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration]; + sessionManager.responseSerializer = [AFImageResponseSerializer serializer]; + + return [self initWithSessionManager:sessionManager + downloadPrioritization:AFImageDownloadPrioritizationFIFO + maximumActiveDownloads:4 + imageCache:[[AFAutoPurgingImageCache alloc] init]]; +} + +- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager + downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization + maximumActiveDownloads:(NSInteger)maximumActiveDownloads + imageCache:(id )imageCache { + if (self = [super init]) { + self.sessionManager = sessionManager; + + self.downloadPrioritizaton = downloadPrioritization; + self.maximumActiveDownloads = maximumActiveDownloads; + self.imageCache = imageCache; + + self.queuedMergedTasks = [[NSMutableArray alloc] init]; + self.mergedTasks = [[NSMutableDictionary alloc] init]; + self.activeRequestCount = 0; + + NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]]; + self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL); + + name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]]; + self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT); + } + + return self; +} + ++ (instancetype)defaultInstance { + static AFImageDownloader *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success + failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure { + return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure]; +} + +- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request + withReceiptID:(nonnull NSUUID *)receiptID + success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success + failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure { + __block NSURLSessionDataTask *task = nil; + dispatch_sync(self.synchronizationQueue, ^{ + NSString *URLIdentifier = request.URL.absoluteString; + if (URLIdentifier == nil) { + if (failure) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(request, nil, error); + }); + } + return; + } + + // 1) Append the success and failure blocks to a pre-existing request if it already exists + AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier]; + if (existingMergedTask != nil) { + AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure]; + [existingMergedTask addResponseHandler:handler]; + task = existingMergedTask.task; + return; + } + + // 2) Attempt to load the image from the image cache if the cache policy allows it + switch (request.cachePolicy) { + case NSURLRequestUseProtocolCachePolicy: + case NSURLRequestReturnCacheDataElseLoad: + case NSURLRequestReturnCacheDataDontLoad: { + UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil]; + if (cachedImage != nil) { + if (success) { + dispatch_async(dispatch_get_main_queue(), ^{ + success(request, nil, cachedImage); + }); + } + return; + } + break; + } + default: + break; + } + + // 3) Create the request and set up authentication, validation and response serialization + NSUUID *mergedTaskIdentifier = [NSUUID UUID]; + NSURLSessionDataTask *createdTask; + __weak __typeof__(self) weakSelf = self; + + createdTask = [self.sessionManager + dataTaskWithRequest:request + completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { + dispatch_async(self.responseQueue, ^{ + __strong __typeof__(weakSelf) strongSelf = weakSelf; + AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; + if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) { + mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier]; + if (error) { + for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { + if (handler.failureBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.failureBlock(request, (NSHTTPURLResponse*)response, error); + }); + } + } + } else { + [strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil]; + + for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) { + if (handler.successBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject); + }); + } + } + + } + } + [strongSelf safelyDecrementActiveTaskCount]; + [strongSelf safelyStartNextTaskIfNecessary]; + }); + }]; + + // 4) Store the response handler for use when the request completes + AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID + success:success + failure:failure]; + AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc] + initWithURLIdentifier:URLIdentifier + identifier:mergedTaskIdentifier + task:createdTask]; + [mergedTask addResponseHandler:handler]; + self.mergedTasks[URLIdentifier] = mergedTask; + + // 5) Either start the request or enqueue it depending on the current active request count + if ([self isActiveRequestCountBelowMaximumLimit]) { + [self startMergedTask:mergedTask]; + } else { + [self enqueueMergedTask:mergedTask]; + } + + task = mergedTask.task; + }); + if (task) { + return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task]; + } else { + return nil; + } +} + +- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { + dispatch_sync(self.synchronizationQueue, ^{ + NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString; + AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; + NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) { + return handler.uuid == imageDownloadReceipt.receiptID; + }]; + + if (index != NSNotFound) { + AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index]; + [mergedTask removeResponseHandler:handler]; + NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString]; + NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason}; + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo]; + if (handler.failureBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error); + }); + } + } + + if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) { + [mergedTask.task cancel]; + [self removeMergedTaskWithURLIdentifier:URLIdentifier]; + } + }); +} + +- (AFImageDownloaderMergedTask*)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { + __block AFImageDownloaderMergedTask *mergedTask = nil; + dispatch_sync(self.synchronizationQueue, ^{ + mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier]; + }); + return mergedTask; +} + +//This method should only be called from safely within the synchronizationQueue +- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier { + AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier]; + [self.mergedTasks removeObjectForKey:URLIdentifier]; + return mergedTask; +} + +- (void)safelyDecrementActiveTaskCount { + dispatch_sync(self.synchronizationQueue, ^{ + if (self.activeRequestCount > 0) { + self.activeRequestCount -= 1; + } + }); +} + +- (void)safelyStartNextTaskIfNecessary { + dispatch_sync(self.synchronizationQueue, ^{ + if ([self isActiveRequestCountBelowMaximumLimit]) { + while (self.queuedMergedTasks.count > 0) { + AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask]; + if (mergedTask.task.state == NSURLSessionTaskStateSuspended) { + [self startMergedTask:mergedTask]; + break; + } + } + } + }); +} + +- (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask { + [mergedTask.task resume]; + ++self.activeRequestCount; +} + +- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask { + switch (self.downloadPrioritizaton) { + case AFImageDownloadPrioritizationFIFO: + [self.queuedMergedTasks addObject:mergedTask]; + break; + case AFImageDownloadPrioritizationLIFO: + [self.queuedMergedTasks insertObject:mergedTask atIndex:0]; + break; + } +} + +- (AFImageDownloaderMergedTask *)dequeueMergedTask { + AFImageDownloaderMergedTask *mergedTask = nil; + mergedTask = [self.queuedMergedTasks firstObject]; + [self.queuedMergedTasks removeObject:mergedTask]; + return mergedTask; +} + +- (BOOL)isActiveRequestCountBelowMaximumLimit { + return self.activeRequestCount < self.maximumActiveDownloads; +} + +@end + +#endif diff --git a/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 100644 index 0000000..3bcf289 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1,103 @@ +// AFNetworkActivityIndicatorManager.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS + +#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 session task 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 manager is currently active. +*/ +@property (readonly, nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +/** + A time interval indicating the minimum duration of networking activity that should occur before the activity indicator is displayed. The default value 1 second. If the network activity indicator should be displayed immediately when network activity occurs, this value should be set to 0 seconds. + + Apple's HIG describes the following: + + > Display the network activity indicator to provide feedback when your app accesses the network for more than a couple of seconds. If the operation finishes sooner than that, you don’t have to show the network activity indicator, because the indicator is likely to disappear before users notice its presence. + + */ +@property (nonatomic, assign) NSTimeInterval activationDelay; + +/** + A time interval indicating the duration of time of no networking activity required before the activity indicator is disabled. This allows for continuous display of the network activity indicator across multiple requests. The default value is 0.17 seconds. + */ + +@property (nonatomic, assign) NSTimeInterval completionDelay; + +/** + 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; + +/** + Set the a custom method to be executed when the network activity indicator manager should be hidden/shown. By default, this is null, and the UIApplication Network Activity Indicator will be managed automatically. If this block is set, it is the responsiblity of the caller to manager the network activity indicator going forward. + + @param block A block to be executed when the network activity indicator status changes. + */ +- (void)setNetworkingActivityActionWithBlock:(nullable void (^)(BOOL networkActivityIndicatorVisible))block; + +@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..e77508e --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m @@ -0,0 +1,261 @@ +// AFNetworkActivityIndicatorManager.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS +#import "AFURLSessionManager.h" + +typedef NS_ENUM(NSInteger, AFNetworkActivityManagerState) { + AFNetworkActivityManagerStateNotActive, + AFNetworkActivityManagerStateDelayingStart, + AFNetworkActivityManagerStateActive, + AFNetworkActivityManagerStateDelayingEnd +}; + +static NSTimeInterval const kDefaultAFNetworkActivityManagerActivationDelay = 1.0; +static NSTimeInterval const kDefaultAFNetworkActivityManagerCompletionDelay = 0.17; + +static NSURLRequest * AFNetworkRequestFromNotification(NSNotification *notification) { + if ([[notification object] respondsToSelector:@selector(originalRequest)]) { + return [(NSURLSessionTask *)[notification object] originalRequest]; + } else { + return nil; + } +} + +typedef void (^AFNetworkActivityActionBlock)(BOOL networkActivityIndicatorVisible); + +@interface AFNetworkActivityIndicatorManager () +@property (readwrite, nonatomic, assign) NSInteger activityCount; +@property (readwrite, nonatomic, strong) NSTimer *activationDelayTimer; +@property (readwrite, nonatomic, strong) NSTimer *completionDelayTimer; +@property (readonly, nonatomic, getter = isNetworkActivityOccurring) BOOL networkActivityOccurring; +@property (nonatomic, copy) AFNetworkActivityActionBlock networkActivityActionBlock; +@property (nonatomic, assign) AFNetworkActivityManagerState currentState; +@property (nonatomic, assign, getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible; + +- (void)updateCurrentStateForNetworkActivityChange; +@end + +@implementation AFNetworkActivityIndicatorManager + ++ (instancetype)sharedManager { + static AFNetworkActivityIndicatorManager *_sharedManager = nil; + static dispatch_once_t oncePredicate; + dispatch_once(&oncePredicate, ^{ + _sharedManager = [[self alloc] init]; + }); + + return _sharedManager; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + self.currentState = AFNetworkActivityManagerStateNotActive; + [[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]; + self.activationDelay = kDefaultAFNetworkActivityManagerActivationDelay; + self.completionDelay = kDefaultAFNetworkActivityManagerCompletionDelay; + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_activationDelayTimer invalidate]; + [_completionDelayTimer invalidate]; +} + +- (void)setEnabled:(BOOL)enabled { + _enabled = enabled; + if (enabled == NO) { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; + } +} + +- (void)setNetworkingActivityActionWithBlock:(void (^)(BOOL networkActivityIndicatorVisible))block { + self.networkActivityActionBlock = block; +} + +- (BOOL)isNetworkActivityOccurring { + @synchronized(self) { + return self.activityCount > 0; + } +} + +- (void)setNetworkActivityIndicatorVisible:(BOOL)networkActivityIndicatorVisible { + if (_networkActivityIndicatorVisible != networkActivityIndicatorVisible) { + [self willChangeValueForKey:@"networkActivityIndicatorVisible"]; + @synchronized(self) { + _networkActivityIndicatorVisible = networkActivityIndicatorVisible; + } + [self didChangeValueForKey:@"networkActivityIndicatorVisible"]; + if (self.networkActivityActionBlock) { + self.networkActivityActionBlock(networkActivityIndicatorVisible); + } else { + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:networkActivityIndicatorVisible]; + } + } +} + +- (void)setActivityCount:(NSInteger)activityCount { + @synchronized(self) { + _activityCount = activityCount; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCurrentStateForNetworkActivityChange]; + }); +} + +- (void)incrementActivityCount { + [self willChangeValueForKey:@"activityCount"]; + @synchronized(self) { + _activityCount++; + } + [self didChangeValueForKey:@"activityCount"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateCurrentStateForNetworkActivityChange]; + }); +} + +- (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 updateCurrentStateForNetworkActivityChange]; + }); +} + +- (void)networkRequestDidStart:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self incrementActivityCount]; + } +} + +- (void)networkRequestDidFinish:(NSNotification *)notification { + if ([AFNetworkRequestFromNotification(notification) URL]) { + [self decrementActivityCount]; + } +} + +#pragma mark - Internal State Management +- (void)setCurrentState:(AFNetworkActivityManagerState)currentState { + @synchronized(self) { + if (_currentState != currentState) { + [self willChangeValueForKey:@"currentState"]; + _currentState = currentState; + switch (currentState) { + case AFNetworkActivityManagerStateNotActive: + [self cancelActivationDelayTimer]; + [self cancelCompletionDelayTimer]; + [self setNetworkActivityIndicatorVisible:NO]; + break; + case AFNetworkActivityManagerStateDelayingStart: + [self startActivationDelayTimer]; + break; + case AFNetworkActivityManagerStateActive: + [self cancelCompletionDelayTimer]; + [self setNetworkActivityIndicatorVisible:YES]; + break; + case AFNetworkActivityManagerStateDelayingEnd: + [self startCompletionDelayTimer]; + break; + } + } + [self didChangeValueForKey:@"currentState"]; + } +} + +- (void)updateCurrentStateForNetworkActivityChange { + if (self.enabled) { + switch (self.currentState) { + case AFNetworkActivityManagerStateNotActive: + if (self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateDelayingStart]; + } + break; + case AFNetworkActivityManagerStateDelayingStart: + //No op. Let the delay timer finish out. + break; + case AFNetworkActivityManagerStateActive: + if (!self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateDelayingEnd]; + } + break; + case AFNetworkActivityManagerStateDelayingEnd: + if (self.isNetworkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateActive]; + } + break; + } + } +} + +- (void)startActivationDelayTimer { + self.activationDelayTimer = [NSTimer + timerWithTimeInterval:self.activationDelay target:self selector:@selector(activationDelayTimerFired) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.activationDelayTimer forMode:NSRunLoopCommonModes]; +} + +- (void)activationDelayTimerFired { + if (self.networkActivityOccurring) { + [self setCurrentState:AFNetworkActivityManagerStateActive]; + } else { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; + } +} + +- (void)startCompletionDelayTimer { + [self.completionDelayTimer invalidate]; + self.completionDelayTimer = [NSTimer timerWithTimeInterval:self.completionDelay target:self selector:@selector(completionDelayTimerFired) userInfo:nil repeats:NO]; + [[NSRunLoop mainRunLoop] addTimer:self.completionDelayTimer forMode:NSRunLoopCommonModes]; +} + +- (void)completionDelayTimerFired { + [self setCurrentState:AFNetworkActivityManagerStateNotActive]; +} + +- (void)cancelActivationDelayTimer { + [self.activationDelayTimer invalidate]; +} + +- (void)cancelCompletionDelayTimer { + [self.completionDelayTimer invalidate]; +} + +@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..d424c9b --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1,48 @@ +// UIActivityIndicatorView+AFNetworking.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import + +/** + 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 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. + */ +- (void)setAnimatingWithStateOfTask:(nullable NSURLSessionTask *)task; + +@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..ed704ed --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m @@ -0,0 +1,124 @@ +// UIActivityIndicatorView+AFNetworking.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import "AFURLSessionManager.h" + +@interface AFActivityIndicatorViewNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIActivityIndicatorView *activityIndicatorView; +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView; + +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task; + +@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; +} + +- (void)setAnimatingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setAnimatingWithStateOfTask:task]; +} + +@end + +@implementation AFActivityIndicatorViewNotificationObserver + +- (instancetype)initWithActivityIndicatorView:(UIActivityIndicatorView *)activityIndicatorView +{ + self = [super init]; + if (self) { + _activityIndicatorView = activityIndicatorView; + } + return self; +} + +- (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]; + } + } +} + +#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]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification object:nil]; +} + +@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..d33e0d4 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1,175 @@ +// UIButton+AFNetworking.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFImageDownloader; + +/** + 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 the Image Downloader +///------------------------------------ + +/** + Set the shared image downloader used to download images. + + @param imageDownloader The shared image downloader used to download images. +*/ ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; + +/** + The shared image downloader used to download images. + */ ++ (AFImageDownloader *)sharedImageDownloader; + +///-------------------- +/// @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 data task 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 data task 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)setImageForState:(UIControlState)state + withURLRequest:(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; + + +///------------------------------- +/// @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 data task 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 data task 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)setBackgroundImageForState:(UIControlState)state + withURLRequest:(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; + + +///------------------------------ +/// @name Canceling Image Loading +///------------------------------ + +/** + Cancels any executing image task for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelImageDownloadTaskForState:(UIControlState)state; + +/** + Cancels any executing background image task for the specified control state of the receiver, if one exists. + + @param state The control state. + */ +- (void)cancelBackgroundImageDownloadTaskForState:(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..5bc49dd --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m @@ -0,0 +1,305 @@ +// UIButton+AFNetworking.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import "UIImageView+AFNetworking.h" +#import "AFImageDownloader.h" + +@interface UIButton (_AFNetworking) +@end + +@implementation UIButton (_AFNetworking) + +#pragma mark - + +static char AFImageDownloadReceiptNormal; +static char AFImageDownloadReceiptHighlighted; +static char AFImageDownloadReceiptSelected; +static char AFImageDownloadReceiptDisabled; + +static const char * af_imageDownloadReceiptKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFImageDownloadReceiptHighlighted; + case UIControlStateSelected: + return &AFImageDownloadReceiptSelected; + case UIControlStateDisabled: + return &AFImageDownloadReceiptDisabled; + case UIControlStateNormal: + default: + return &AFImageDownloadReceiptNormal; + } +} + +- (AFImageDownloadReceipt *)af_imageDownloadReceiptForState:(UIControlState)state { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_imageDownloadReceiptKeyForState(state)); +} + +- (void)af_setImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_imageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - + +static char AFBackgroundImageDownloadReceiptNormal; +static char AFBackgroundImageDownloadReceiptHighlighted; +static char AFBackgroundImageDownloadReceiptSelected; +static char AFBackgroundImageDownloadReceiptDisabled; + +static const char * af_backgroundImageDownloadReceiptKeyForState(UIControlState state) { + switch (state) { + case UIControlStateHighlighted: + return &AFBackgroundImageDownloadReceiptHighlighted; + case UIControlStateSelected: + return &AFBackgroundImageDownloadReceiptSelected; + case UIControlStateDisabled: + return &AFBackgroundImageDownloadReceiptDisabled; + case UIControlStateNormal: + default: + return &AFBackgroundImageDownloadReceiptNormal; + } +} + +- (AFImageDownloadReceipt *)af_backgroundImageDownloadReceiptForState:(UIControlState)state { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state)); +} + +- (void)af_setBackgroundImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt + forState:(UIControlState)state +{ + objc_setAssociatedObject(self, af_backgroundImageDownloadReceiptKeyForState(state), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIButton (AFNetworking) + ++ (AFImageDownloader *)sharedImageDownloader { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { + objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, 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:(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 +{ + if ([self isActiveTaskURLEqualToURLRequest:urlRequest forState:state]) { + return; + } + + [self cancelImageDownloadTaskForState:state]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setImage:cachedImage forState:state]; + } + [self af_setImageDownloadReceipt:nil forState:state]; + } else { + if (placeholderImage) { + [self setImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if(responseObject) { + [strongSelf setImage:responseObject forState:state]; + } + [strongSelf af_setImageDownloadReceipt:nil forState:state]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_imageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf af_setImageDownloadReceipt:nil forState:state]; + } + }]; + + [self af_setImageDownloadReceipt:receipt forState:state]; + } +} + +#pragma mark - + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url +{ + [self setBackgroundImageForState:state withURL:url placeholderImage:nil]; +} + +- (void)setBackgroundImageForState:(UIControlState)state + withURL:(NSURL *)url + placeholderImage:(nullable 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:(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 +{ + if ([self isActiveBackgroundTaskURLEqualToURLRequest:urlRequest forState:state]) { + return; + } + + [self cancelBackgroundImageDownloadTaskForState:state]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + [self setBackgroundImage:cachedImage forState:state]; + } + [self af_setBackgroundImageDownloadReceipt:nil forState:state]; + } else { + if (placeholderImage) { + [self setBackgroundImage:placeholderImage forState:state]; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if(responseObject) { + [strongSelf setBackgroundImage:responseObject forState:state]; + } + [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([[strongSelf af_backgroundImageDownloadReceiptForState:state].receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf af_setBackgroundImageDownloadReceipt:nil forState:state]; + } + }]; + + [self af_setBackgroundImageDownloadReceipt:receipt forState:state]; + } +} + +#pragma mark - + +- (void)cancelImageDownloadTaskForState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; + if (receipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; + [self af_setImageDownloadReceipt:nil forState:state]; + } +} + +- (void)cancelBackgroundImageDownloadTaskForState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; + if (receipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:receipt]; + [self af_setBackgroundImageDownloadReceipt:nil forState:state]; + } +} + +- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_imageDownloadReceiptForState:state]; + return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + +- (BOOL)isActiveBackgroundTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest forState:(UIControlState)state { + AFImageDownloadReceipt *receipt = [self af_backgroundImageDownloadReceiptForState:state]; + return [receipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + + +@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..14744cd --- /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 TARGET_OS_IOS || TARGET_OS_TV + +#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..8929252 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1,109 @@ +// UIImageView+AFNetworking.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFImageDownloader; + +/** + 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 the Image Downloader +///------------------------------------ + +/** + Set the shared image downloader used to download images. + + @param imageDownloader The shared image downloader used to download images. + */ ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader; + +/** + The shared image downloader used to download images. + */ ++ (AFImageDownloader *)sharedImageDownloader; + +///-------------------- +/// @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 data task 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 data task 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)cancelImageDownloadTask; + +@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..5934d68 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m @@ -0,0 +1,161 @@ +// UIImageView+AFNetworking.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import "AFImageDownloader.h" + +@interface UIImageView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt; +@end + +@implementation UIImageView (_AFNetworking) + +- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt { + return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt)); +} + +- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt { + objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIImageView (AFNetworking) + ++ (AFImageDownloader *)sharedImageDownloader { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; +#pragma clang diagnostic pop +} + ++ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { + objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, 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 +{ + + if ([urlRequest URL] == nil) { + [self cancelImageDownloadTask]; + self.image = placeholderImage; + return; + } + + if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){ + return; + } + + [self cancelImageDownloadTask]; + + AFImageDownloader *downloader = [[self class] sharedImageDownloader]; + id imageCache = downloader.imageCache; + + //Use the image from the image cache if it exists + UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil]; + if (cachedImage) { + if (success) { + success(urlRequest, nil, cachedImage); + } else { + self.image = cachedImage; + } + [self clearActiveDownloadInformation]; + } else { + if (placeholderImage) { + self.image = placeholderImage; + } + + __weak __typeof(self)weakSelf = self; + NSUUID *downloadID = [NSUUID UUID]; + AFImageDownloadReceipt *receipt; + receipt = [downloader + downloadImageForURLRequest:urlRequest + withReceiptID:downloadID + success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { + if (success) { + success(request, response, responseObject); + } else if(responseObject) { + strongSelf.image = responseObject; + } + [strongSelf clearActiveDownloadInformation]; + } + + } + failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) { + __strong __typeof(weakSelf)strongSelf = weakSelf; + if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) { + if (failure) { + failure(request, response, error); + } + [strongSelf clearActiveDownloadInformation]; + } + }]; + + self.af_activeImageDownloadReceipt = receipt; + } +} + +- (void)cancelImageDownloadTask { + if (self.af_activeImageDownloadReceipt != nil) { + [[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt]; + [self clearActiveDownloadInformation]; + } +} + +- (void)clearActiveDownloadInformation { + self.af_activeImageDownloadReceipt = nil; +} + +- (BOOL)isActiveTaskURLEqualToURLRequest:(NSURLRequest *)urlRequest { + return [self.af_activeImageDownloadReceipt.task.originalRequest.URL.absoluteString isEqualToString:urlRequest.URL.absoluteString]; +} + +@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..febacfc --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1,42 @@ +// UIKit+AFNetworking.h +// +// Copyright (c) 2011–2016 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. + +#if TARGET_OS_IOS || TARGET_OS_TV +#import + +#ifndef _UIKIT_AFNETWORKING_ + #define _UIKIT_AFNETWORKING_ + +#if TARGET_OS_IOS + #import "AFAutoPurgingImageCache.h" + #import "AFImageDownloader.h" + #import "AFNetworkActivityIndicatorManager.h" + #import "UIRefreshControl+AFNetworking.h" + #import "UIWebView+AFNetworking.h" +#endif + + #import "UIActivityIndicatorView+AFNetworking.h" + #import "UIButton+AFNetworking.h" + #import "UIImageView+AFNetworking.h" + #import "UIProgressView+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..8ea0a73 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1,64 @@ +// UIProgressView+AFNetworking.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import + +NS_ASSUME_NONNULL_BEGIN + + +/** + 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. + */ +@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. + */ +- (void)setProgressWithUploadProgressOfTask:(NSURLSessionUploadTask *)task + animated:(BOOL)animated; + +/** + 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. + */ +- (void)setProgressWithDownloadProgressOfTask:(NSURLSessionDownloadTask *)task + 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..058755e --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m @@ -0,0 +1,118 @@ +// UIProgressView+AFNetworking.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS || TARGET_OS_TV + +#import "AFURLSessionManager.h" + +static void * AFTaskCountOfBytesSentContext = &AFTaskCountOfBytesSentContext; +static void * AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext; + +#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 - + +- (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]; +} + +#pragma mark - NSKeyValueObserving + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(__unused NSDictionary *)change + context:(void *)context +{ + 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) {} + } + } + } +} + +@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..215eafc --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1,53 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2011–2016 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 TARGET_OS_IOS + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + 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 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. + */ +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; + +@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..aba6d61 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m @@ -0,0 +1,122 @@ +// UIRefreshControl+AFNetworking.m +// +// Copyright (c) 2011–2016 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 "UIRefreshControl+AFNetworking.h" +#import + +#if TARGET_OS_IOS + +#import "AFURLSessionManager.h" + +@interface AFRefreshControlNotificationObserver : NSObject +@property (readonly, nonatomic, weak) UIRefreshControl *refreshControl; +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl; + +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task; + +@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; +} + +- (void)setRefreshingWithStateOfTask:(NSURLSessionTask *)task { + [[self af_notificationObserver] setRefreshingWithStateOfTask:task]; +} + +@end + +@implementation AFRefreshControlNotificationObserver + +- (instancetype)initWithActivityRefreshControl:(UIRefreshControl *)refreshControl +{ + self = [super init]; + if (self) { + _refreshControl = refreshControl; + } + return self; +} + +- (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 + } +} + +#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]; + + [notificationCenter removeObserver:self name:AFNetworkingTaskDidCompleteNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidResumeNotification object:nil]; + [notificationCenter removeObserver:self name:AFNetworkingTaskDidSuspendNotification 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..b9a56af --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h @@ -0,0 +1,80 @@ +// UIWebView+AFNetworking.h +// Copyright (c) 2011–2016 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 TARGET_OS_IOS + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class AFHTTPSessionManager; + +/** + 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 session manager used to download all requests. + */ +@property (nonatomic, strong) AFHTTPSessionManager *sessionManager; + +/** + 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 progress object monitoring the current download progress. + @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 data 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 single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)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 progress object monitoring the current download progress. + @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 data 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 single argument: the error that occurred. + */ +- (void)loadRequest:(NSURLRequest *)request + MIMEType:(nullable NSString *)MIMEType + textEncodingName:(nullable NSString *)textEncodingName + progress:(NSProgress * _Nullable __autoreleasing * _Nullable)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..751c499 --- /dev/null +++ b/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m @@ -0,0 +1,162 @@ +// UIWebView+AFNetworking.m +// Copyright (c) 2011–2016 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 TARGET_OS_IOS + +#import "AFHTTPSessionManager.h" +#import "AFURLResponseSerialization.h" +#import "AFURLRequestSerialization.h" + +@interface UIWebView (_AFNetworking) +@property (readwrite, nonatomic, strong, setter = af_setURLSessionTask:) NSURLSessionDataTask *af_URLSessionTask; +@end + +@implementation UIWebView (_AFNetworking) + +- (NSURLSessionDataTask *)af_URLSessionTask { + return (NSURLSessionDataTask *)objc_getAssociatedObject(self, @selector(af_URLSessionTask)); +} + +- (void)af_setURLSessionTask:(NSURLSessionDataTask *)af_URLSessionTask { + objc_setAssociatedObject(self, @selector(af_URLSessionTask), af_URLSessionTask, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation UIWebView (AFNetworking) + +- (AFHTTPSessionManager *)sessionManager { + static AFHTTPSessionManager *_af_defaultHTTPSessionManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _af_defaultHTTPSessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + _af_defaultHTTPSessionManager.requestSerializer = [AFHTTPRequestSerializer serializer]; + _af_defaultHTTPSessionManager.responseSerializer = [AFHTTPResponseSerializer serializer]; + }); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu" + return objc_getAssociatedObject(self, @selector(sessionManager)) ?: _af_defaultHTTPSessionManager; +#pragma clang diagnostic pop +} + +- (void)setSessionManager:(AFHTTPSessionManager *)sessionManager { + objc_setAssociatedObject(self, @selector(sessionManager), sessionManager, 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:(NSProgress * _Nullable __autoreleasing * _Nullable)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:(NSProgress * _Nullable __autoreleasing * _Nullable)progress + success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success + failure:(void (^)(NSError *error))failure +{ + NSParameterAssert(request); + + if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) { + [self.af_URLSessionTask cancel]; + } + self.af_URLSessionTask = nil; + + __weak __typeof(self)weakSelf = self; + NSURLSessionDataTask *dataTask; + dataTask = [self.sessionManager + GET:request.URL.absoluteString + parameters:nil + progress:nil + success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (success) { + success((NSHTTPURLResponse *)task.response, responseObject); + } + [strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]]; + + if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [strongSelf.delegate webViewDidFinishLoad:strongSelf]; + } + } + failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) { + if (failure) { + failure(error); + } + }]; + self.af_URLSessionTask = dataTask; + if (progress != nil) { + *progress = [self.sessionManager downloadProgressForTask:dataTask]; + } + [self.af_URLSessionTask resume]; + + if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) { + [self.delegate webViewDidStartLoad:self]; + } +} + +@end + +#endif \ No newline at end of file diff --git a/Pods/Alamofire/LICENSE b/Pods/Alamofire/LICENSE new file mode 100644 index 0000000..5b7934d --- /dev/null +++ b/Pods/Alamofire/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014–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/Alamofire/README.md b/Pods/Alamofire/README.md new file mode 100644 index 0000000..81a0703 --- /dev/null +++ b/Pods/Alamofire/README.md @@ -0,0 +1,1072 @@ +![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/assets/alamofire.png) + +[![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg)](https://travis-ci.org/Alamofire/Alamofire) +[![Cocoapods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](http://cocoadocs.org/docsets/Alamofire) +[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](http://twitter.com/AlamofireSF) + +Alamofire is an HTTP networking library written in Swift. + +## Features + +- [x] Chainable Request / Response methods +- [x] URL / JSON / plist Parameter Encoding +- [x] Upload File / Data / Stream / MultipartFormData +- [x] Download using Request or Resume data +- [x] Authentication with NSURLCredential +- [x] HTTP Response Validation +- [x] TLS Certificate and Public Key Pinning +- [x] Progress Closure & NSProgress +- [x] cURL Debug Output +- [x] Comprehensive Unit Test Coverage +- [x] [Complete Documentation](http://cocoadocs.org/docsets/Alamofire) + +## Requirements + +- iOS 8.0+ / Mac OS X 10.9+ / watchOS 2 +- Xcode 7.0+ + +## Migration Guides + +- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md) + +## Communication + +- If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). (Tag 'alamofire') +- If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/alamofire). +- If you **found a bug**, open an issue. +- If you **have a feature request**, open an issue. +- If you **want to contribute**, submit a pull request. + +## Installation + +> **Embedded frameworks require a minimum deployment target of iOS 8 or OS X Mavericks (10.9).** +> +> Alamofire is no longer supported on iOS 7 due to the lack of support for frameworks. Without frameworks, running Travis-CI against iOS 7 would require a second duplicated test target. The separate test suite would need to import all the Swift files and the tests would need to be duplicated and re-written. This split would be too difficult to maintain to ensure the highest possible quality of the Alamofire ecosystem. + +### CocoaPods + +[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. + +CocoaPods 0.38.2 is required to build Alamofire on the `swift-2.0` branch. It adds support for Xcode 7, Swift 2.0 and embedded frameworks. You can install it with the following command: + +```bash +$ gem install cocoapods +``` + +To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +pod 'Alamofire', '~> 2.0' +``` + +Then, run the following command: + +```bash +$ pod install +``` + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application. + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "Alamofire/Alamofire" ~> 2.0 +``` + +### Manually + +If you prefer not to use either of the aforementioned dependency managers, you can integrate Alamofire into your project manually. + +#### Embedded Framework + +- Add Alamofire as a [submodule](http://git-scm.com/docs/git-submodule) by opening the Terminal, `cd`-ing into your top-level project directory, and entering the following command: + +```bash +$ git submodule add https://github.com/Alamofire/Alamofire.git +``` + +- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. + + > It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. + +- Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target. +- Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. +- In the tab bar at the top of that window, open the "General" panel. +- Click on the `+` button under the "Embedded Binaries" section. +- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. + + > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. + +- Select the top `Alamofire.framework` for iOS and the bottom one for OS X. + + > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as either `Alamofire iOS` or `Alamofire OSX`. + +- And that's it! + +> The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.> > + +--- + +## Usage + +### Making a Request + +```swift +import Alamofire + +Alamofire.request(.GET, "http://httpbin.org/get") +``` + +### Response Handling + +```swift +Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) + .response { request, response, data, error in + print(request) + print(response) + print(error) + } +``` + +> Networking in Alamofire is done _asynchronously_. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are [very good reasons](https://developer.apple.com/library/ios/qa/qa1693/_index.html) for doing it this way. + +> Rather than blocking execution to wait for a response from the server, a [callback](http://en.wikipedia.org/wiki/Callback_%28computer_programming%29) is specified to handle the response once it's received. The result of a request is only available inside the scope of a response handler. Any execution contingent on the response or data received from the server must be done within a handler. + +### Response Serialization + +**Built-in Response Methods** + +- `response()` +- `responseString(encoding: NSStringEncoding)` +- `responseJSON(options: NSJSONReadingOptions)` +- `responsePropertyList(options: NSPropertyListReadOptions)` + +#### Response String Handler + +```swift +Alamofire.request(.GET, "http://httpbin.org/get") + .responseString { _, _, result in + print("Success: \(result.isSuccess)") + print("Response String: \(result.value)") + } +``` + +#### Response JSON Handler + +```swift +Alamofire.request(.GET, "http://httpbin.org/get") + .responseJSON { _, _, result in + print(result) + debugPrint(result) + } +``` + +#### Chained Response Handlers + +Response handlers can even be chained: + +```swift +Alamofire.request(.GET, "http://httpbin.org/get") + .responseString { _, _, result in + print("Response String: \(result.value)") + } + .responseJSON { _, _, result in + print("Response JSON: \(result.value)") + } +``` + +### HTTP Methods + +`Alamofire.Method` lists the HTTP methods defined in [RFC 7231 §4.3](http://tools.ietf.org/html/rfc7231#section-4.3): + +```swift +public enum Method: String { + case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT +} +``` + +These values can be passed as the first argument of the `Alamofire.request` method: + +```swift +Alamofire.request(.POST, "http://httpbin.org/post") + +Alamofire.request(.PUT, "http://httpbin.org/put") + +Alamofire.request(.DELETE, "http://httpbin.org/delete") +``` + +### Parameters + +#### GET Request With URL-Encoded Parameters + +```swift +Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) +// http://httpbin.org/get?foo=bar +``` + +#### POST Request With URL-Encoded Parameters + +```swift +let parameters = [ + "foo": "bar", + "baz": ["a", 1], + "qux": [ + "x": 1, + "y": 2, + "z": 3 + ] +] + +Alamofire.request(.POST, "http://httpbin.org/post", parameters: parameters) +// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3 +``` + +### Parameter Encoding + +Parameters can also be encoded as JSON, Property List, or any custom format, using the `ParameterEncoding` enum: + +```swift +enum ParameterEncoding { + case URL + case URLEncodedInURL + case JSON + case PropertyList(format: NSPropertyListFormat, options: NSPropertyListWriteOptions) + case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) + + func encode(request: NSURLRequest, parameters: [String: AnyObject]?) -> (NSURLRequest, NSError?) + { ... } +} +``` + +- `URL`: A query string to be set as or appended to any existing URL query for `GET`, `HEAD`, and `DELETE` requests, or set as the body for requests with any other HTTP method. The `Content-Type` HTTP header field of an encoded request with HTTP body is set to `application/x-www-form-urlencoded`. _Since there is no published specification for how to encode collection types, Alamofire follows the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`)._ +- `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same implementation as the `.URL` case, but always applies the encoded result to the URL. +- `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`. +- `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/x-plist`. +- `Custom`: Uses the associated closure value to construct a new request given an existing request and parameters. + +#### Manual Parameter Encoding of an NSURLRequest + +```swift +let URL = NSURL(string: "http://httpbin.org/get")! +var request = NSURLRequest(URL: URL) + +let parameters = ["foo": "bar"] +let encoding = Alamofire.ParameterEncoding.URL +(request, _) = encoding.encode(request, parameters: parameters) +``` + +#### POST Request with JSON-encoded Parameters + +```swift +let parameters = [ + "foo": [1,2,3], + "bar": [ + "baz": "qux" + ] +] + +Alamofire.request(.POST, "http://httpbin.org/post", parameters: parameters, encoding: .JSON) +// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}} +``` + +### HTTP Headers + +Adding a custom HTTP header to a `Request` is supported directly in the global `request` method. This makes it easy to attach HTTP headers to a `Request` that can be constantly changing. + +> For HTTP headers that do not change, it is recommended to set them on the `NSURLSessionConfiguration` so they are automatically applied to any `NSURLSessionTask` created by the underlying `NSURLSession`. + +```swift +let headers = [ + "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", + "Content-Type": "application/x-www-form-urlencoded" +] + +Alamofire.request(.GET, "http://httpbin.org/get", headers: headers) + .responseJSON { _, _, result in + debugPrint(result) + } +``` + +### Caching + +Caching is handled on the system framework level by [`NSURLCache`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/occ/cl/NSURLCache). + +### Uploading + +**Supported Upload Types** + +- File +- Data +- Stream +- MultipartFormData + +#### Uploading a File + +```swift +let fileURL = NSBundle.mainBundle().URLForResource("Default", withExtension: "png") +Alamofire.upload(.POST, "http://httpbin.org/post", file: fileURL) +``` + +#### Uploading with Progress + +```swift +Alamofire.upload(.POST, "http://httpbin.org/post", file: fileURL) + .progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in + print(totalBytesWritten) + + // This closure is NOT called on the main queue for performance + // reasons. To update your ui, dispatch to the main queue. + dispatch_async(dispatch_get_main_queue) { + print("Total bytes written on main queue: \(totalBytesWritten)") + } + } + .responseJSON { request, response, result in + debugPrint(result) + } +``` + +#### Uploading MultipartFormData + +```swift +Alamofire.upload( + .POST, + URLString: "http://httpbin.org/post", + multipartFormData: { multipartFormData in + multipartFormData.appendBodyPart(fileURL: unicornImageURL, name: "unicorn") + multipartFormData.appendBodyPart(fileURL: rainbowImageURL, name: "rainbow") + }, + encodingCompletion: { encodingResult in + switch encodingResult { + case .Success(let upload, _, _): + upload.responseJSON { request, response, result in + debugPrint(result) + } + case .Failure(let encodingError): + print(encodingError) + } + } +) +``` + +### Downloading + +**Supported Download Types** + +- Request +- Resume Data + +#### Downloading a File + +```swift +Alamofire.download(.GET, "http://httpbin.org/stream/100") { temporaryURL, response in + let fileManager = NSFileManager.defaultManager() + if let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL { + let pathComponent = response.suggestedFilename + return directoryURL.URLByAppendingPathComponent(pathComponent!) + } + + return temporaryURL +} +``` + +#### Using the Default Download Destination + +```swift +let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask) +Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination) +``` + +#### Downloading a File w/Progress + +```swift +Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination) + .progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in + print(totalBytesRead) + + // This closure is NOT called on the main queue for performance + // reasons. To update your ui, dispatch to the main queue. + dispatch_async(dispatch_get_main_queue) { + print("Total bytes read on main queue: \(totalBytesRead)") + } + } + .response { request, response, _, error in + print(response) + } +``` + +#### Accessing Resume Data for Failed Downloads + +```swift +Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination) + .response { request, response, data, error in + if let + data = data, + resumeDataString = NSString(data: data, encoding: NSUTF8StringEncoding) + { + print("Resume Data: \(resumeDataString)") + } else { + print("Resume Data was empty") + } + } +``` + +> The `data` parameter is automatically populated with the `resumeData` if available. + +```swift +let download = Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination) +download.response { request, response, data, error in + if let + resumeData = download.resumeData, + resumeDataString = NSString(data: data, encoding: NSUTF8StringEncoding) + { + print("Resume Data: \(resumeDataString)") + } else { + print("Resume Data was empty") + } +} +``` + +### Authentication + +Authentication is handled on the system framework level by [`NSURLCredential` and `NSURLAuthenticationChallenge`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLAuthenticationChallenge_Class/Reference/Reference.html). + +**Supported Authentication Schemes** + +- [HTTP Basic](http://en.wikipedia.org/wiki/Basic_access_authentication) +- [HTTP Digest](http://en.wikipedia.org/wiki/Digest_access_authentication) +- [Kerberos](http://en.wikipedia.org/wiki/Kerberos_%28protocol%29) +- [NTLM](http://en.wikipedia.org/wiki/NT_LAN_Manager) + +#### HTTP Basic Authentication + +The `authenticate` method on a `Request` will automatically provide an `NSURLCredential` to an `NSURLAuthenticationChallenge` when appropriate: + +```swift +let user = "user" +let password = "password" + +Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)") + .authenticate(user: user, password: password) + .response { request, response, _, error in + print(response) + } +``` + +Depending upon your server implementation, an `Authorization` header may also be appropriate: + +```swift +let user = "user" +let password = "password" + +let credentialData = "\(user):\(password)".dataUsingEncoding(NSUTF8StringEncoding)! +let base64Credentials = credentialData.base64EncodedStringWithOptions(nil) + +let headers = ["Authorization": "Basic \(base64Credentials)"] + +Alamofire.request(.GET, "http://httpbin.org/basic-auth/user/password", headers: headers) + .responseJSON { _, _, result in + print(result) + } +``` + +#### Authentication with NSURLCredential + +```swift +let user = "user" +let password = "password" + +let credential = NSURLCredential(user: user, password: password, persistence: .ForSession) + +Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)") + .authenticate(usingCredential: credential) + .response { request, response, _, error in + print(response) + } +``` + +### Validation + +By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling `validate` before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type. + +#### Manual Validation + +```swift +Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) + .validate(statusCode: 200..<300) + .validate(contentType: ["application/json"]) + .response { _, _, _, error in + print(error) + } +``` + +#### Automatic Validation + +Automatically validates status code within `200...299` range, and that the `Content-Type` header of the response matches the `Accept` header of the request, if one is provided. + +```swift +Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) + .validate() + .responseJSON { _, _, result in + switch result { + case .Success: + print("Validation Successful") + case .Failure(_, let error): + print(error) + } + } +``` + +### Printable + +```swift +let request = Alamofire.request(.GET, "http://httpbin.org/ip") + +print(request) +// GET http://httpbin.org/ip (200) +``` + +### DebugPrintable + +```swift +let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]) + +debugPrint(request) +``` + +#### Output (cURL) + +```bash +$ curl -i \ + -H "User-Agent: Alamofire" \ + -H "Accept-Encoding: Accept-Encoding: gzip;q=1.0,compress;q=0.5" \ + -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \ + "http://httpbin.org/get?foo=bar" +``` + +--- + +## Advanced Usage + +> Alamofire is built on `NSURLSession` and the Foundation URL Loading System. To make the most of +this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack. + +**Recommended Reading** + +- [URL Loading System Programming Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html) +- [NSURLSession Class Reference](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/Introduction/Introduction.html#//apple_ref/occ/cl/NSURLSession) +- [NSURLCache Class Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/occ/cl/NSURLCache) +- [NSURLAuthenticationChallenge Class Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLAuthenticationChallenge_Class/Reference/Reference.html) + +### Manager + +Top-level convenience methods like `Alamofire.request` use a shared instance of `Alamofire.Manager`, which is configured with the default `NSURLSessionConfiguration`. + +As such, the following two statements are equivalent: + +```swift +Alamofire.request(.GET, "http://httpbin.org/get") +``` + +```swift +let manager = Alamofire.Manager.sharedInstance +manager.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/get"))) +``` + +Applications can create managers for background and ephemeral sessions, as well as new managers that customize the default session configuration, such as for default headers (`HTTPAdditionalHeaders`) or timeout interval (`timeoutIntervalForRequest`). + +#### Creating a Manager with Default Configuration + +```swift +let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() +let manager = Alamofire.Manager(configuration: configuration) +``` + +#### Creating a Manager with Background Configuration + +```swift +let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.example.app.background") +let manager = Alamofire.Manager(configuration: configuration) +``` + +#### Creating a Manager with Ephemeral Configuration + +```swift +let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration() +let manager = Alamofire.Manager(configuration: configuration) +``` + +#### Modifying Session Configuration + +```swift +var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders ?? [:] +defaultHeaders["DNT"] = "1 (Do Not Track Enabled)" + +let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() +configuration.HTTPAdditionalHeaders = defaultHeaders + +let manager = Alamofire.Manager(configuration: configuration) +``` + +> This is **not** recommended for `Authorization` or `Content-Type` headers. Instead, use `URLRequestConvertible` and `ParameterEncoding`, respectively. + +### Request + +The result of a `request`, `upload`, or `download` method is an instance of `Alamofire.Request`. A request is always created using a constructor method from an owning manager, and never initialized directly. + +Methods like `authenticate`, `validate`, and `response` return the caller in order to facilitate chaining. + +Requests can be suspended, resumed, and cancelled: + +- `suspend()`: Suspends the underlying task and dispatch queue +- `resume()`: Resumes the underlying task and dispatch queue. If the owning manager does not have `startRequestsImmediately` set to `true`, the request must call `resume()` in order to start. +- `cancel()`: Cancels the underlying task, producing an error that is passed to any registered response handlers. + +### Response Serialization + +#### Creating a Custom Response Serializer + +Alamofire provides built-in response serialization for strings, JSON, and property lists, but others can be added in extensions on `Alamofire.Request`. + +For example, here's how a response handler using [Ono](https://github.com/mattt/Ono) might be implemented: + +```swift +extension Request { + public static func XMLResponseSerializer() -> GenericResponseSerializer { + return GenericResponseSerializer { request, response, data in + guard let validData = data else { + let failureReason = "Data could not be serialized. Input data was nil." + let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason) + return .Failure(data, error) + } + + do { + let XML = try ONOXMLDocument(data: validData) + return .Success(XML) + } catch { + return .Failure(data, error as NSError) + } + } + } + + public func responseXMLDocument(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result) -> Void) -> Self { + return response(responseSerializer: Request.XMLResponseSerializer(), completionHandler: completionHandler) + } +} +``` + +#### Generic Response Object Serialization + +Generics can be used to provide automatic, type-safe response object serialization. + +```swift +public protocol ResponseObjectSerializable { + init?(response: NSHTTPURLResponse, representation: AnyObject) +} + +extension Request { + public func responseObject(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result) -> Void) -> Self { + let responseSerializer = GenericResponseSerializer { request, response, data in + let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments) + let result = JSONResponseSerializer.serializeResponse(request, response, data) + + switch result { + case .Success(let value): + if let + response = response, + responseObject = T(response: response, representation: value) + { + return .Success(responseObject) + } else { + let failureReason = "JSON could not be serialized into response object: \(value)" + let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason) + return .Failure(data, error) + } + case .Failure(let data, let error): + return .Failure(data, error) + } + } + + return response(responseSerializer: responseSerializer, completionHandler: completionHandler) + } +} +``` + +```swift +final class User: ResponseObjectSerializable { + let username: String + let name: String + + init?(response: NSHTTPURLResponse, representation: AnyObject) { + self.username = response.URL!.lastPathComponent! + self.name = representation.valueForKeyPath("name") as! String + } +} +``` + +```swift +Alamofire.request(.GET, "http://example.com/users/mattt") + .responseObject { (_, _, result: Result) in + debugPrint(result) + } +``` + +The same approach can also be used to handle endpoints that return a representation of a collection of objects: + +```swift +public protocol ResponseCollectionSerializable { + static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [Self] +} + +extension Alamofire.Request { + public func responseCollection(completionHandler: (NSURLRequest?, NSHTTPURLResponse?, Result<[T]>) -> Void) -> Self { + let responseSerializer = GenericResponseSerializer<[T]> { request, response, data in + let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments) + let result = JSONSerializer.serializeResponse(request, response, data) + + switch result { + case .Success(let value): + if let response = response { + return .Success(T.collection(response: response, representation: value)) + } else { + let failureReason = "Response collection could not be serialized due to nil response" + let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason) + return .Failure(data, error) + } + case .Failure(let data, let error): + return .Failure(data, error) + } + } + + return response(responseSerializer: responseSerializer, completionHandler: completionHandler) + } +} +``` + +```swift +final class User: ResponseObjectSerializable, ResponseCollectionSerializable { + let username: String + let name: String + + init?(response: NSHTTPURLResponse, representation: AnyObject) { + self.username = response.URL!.lastPathComponent! + self.name = representation.valueForKeyPath("name") as! String + } + + static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] { + var users: [User] = [] + + if let representation = representation as? [[String: AnyObject]] { + for userRepresentation in representation { + if let user = User(response: response, representation: userRepresentation) { + users.append(user) + } + } + } + + return users + } +} +``` + +```swift +Alamofire.request(.GET, "http://example.com/users") + .responseCollection { (_, _, result: Result<[User]>) in + debugPrint(result) + } +``` + +### URLStringConvertible + +Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to construct URL requests. `NSString`, `NSURL`, `NSURLComponents`, and `NSURLRequest` conform to `URLStringConvertible` by default, allowing any of them to be passed as `URLString` parameters to the `request`, `upload`, and `download` methods: + +```swift +let string = NSString(string: "http://httpbin.org/post") +Alamofire.request(.POST, string) + +let URL = NSURL(string: string)! +Alamofire.request(.POST, URL) + +let URLRequest = NSURLRequest(URL: URL) +Alamofire.request(.POST, URLRequest) // overrides `HTTPMethod` of `URLRequest` + +let URLComponents = NSURLComponents(URL: URL, resolvingAgainstBaseURL: true) +Alamofire.request(.POST, URLComponents) +``` + +Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLStringConvertible` as a convenient way to map domain-specific models to server resources. + +#### Type-Safe Routing + +```swift +extension User: URLStringConvertible { + static let baseURLString = "http://example.com" + + var URLString: String { + return User.baseURLString + "/users/\(username)/" + } +} +``` + +```swift +let user = User(username: "mattt") +Alamofire.request(.GET, user) // http://example.com/users/mattt +``` + +### URLRequestConvertible + +Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. `NSURLRequest` conforms to `URLRequestConvertible` by default, allowing it to be passed into `request`, `upload`, and `download` methods directly (this is the recommended way to specify custom HTTP body for individual requests): + +```swift +let URL = NSURL(string: "http://httpbin.org/post")! +let mutableURLRequest = NSMutableURLRequest(URL: URL) +mutableURLRequest.HTTPMethod = "POST" + +let parameters = ["foo": "bar"] + +do { + mutableURLRequest.HTTPBody = try NSJSONSerialization.dataWithJSONObject(parameters, options: NSJSONWritingOptions()) +} catch { + // No-op +} + +mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + +Alamofire.request(mutableURLRequest) +``` + +Applications interacting with web applications in a significant manner are encouraged to have custom types conform to `URLRequestConvertible` as a way to ensure consistency of requested endpoints. Such an approach can be used to abstract away server-side inconsistencies and provide type-safe routing, as well as manage authentication credentials and other state. + +#### API Parameter Abstraction + +```swift +enum Router: URLRequestConvertible { + static let baseURLString = "http://example.com" + static let perPage = 50 + + case Search(query: String, page: Int) + + // MARK: URLRequestConvertible + + var URLRequest: NSMutableURLRequest { + let result: (path: String, parameters: [String: AnyObject]) = { + switch self { + case .Search(let query, let page) where page > 1: + return ("/search", ["q": query, "offset": Router.perPage * page]) + case .Search(let query, _): + return ("/search", ["q": query]) + } + }() + + let URL = NSURL(string: Router.baseURLString)! + let URLRequest = NSURLRequest(URL: URL.URLByAppendingPathComponent(result.path)) + let encoding = Alamofire.ParameterEncoding.URL + + return encoding.encode(URLRequest, parameters: result.parameters).0 + } +} +``` + +```swift +Alamofire.request(Router.Search(query: "foo bar", page: 1)) // ?q=foo%20bar&offset=50 +``` + +#### CRUD & Authorization + +```swift +enum Router: URLRequestConvertible { + static let baseURLString = "http://example.com" + static var OAuthToken: String? + + case CreateUser([String: AnyObject]) + case ReadUser(String) + case UpdateUser(String, [String: AnyObject]) + case DestroyUser(String) + + var method: Alamofire.Method { + switch self { + case .CreateUser: + return .POST + case .ReadUser: + return .GET + case .UpdateUser: + return .PUT + case .DestroyUser: + return .DELETE + } + } + + var path: String { + switch self { + case .CreateUser: + return "/users" + case .ReadUser(let username): + return "/users/\(username)" + case .UpdateUser(let username, _): + return "/users/\(username)" + case .DestroyUser(let username): + return "/users/\(username)" + } + } + + // MARK: URLRequestConvertible + + var URLRequest: NSMutableURLRequest { + let URL = NSURL(string: Router.baseURLString)! + let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path)) + mutableURLRequest.HTTPMethod = method.rawValue + + if let token = Router.OAuthToken { + mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + } + + switch self { + case .CreateUser(let parameters): + return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0 + case .UpdateUser(_, let parameters): + return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0 + default: + return mutableURLRequest + } + } +} +``` + +```swift +Alamofire.request(Router.ReadUser("mattt")) // GET /users/mattt +``` + +### Security + +Using a secure HTTPS connection when communicating with servers and web services is an important step in securing sensitive data. By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework. While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities. In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the `ServerTrustPolicy`. + +#### ServerTrustPolicy + +The `ServerTrustPolicy` enumeration evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when connecting to a server over a secure HTTPS connection. + +```swift +let serverTrustPolicy = ServerTrustPolicy.PinCertificates( + certificates: ServerTrustPolicy.certificatesInBundle(), + validateCertificateChain: true, + validateHost: true +) +``` + +There are many different cases of server trust evaluation giving you complete control over the validation process: + +* `PerformDefaultEvaluation`: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge. +* `PinCertificates`: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates. +* `PinPublicKeys`: Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys. +* `DisableEvaluation`: Disables all evaluation which in turn will always consider any server trust as valid. +* `CustomEvaluation`: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution. + +#### Server Trust Policy Manager + +The `ServerTrustPolicyManager` is responsible for storing an internal mapping of server trust policies to a particular host. This allows Alamofire to evaluate each host against a different server trust policy. + +```swift +let serverTrustPolicies: [String: ServerTrustPolicy] = [ + "test.example.com": .PinCertificates( + certificates: ServerTrustPolicy.certificatesInBundle(), + validateCertificateChain: true, + validateHost: true + ), + "insecure.expired-apis.com": .DisableEvaluation +] + +let manager = Manager( + configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), + serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies) +) +``` + +> Make sure to keep a reference to the new `Manager` instance, otherwise your requests will all get cancelled when your `manager` is deallocated. + +These server trust policies will result in the following behavior: + +* `test.example.com` will always use certificate pinning with certificate chain and host validation enabled thus requiring the following criteria to be met to allow the TLS handshake to succeed: + * Certificate chain MUST be valid. + * Certificate chain MUST include one of the pinned certificates. + * Challenge host MUST match the host in the certificate chain's leaf certificate. +* `insecure.expired-apis.com` will never evaluate the certificate chain and will always allow the TLS handshake to succeed. +* All other hosts will use the default evaluation provided by Apple. + +##### Subclassing Server Trust Policy Manager + +If you find yourself needing more flexible server trust policy matching behavior (i.e. wildcarded domains), then subclass the `ServerTrustPolicyManager` and override the `serverTrustPolicyForHost` method with your own custom implementation. + +```swift +class CustomServerTrustPolicyManager: ServerTrustPolicyManager { + override func serverTrustPolicyForHost(host: String) -> ServerTrustPolicy? { + var policy: ServerTrustPolicy? + + // Implement your custom domain matching behavior... + + return policy + } +} +``` + +#### Validating the Host + +The `.PerformDefaultEvaluation`, `.PinCertificates` and `.PinPublicKeys` server trust policies all take a `validateHost` parameter. Setting the value to `true` will cause the server trust evaluation to verify that hostname in the certificate matches the hostname of the challenge. If they do not match, evaluation will fail. A `validateHost` value of `false` will still evaluate the full certificate chain, but will not validate the hostname of the leaf certificate. + +> It is recommended that `validateHost` always be set to `true` in production environments. + +#### Validating the Certificate Chain + +Pinning certificates and public keys both have the option of validating the certificate chain using the `validateCertificateChain` parameter. By setting this value to `true`, the full certificate chain will be evaluated in addition to performing a byte equality check against the pinned certficates or public keys. A value of `false` will skip the certificate chain validation, but will still perform the byte equality check. + +There are several cases where it may make sense to disable certificate chain validation. The most common use cases for disabling validation are self-signed and expired certificates. The evaluation would always fail in both of these cases, but the byte equality check will still ensure you are receiving the certificate you expect from the server. + +> It is recommended that `validateCertificateChain` always be set to `true` in production environments. + +--- + +## Component Libraries + +In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem. + +* [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache and a priority-based image downloading system. + +## Open Rdars + +The following rdars have some affect on the current implementation of Alamofire. + +* [rdar://22024442](http://www.openradar.me/radar?id=6082025006039040) - Array of [SecCertificate] crashing Swift 2.0 compiler in optimized builds +* [rdar://21349340](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case +* [rdar://22307360](http://www.openradar.me/radar?id=4895563208196096) - Swift #available check not working properly with min deployment target + +## FAQ + +### What's the origin of the name Alamofire? + +Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. + +--- + +## Credits + +Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. + +### Security Disclosure + +If you believe you have identified a security vulnerability with Alamofire, 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 + +Alamofire is released under the MIT license. See LICENSE for details. diff --git a/Pods/Alamofire/Source/Alamofire.swift b/Pods/Alamofire/Source/Alamofire.swift new file mode 100644 index 0000000..e6369f1 --- /dev/null +++ b/Pods/Alamofire/Source/Alamofire.swift @@ -0,0 +1,368 @@ +// Alamofire.swift +// +// Copyright (c) 2014–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 Foundation + +// MARK: - URLStringConvertible + +/** + Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to + construct URL requests. +*/ +public protocol URLStringConvertible { + /** + A URL that conforms to RFC 2396. + + Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808. + + See https://tools.ietf.org/html/rfc2396 + See https://tools.ietf.org/html/rfc1738 + See https://tools.ietf.org/html/rfc1808 + */ + var URLString: String { get } +} + +extension String: URLStringConvertible { + public var URLString: String { + return self + } +} + +extension URL: URLStringConvertible { + public var URLString: String { + return absoluteString + } +} + +extension URLComponents: URLStringConvertible { + public var URLString: String { + return url!.URLString + } +} + +extension Foundation.URLRequest: URLStringConvertible { + public var URLString: String { + return url!.URLString + } +} + +// MARK: - URLRequestConvertible + +/** + Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. +*/ +public protocol URLRequestConvertible { + /// The URL request. + var URLRequest: NSMutableURLRequest { get } +} + +extension Foundation.URLRequest: URLRequestConvertible { + public var URLRequest: NSMutableURLRequest { + return (self as NSURLRequest).mutableCopy() as! NSMutableURLRequest + } +} + +// MARK: - Convenience + +func URLRequest( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil) + -> NSMutableURLRequest +{ + let mutableURLRequest = NSMutableURLRequest(url: URL(string: URLString.URLString)!) + mutableURLRequest.httpMethod = method.rawValue + + if let headers = headers { + for (headerField, headerValue) in headers { + mutableURLRequest.setValue(headerValue, forHTTPHeaderField: headerField) + } + } + + return mutableURLRequest +} + +// MARK: - Request Methods + +/** + Creates a request using the shared manager instance for the specified method, URL string, parameters, and + parameter encoding. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter parameters: The parameters. `nil` by default. + - parameter encoding: The parameter encoding. `.URL` by default. + - parameter headers: The HTTP headers. `nil` by default. + + - returns: The created request. +*/ +public func request( + _ method: Method, + _ URLString: URLStringConvertible, + parameters: [String: AnyObject]? = nil, + encoding: ParameterEncoding = .url, + headers: [String: String]? = nil) + -> Request +{ + return Manager.sharedInstance.request( + method, + URLString, + parameters: parameters, + encoding: encoding, + headers: headers + ) +} + +/** + Creates a request using the shared manager instance for the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request + + - returns: The created request. +*/ +public func request(_ URLRequest: URLRequestConvertible) -> Request { + return Manager.sharedInstance.request(URLRequest.URLRequest as! URLRequestConvertible) +} + +// MARK: - Upload Methods + +// MARK: File + +/** + Creates an upload request using the shared manager instance for the specified method, URL string, and file. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter file: The file to upload. + + - returns: The created upload request. +*/ +public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + file: URL) + -> Request +{ + return Manager.sharedInstance.upload(method, URLString, headers: headers, file: file) +} + +/** + Creates an upload request using the shared manager instance for the specified URL request and file. + + - parameter URLRequest: The URL request. + - parameter file: The file to upload. + + - returns: The created upload request. +*/ +public func upload(_ URLRequest: URLRequestConvertible, file: URL) -> Request { + return Manager.sharedInstance.upload(URLRequest, file: file) +} + +// MARK: Data + +/** + Creates an upload request using the shared manager instance for the specified method, URL string, and data. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter data: The data to upload. + + - returns: The created upload request. +*/ +public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + data: Data) + -> Request +{ + return Manager.sharedInstance.upload(method, URLString, headers: headers, data: data) +} + +/** + Creates an upload request using the shared manager instance for the specified URL request and data. + + - parameter URLRequest: The URL request. + - parameter data: The data to upload. + + - returns: The created upload request. +*/ +public func upload(_ URLRequest: URLRequestConvertible, data: Data) -> Request { + return Manager.sharedInstance.upload(URLRequest, data: data) +} + +// MARK: Stream + +/** + Creates an upload request using the shared manager instance for the specified method, URL string, and stream. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter stream: The stream to upload. + + - returns: The created upload request. +*/ +public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + stream: InputStream) + -> Request +{ + return Manager.sharedInstance.upload(method, URLString, headers: headers, stream: stream) +} + +/** + Creates an upload request using the shared manager instance for the specified URL request and stream. + + - parameter URLRequest: The URL request. + - parameter stream: The stream to upload. + + - returns: The created upload request. +*/ +public func upload(_ URLRequest: URLRequestConvertible, stream: InputStream) -> Request { + return Manager.sharedInstance.upload(URLRequest, stream: stream) +} + +// MARK: MultipartFormData + +/** + Creates an upload request using the shared manager instance for the specified method and URL string. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + `MultipartFormDataEncodingMemoryThreshold` by default. + - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. +*/ +public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + multipartFormData: (MultipartFormData) -> Void, + encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, + encodingCompletion: ((Manager.MultipartFormDataEncodingResult) -> Void)?) +{ + return Manager.sharedInstance.upload( + method, + URLString, + headers: headers, + multipartFormData: multipartFormData, + encodingMemoryThreshold: encodingMemoryThreshold, + encodingCompletion: encodingCompletion + ) +} + +/** + Creates an upload request using the shared manager instance for the specified method and URL string. + + - parameter URLRequest: The URL request. + - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + `MultipartFormDataEncodingMemoryThreshold` by default. + - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. +*/ +public func upload( + _ URLRequest: URLRequestConvertible, + multipartFormData: @escaping (MultipartFormData) -> Void, + encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, + encodingCompletion: ((Manager.MultipartFormDataEncodingResult) -> Void)?) +{ + return Manager.sharedInstance.upload( + URLRequest, + multipartFormData: multipartFormData, + encodingMemoryThreshold: encodingMemoryThreshold, + encodingCompletion: encodingCompletion + ) +} + +// MARK: - Download Methods + +// MARK: URL Request + +/** + Creates a download request using the shared manager instance for the specified method and URL string. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter parameters: The parameters. `nil` by default. + - parameter encoding: The parameter encoding. `.URL` by default. + - parameter headers: The HTTP headers. `nil` by default. + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. +*/ +public func download( + _ method: Method, + _ URLString: URLStringConvertible, + parameters: [String: AnyObject]? = nil, + encoding: ParameterEncoding = .url, + headers: [String: String]? = nil, + destination: Request.DownloadFileDestination) + -> Request +{ + return Manager.sharedInstance.download( + method, + URLString, + parameters: parameters, + encoding: encoding, + headers: headers, + destination: destination + ) +} + +/** + Creates a download request using the shared manager instance for the specified URL request. + + - parameter URLRequest: The URL request. + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. +*/ +public func download(_ URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { + return Manager.sharedInstance.download(URLRequest, destination: destination) +} + +// MARK: Resume Data + +/** + Creates a request using the shared manager instance for downloading from the resume data produced from a + previous request cancellation. + + - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` + when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional + information. + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. +*/ +public func download(resumeData data: Data, destination: Request.DownloadFileDestination) -> Request { + return Manager.sharedInstance.download(data, destination: destination) +} diff --git a/Pods/Alamofire/Source/Download.swift b/Pods/Alamofire/Source/Download.swift new file mode 100644 index 0000000..532a1f7 --- /dev/null +++ b/Pods/Alamofire/Source/Download.swift @@ -0,0 +1,244 @@ +// Download.swift +// +// Copyright (c) 2014–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 Foundation + +extension Manager { + fileprivate enum Downloadable { + case request(Foundation.URLRequest) + case resumeData(Data) + } + + fileprivate func download(_ downloadable: Downloadable, destination: @escaping Request.DownloadFileDestination) -> Request { + var downloadTask: URLSessionDownloadTask! + + switch downloadable { + case .request(let request): + queue.sync { + downloadTask = self.session.downloadTask(with: request) + } + case .resumeData(let resumeData): + queue.sync { + downloadTask = self.session.downloadTask(withResumeData: resumeData) + } + } + + let request = Request(session: session, task: downloadTask) + + if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate { + downloadDelegate.downloadTaskDidFinishDownloadingToURL = { session, downloadTask, URL in + return destination(URL, downloadTask.response as! HTTPURLResponse) + } + } + + delegate[request.delegate.task] = request.delegate + + if startRequestsImmediately { + request.resume() + } + + return request + } + + // MARK: Request + + /** + Creates a download request for the specified method, URL string, parameters, parameter encoding, headers + and destination. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter parameters: The parameters. `nil` by default. + - parameter encoding: The parameter encoding. `.URL` by default. + - parameter headers: The HTTP headers. `nil` by default. + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. + */ + public func download( + _ method: Method, + _ URLString: URLStringConvertible, + parameters: [String: AnyObject]? = nil, + encoding: ParameterEncoding = .url, + headers: [String: String]? = nil, + destination: Request.DownloadFileDestination) + -> Request + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 + + return download(encodedURLRequest, destination: destination) + } + + /** + Creates a request for downloading from the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. + */ + public func download(_ URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request { + return download(.request(URLRequest.URLRequest), destination: destination) + } + + // MARK: Resume Data + + /** + Creates a request for downloading from the resume data produced from a previous request cancellation. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter resumeData: The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` + when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for + additional information. + - parameter destination: The closure used to determine the destination of the downloaded file. + + - returns: The created download request. + */ + public func download(_ resumeData: Data, destination: Request.DownloadFileDestination) -> Request { + return download(.resumeData(resumeData), destination: destination) + } +} + +// MARK: - + +extension Request { + /** + A closure executed once a request has successfully completed in order to determine where to move the temporary + file written to during the download process. The closure takes two arguments: the temporary file URL and the URL + response, and returns a single argument: the file URL where the temporary file should be moved. + */ + public typealias DownloadFileDestination = (URL, HTTPURLResponse) -> URL + + /** + Creates a download file destination closure which uses the default file manager to move the temporary file to a + file URL in the first available directory with the specified search path directory and search path domain mask. + + - parameter directory: The search path directory. `.DocumentDirectory` by default. + - parameter domain: The search path domain mask. `.UserDomainMask` by default. + + - returns: A download file destination closure. + */ + public class func suggestedDownloadDestination( + directory: FileManager.SearchPathDirectory = .documentDirectory, + domain: FileManager.SearchPathDomainMask = .userDomainMask) + -> DownloadFileDestination + { + return { temporaryURL, response -> URL in + let directoryURLs = FileManager.default.urls(for: directory, in: domain) + + if !directoryURLs.isEmpty { + return directoryURLs[0].URLByAppendingPathComponent(response.suggestedFilename!) + } + + return temporaryURL + } + } + + /// The resume data of the underlying download task if available after a failure. + public var resumeData: Data? { + var data: Data? + + if let delegate = delegate as? DownloadTaskDelegate { + data = delegate.resumeData + } + + return data + } + + // MARK: - DownloadTaskDelegate + + class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { + var downloadTask: URLSessionDownloadTask? { return task as? URLSessionDownloadTask } + var downloadProgress: ((Int64, Int64, Int64) -> Void)? + + var resumeData: Data? + override var data: Data? { return resumeData } + + // MARK: - NSURLSessionDownloadDelegate + + // MARK: Override Closures + + var downloadTaskDidFinishDownloadingToURL: ((Foundation.URLSession, URLSessionDownloadTask, URL) -> URL)? + var downloadTaskDidWriteData: ((Foundation.URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + var downloadTaskDidResumeAtOffset: ((Foundation.URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + // MARK: Delegate Methods + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) + { + if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { + do { + let destination = downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) + try FileManager.default.moveItem(at: location, to: destination) + } catch { + self.error = error as NSError + } + } + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + { + if let downloadTaskDidWriteData = downloadTaskDidWriteData { + downloadTaskDidWriteData( + session, + downloadTask, + bytesWritten, + totalBytesWritten, + totalBytesExpectedToWrite + ) + } else { + progress.totalUnitCount = totalBytesExpectedToWrite + progress.completedUnitCount = totalBytesWritten + + downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) + } + } + + func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + { + if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { + downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) + } else { + progress.totalUnitCount = expectedTotalBytes + progress.completedUnitCount = fileOffset + } + } + } +} diff --git a/Pods/Alamofire/Source/Error.swift b/Pods/Alamofire/Source/Error.swift new file mode 100644 index 0000000..9f11d4f --- /dev/null +++ b/Pods/Alamofire/Source/Error.swift @@ -0,0 +1,66 @@ +// Error.swift +// +// Copyright (c) 2014–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 Foundation + +/// The `Error` struct provides a convenience for creating custom Alamofire NSErrors. +public struct Error { + /// The domain used for creating all Alamofire errors. + public static let Domain = "com.alamofire.error" + + /// The custom error codes generated by Alamofire. + public enum Code: Int { + case inputStreamReadFailed = -6000 + case outputStreamWriteFailed = -6001 + case contentTypeValidationFailed = -6002 + case statusCodeValidationFailed = -6003 + case dataSerializationFailed = -6004 + case stringSerializationFailed = -6005 + case jsonSerializationFailed = -6006 + case propertyListSerializationFailed = -6007 + } + + /** + Creates an `NSError` with the given error code and failure reason. + + - parameter code: The error code. + - parameter failureReason: The failure reason. + + - returns: An `NSError` with the given error code and failure reason. + */ + public static func errorWithCode(_ code: Code, failureReason: String) -> NSError { + return errorWithCode(code.rawValue, failureReason: failureReason) + } + + /** + Creates an `NSError` with the given error code and failure reason. + + - parameter code: The error code. + - parameter failureReason: The failure reason. + + - returns: An `NSError` with the given error code and failure reason. + */ + public static func errorWithCode(_ code: Int, failureReason: String) -> NSError { + let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason] + return NSError(domain: Domain, code: code, userInfo: userInfo) + } +} diff --git a/Pods/Alamofire/Source/Manager.swift b/Pods/Alamofire/Source/Manager.swift new file mode 100644 index 0000000..c056b43 --- /dev/null +++ b/Pods/Alamofire/Source/Manager.swift @@ -0,0 +1,668 @@ +// Manager.swift +// +// Copyright (c) 2014–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 Foundation + +/** + Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`. +*/ +open class Manager { + + // MARK: - Properties + + /** + A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly + for any ad hoc requests. + */ + open static let sharedInstance: Manager = { + let configuration = URLSessionConfiguration.default + configuration.httpAdditionalHeaders = Manager.defaultHTTPHeaders + + return Manager(configuration: configuration) + }() + + /** + Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers. + */ + open static let defaultHTTPHeaders: [String: String] = { + // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3 + let acceptEncoding: String = "gzip;q=1.0,compress;q=0.5" + + // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5 + let acceptLanguage: String = { + var components: [String] = [] + for (index, languageCode) in (Locale.preferredLanguages as [String]).enumerated() { + let q = 1.0 - (Double(index) * 0.1) + components.append("\(languageCode);q=\(q)") + if q <= 0.5 { + break + } + } + + return components.joined(separator: ",") + }() + + // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3 + let userAgent: String = { + if let info = Bundle.main.infoDictionary { + let executable: AnyObject = info[kCFBundleExecutableKey as String] as AnyObject ?? "Unknown" as AnyObject + let bundle: AnyObject = info[kCFBundleIdentifierKey as String] as AnyObject ?? "Unknown" as AnyObject + let version: AnyObject = info[kCFBundleVersionKey as String] as AnyObject ?? "Unknown" as AnyObject + let os: AnyObject = ProcessInfo.processInfo.operatingSystemVersionString as AnyObject ?? "Unknown" as AnyObject + + var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString + let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString + + if CFStringTransform(mutableUserAgent, UnsafeMutablePointer(nil), transform, false) { + return mutableUserAgent as String + } + } + + return "Alamofire" + }() + + return [ + "Accept-Encoding": acceptEncoding, + "Accept-Language": acceptLanguage, + "User-Agent": userAgent + ] + }() + + let queue = DispatchQueue(label: nil, attributes: []) + + /// The underlying session. + open let session: URLSession + + /// The session delegate handling all the task and session delegate callbacks. + open let delegate: SessionDelegate + + /// Whether to start requests immediately after being constructed. `true` by default. + open var startRequestsImmediately: Bool = true + + /** + The background completion handler closure provided by the UIApplicationDelegate + `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background + completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation + will automatically call the handler. + + If you need to handle your own events before the handler is called, then you need to override the + SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished. + + `nil` by default. + */ + open var backgroundCompletionHandler: (() -> Void)? + + // MARK: - Lifecycle + + /** + Initializes the `Manager` instance with the given configuration and server trust policy. + + - parameter configuration: The configuration used to construct the managed session. + `NSURLSessionConfiguration.defaultSessionConfiguration()` by default. + - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust + challenges. `nil` by default. + + - returns: The new `Manager` instance. + */ + public init( + configuration: URLSessionConfiguration = URLSessionConfiguration.default, + serverTrustPolicyManager: ServerTrustPolicyManager? = nil) + { + self.delegate = SessionDelegate() + self.session = URLSession(configuration: configuration, delegate: self.delegate, delegateQueue: nil) + self.session.serverTrustPolicyManager = serverTrustPolicyManager + + self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in + guard let strongSelf = self else { return } + DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() } + } + } + + deinit { + session.invalidateAndCancel() + } + + // MARK: - Request + + /** + Creates a request for the specified method, URL string, parameters, parameter encoding and headers. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter parameters: The parameters. `nil` by default. + - parameter encoding: The parameter encoding. `.URL` by default. + - parameter headers: The HTTP headers. `nil` by default. + + - returns: The created request. + */ + open func request( + _ method: Method, + _ URLString: URLStringConvertible, + parameters: [String: AnyObject]? = nil, + encoding: ParameterEncoding = .url, + headers: [String: String]? = nil) + -> Request + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0 + return request(encodedURLRequest) + } + + /** + Creates a request for the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request + + - returns: The created request. + */ + open func request(_ URLRequest: URLRequestConvertible) -> Request { + var dataTask: URLSessionDataTask! + + queue.sync { + dataTask = self.session.dataTask(with: URLRequest.URLRequest) + } + + let request = Request(session: session, task: dataTask) + delegate[request.delegate.task] = request.delegate + + if startRequestsImmediately { + request.resume() + } + + return request + } + + // MARK: - SessionDelegate + + /** + Responsible for handling all delegate callbacks for the underlying session. + */ + public final class SessionDelegate: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate { + fileprivate var subdelegates: [Int: Request.TaskDelegate] = [:] + fileprivate let subdelegateQueue = DispatchQueue(label: nil, attributes: DispatchQueue.Attributes.concurrent) + + subscript(task: URLSessionTask) -> Request.TaskDelegate? { + get { + var subdelegate: Request.TaskDelegate? + subdelegateQueue.sync { + subdelegate = self.subdelegates[task.taskIdentifier] + } + + return subdelegate + } + + set { + subdelegateQueue.async(flags: .barrier, execute: { + self.subdelegates[task.taskIdentifier] = newValue + }) + } + } + + // MARK: - NSURLSessionDelegate + + // MARK: Override Closures + + /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didBecomeInvalidWithError:`. + public var sessionDidBecomeInvalidWithError: ((Foundation.URLSession, NSError?) -> Void)? + + /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didReceiveChallenge:completionHandler:`. + public var sessionDidReceiveChallenge: ((Foundation.URLSession, URLAuthenticationChallenge) -> (Foundation.URLSession.AuthChallengeDisposition, URLCredential?))? + + /// Overrides default behavior for NSURLSessionDelegate method `URLSessionDidFinishEventsForBackgroundURLSession:`. + public var sessionDidFinishEventsForBackgroundURLSession: ((Foundation.URLSession) -> Void)? + + // MARK: Delegate Methods + + /** + Tells the delegate that the session has been invalidated. + + - parameter session: The session object that was invalidated. + - parameter error: The error that caused invalidation, or nil if the invalidation was explicit. + */ + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + sessionDidBecomeInvalidWithError?(session, error) + } + + /** + Requests credentials from the delegate in response to a session-level authentication request from the remote server. + + - parameter session: The session containing the task that requested authentication. + - parameter challenge: An object that contains the request for authentication. + - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential. + */ + public func urlSession( + _ session: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: (@escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) + { + var disposition: Foundation.URLSession.AuthChallengeDisposition = .performDefaultHandling + var credential: URLCredential? + + if let sessionDidReceiveChallenge = sessionDidReceiveChallenge { + (disposition, credential) = sessionDidReceiveChallenge(session, challenge) + } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + let host = challenge.protectionSpace.host + + if let + serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host), + let serverTrust = challenge.protectionSpace.serverTrust + { + if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) { + disposition = .useCredential + credential = URLCredential(trust: serverTrust) + } else { + disposition = .cancelAuthenticationChallenge + } + } + } + + completionHandler(disposition, credential) + } + + /** + Tells the delegate that all messages enqueued for a session have been delivered. + + - parameter session: The session that no longer has any outstanding requests. + */ + public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { + sessionDidFinishEventsForBackgroundURLSession?(session) + } + + // MARK: - NSURLSessionTaskDelegate + + // MARK: Override Closures + + /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`. + public var taskWillPerformHTTPRedirection: ((Foundation.URLSession, URLSessionTask, HTTPURLResponse, Foundation.URLRequest) -> Foundation.URLRequest?)? + + /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`. + public var taskDidReceiveChallenge: ((Foundation.URLSession, URLSessionTask, URLAuthenticationChallenge) -> (Foundation.URLSession.AuthChallengeDisposition, URLCredential?))? + + /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`. + public var taskNeedNewBodyStream: ((Foundation.URLSession, URLSessionTask) -> InputStream?)? + + /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`. + public var taskDidSendBodyData: ((Foundation.URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`. + public var taskDidComplete: ((Foundation.URLSession, URLSessionTask, NSError?) -> Void)? + + // MARK: Delegate Methods + + /** + Tells the delegate that the remote server requested an HTTP redirect. + + - parameter session: The session containing the task whose request resulted in a redirect. + - parameter task: The task whose request resulted in a redirect. + - parameter response: An object containing the server’s response to the original request. + - parameter request: A URL request object filled out with the new location. + - parameter completionHandler: A closure that your handler should call with either the value of the request + parameter, a modified URL request object, or NULL to refuse the redirect and + return the body of the redirect response. + */ + public func urlSession( + _ session: URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: URLRequest, + completionHandler: (@escaping (Foundation.URLRequest?) -> Void)) + { + var redirectRequest: Foundation.URLRequest? = request + + if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { + redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) + } + + completionHandler(redirectRequest) + } + + /** + Requests credentials from the delegate in response to an authentication request from the remote server. + + - parameter session: The session containing the task whose request requires authentication. + - parameter task: The task whose request requires authentication. + - parameter challenge: An object that contains the request for authentication. + - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential. + */ + public func urlSession( + _ session: URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: (@escaping (Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) + { + if let taskDidReceiveChallenge = taskDidReceiveChallenge { + completionHandler(taskDidReceiveChallenge(session, task, challenge)) + } else if let delegate = self[task] { + delegate.urlSession( + session, + task: task, + didReceive: challenge, + completionHandler: completionHandler + ) + } else { + urlSession(session, didReceive: challenge, completionHandler: completionHandler) + } + } + + /** + Tells the delegate when a task requires a new request body stream to send to the remote server. + + - parameter session: The session containing the task that needs a new body stream. + - parameter task: The task that needs a new body stream. + - parameter completionHandler: A completion handler that your delegate method should call with the new body stream. + */ + public func urlSession( + _ session: URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: (@escaping (InputStream?) -> Void)) + { + if let taskNeedNewBodyStream = taskNeedNewBodyStream { + completionHandler(taskNeedNewBodyStream(session, task)) + } else if let delegate = self[task] { + delegate.urlSession(session, task: task, needNewBodyStream: completionHandler) + } + } + + /** + Periodically informs the delegate of the progress of sending body content to the server. + + - parameter session: The session containing the data task. + - parameter task: The data task. + - parameter bytesSent: The number of bytes sent since the last time this delegate method was called. + - parameter totalBytesSent: The total number of bytes sent so far. + - parameter totalBytesExpectedToSend: The expected length of the body data. + */ + public func urlSession( + _ session: URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + { + if let taskDidSendBodyData = taskDidSendBodyData { + taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } else if let delegate = self[task] as? Request.UploadTaskDelegate { + delegate.URLSession( + session, + task: task, + didSendBodyData: bytesSent, + totalBytesSent: totalBytesSent, + totalBytesExpectedToSend: totalBytesExpectedToSend + ) + } + } + + /** + Tells the delegate that the task finished transferring data. + + - parameter session: The session containing the task whose request finished transferring data. + - parameter task: The task whose request finished transferring data. + - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil. + */ + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + if let taskDidComplete = taskDidComplete { + taskDidComplete(session, task, error) + } else if let delegate = self[task] { + delegate.urlSession(session, task: task, didCompleteWithError: error) + } + + self[task] = nil + } + + // MARK: - NSURLSessionDataDelegate + + // MARK: Override Closures + + /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`. + public var dataTaskDidReceiveResponse: ((Foundation.URLSession, URLSessionDataTask, URLResponse) -> Foundation.URLSession.ResponseDisposition)? + + /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`. + public var dataTaskDidBecomeDownloadTask: ((Foundation.URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? + + /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`. + public var dataTaskDidReceiveData: ((Foundation.URLSession, URLSessionDataTask, Data) -> Void)? + + /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`. + public var dataTaskWillCacheResponse: ((Foundation.URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? + + // MARK: Delegate Methods + + /** + Tells the delegate that the data task received the initial reply (headers) from the server. + + - parameter session: The session containing the data task that received an initial reply. + - parameter dataTask: The data task that received an initial reply. + - parameter response: A URL response object populated with headers. + - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a + constant to indicate whether the transfer should continue as a data task or + should become a download task. + */ + public func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: (@escaping (Foundation.URLSession.ResponseDisposition) -> Void)) + { + var disposition: Foundation.URLSession.ResponseDisposition = .allow + + if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { + disposition = dataTaskDidReceiveResponse(session, dataTask, response) + } + + completionHandler(disposition) + } + + /** + Tells the delegate that the data task was changed to a download task. + + - parameter session: The session containing the task that was replaced by a download task. + - parameter dataTask: The data task that was replaced by a download task. + - parameter downloadTask: The new download task that replaced the data task. + */ + public func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didBecome downloadTask: URLSessionDownloadTask) + { + if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask { + dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask) + } else { + let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask) + self[downloadTask] = downloadDelegate + } + } + + /** + Tells the delegate that the data task has received some of the expected data. + + - parameter session: The session containing the data task that provided data. + - parameter dataTask: The data task that provided data. + - parameter data: A data object containing the transferred data. + */ + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + if let dataTaskDidReceiveData = dataTaskDidReceiveData { + dataTaskDidReceiveData(session, dataTask, data) + } else if let delegate = self[dataTask] as? Request.DataTaskDelegate { + delegate.urlSession(session, dataTask: dataTask, didReceive: data) + } + } + + /** + Asks the delegate whether the data (or upload) task should store the response in the cache. + + - parameter session: The session containing the data (or upload) task. + - parameter dataTask: The data (or upload) task. + - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current + caching policy and the values of certain received headers, such as the Pragma + and Cache-Control headers. + - parameter completionHandler: A block that your handler must call, providing either the original proposed + response, a modified version of that response, or NULL to prevent caching the + response. If your delegate implements this method, it must call this completion + handler; otherwise, your app leaks memory. + */ + public func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: (@escaping (CachedURLResponse?) -> Void)) + { + if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { + completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse)) + } else if let delegate = self[dataTask] as? Request.DataTaskDelegate { + delegate.urlSession( + session, + dataTask: dataTask, + willCacheResponse: proposedResponse, + completionHandler: completionHandler + ) + } else { + completionHandler(proposedResponse) + } + } + + // MARK: - NSURLSessionDownloadDelegate + + // MARK: Override Closures + + /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`. + public var downloadTaskDidFinishDownloadingToURL: ((Foundation.URLSession, URLSessionDownloadTask, URL) -> Void)? + + /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`. + public var downloadTaskDidWriteData: ((Foundation.URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? + + /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`. + public var downloadTaskDidResumeAtOffset: ((Foundation.URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? + + // MARK: Delegate Methods + + /** + Tells the delegate that a download task has finished downloading. + + - parameter session: The session containing the download task that finished. + - parameter downloadTask: The download task that finished. + - parameter location: A file URL for the temporary file. Because the file is temporary, you must either + open the file for reading or move it to a permanent location in your app’s sandbox + container directory before returning from this delegate method. + */ + public func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL) + { + if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL { + downloadTaskDidFinishDownloadingToURL(session, downloadTask, location) + } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate { + delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) + } + } + + /** + Periodically informs the delegate about the download’s progress. + + - parameter session: The session containing the download task. + - parameter downloadTask: The download task. + - parameter bytesWritten: The number of bytes transferred since the last time this delegate + method was called. + - parameter totalBytesWritten: The total number of bytes transferred so far. + - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length + header. If this header was not provided, the value is + `NSURLSessionTransferSizeUnknown`. + */ + public func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didWriteData bytesWritten: Int64, + totalBytesWritten: Int64, + totalBytesExpectedToWrite: Int64) + { + if let downloadTaskDidWriteData = downloadTaskDidWriteData { + downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) + } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate { + delegate.urlSession( + session, + downloadTask: downloadTask, + didWriteData: bytesWritten, + totalBytesWritten: totalBytesWritten, + totalBytesExpectedToWrite: totalBytesExpectedToWrite + ) + } + } + + /** + Tells the delegate that the download task has resumed downloading. + + - parameter session: The session containing the download task that finished. + - parameter downloadTask: The download task that resumed. See explanation in the discussion. + - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the + existing content, then this value is zero. Otherwise, this value is an + integer representing the number of bytes on disk that do not need to be + retrieved again. + - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header. + If this header was not provided, the value is NSURLSessionTransferSizeUnknown. + */ + public func urlSession( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didResumeAtOffset fileOffset: Int64, + expectedTotalBytes: Int64) + { + if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { + downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) + } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate { + delegate.urlSession( + session, + downloadTask: downloadTask, + didResumeAtOffset: fileOffset, + expectedTotalBytes: expectedTotalBytes + ) + } + } + + // MARK: - NSURLSessionStreamDelegate + + var _streamTaskReadClosed: Any? + var _streamTaskWriteClosed: Any? + var _streamTaskBetterRouteDiscovered: Any? + var _streamTaskDidBecomeInputStream: Any? + + // MARK: - NSObject + + public override func responds(to selector: Selector) -> Bool { + switch selector { + case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)): + return sessionDidBecomeInvalidWithError != nil + case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)): + return sessionDidReceiveChallenge != nil + case #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)): + return sessionDidFinishEventsForBackgroundURLSession != nil + case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)): + return taskWillPerformHTTPRedirection != nil + case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)): + return dataTaskDidReceiveResponse != nil + default: + return type(of: self).instancesRespond(to: selector) + } + } + } +} diff --git a/Pods/Alamofire/Source/MultipartFormData.swift b/Pods/Alamofire/Source/MultipartFormData.swift new file mode 100644 index 0000000..bce6ab8 --- /dev/null +++ b/Pods/Alamofire/Source/MultipartFormData.swift @@ -0,0 +1,668 @@ +// MultipartFormData.swift +// +// Copyright (c) 2014–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 Foundation + +#if os(iOS) || os(watchOS) +import MobileCoreServices +#elseif os(OSX) +import CoreServices +#endif + +/** + Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode + multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead + to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the + data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for + larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset. + + For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well + and the w3 form documentation. + + - https://www.ietf.org/rfc/rfc2388.txt + - https://www.ietf.org/rfc/rfc2045.txt + - https://www.w3.org/TR/html401/interact/forms.html#h-17.13 +*/ +open class MultipartFormData { + + // MARK: - Helper Types + + struct EncodingCharacters { + static let CRLF = "\r\n" + } + + struct BoundaryGenerator { + enum BoundaryType { + case initial, encapsulated, final + } + + static func randomBoundary() -> String { + return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random()) + } + + static func boundaryData(boundaryType: BoundaryType, boundary: String) -> Data { + let boundaryText: String + + switch boundaryType { + case .initial: + boundaryText = "--\(boundary)\(EncodingCharacters.CRLF)" + case .encapsulated: + boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)" + case .final: + boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)" + } + + return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)! + } + } + + class BodyPart { + let headers: [String: String] + let bodyStream: InputStream + let bodyContentLength: UInt64 + var hasInitialBoundary = false + var hasFinalBoundary = false + + init(headers: [String: String], bodyStream: InputStream, bodyContentLength: UInt64) { + self.headers = headers + self.bodyStream = bodyStream + self.bodyContentLength = bodyContentLength + } + } + + // MARK: - Properties + + /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. + open var contentType: String { return "multipart/form-data; boundary=\(boundary)" } + + /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. + open var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } + + /// The boundary used to separate the body parts in the encoded form data. + open let boundary: String + + fileprivate var bodyParts: [BodyPart] + fileprivate var bodyPartError: NSError? + fileprivate let streamBufferSize: Int + + // MARK: - Lifecycle + + /** + Creates a multipart form data object. + + - returns: The multipart form data object. + */ + public init() { + self.boundary = BoundaryGenerator.randomBoundary() + self.bodyParts = [] + + /** + * The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more + * information, please refer to the following article: + * - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html + */ + + self.streamBufferSize = 1024 + } + + // MARK: - Body Parts + + /** + Creates a body part from the data and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - `Content-Disposition: form-data; name=#{name}` (HTTP Header) + - Encoded data + - Multipart form boundary + + - parameter data: The data to encode into the multipart form data. + - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + */ + open func appendBodyPart(data: Data, name: String) { + let headers = contentHeaders(name: name) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + appendBodyPart(stream: stream, length: length, headers: headers) + } + + /** + Creates a body part from the data and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - `Content-Disposition: form-data; name=#{name}` (HTTP Header) + - `Content-Type: #{generated mimeType}` (HTTP Header) + - Encoded data + - Multipart form boundary + + - parameter data: The data to encode into the multipart form data. + - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. + */ + open func appendBodyPart(data: Data, name: String, mimeType: String) { + let headers = contentHeaders(name: name, mimeType: mimeType) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + appendBodyPart(stream: stream, length: length, headers: headers) + } + + /** + Creates a body part from the data and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + - `Content-Type: #{mimeType}` (HTTP Header) + - Encoded file data + - Multipart form boundary + + - parameter data: The data to encode into the multipart form data. + - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. + - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. + - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. + */ + open func appendBodyPart(data: Data, name: String, fileName: String, mimeType: String) { + let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) + let stream = InputStream(data: data) + let length = UInt64(data.count) + + appendBodyPart(stream: stream, length: length, headers: headers) + } + + /** + Creates a body part from the file and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) + - `Content-Type: #{generated mimeType}` (HTTP Header) + - Encoded file data + - Multipart form boundary + + The filename in the `Content-Disposition` HTTP header is generated from the last path component of the + `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the + system associated MIME type. + + - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. + - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. + */ + open func appendBodyPart(fileURL: URL, name: String) { + if let + fileName = fileURL.lastPathComponent, + let pathExtension = fileURL.pathExtension + { + let mimeType = mimeTypeForPathExtension(pathExtension) + appendBodyPart(fileURL: fileURL, name: name, fileName: fileName, mimeType: mimeType) + } else { + let failureReason = "Failed to extract the fileName of the provided URL: \(fileURL)" + setBodyPartError(Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason)) + } + } + + /** + Creates a body part from the file and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) + - Content-Type: #{mimeType} (HTTP Header) + - Encoded file data + - Multipart form boundary + + - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. + - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. + - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. + - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. + */ + open func appendBodyPart(fileURL: URL, name: String, fileName: String, mimeType: String) { + let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) + + //============================================================ + // Check 1 - is file URL? + //============================================================ + + guard fileURL.isFileURL else { + let failureReason = "The file URL does not point to a file URL: \(fileURL)" + let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) + setBodyPartError(error) + return + } + + //============================================================ + // Check 2 - is file URL reachable? + //============================================================ + + var isReachable = true + + if #available(OSX 10.10, *) { + isReachable = (fileURL as NSURL).checkPromisedItemIsReachableAndReturnError(nil) + } + + guard isReachable else { + let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: "The file URL is not reachable: \(fileURL)") + setBodyPartError(error) + return + } + + //============================================================ + // Check 3 - is file URL a directory? + //============================================================ + + var isDirectory: ObjCBool = false + + guard let + path = fileURL.path, FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory else + { + let failureReason = "The file URL is a directory, not a file: \(fileURL)" + let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) + setBodyPartError(error) + return + } + + //============================================================ + // Check 4 - can the file size be extracted? + //============================================================ + + var bodyContentLength: UInt64? + + do { + if let + path = fileURL.path, + let fileSize = try FileManager.default.attributesOfItem(atPath: path)[FileAttributeKey.size] as? NSNumber + { + bodyContentLength = fileSize.uint64Value + } + } catch { + // No-op + } + + guard let length = bodyContentLength else { + let failureReason = "Could not fetch attributes from the file URL: \(fileURL)" + let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) + setBodyPartError(error) + return + } + + //============================================================ + // Check 5 - can a stream be created from file URL? + //============================================================ + + guard let stream = InputStream(url: fileURL) else { + let failureReason = "Failed to create an input stream from the file URL: \(fileURL)" + let error = Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) + setBodyPartError(error) + return + } + + appendBodyPart(stream: stream, length: length, headers: headers) + } + + /** + Creates a body part from the stream and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) + - `Content-Type: #{mimeType}` (HTTP Header) + - Encoded stream data + - Multipart form boundary + + - parameter stream: The input stream to encode in the multipart form data. + - parameter length: The content length of the stream. + - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. + - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. + - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. + */ + open func appendBodyPart( + stream: InputStream, + length: UInt64, + name: String, + fileName: String, + mimeType: String) + { + let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) + appendBodyPart(stream: stream, length: length, headers: headers) + } + + /** + Creates a body part with the headers, stream and length and appends it to the multipart form data object. + + The body part data will be encoded using the following format: + + - HTTP headers + - Encoded stream data + - Multipart form boundary + + - parameter stream: The input stream to encode in the multipart form data. + - parameter length: The content length of the stream. + - parameter headers: The HTTP headers for the body part. + */ + open func appendBodyPart(stream: InputStream, length: UInt64, headers: [String: String]) { + let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) + bodyParts.append(bodyPart) + } + + // MARK: - Data Encoding + + /** + Encodes all the appended body parts into a single `NSData` object. + + It is important to note that this method will load all the appended body parts into memory all at the same + time. This method should only be used when the encoded data will have a small memory footprint. For large data + cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. + + - throws: An `NSError` if encoding encounters an error. + + - returns: The encoded `NSData` if encoding is successful. + */ + open func encode() throws -> Data { + if let bodyPartError = bodyPartError { + throw bodyPartError + } + + let encoded = NSMutableData() + + bodyParts.first?.hasInitialBoundary = true + bodyParts.last?.hasFinalBoundary = true + + for bodyPart in bodyParts { + let encodedData = try encodeBodyPart(bodyPart) + encoded.append(encodedData) + } + + return encoded as Data + } + + /** + Writes the appended body parts into the given file URL. + + This process is facilitated by reading and writing with input and output streams, respectively. Thus, + this approach is very memory efficient and should be used for large body part data. + + - parameter fileURL: The file URL to write the multipart form data into. + + - throws: An `NSError` if encoding encounters an error. + */ + open func writeEncodedDataToDisk(_ fileURL: URL) throws { + if let bodyPartError = bodyPartError { + throw bodyPartError + } + + if let path = fileURL.path, FileManager.default.fileExists(atPath: path) { + let failureReason = "A file already exists at the given file URL: \(fileURL)" + throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) + } else if !fileURL.isFileURL { + let failureReason = "The URL does not point to a valid file: \(fileURL)" + throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) + } + + let outputStream: OutputStream + + if let possibleOutputStream = OutputStream(url: fileURL, append: false) { + outputStream = possibleOutputStream + } else { + let failureReason = "Failed to create an output stream with the given URL: \(fileURL)" + throw Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) + } + + outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) + outputStream.open() + + self.bodyParts.first?.hasInitialBoundary = true + self.bodyParts.last?.hasFinalBoundary = true + + for bodyPart in self.bodyParts { + try writeBodyPart(bodyPart, toOutputStream: outputStream) + } + + outputStream.close() + outputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) + } + + // MARK: - Private - Body Part Encoding + + fileprivate func encodeBodyPart(_ bodyPart: BodyPart) throws -> Data { + let encoded = NSMutableData() + + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + encoded.append(initialData) + + let headerData = encodeHeaderDataForBodyPart(bodyPart) + encoded.append(headerData) + + let bodyStreamData = try encodeBodyStreamDataForBodyPart(bodyPart) + encoded.append(bodyStreamData) + + if bodyPart.hasFinalBoundary { + encoded.append(finalBoundaryData()) + } + + return encoded as Data + } + + fileprivate func encodeHeaderDataForBodyPart(_ bodyPart: BodyPart) -> Data { + var headerText = "" + + for (key, value) in bodyPart.headers { + headerText += "\(key): \(value)\(EncodingCharacters.CRLF)" + } + headerText += EncodingCharacters.CRLF + + return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)! + } + + fileprivate func encodeBodyStreamDataForBodyPart(_ bodyPart: BodyPart) throws -> Data { + let inputStream = bodyPart.bodyStream + inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) + inputStream.open() + + var error: NSError? + let encoded = NSMutableData() + + while inputStream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) + + if inputStream.streamError != nil { + error = inputStream.streamError as! NSError + break + } + + if bytesRead > 0 { + encoded.append(buffer, length: bytesRead) + } else if bytesRead < 0 { + let failureReason = "Failed to read from input stream: \(inputStream)" + error = Error.errorWithCode(.inputStreamReadFailed, failureReason: failureReason) + break + } else { + break + } + } + + inputStream.close() + inputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) + + if let error = error { + throw error + } + + return encoded as Data + } + + // MARK: - Private - Writing Body Part to Output Stream + + fileprivate func writeBodyPart(_ bodyPart: BodyPart, toOutputStream outputStream: OutputStream) throws { + try writeInitialBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) + try writeHeaderDataForBodyPart(bodyPart, toOutputStream: outputStream) + try writeBodyStreamForBodyPart(bodyPart, toOutputStream: outputStream) + try writeFinalBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) + } + + fileprivate func writeInitialBoundaryDataForBodyPart( + _ bodyPart: BodyPart, + toOutputStream outputStream: OutputStream) + throws + { + let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() + return try writeData(initialData, toOutputStream: outputStream) + } + + fileprivate func writeHeaderDataForBodyPart(_ bodyPart: BodyPart, toOutputStream outputStream: OutputStream) throws { + let headerData = encodeHeaderDataForBodyPart(bodyPart) + return try writeData(headerData, toOutputStream: outputStream) + } + + fileprivate func writeBodyStreamForBodyPart(_ bodyPart: BodyPart, toOutputStream outputStream: OutputStream) throws { + let inputStream = bodyPart.bodyStream + inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode) + inputStream.open() + + while inputStream.hasBytesAvailable { + var buffer = [UInt8](repeating: 0, count: streamBufferSize) + let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) + + if let streamError = inputStream.streamError { + throw streamError + } + + if bytesRead > 0 { + if buffer.count != bytesRead { + buffer = Array(buffer[0.. 0 { + if outputStream.hasSpaceAvailable { + let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) + + if let streamError = outputStream.streamError { + throw streamError + } + + if bytesWritten < 0 { + let failureReason = "Failed to write to output stream: \(outputStream)" + throw Error.errorWithCode(.outputStreamWriteFailed, failureReason: failureReason) + } + + bytesToWrite -= bytesWritten + + if bytesToWrite > 0 { + buffer = Array(buffer[bytesWritten.. String { + if let + id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(), + let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() + { + return contentType as String + } + + return "application/octet-stream" + } + + // MARK: - Private - Content Headers + + fileprivate func contentHeaders(name: String) -> [String: String] { + return ["Content-Disposition": "form-data; name=\"\(name)\""] + } + + fileprivate func contentHeaders(name: String, mimeType: String) -> [String: String] { + return [ + "Content-Disposition": "form-data; name=\"\(name)\"", + "Content-Type": "\(mimeType)" + ] + } + + fileprivate func contentHeaders(name: String, fileName: String, mimeType: String) -> [String: String] { + return [ + "Content-Disposition": "form-data; name=\"\(name)\"; filename=\"\(fileName)\"", + "Content-Type": "\(mimeType)" + ] + } + + // MARK: - Private - Boundary Encoding + + fileprivate func initialBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(boundaryType: .initial, boundary: boundary) + } + + fileprivate func encapsulatedBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(boundaryType: .encapsulated, boundary: boundary) + } + + fileprivate func finalBoundaryData() -> Data { + return BoundaryGenerator.boundaryData(boundaryType: .final, boundary: boundary) + } + + // MARK: - Private - Errors + + fileprivate func setBodyPartError(_ error: NSError) { + if bodyPartError == nil { + bodyPartError = error + } + } +} diff --git a/Pods/Alamofire/Source/ParameterEncoding.swift b/Pods/Alamofire/Source/ParameterEncoding.swift new file mode 100644 index 0000000..662793b --- /dev/null +++ b/Pods/Alamofire/Source/ParameterEncoding.swift @@ -0,0 +1,217 @@ +// ParameterEncoding.swift +// +// Copyright (c) 2014–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 Foundation + +/** + HTTP method definitions. + + See https://tools.ietf.org/html/rfc7231#section-4.3 +*/ +public enum Method: String { + case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT +} + +// MARK: ParameterEncoding + +/** + Used to specify the way in which a set of parameters are applied to a URL request. + + - `URL`: Creates a query string to be set as or appended to any existing URL query for `GET`, `HEAD`, + and `DELETE` requests, or set as the body for requests with any other HTTP method. The + `Content-Type` HTTP header field of an encoded request with HTTP body is set to + `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification + for how to encode collection types, the convention of appending `[]` to the key for array + values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested + dictionary values (`foo[bar]=baz`). + + - `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same + implementation as the `.URL` case, but always applies the encoded result to the URL. + + - `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is + set as the body of the request. The `Content-Type` HTTP header field of an encoded request is + set to `application/json`. + + - `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, + according to the associated format and write options values, which is set as the body of the + request. The `Content-Type` HTTP header field of an encoded request is set to + `application/x-plist`. + + - `Custom`: Uses the associated closure value to construct a new request given an existing request and + parameters. +*/ +public enum ParameterEncoding { + case url + case urlEncodedInURL + case json + case propertyList(PropertyListSerialization.PropertyListFormat, PropertyListSerialization.WriteOptions) + case custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) + + /** + Creates a URL request by encoding parameters and applying them onto an existing request. + + - parameter URLRequest: The request to have parameters applied + - parameter parameters: The parameters to apply + + - returns: A tuple containing the constructed request and the error that occurred during parameter encoding, + if any. + */ + public func encode( + _ URLRequest: URLRequestConvertible, + parameters: [String: AnyObject]?) + -> (NSMutableURLRequest, NSError?) + { + var mutableURLRequest = URLRequest.URLRequest + + guard let parameters = parameters else { + return (mutableURLRequest, nil) + } + + var encodingError: NSError? = nil + + switch self { + case .url, .urlEncodedInURL: + func query(_ parameters: [String: AnyObject]) -> String { + var components: [(String, String)] = [] + for key in Array(parameters.keys).sorted(by: <) { + let value = parameters[key]! + components += queryComponents(key, value) + } + + return (components.map { "\($0)=\($1)" } as [String]).joined(separator: "&") + } + + func encodesParametersInURL(_ method: Method) -> Bool { + switch self { + case .urlEncodedInURL: + return true + default: + break + } + + switch method { + case .GET, .HEAD, .DELETE: + return true + default: + return false + } + } + + if let method = Method(rawValue: mutableURLRequest.httpMethod), encodesParametersInURL(method) { + if let URLComponents = URLComponents(url: mutableURLRequest.url!, resolvingAgainstBaseURL: false) { + let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) + URLComponents.percentEncodedQuery = percentEncodedQuery + mutableURLRequest.url = URLComponents.url + } + } else { + if mutableURLRequest.value(forHTTPHeaderField: "Content-Type") == nil { + mutableURLRequest.setValue( + "application/x-www-form-urlencoded; charset=utf-8", + forHTTPHeaderField: "Content-Type" + ) + } + + mutableURLRequest.httpBody = query(parameters).data( + using: String.Encoding.utf8, + allowLossyConversion: false + ) + } + case .json: + do { + let options = JSONSerialization.WritingOptions() + let data = try JSONSerialization.data(withJSONObject: parameters, options: options) + + mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + mutableURLRequest.httpBody = data + } catch { + encodingError = error as NSError + } + case .propertyList(let format, let options): + do { + let data = try PropertyListSerialization.data( + fromPropertyList: parameters, + format: format, + options: options + ) + mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") + mutableURLRequest.httpBody = data + } catch { + encodingError = error as NSError + } + case .custom(let closure): + (mutableURLRequest, encodingError) = closure(mutableURLRequest, parameters) + } + + return (mutableURLRequest, encodingError) + } + + /** + Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. + + - parameter key: The key of the query component. + - parameter value: The value of the query component. + + - returns: The percent-escaped, URL encoded query string components. + */ + public func queryComponents(_ key: String, _ value: AnyObject) -> [(String, String)] { + var components: [(String, String)] = [] + if let dictionary = value as? [String: AnyObject] { + for (nestedKey, value) in dictionary { + components += queryComponents("\(key)[\(nestedKey)]", value) + } + } else if let array = value as? [AnyObject] { + for value in array { + components += queryComponents("\(key)[]", value) + } + } else { + components.append((escape(key), escape("\(value)"))) + } + + return components + } + + /** + 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. + */ + public func escape(_ string: String) -> String { + let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 + let subDelimitersToEncode = "!$&'()*+,;=" + + let allowedCharacterSet = (CharacterSet.urlQueryAllowed as NSCharacterSet).mutableCopy() as! NSMutableCharacterSet + allowedCharacterSet.removeCharacters(in: generalDelimitersToEncode + subDelimitersToEncode) + + return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet as CharacterSet) ?? "" + } +} diff --git a/Pods/Alamofire/Source/Request.swift b/Pods/Alamofire/Source/Request.swift new file mode 100644 index 0000000..5ab9a8b --- /dev/null +++ b/Pods/Alamofire/Source/Request.swift @@ -0,0 +1,536 @@ +// Request.swift +// +// Copyright (c) 2014–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 Foundation + +/** + Responsible for sending a request and receiving the response and associated data from the server, as well as + managing its underlying `NSURLSessionTask`. +*/ +open class Request { + + // MARK: - Properties + + /// The delegate for the underlying task. + open let delegate: TaskDelegate + + /// The underlying task. + open var task: URLSessionTask { return delegate.task } + + /// The session belonging to the underlying task. + open let session: URLSession + + /// The request sent or to be sent to the server. + open var request: Foundation.URLRequest? { return task.originalRequest } + + /// The response received from the server, if any. + open var response: HTTPURLResponse? { return task.response as? HTTPURLResponse } + + /// The progress of the request lifecycle. + open var progress: Progress { return delegate.progress } + + // MARK: - Lifecycle + + init(session: URLSession, task: URLSessionTask) { + self.session = session + + switch task { + case is URLSessionUploadTask: + self.delegate = UploadTaskDelegate(task: task) + case is URLSessionDataTask: + self.delegate = DataTaskDelegate(task: task) + case is URLSessionDownloadTask: + self.delegate = DownloadTaskDelegate(task: task) + default: + self.delegate = TaskDelegate(task: task) + } + } + + // MARK: - Authentication + + /** + Associates an HTTP Basic credential with the request. + + - parameter user: The user. + - parameter password: The password. + - parameter persistence: The URL credential persistence. `.ForSession` by default. + + - returns: The request. + */ + open func authenticate( + user: String, + password: String, + persistence: URLCredential.Persistence = .forSession) + -> Self + { + let credential = URLCredential(user: user, password: password, persistence: persistence) + + return authenticate(usingCredential: credential) + } + + /** + Associates a specified credential with the request. + + - parameter credential: The credential. + + - returns: The request. + */ + open func authenticate(usingCredential credential: URLCredential) -> Self { + delegate.credential = credential + + return self + } + + // MARK: - Progress + + /** + Sets a closure to be called periodically during the lifecycle of the request as data is written to or read + from the server. + + - For uploads, the progress closure returns the bytes written, total bytes written, and total bytes expected + to write. + - For downloads and data tasks, the progress closure returns the bytes read, total bytes read, and total bytes + expected to read. + + - parameter closure: The code to be executed periodically during the lifecycle of the request. + + - returns: The request. + */ + open func progress(_ closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self { + if let uploadDelegate = delegate as? UploadTaskDelegate { + uploadDelegate.uploadProgress = closure + } else if let dataDelegate = delegate as? DataTaskDelegate { + dataDelegate.dataProgress = closure + } else if let downloadDelegate = delegate as? DownloadTaskDelegate { + downloadDelegate.downloadProgress = closure + } + + return self + } + + /** + Sets a closure to be called periodically during the lifecycle of the request as data is read from the server. + + This closure returns the bytes most recently received from the server, not including data from previous calls. + If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is + also important to note that the `response` closure will be called with nil `responseData`. + + - parameter closure: The code to be executed periodically during the lifecycle of the request. + + - returns: The request. + */ + open func stream(_ closure: ((Data) -> Void)? = nil) -> Self { + if let dataDelegate = delegate as? DataTaskDelegate { + dataDelegate.dataStream = closure + } + + return self + } + + // MARK: - State + + /** + Suspends the request. + */ + open func suspend() { + task.suspend() + } + + /** + Resumes the request. + */ + open func resume() { + task.resume() + } + + /** + Cancels the request. + */ + open func cancel() { + if let + downloadDelegate = delegate as? DownloadTaskDelegate, + let downloadTask = downloadDelegate.downloadTask + { + downloadTask.cancel { data in + downloadDelegate.resumeData = data + } + } else { + task.cancel() + } + } + + // MARK: - TaskDelegate + + /** + The task delegate is responsible for handling all delegate callbacks for the underlying task as well as + executing all operations attached to the serial operation queue upon task completion. + */ + open class TaskDelegate: NSObject { + + /// The serial operation queue used to execute all operations after the task completes. + open let queue: OperationQueue + + let task: URLSessionTask + let progress: Progress + + var data: Data? { return nil } + var error: Error? + + var credential: URLCredential? + + init(task: URLSessionTask) { + self.task = task + self.progress = Progress(totalUnitCount: 0) + self.queue = { + let operationQueue = OperationQueue() + operationQueue.maxConcurrentOperationCount = 1 + operationQueue.isSuspended = true + + if #available(OSX 10.10, *) { + operationQueue.qualityOfService = QualityOfService.utility + } + + return operationQueue + }() + } + + deinit { + queue.cancelAllOperations() + queue.isSuspended = false + } + + // MARK: - NSURLSessionTaskDelegate + + // MARK: Override Closures + + var taskWillPerformHTTPRedirection: ((Foundation.URLSession, URLSessionTask, HTTPURLResponse, Foundation.URLRequest) -> Foundation.URLRequest?)? + var taskDidReceiveChallenge: ((Foundation.URLSession, URLSessionTask, URLAuthenticationChallenge) -> (Foundation.URLSession.AuthChallengeDisposition, URLCredential?))? + var taskNeedNewBodyStream: ((Foundation.URLSession, URLSessionTask) -> InputStream?)? + var taskDidCompleteWithError: ((Foundation.URLSession, URLSessionTask, NSError?) -> Void)? + + // MARK: Delegate Methods + + func urlSession( + _ session: Foundation.URLSession, + task: URLSessionTask, + willPerformHTTPRedirection response: HTTPURLResponse, + newRequest request: Foundation.URLRequest, + completionHandler: ((Foundation.URLRequest?) -> Void)) + { + var redirectRequest: Foundation.URLRequest? = request + + if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { + redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) + } + + completionHandler(redirectRequest) + } + + func urlSession( + _ session: Foundation.URLSession, + task: URLSessionTask, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: ((Foundation.URLSession.AuthChallengeDisposition, URLCredential?) -> Void)) + { + var disposition: Foundation.URLSession.AuthChallengeDisposition = .performDefaultHandling + var credential: URLCredential? + + if let taskDidReceiveChallenge = taskDidReceiveChallenge { + (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) + } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { + let host = challenge.protectionSpace.host + + if let + serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host), + let serverTrust = challenge.protectionSpace.serverTrust + { + if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) { + disposition = .useCredential + credential = URLCredential(trust: serverTrust) + } else { + disposition = .cancelAuthenticationChallenge + } + } + } else { + if challenge.previousFailureCount > 0 { + disposition = .cancelAuthenticationChallenge + } else { + credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) + + if credential != nil { + disposition = .useCredential + } + } + } + + completionHandler(disposition, credential) + } + + func urlSession( + _ session: Foundation.URLSession, + task: URLSessionTask, + needNewBodyStream completionHandler: ((InputStream?) -> Void)) + { + var bodyStream: InputStream? + + if let taskNeedNewBodyStream = taskNeedNewBodyStream { + bodyStream = taskNeedNewBodyStream(session, task) + } + + completionHandler(bodyStream) + } + + func urlSession(_ session: Foundation.URLSession, task: URLSessionTask, didCompleteWithError error: NSError?) { + if let taskDidCompleteWithError = taskDidCompleteWithError { + taskDidCompleteWithError(session, task, error) + } else { + if let error = error { + self.error = error + + if let + downloadDelegate = self as? DownloadTaskDelegate, + let userInfo = error.userInfo as? [String: AnyObject], + let resumeData = userInfo[NSURLSessionDownloadTaskResumeData] as? Data + { + downloadDelegate.resumeData = resumeData + } + } + + queue.isSuspended = false + } + } + } + + // MARK: - DataTaskDelegate + + class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { + var dataTask: URLSessionDataTask? { return task as? URLSessionDataTask } + + fileprivate var totalBytesReceived: Int64 = 0 + fileprivate var mutableData: NSMutableData + override var data: Data? { + if dataStream != nil { + return nil + } else { + return mutableData as Data + } + } + + fileprivate var expectedContentLength: Int64? + fileprivate var dataProgress: ((_ bytesReceived: Int64, _ totalBytesReceived: Int64, _ totalBytesExpectedToReceive: Int64) -> Void)? + fileprivate var dataStream: ((_ data: Data) -> Void)? + + override init(task: URLSessionTask) { + mutableData = NSMutableData() + super.init(task: task) + } + + // MARK: - NSURLSessionDataDelegate + + // MARK: Override Closures + + var dataTaskDidReceiveResponse: ((Foundation.URLSession, URLSessionDataTask, URLResponse) -> Foundation.URLSession.ResponseDisposition)? + var dataTaskDidBecomeDownloadTask: ((Foundation.URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? + var dataTaskDidReceiveData: ((Foundation.URLSession, URLSessionDataTask, Data) -> Void)? + var dataTaskWillCacheResponse: ((Foundation.URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? + + // MARK: Delegate Methods + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didReceive response: URLResponse, + completionHandler: (@escaping (Foundation.URLSession.ResponseDisposition) -> Void)) + { + var disposition: Foundation.URLSession.ResponseDisposition = .allow + + expectedContentLength = response.expectedContentLength + + if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { + disposition = dataTaskDidReceiveResponse(session, dataTask, response) + } + + completionHandler(disposition) + } + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + didBecome downloadTask: URLSessionDownloadTask) + { + dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) + } + + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + if let dataTaskDidReceiveData = dataTaskDidReceiveData { + dataTaskDidReceiveData(session, dataTask, data) + } else { + if let dataStream = dataStream { + dataStream(data) + } else { + mutableData.append(data) + } + + totalBytesReceived += data.count + let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown + + progress.totalUnitCount = totalBytesExpected + progress.completedUnitCount = totalBytesReceived + + dataProgress?( + Int64(data.count), + totalBytesReceived, + totalBytesExpected + ) + } + } + + func urlSession( + _ session: URLSession, + dataTask: URLSessionDataTask, + willCacheResponse proposedResponse: CachedURLResponse, + completionHandler: (@escaping (CachedURLResponse?) -> Void)) + { + var cachedResponse: CachedURLResponse? = proposedResponse + + if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { + cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) + } + + completionHandler(cachedResponse) + } + } +} + +// MARK: - CustomStringConvertible + +extension Request: CustomStringConvertible { + + /** + The textual representation used when written to an output stream, which includes the HTTP method and URL, as + well as the response status code if a response has been received. + */ + public var description: String { + var components: [String] = [] + + if let HTTPMethod = request?.httpMethod { + components.append(HTTPMethod) + } + + if let URLString = request?.url?.absoluteString { + components.append(URLString) + } + + if let response = response { + components.append("(\(response.statusCode))") + } + + return components.joined(separator: " ") + } +} + +// MARK: - CustomDebugStringConvertible + +extension Request: CustomDebugStringConvertible { + func cURLRepresentation() -> String { + var components = ["$ curl -i"] + + guard let request = self.request else { + return "$ curl command could not be created" + } + + let URL = request.url + + if let HTTPMethod = request.httpMethod, HTTPMethod != "GET" { + components.append("-X \(HTTPMethod)") + } + + if let credentialStorage = self.session.configuration.urlCredentialStorage { + let protectionSpace = URLProtectionSpace( + host: URL!.host!, + port: (URL! as NSURL).port?.intValue ?? 0, + protocol: URL!.scheme, + realm: URL!.host!, + authenticationMethod: NSURLAuthenticationMethodHTTPBasic + ) + + if let credentials = credentialStorage.credentials(for: protectionSpace)?.values { + for credential in credentials { + components.append("-u \(credential.user!):\(credential.password!)") + } + } else { + if let credential = delegate.credential { + components.append("-u \(credential.user!):\(credential.password!)") + } + } + } + + if session.configuration.httpShouldSetCookies { + if let + cookieStorage = session.configuration.httpCookieStorage, + let cookies = cookieStorage.cookies(for: URL!), !cookies.isEmpty + { + let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value ?? String());" } + components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"") + } + } + + if let headerFields = request.allHTTPHeaderFields { + for (field, value) in headerFields { + switch field { + case "Cookie": + continue + default: + components.append("-H \"\(field): \(value)\"") + } + } + } + + if let additionalHeaders = session.configuration.httpAdditionalHeaders { + for (field, value) in additionalHeaders { + switch field { + case "Cookie": + continue + default: + components.append("-H \"\(field): \(value)\"") + } + } + } + + if let + HTTPBodyData = request.httpBody, + let HTTPBody = NSString(data: HTTPBodyData, encoding: String.Encoding.utf8.rawValue) + { + let escapedBody = HTTPBody.replacingOccurrences(of: "\"", with: "\\\"") + components.append("-d \"\(escapedBody)\"") + } + + components.append("\"\(URL!.absoluteString)\"") + + return components.joined(separator: " \\\n\t") + } + + /// The textual representation used when written to an output stream, in the form of a cURL command. + public var debugDescription: String { + return cURLRepresentation() + } +} diff --git a/Pods/Alamofire/Source/ResponseSerialization.swift b/Pods/Alamofire/Source/ResponseSerialization.swift new file mode 100644 index 0000000..b0b1ac1 --- /dev/null +++ b/Pods/Alamofire/Source/ResponseSerialization.swift @@ -0,0 +1,332 @@ +// ResponseSerialization.swift +// +// Copyright (c) 2014–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 Foundation + +// MARK: ResponseSerializer + +/** + The type in which all response serializers must conform to in order to serialize a response. +*/ +public protocol ResponseSerializer { + /// The type of serialized object to be created by this `ResponseSerializer`. + associatedtype SerializedObject + + /** + A closure used by response handlers that takes a request, response, and data and returns a result. + */ + var serializeResponse: (Foundation.URLRequest?, HTTPURLResponse?, Data?) -> Result { get } +} + +// MARK: - + +/** + A generic `ResponseSerializer` used to serialize a request, response, and data into a serialized object. +*/ +public struct GenericResponseSerializer: ResponseSerializer { + /// The type of serialized object to be created by this `ResponseSerializer`. + public typealias SerializedObject = T + + /** + A closure used by response handlers that takes a request, response, and data and returns a result. + */ + public var serializeResponse: (Foundation.URLRequest?, HTTPURLResponse?, Data?) -> Result + + /** + Initializes the `GenericResponseSerializer` instance with the given serialize response closure. + + - parameter serializeResponse: The closure used to serialize the response. + + - returns: The new generic response serializer instance. + */ + public init(serializeResponse: @escaping (Foundation.URLRequest?, HTTPURLResponse?, Data?) -> Result) { + self.serializeResponse = serializeResponse + } +} + +// MARK: - Default + +extension Request { + + /** + Adds a handler to be called once the request has finished. + + - parameter queue: The queue on which the completion handler is dispatched. + - parameter completionHandler: The code to be executed once the request has finished. + + - returns: The request. + */ + public func response( + queue: DispatchQueue? = nil, + completionHandler: @escaping (Foundation.URLRequest?, HTTPURLResponse?, Data?, Error?) -> Void) + -> Self + { + delegate.queue.addOperation { + (queue ?? DispatchQueue.main).async { + completionHandler(self.request, self.response, self.delegate.data, self.delegate.error) + } + } + + return self + } + + /** + Adds a handler to be called once the request has finished. + + - parameter queue: The queue on which the completion handler is dispatched. + - parameter responseSerializer: The response serializer responsible for serializing the request, response, + and data. + - parameter completionHandler: The code to be executed once the request has finished. + + - returns: The request. + */ + public func response( + queue: DispatchQueue? = nil, + responseSerializer: T, + completionHandler: @escaping (Foundation.URLRequest?, HTTPURLResponse?, Result) -> Void) + -> Self where T.SerializedObject == V + { + delegate.queue.addOperation { + let result: Result = { + if let error = self.delegate.error { + return .failure(self.delegate.data, error) + } else { + return responseSerializer.serializeResponse(self.request, self.response, self.delegate.data) + } + }() + + (queue ?? DispatchQueue.main).async { + completionHandler(self.request, self.response, result) + } + } + + return self + } +} + +// MARK: - Data + +extension Request { + + /** + Creates a response serializer that returns the associated data as-is. + + - returns: A data response serializer. + */ + public static func dataResponseSerializer() -> GenericResponseSerializer { + return GenericResponseSerializer { _, _, data in + guard let validData = data else { + let failureReason = "Data could not be serialized. Input data was nil." + let error = Error.errorWithCode(.dataSerializationFailed, failureReason: failureReason) + return .failure(data, error) + } + + return .success(validData) + } + } + + /** + Adds a handler to be called once the request has finished. + + - parameter completionHandler: The code to be executed once the request has finished. + + - returns: The request. + */ + public func responseData(_ completionHandler: (Foundation.URLRequest?, HTTPURLResponse?, Result) -> Void) -> Self { + return response(responseSerializer: Request.dataResponseSerializer(), completionHandler: completionHandler) + } +} + +// MARK: - String + +extension Request { + + /** + Creates a response serializer that returns a string initialized from the response data with the specified + string encoding. + + - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server + response, falling back to the default HTTP default character set, ISO-8859-1. + + - returns: A string response serializer. + */ + public static func stringResponseSerializer( + encoding: String.Encoding? = nil) + -> GenericResponseSerializer + { + var encoding = encoding + return GenericResponseSerializer { _, response, data in + guard let validData = data else { + let failureReason = "String could not be serialized because input data was nil." + let error = Error.errorWithCode(.stringSerializationFailed, failureReason: failureReason) + return .failure(data, error) + } + + if let encodingName = response?.textEncodingName, encoding == nil { + encoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding( + CFStringConvertIANACharSetNameToEncoding(encodingName as CFString) + )) + } + + let actualEncoding = encoding ?? String.Encoding.isoLatin1 + + if let string = NSString(data: validData, encoding: actualEncoding.rawValue) as? String { + return .success(string) + } else { + let failureReason = "String could not be serialized with encoding: \(actualEncoding)" + let error = Error.errorWithCode(.stringSerializationFailed, failureReason: failureReason) + return .failure(data, error) + } + } + } + + /** + Adds a handler to be called once the request has finished. + + - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the + server response, falling back to the default HTTP default character set, + ISO-8859-1. + - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3 + arguments: the URL request, the URL response and the result produced while + creating the string. + + - returns: The request. + */ + public func responseString( + encoding: String.Encoding? = nil, + completionHandler: (Foundation.URLRequest?, HTTPURLResponse?, Result) -> Void) + -> Self + { + return response( + responseSerializer: Request.stringResponseSerializer(encoding: encoding), + completionHandler: completionHandler + ) + } +} + +// MARK: - JSON + +extension Request { + + /** + Creates a response serializer that returns a JSON object constructed from the response data using + `NSJSONSerialization` with the specified reading options. + + - parameter options: The JSON serialization reading options. `.AllowFragments` by default. + + - returns: A JSON object response serializer. + */ + public static func JSONResponseSerializer( + options: JSONSerialization.ReadingOptions = .allowFragments) + -> GenericResponseSerializer + { + return GenericResponseSerializer { _, _, data in + guard let validData = data else { + let failureReason = "JSON could not be serialized because input data was nil." + let error = Error.errorWithCode(.jsonSerializationFailed, failureReason: failureReason) + return .failure(data, error) + } + + do { + let JSON = try JSONSerialization.jsonObject(with: validData, options: options) + return .success(JSON) + } catch { + return .failure(data, error as NSError) + } + } + } + + /** + Adds a handler to be called once the request has finished. + + - parameter options: The JSON serialization reading options. `.AllowFragments` by default. + - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3 + arguments: the URL request, the URL response and the result produced while + creating the JSON object. + + - returns: The request. + */ + public func responseJSON( + options: JSONSerialization.ReadingOptions = .allowFragments, + completionHandler: (Foundation.URLRequest?, HTTPURLResponse?, Result) -> Void) + -> Self + { + return response( + responseSerializer: Request.JSONResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} + +// MARK: - Property List + +extension Request { + + /** + Creates a response serializer that returns an object constructed from the response data using + `NSPropertyListSerialization` with the specified reading options. + + - parameter options: The property list reading options. `NSPropertyListReadOptions()` by default. + + - returns: A property list object response serializer. + */ + public static func propertyListResponseSerializer( + options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions()) + -> GenericResponseSerializer + { + return GenericResponseSerializer { _, _, data in + guard let validData = data else { + let failureReason = "Property list could not be serialized because input data was nil." + let error = Error.errorWithCode(.propertyListSerializationFailed, failureReason: failureReason) + return .failure(data, error) + } + + do { + let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil) + return .success(plist) + } catch { + return .failure(data, error as NSError) + } + } + } + + /** + Adds a handler to be called once the request has finished. + + - parameter options: The property list reading options. `0` by default. + - parameter completionHandler: A closure to be executed once the request has finished. The closure takes 3 + arguments: the URL request, the URL response and the result produced while + creating the property list. + + - returns: The request. + */ + public func responsePropertyList( + options: PropertyListSerialization.ReadOptions = PropertyListSerialization.ReadOptions(), + completionHandler: (Foundation.URLRequest?, HTTPURLResponse?, Result) -> Void) + -> Self + { + return response( + responseSerializer: Request.propertyListResponseSerializer(options: options), + completionHandler: completionHandler + ) + } +} diff --git a/Pods/Alamofire/Source/Result.swift b/Pods/Alamofire/Source/Result.swift new file mode 100644 index 0000000..15e73e6 --- /dev/null +++ b/Pods/Alamofire/Source/Result.swift @@ -0,0 +1,114 @@ +// Result.swift +// +// Copyright (c) 2014–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 Foundation + +/** + Used to represent whether a request was successful or encountered an error. + + - Success: The request and all post processing operations were successful resulting in the serialization of the + provided associated value. + - Failure: The request encountered an error resulting in a failure. The associated values are the original data + provided by the server as well as the error that caused the failure. +*/ +public enum Result { + case success(Value) + case failure(Data?, Error) + + /// Returns `true` if the result is a success, `false` otherwise. + public var isSuccess: Bool { + switch self { + case .success: + return true + case .failure: + return false + } + } + + /// Returns `true` if the result is a failure, `false` otherwise. + public var isFailure: Bool { + return !isSuccess + } + + /// Returns the associated value if the result is a success, `nil` otherwise. + public var value: Value? { + switch self { + case .success(let value): + return value + case .failure: + return nil + } + } + + /// Returns the associated data value if the result is a failure, `nil` otherwise. + public var data: Data? { + switch self { + case .success: + return nil + case .failure(let data, _): + return data + } + } + + /// Returns the associated error value if the result is a failure, `nil` otherwise. + public var error: Error? { + switch self { + case .success: + return nil + case .failure(_, let error): + return error + } + } +} + +// MARK: - CustomStringConvertible + +extension Result: CustomStringConvertible { + public var description: String { + switch self { + case .success: + return "SUCCESS" + case .failure: + return "FAILURE" + } + } +} + +// MARK: - CustomDebugStringConvertible + +extension Result: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .success(let value): + return "SUCCESS: \(value)" + case .failure(let data, let error): + if let + data = data, + let utf8Data = NSString(data: data, encoding: String.Encoding.utf8.rawValue) + { + return "FAILURE: \(error) \(utf8Data)" + } else { + return "FAILURE with Error: \(error)" + } + } + } +} diff --git a/Pods/Alamofire/Source/ServerTrustPolicy.swift b/Pods/Alamofire/Source/ServerTrustPolicy.swift new file mode 100644 index 0000000..3d8a1d5 --- /dev/null +++ b/Pods/Alamofire/Source/ServerTrustPolicy.swift @@ -0,0 +1,305 @@ +// ServerTrustPolicy.swift +// +// Copyright (c) 2014–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 Foundation + +/// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. +open class ServerTrustPolicyManager { + /// The dictionary of policies mapped to a particular host. + open let policies: [String: ServerTrustPolicy] + + /** + Initializes the `ServerTrustPolicyManager` instance with the given policies. + + Since different servers and web services can have different leaf certificates, intermediate and even root + certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This + allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key + pinning for host3 and disabling evaluation for host4. + + - parameter policies: A dictionary of all policies mapped to a particular host. + + - returns: The new `ServerTrustPolicyManager` instance. + */ + public init(policies: [String: ServerTrustPolicy]) { + self.policies = policies + } + + /** + Returns the `ServerTrustPolicy` for the given host if applicable. + + By default, this method will return the policy that perfectly matches the given host. Subclasses could override + this method and implement more complex mapping implementations such as wildcards. + + - parameter host: The host to use when searching for a matching policy. + + - returns: The server trust policy for the given host if found. + */ + open func serverTrustPolicyForHost(_ host: String) -> ServerTrustPolicy? { + return policies[host] + } +} + +// MARK: - + +extension URLSession { + fileprivate struct AssociatedKeys { + static var ManagerKey = "NSURLSession.ServerTrustPolicyManager" + } + + var serverTrustPolicyManager: ServerTrustPolicyManager? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.ManagerKey) as? ServerTrustPolicyManager + } + set (manager) { + objc_setAssociatedObject(self, &AssociatedKeys.ManagerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} + +// MARK: - ServerTrustPolicy + +/** + The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when + connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust + with a given set of criteria to determine whether the server trust is valid and the connection should be made. + + Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) 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 pinning enabled. + + - PerformDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to + validate the host provided by the challenge. Applications are encouraged to always + validate the host in production environments to guarantee the validity of the server's + certificate chain. + + - PinCertificates: Uses the pinned certificates to validate the server trust. The server trust is + considered valid if one of the pinned certificates match one of the server certificates. + By validating both the certificate chain and host, certificate pinning provides a very + secure form of server trust validation mitigating most, if not all, MITM attacks. + Applications are encouraged to always validate the host and require a valid certificate + chain in production environments. + + - PinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered + valid if one of the pinned public keys match one of the server certificate public keys. + By validating both the certificate chain and host, public key pinning provides a very + secure form of server trust validation mitigating most, if not all, MITM attacks. + Applications are encouraged to always validate the host and require a valid certificate + chain in production environments. + + - DisableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. + + - CustomEvaluation: Uses the associated closure to evaluate the validity of the server trust. +*/ +public enum ServerTrustPolicy { + case performDefaultEvaluation(validateHost: Bool) + case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) + case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) + case disableEvaluation + case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) + + // MARK: - Bundle Location + + /** + Returns all certificates within the given bundle with a `.cer` file extension. + + - parameter bundle: The bundle to search for all `.cer` files. + + - returns: All certificates within the given bundle. + */ + public static func certificatesInBundle(_ bundle: Bundle = Bundle.main) -> [SecCertificate] { + var certificates: [SecCertificate] = [] + + for path in bundle.paths(forResourcesOfType: ".cer", inDirectory: nil) { + if let + certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)), + let certificate = SecCertificateCreateWithData(nil, certificateData as CFData) + { + certificates.append(certificate) + } + } + + return certificates + } + + /** + Returns all public keys within the given bundle with a `.cer` file extension. + + - parameter bundle: The bundle to search for all `*.cer` files. + + - returns: All public keys within the given bundle. + */ + public static func publicKeysInBundle(_ bundle: Bundle = Bundle.main) -> [SecKey] { + var publicKeys: [SecKey] = [] + + for certificate in certificatesInBundle(bundle) { + if let publicKey = publicKeyForCertificate(certificate) { + publicKeys.append(publicKey) + } + } + + return publicKeys + } + + // MARK: - Evaluation + + /** + Evaluates whether the server trust is valid for the given host. + + - parameter serverTrust: The server trust to evaluate. + - parameter host: The host of the challenge protection space. + + - returns: Whether the server trust is valid. + */ + public func evaluateServerTrust(_ serverTrust: SecTrust, isValidForHost host: String) -> Bool { + var serverTrustIsValid = false + + switch self { + case let .performDefaultEvaluation(validateHost): + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, [policy]) + + serverTrustIsValid = trustIsValid(serverTrust) + case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): + if validateCertificateChain { + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, [policy]) + + SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) + SecTrustSetAnchorCertificatesOnly(serverTrust, true) + + serverTrustIsValid = trustIsValid(serverTrust) + } else { + let serverCertificatesDataArray = certificateDataForTrust(serverTrust) + + //====================================================================================================== + // The following `[] +` is a temporary workaround for a Swift 2.0 compiler error. This workaround should + // be removed once the following radar has been resolved: + // - http://openradar.appspot.com/radar?id=6082025006039040 + //====================================================================================================== + + let pinnedCertificatesDataArray = certificateDataForCertificates([] + pinnedCertificates) + + outerLoop: for serverCertificateData in serverCertificatesDataArray { + for pinnedCertificateData in pinnedCertificatesDataArray { + if serverCertificateData == pinnedCertificateData { + serverTrustIsValid = true + break outerLoop + } + } + } + } + case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): + var certificateChainEvaluationPassed = true + + if validateCertificateChain { + let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) + SecTrustSetPolicies(serverTrust, [policy]) + + certificateChainEvaluationPassed = trustIsValid(serverTrust) + } + + if certificateChainEvaluationPassed { + outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeysForTrust(serverTrust) as [AnyObject] { + for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { + if serverPublicKey.isEqual(pinnedPublicKey) { + serverTrustIsValid = true + break outerLoop + } + } + } + } + case .disableEvaluation: + serverTrustIsValid = true + case let .customEvaluation(closure): + serverTrustIsValid = closure(serverTrust, host) + } + + return serverTrustIsValid + } + + // MARK: - Private - Trust Validation + + fileprivate func trustIsValid(_ trust: SecTrust) -> Bool { + var isValid = false + + var result = SecTrustResultType(kSecTrustResultInvalid) + let status = SecTrustEvaluate(trust, &result) + + if status == errSecSuccess { + let unspecified = SecTrustResultType(kSecTrustResultUnspecified) + let proceed = SecTrustResultType(kSecTrustResultProceed) + + isValid = result == unspecified || result == proceed + } + + return isValid + } + + // MARK: - Private - Certificate Data + + fileprivate func certificateDataForTrust(_ trust: SecTrust) -> [Data] { + var certificates: [SecCertificate] = [] + + for index in 0.. [Data] { + return certificates.map { SecCertificateCopyData($0) as Data } + } + + // MARK: - Private - Public Key Extraction + + fileprivate static func publicKeysForTrust(_ trust: SecTrust) -> [SecKey] { + var publicKeys: [SecKey] = [] + + for index in 0.. SecKey? { + var publicKey: SecKey? + + let policy = SecPolicyCreateBasicX509() + var trust: SecTrust? + let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) + + if let trust = trust, trustCreationStatus == errSecSuccess { + publicKey = SecTrustCopyPublicKey(trust) + } + + return publicKey + } +} diff --git a/Pods/Alamofire/Source/Stream.swift b/Pods/Alamofire/Source/Stream.swift new file mode 100644 index 0000000..349f8e4 --- /dev/null +++ b/Pods/Alamofire/Source/Stream.swift @@ -0,0 +1,180 @@ +// Stream.swift +// +// Copyright (c) 2014–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 Foundation + +#if !os(watchOS) + +@available(iOS 9.0, OSX 10.11, *) +extension Manager { + fileprivate enum Streamable { + case stream(String, Int) + case netService(Foundation.NetService) + } + + fileprivate func stream(_ streamable: Streamable) -> Request { + var streamTask: URLSessionStreamTask! + + switch streamable { + case .stream(let hostName, let port): + queue.sync { + streamTask = self.session.streamTask(withHostName: hostName, port: port) + } + case .netService(let netService): + queue.sync { + streamTask = self.session.streamTask(with: netService) + } + } + + let request = Request(session: session, task: streamTask) + + delegate[request.delegate.task] = request.delegate + + if startRequestsImmediately { + request.resume() + } + + return request + } + + /** + Creates a request for bidirectional streaming with the given hostname and port. + + - parameter hostName: The hostname of the server to connect to. + - parameter port: The port of the server to connect to. + + :returns: The created stream request. + */ + public func stream(hostName: String, port: Int) -> Request { + return stream(.stream(hostName, port)) + } + + /** + Creates a request for bidirectional streaming with the given `NSNetService`. + + - parameter netService: The net service used to identify the endpoint. + + - returns: The created stream request. + */ + public func stream(netService: NetService) -> Request { + return stream(.netService(netService)) + } +} + +// MARK: - + +@available(iOS 9.0, OSX 10.11, *) +extension Manager.SessionDelegate: URLSessionStreamDelegate { + + // MARK: Override Closures + + /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:readClosedForStreamTask:`. + public var streamTaskReadClosed: ((Foundation.URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskReadClosed as? (Foundation.URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskReadClosed = newValue + } + } + + /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:writeClosedForStreamTask:`. + public var streamTaskWriteClosed: ((Foundation.URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskWriteClosed as? (Foundation.URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskWriteClosed = newValue + } + } + + /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:betterRouteDiscoveredForStreamTask:`. + public var streamTaskBetterRouteDiscovered: ((Foundation.URLSession, URLSessionStreamTask) -> Void)? { + get { + return _streamTaskBetterRouteDiscovered as? (Foundation.URLSession, URLSessionStreamTask) -> Void + } + set { + _streamTaskBetterRouteDiscovered = newValue + } + } + + /// Overrides default behavior for NSURLSessionStreamDelegate method `URLSession:streamTask:didBecomeInputStream:outputStream:`. + public var streamTaskDidBecomeInputStream: ((Foundation.URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? { + get { + return _streamTaskDidBecomeInputStream as? (Foundation.URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void + } + set { + _streamTaskDidBecomeInputStream = newValue + } + } + + // MARK: Delegate Methods + + /** + Tells the delegate that the read side of the connection has been closed. + + - parameter session: The session. + - parameter streamTask: The stream task. + */ + public func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) { + streamTaskReadClosed?(session, streamTask) + } + + /** + Tells the delegate that the write side of the connection has been closed. + + - parameter session: The session. + - parameter streamTask: The stream task. + */ + public func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) { + streamTaskWriteClosed?(session, streamTask) + } + + /** + Tells the delegate that the system has determined that a better route to the host is available. + + - parameter session: The session. + - parameter streamTask: The stream task. + */ + public func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) { + streamTaskBetterRouteDiscovered?(session, streamTask) + } + + /** + Tells the delegate that the stream task has been completed and provides the unopened stream objects. + + - parameter session: The session. + - parameter streamTask: The stream task. + - parameter inputStream: The new input stream. + - parameter outputStream: The new output stream. + */ + public func urlSession( + _ session: URLSession, + streamTask: URLSessionStreamTask, + didBecome inputStream: InputStream, + outputStream: OutputStream) + { + streamTaskDidBecomeInputStream?(session, streamTask, inputStream, outputStream) + } +} + +#endif diff --git a/Pods/Alamofire/Source/Upload.swift b/Pods/Alamofire/Source/Upload.swift new file mode 100644 index 0000000..cbeff41 --- /dev/null +++ b/Pods/Alamofire/Source/Upload.swift @@ -0,0 +1,372 @@ +// Upload.swift +// +// Copyright (c) 2014–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 Foundation + +extension Manager { + fileprivate enum Uploadable { + case data(Foundation.URLRequest, Foundation.Data) + case file(Foundation.URLRequest, URL) + case stream(Foundation.URLRequest, InputStream) + } + + fileprivate func upload(_ uploadable: Uploadable) -> Request { + var uploadTask: URLSessionUploadTask! + var HTTPBodyStream: InputStream? + + switch uploadable { + case .data(let request, let data): + queue.sync { + uploadTask = self.session.uploadTask(with: request, from: data) + } + case .file(let request, let fileURL): + queue.sync { + uploadTask = self.session.uploadTask(with: request, fromFile: fileURL) + } + case .stream(let request, let stream): + queue.sync { + uploadTask = self.session.uploadTask(withStreamedRequest: request) + } + + HTTPBodyStream = stream + } + + let request = Request(session: session, task: uploadTask) + + if HTTPBodyStream != nil { + request.delegate.taskNeedNewBodyStream = { _, _ in + return HTTPBodyStream + } + } + + delegate[request.delegate.task] = request.delegate + + if startRequestsImmediately { + request.resume() + } + + return request + } + + // MARK: File + + /** + Creates a request for uploading a file to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request + - parameter file: The file to upload + + - returns: The created upload request. + */ + public func upload(_ URLRequest: URLRequestConvertible, file: URL) -> Request { + return upload(.file(URLRequest.URLRequest as URLRequest, file)) + } + + /** + Creates a request for uploading a file to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter file: The file to upload + + - returns: The created upload request. + */ + public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + file: URL) + -> Request + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + return upload(mutableURLRequest, file: file) + } + + // MARK: Data + + /** + Creates a request for uploading data to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request. + - parameter data: The data to upload. + + - returns: The created upload request. + */ + public func upload(_ URLRequest: URLRequestConvertible, data: Data) -> Request { + return upload(.data(URLRequest.URLRequest as URLRequest, data)) + } + + /** + Creates a request for uploading data to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter data: The data to upload + + - returns: The created upload request. + */ + public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + data: Data) + -> Request + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + + return upload(mutableURLRequest, data: data) + } + + // MARK: Stream + + /** + Creates a request for uploading a stream to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request. + - parameter stream: The stream to upload. + + - returns: The created upload request. + */ + public func upload(_ URLRequest: URLRequestConvertible, stream: InputStream) -> Request { + return upload(.stream(URLRequest.URLRequest as URLRequest, stream)) + } + + /** + Creates a request for uploading a stream to the specified URL request. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter stream: The stream to upload. + + - returns: The created upload request. + */ + public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + stream: InputStream) + -> Request + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + + return upload(mutableURLRequest, stream: stream) + } + + // MARK: MultipartFormData + + /// Default memory threshold used when encoding `MultipartFormData`. + public static let MultipartFormDataEncodingMemoryThreshold: UInt64 = 10 * 1024 * 1024 + + /** + Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as + associated values. + + - Success: Represents a successful `MultipartFormData` encoding and contains the new `Request` along with + streaming information. + - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding + error. + */ + public enum MultipartFormDataEncodingResult { + case success(request: Request, streamingFromDisk: Bool, streamFileURL: URL?) + case failure(Error) + } + + /** + Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request. + + It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative + payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + used for larger payloads such as video content. + + The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + technique was used. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter method: The HTTP method. + - parameter URLString: The URL string. + - parameter headers: The HTTP headers. `nil` by default. + - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + `MultipartFormDataEncodingMemoryThreshold` by default. + - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. + */ + public func upload( + _ method: Method, + _ URLString: URLStringConvertible, + headers: [String: String]? = nil, + multipartFormData: (MultipartFormData) -> Void, + encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, + encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) + { + let mutableURLRequest = URLRequest(method, URLString, headers: headers) + + return upload( + mutableURLRequest, + multipartFormData: multipartFormData, + encodingMemoryThreshold: encodingMemoryThreshold, + encodingCompletion: encodingCompletion + ) + } + + /** + Encodes the `MultipartFormData` and creates a request to upload the result to the specified URL request. + + It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative + payload is small, encoding the data in-memory and directly uploading to a server is the by far the most + efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to + be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory + footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be + used for larger payloads such as video content. + + The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory + or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`, + encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk + during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding + technique was used. + + If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned. + + - parameter URLRequest: The URL request. + - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`. + - parameter encodingMemoryThreshold: The encoding memory threshold in bytes. + `MultipartFormDataEncodingMemoryThreshold` by default. + - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete. + */ + public func upload( + _ URLRequest: URLRequestConvertible, + multipartFormData: @escaping (MultipartFormData) -> Void, + encodingMemoryThreshold: UInt64 = Manager.MultipartFormDataEncodingMemoryThreshold, + encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) + { + DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.default).async { + let formData = MultipartFormData() + multipartFormData(formData) + + let URLRequestWithContentType = URLRequest.URLRequest + URLRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type") + + let isBackgroundSession = self.session.configuration.identifier != nil + + if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession { + do { + let data = try formData.encode() + let encodingResult = MultipartFormDataEncodingResult.success( + request: self.upload(URLRequestWithContentType, data: data as Data), + streamingFromDisk: false, + streamFileURL: nil + ) + + DispatchQueue.main.async { + encodingCompletion?(encodingResult) + } + } catch { + DispatchQueue.main.async { + encodingCompletion?(.failure(error as NSError)) + } + } + } else { + let fileManager = FileManager.default + let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()) + let directoryURL = tempDirectoryURL.appendingPathComponent("com.alamofire.manager/multipart.form.data") + let fileName = UUID().uuidString + let fileURL = directoryURL.URLByAppendingPathComponent(fileName) + + do { + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + try formData.writeEncodedDataToDisk(fileURL) + + DispatchQueue.main.async { + let encodingResult = MultipartFormDataEncodingResult.Success( + request: self.upload(URLRequestWithContentType, file: fileURL), + streamingFromDisk: true, + streamFileURL: fileURL + ) + encodingCompletion?(encodingResult) + } + } catch { + DispatchQueue.main.async { + encodingCompletion?(.failure(error as NSError)) + } + } + } + } + } +} + +// MARK: - + +extension Request { + + // MARK: - UploadTaskDelegate + + class UploadTaskDelegate: DataTaskDelegate { + var uploadTask: URLSessionUploadTask? { return task as? URLSessionUploadTask } + var uploadProgress: ((Int64, Int64, Int64) -> Void)! + + // MARK: - NSURLSessionTaskDelegate + + // MARK: Override Closures + + var taskDidSendBodyData: ((Foundation.URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? + + // MARK: Delegate Methods + + func URLSession( + _ session: Foundation.URLSession, + task: URLSessionTask, + didSendBodyData bytesSent: Int64, + totalBytesSent: Int64, + totalBytesExpectedToSend: Int64) + { + if let taskDidSendBodyData = taskDidSendBodyData { + taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) + } else { + progress.totalUnitCount = totalBytesExpectedToSend + progress.completedUnitCount = totalBytesSent + + uploadProgress?(bytesSent, totalBytesSent, totalBytesExpectedToSend) + } + } + } +} diff --git a/Pods/Alamofire/Source/Validation.swift b/Pods/Alamofire/Source/Validation.swift new file mode 100644 index 0000000..5ad1017 --- /dev/null +++ b/Pods/Alamofire/Source/Validation.swift @@ -0,0 +1,187 @@ +// Validation.swift +// +// Copyright (c) 2014–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 Foundation + +extension Request { + + /** + Used to represent whether validation was successful or encountered an error resulting in a failure. + + - Success: The validation was successful. + - Failure: The validation failed encountering the provided error. + */ + public enum ValidationResult { + case success + case failure(Error) + } + + /** + A closure used to validate a request that takes a URL request and URL response, and returns whether the + request was valid. + */ + public typealias Validation = (Foundation.URLRequest?, HTTPURLResponse) -> ValidationResult + + /** + Validates the request, using the specified closure. + + If validation fails, subsequent calls to response handlers will have an associated error. + + - parameter validation: A closure to validate the request. + + - returns: The request. + */ + public func validate(_ validation: @escaping Validation) -> Self { + delegate.queue.addOperation { + if let + response = self.response, self.delegate.error == nil, + case let .failure(error) = validation(self.request, response) + { + self.delegate.error = error + } + } + + return self + } + + // MARK: - Status Code + + /** + Validates that the response has a status code in the specified range. + + If validation fails, subsequent calls to response handlers will have an associated error. + + - parameter range: The range of acceptable status codes. + + - returns: The request. + */ + public func validate(statusCode acceptableStatusCode: S) -> Self where S.Iterator.Element == Int { + return validate { _, response in + if acceptableStatusCode.contains(response.statusCode) { + return .success + } else { + let failureReason = "Response status code was unacceptable: \(response.statusCode)" + return .failure(Error.errorWithCode(.statusCodeValidationFailed, failureReason: failureReason)) + } + } + } + + // MARK: - Content-Type + + fileprivate struct MIMEType { + let type: String + let subtype: String + + init?(_ string: String) { + let components: [String] = { + let stripped = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + let split = stripped.substring(to: stripped.range(of: ";")?.upperBound ?? stripped.endIndex) + return split.components(separatedBy: "/") + }() + + if let + type = components.first, + let subtype = components.last + { + self.type = type + self.subtype = subtype + } else { + return nil + } + } + + func matches(_ MIME: MIMEType) -> Bool { + switch (type, subtype) { + case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"): + return true + default: + return false + } + } + } + + /** + Validates that the response has a content type in the specified array. + + If validation fails, subsequent calls to response handlers will have an associated error. + + - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. + + - returns: The request. + */ + public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { + return validate { _, response in + if let + responseContentType = response.mimeType, + let responseMIMEType = MIMEType(responseContentType) + { + for contentType in acceptableContentTypes { + if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { + return .success + } + } + } else { + for contentType in acceptableContentTypes { + if let MIMEType = MIMEType(contentType), MIMEType.type == "*" && MIMEType.subtype == "*" { + return .success + } + } + } + + let failureReason: String + + if let responseContentType = response.mimeType { + failureReason = ( + "Response content type \"\(responseContentType)\" does not match any acceptable " + + "content types: \(acceptableContentTypes)" + ) + } else { + failureReason = "Response content type was missing and acceptable content type does not match \"*/*\"" + } + + return .failure(Error.errorWithCode(.contentTypeValidationFailed, failureReason: failureReason)) + } + } + + // MARK: - Automatic + + /** + Validates that the response has a status code in the default acceptable range of 200...299, and that the content + type matches any specified in the Accept HTTP header field. + + If validation fails, subsequent calls to response handlers will have an associated error. + + - returns: The request. + */ + public func validate() -> Self { + let acceptableStatusCodes: CountableRange = 200..<300 + let acceptableContentTypes: [String] = { + if let accept = request?.value(forHTTPHeaderField: "Accept") { + return accept.components(separatedBy: ",") + } + + return ["*/*"] + }() + + return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes) + } +} diff --git a/Pods/ChameleonFramework/LICENSE.md b/Pods/ChameleonFramework/LICENSE.md new file mode 100755 index 0000000..7fd117d --- /dev/null +++ b/Pods/ChameleonFramework/LICENSE.md @@ -0,0 +1,21 @@ +##The MIT License (MIT) + +> Copyright (c) 2014-2015 Vicc Alexander + +> 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/ChameleonFramework/Pod/Classes/Objective-C/Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon.h new file mode 100755 index 0000000..9573d11 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon.h @@ -0,0 +1,31 @@ +// +// Chameleon.h +// Chameleon +// +// Created by Vicc Alexander on 9/24/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import + +//! Project version number for Chameleon. +FOUNDATION_EXPORT double ChameleonVersionNumber; + +//! Project version string for Chameleon. +FOUNDATION_EXPORT const unsigned char ChameleonVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import "Chameleon_.h" + +#import "UIButton+Chameleon.h" +#import "UILabel+Chameleon.h" +#import "UIColor+ChameleonPrivate.h" +#import "UIImage+ChameleonPrivate.h" +#import "UIView+ChameleonPrivate.h" +#import "UIAppearance+Swift.h" + +#import "NSArray+Chameleon.h" +#import "UIColor+Chameleon.h" +#import "UINavigationController+Chameleon.h" +#import "UIViewController+Chameleon.h" diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.h new file mode 100755 index 0000000..9c9c345 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.h @@ -0,0 +1,21 @@ +// +// Constants.h +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import +#import + +/** + * A contrasting status bar, intended for use on any backgrounds. + * + * @since 2.0 + */ +extern const UIStatusBarStyle UIStatusBarStyleContrast; + +@interface ChameleonConstants : NSObject + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.m new file mode 100755 index 0000000..02cdc7f --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.m @@ -0,0 +1,15 @@ +// +// Constants.m +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "ChameleonConstants.h" + +const UIStatusBarStyle UIStatusBarStyleContrast = 100; + +@implementation ChameleonConstants + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonEnums.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonEnums.h new file mode 100755 index 0000000..0a9fed3 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonEnums.h @@ -0,0 +1,39 @@ +// +// ChameleonEnums.h +// Chameleon +// +// Created by Vicc Alexander on 6/8/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#ifndef Chameleon_ChameleonEnums_h +#define Chameleon_ChameleonEnums_h + +/** + * Specifies how text-based UI elements and other content such as switch knobs, should be colored. + * + * @since 2.0 + */ + +typedef NS_ENUM(NSUInteger, UIContentStyle) { + /** + * Automatically chooses and colors text-based elements with the shade that best contrasts its @c backgroundColor. + * + * @since 2.0 + */ + UIContentStyleContrast, + /** + * Colors text-based elements using a light shade. + * + * @since 2.0 + */ + UIContentStyleLight, + /** + * Colors text-based elements using a light shade. + * + * @since 2.0 + */ + UIContentStyleDark +}; + +#endif diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonMacros.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonMacros.h new file mode 100755 index 0000000..237c579 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/ChameleonMacros.h @@ -0,0 +1,125 @@ + +// ChameleonMacros.h + +/* + + The MIT License (MIT) + + Copyright (c) 2014-2015 Vicc Alexander. + + 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 "UIColor+Chameleon.h" + +#pragma mark - Light Shades Shorthand + +#define FlatBlack [UIColor flatBlackColor] +#define FlatBlue [UIColor flatBlueColor] +#define FlatBrown [UIColor flatBrownColor] +#define FlatCoffee [UIColor flatCoffeeColor] +#define FlatForestGreen [UIColor flatForestGreenColor] +#define FlatGray [UIColor flatGrayColor] +#define FlatGreen [UIColor flatGreenColor] +#define FlatLime [UIColor flatLimeColor] +#define FlatMagenta [UIColor flatMagentaColor] +#define FlatMaroon [UIColor flatMaroonColor] +#define FlatMint [UIColor flatMintColor] +#define FlatNavyBlue [UIColor flatNavyBlueColor] +#define FlatOrange [UIColor flatOrangeColor] +#define FlatPink [UIColor flatPinkColor] +#define FlatPlum [UIColor flatPlumColor] +#define FlatPowderBlue [UIColor flatPowderBlueColor] +#define FlatPurple [UIColor flatPurpleColor] +#define FlatRed [UIColor flatRedColor] +#define FlatSand [UIColor flatSandColor] +#define FlatSkyBlue [UIColor flatSkyBlueColor] +#define FlatTeal [UIColor flatTealColor] +#define FlatWatermelon [UIColor flatWatermelonColor] +#define FlatWhite [UIColor flatWhiteColor] +#define FlatYellow [UIColor flatYellowColor] + +// --------------------------------------------------- + +#pragma mark - Dark Shades Shorthand + +#define FlatBlackDark [UIColor flatBlackColorDark] +#define FlatBlueDark [UIColor flatBlueColorDark] +#define FlatBrownDark [UIColor flatBrownColorDark] +#define FlatCoffeeDark [UIColor flatCoffeeColorDark] +#define FlatForestGreenDark [UIColor flatForestGreenColorDark] +#define FlatGrayDark [UIColor flatGrayColorDark] +#define FlatGreenDark [UIColor flatGreenColorDark] +#define FlatLimeDark [UIColor flatLimeColorDark] +#define FlatMagentaDark [UIColor flatMagentaColorDark] +#define FlatMaroonDark [UIColor flatMaroonColorDark] +#define FlatMintDark [UIColor flatMintColorDark] +#define FlatNavyBlueDark [UIColor flatNavyBlueColorDark] +#define FlatOrangeDark [UIColor flatOrangeColorDark] +#define FlatPinkDark [UIColor flatPinkColorDark] +#define FlatPlumDark [UIColor flatPlumColorDark] +#define FlatPowderBlueDark [UIColor flatPowderBlueColorDark] +#define FlatPurpleDark [UIColor flatPurpleColorDark] +#define FlatRedDark [UIColor flatRedColorDark] +#define FlatSandDark [UIColor flatSandColorDark] +#define FlatSkyBlueDark [UIColor flatSkyBlueColorDark] +#define FlatTealDark [UIColor flatTealColorDark] +#define FlatWatermelonDark [UIColor flatWatermelonColorDark] +#define FlatWhiteDark [UIColor flatWhiteColorDark] +#define FlatYellowDark [UIColor flatYellowColorDark] + +// --------------------------------------------------- + +#pragma mark - Special Colors Shorthand + +#define RandomFlatColor [UIColor randomFlatColor] +#define ClearColor [UIColor clearColor] + +// --------------------------------------------------- + +#pragma mark - UIColor Methods Shorthand + +#define AverageColorFromImage(image) [UIColor colorWithAverageColorFromImage:image] +#define AverageColorFromImageWithAlpha(image, alpha) [UIColor colorWithAverageColorFromImage:image withAlpha:alpha] + +#define ComplementaryFlatColor(color) [UIColor colorWithComplementaryFlatColorOf:color] +#define ComplementaryFlatColorWithAlpha(color, alpha) [UIColor colorWithComplementaryFlatColorOf:color withAlpha:alpha] + +#define ContrastColor(backgroundColor, returnFlat) [UIColor colorWithContrastingBlackOrWhiteColorOn:backgroundColor isFlat:returnFlat] +#define ContrastColorWithAlpha(backgroundColor, returnFlat, alpha) [UIColor colorWithContrastingBlackOrWhiteColorOn:backgroundColor isFlat:returnFlat alpha:alpha] + +#define GradientColor(gradientStyle, frame, colors) [UIColor colorWithGradientStyle:gradientStyle withFrame:frame andColors:colors] + +#define HexColor(hexString) [UIColor colorWithHexString:hexString] +#define HexColorWithAlpha(hexString, alpha) [UIColor colorWithHexString:hexString withAlpha:alpha] + +#define RandomFlatColorInArray(colors) [UIColor colorWithRandomColorInArray:colors] +#define RandomFlatColorExcluding(colors) [UIColor colorWithRandomFlatColorExcludingColorsInArray:colors]; +#define RandomFlatColorWithShade(shade) [UIColor colorWithRandomFlatColorOfShadeStyle:shade] +#define RandomFlatColorWithShadeAndAlpha(shade, alpha) [UIColor colorWithRandomFlatColorOfShadeStyle:shade withAlpha:alpha] + +// --------------------------------------------------- + +#pragma mark - NSArray Shorthand + +#define ColorsWithScheme(colorSchemeType, color, isFlatScheme) [NSArray arrayOfColorsWithColorScheme:colorSchemeType usingColor:color withFlatScheme:isFlatScheme] +#define ColorsFromImage(image, isFlatScheme) [NSArray arrayOfColorsFromImage:image withFlatScheme:isFlatScheme] + + diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.h new file mode 100755 index 0000000..d492a47 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.h @@ -0,0 +1,66 @@ +// +// ChameleonInternal.h +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import +#import + +#import "ChameleonConstants.h" +#import "ChameleonEnums.h" +#import "ChameleonMacros.h" + +#import "NSArray+Chameleon.h" +#import "UIColor+Chameleon.h" +#import "UINavigationController+Chameleon.h" +#import "UIViewController+Chameleon.h" + +@interface Chameleon : NSObject + +#pragma mark - Global Theming + +/** + * Set a global theme using a primary color and the specified content style. + * + * @param primaryColor The primary color to theme all controllers with. + * @param contentStyle The contentStyle. + * + * @note By default the secondary color will be a darker shade of the specified primary color. + * + * @since 2.0 + */ ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle; + +/** + * Set a global theme using a primary color, secondary color, and the specified content style. + * + * @param primaryColor The primary color to theme all controllers with. + * @param secondaryColor The secondary color to theme all controllers with. + * @param contentStyle The contentStyle. + * + * @since 2.0 + */ ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + andContentStyle:(UIContentStyle)contentStyle; + +/** + * Set a global theme using a primary color, secondary color, font name, and the specified content style. + * + * @param primaryColor The primary color to theme all controllers with. + * @param secondaryColor The secondary color to theme all controllers with. + * @param fontName The default font for all text-based UI elements. + * @param contentStyle The contentStyle. + * + * @since 2.0 + */ ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + usingFontName:(NSString *)fontName + andContentStyle:(UIContentStyle)contentStyle; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.m new file mode 100755 index 0000000..4aab5a2 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.m @@ -0,0 +1,793 @@ +// +// ChameleonInternal.m +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "Chameleon_.h" +#import "UILabel+Chameleon.h" +#import "UIButton+Chameleon.h" +#import "UIAppearance+Swift.h" + +@implementation Chameleon + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor contentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeNavigationBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; +} + + ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + andContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor contentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor secondaryColor:secondaryColor withContentStyle:contentStyle]; + [[self class] customizeNavigationBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; +} + ++ (void)setGlobalThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + usingFontName:(NSString *)fontName + andContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[UILabel appearance] setSubstituteFontName:fontName]; + [[UIButton appearance] setSubstituteFontName:fontName]; + + [[self class] customizeNavigationBarWithBarColor:primaryColor textColor:ContrastColor(primaryColor, YES) fontName:fontName fontSize:20 buttonColor:ContrastColor(primaryColor, YES)]; + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor fontName:fontName fontSize:18 contentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withFontName:fontName withFontSize:14 withContentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor secondaryColor:secondaryColor withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + + /* + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor fontName:fontName fontSize:18 contentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor fontName:fontName fontSize:17 contentStyle:contentStyle]; + [[self class] customizeLabelWithPrimaryColor:primaryColor fontName:fontName fontSize:18 withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite tintColor:primaryColor fontName:fontName fontSize:11]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + + */ +} + +#pragma mark - UIBarButtonItem + ++ (void)customizeBarButtonItemWithPrimaryColor:(UIColor *)primaryColor + contentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIBarButtonItem appearance] setTintColor:primaryColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + + +} + ++ (void)customizeBarButtonItemWithPrimaryColor:(UIColor *)primaryColor + fontName:(NSString *)fontName + fontSize:(float)fontSize + contentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIBarButtonItem appearance] setTintColor:primaryColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + + + if ([UIFont fontWithName:fontName size:fontSize]) { + [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:@{ NSForegroundColorAttributeName:contentColor, + NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize]} forState:UIControlStateNormal]; + } +} + +#pragma mark - UIButton + ++ (void)customizeButtonWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIButton appearance] setTintColor:contentColor]; + [[UIButton appearance] setBackgroundColor:primaryColor]; + + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setTintColor:primaryColor]; + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearance] setTitleShadowColor:ClearColor forState:UIControlStateNormal]; +} + ++ (void)customizeButtonWithPrimaryColor:(UIColor *)primaryColor + secondaryColor:(UIColor *)secondaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + UIColor *secondaryContentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + secondaryContentColor = ContrastColor(secondaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + secondaryContentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + secondaryContentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + secondaryContentColor = ContrastColor(secondaryColor, NO); + break; + } + } + + [[UIButton appearance] setTintColor:secondaryContentColor]; + [[UIButton appearance] setBackgroundColor:secondaryColor]; + + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setTintColor:primaryColor]; + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearance] setTitleShadowColor:ClearColor forState:UIControlStateNormal]; +} + +#pragma mark - UIImagePickerController + ++ (void)customizeImagePickerControllerWithPrimaryColor:(UIColor *)primaryColor withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + //Workaround for Swift http://stackoverflow.com/a/28765193 + [[UIButton appearanceWhenContainedWithin:@[[UIView class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UIView class],[UIImagePickerController class]]] setTintColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UINavigationBar class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UINavigationBar class],[UIImagePickerController class]]] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedWithin:@[[UITableViewCell class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UIView class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UIView class],[UIImagePickerController class]]] setTintColor:contentColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class],[UIImagePickerController class]]] setTintColor:contentColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UITableViewCell class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; +} + + +#pragma mark - UILabel + ++ (void)customizeLabelWithPrimaryColor:(UIColor *)primaryColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UILabel appearanceWhenContainedIn:[UINavigationBar class], nil] setTextColor:contentColor]; + [[UILabel appearanceWhenContainedIn:[UIToolbar class], nil] setTextColor:contentColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + + if (font) { + [[UILabel appearance] setFont:[UIFont fontWithName:fontName size:fontSize]]; + [[UILabel appearanceWhenContainedIn:[UITextField class], nil] setFont:[UIFont fontWithName:fontName size:14]]; + [[UILabel appearanceWhenContainedIn:[UIButton class], nil] setFont:[UIFont fontWithName:fontName size:18]]; + } +} + +#pragma mark - UINavigationBar + ++ (void)customizeNavigationBarWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UINavigationBar appearance] setBarTintColor:primaryColor]; + [[UINavigationBar appearance] setTintColor:contentColor]; + [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:contentColor}]; + [[UINavigationBar appearance] setShadowImage:[UIImage new]]; + // [[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; +} + ++ (void)customizeNavigationBarWithBarColor:(UIColor *)barColor + textColor:(UIColor *)textColor + buttonColor:(UIColor *)buttonColor { + + [[UINavigationBar appearance] setBarTintColor:barColor]; + [[UINavigationBar appearance] setTintColor:buttonColor]; + [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:textColor}]; + [[UINavigationBar appearance] setShadowImage:[UIImage new]]; + // [[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; +} + ++ (void)customizeNavigationBarWithBarColor:(UIColor *)barColor + textColor:(UIColor *)textColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize + buttonColor:(UIColor *)buttonColor { + + [[UINavigationBar appearance] setBarTintColor:barColor]; + [[UINavigationBar appearance] setTintColor:buttonColor]; + [[UINavigationBar appearance] setShadowImage:[UIImage new]]; + // [[UINavigationBar appearance] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; + + if ([UIFont fontWithName:fontName size:fontSize]) { + [[UINavigationBar appearance] setTitleTextAttributes:@{ NSForegroundColorAttributeName:textColor, NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize] }]; + } +} + +#pragma mark - UIPageControl + ++ (void)customizePageControlWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIPageControl appearance] setCurrentPageIndicatorTintColor:primaryColor]; + [[UIPageControl appearance] setPageIndicatorTintColor:[primaryColor colorWithAlphaComponent:0.4]]; + [[UIPageControl appearanceWhenContainedIn:[UINavigationBar class], nil] setCurrentPageIndicatorTintColor:contentColor]; + [[UIPageControl appearanceWhenContainedIn:[UINavigationBar class], nil] setPageIndicatorTintColor:[contentColor colorWithAlphaComponent:0.4]]; + [[UIPageControl appearanceWhenContainedIn:[UIToolbar class], nil] setCurrentPageIndicatorTintColor:contentColor]; + [[UIPageControl appearanceWhenContainedIn:[UIToolbar class], nil] setPageIndicatorTintColor:[contentColor colorWithAlphaComponent:0.4]]; +} + +#pragma mark - UIProgressView + ++ (void)customizeProgressViewWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIProgressView appearance] setProgressTintColor:primaryColor]; + [[UIProgressView appearanceWhenContainedIn:[UINavigationBar class], nil] setProgressTintColor:contentColor]; + [[UIProgressView appearanceWhenContainedIn:[UIToolbar class], nil] setProgressTintColor:contentColor]; + [[UIProgressView appearance] setTrackTintColor:[UIColor lightGrayColor]]; + [[UIProgressView appearanceWhenContainedIn:[UINavigationBar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UIProgressView appearanceWhenContainedIn:[UIToolbar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + ++ (void)customizeProgressViewWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UIProgressView appearance] setProgressTintColor:secondaryColor]; + [[UIProgressView appearanceWhenContainedIn:[UINavigationBar class], nil] setProgressTintColor:secondaryColor]; + [[UIProgressView appearanceWhenContainedIn:[UIToolbar class], nil] setProgressTintColor:secondaryColor]; + [[UIProgressView appearance] setTrackTintColor:[UIColor lightGrayColor]]; + [[UIProgressView appearanceWhenContainedIn:[UINavigationBar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UIProgressView appearanceWhenContainedIn:[UIToolbar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + +#pragma mark - UISearchBar + ++ (void)customizeSearchBarWithPrimaryColor:(UIColor *)primaryColor withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISearchBar appearance] setBarTintColor:primaryColor]; + [[UISearchBar appearance] setBackgroundColor:primaryColor]; + [[UISearchBar appearance] setTintColor:contentColor]; + [[UISearchBar appearance] setBackgroundImage:[UIImage new] forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault]; +} + +#pragma mark - UISegmentedControl + ++ (void)customizeSegmentedControlWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISegmentedControl appearance] setTintColor:primaryColor]; + [[UISegmentedControl appearanceWhenContainedIn:[UINavigationBar class], nil] + setTintColor:contentColor]; + [[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] + setTintColor:contentColor]; +} + ++ (void)customizeSegmentedControlWithPrimaryColor:(UIColor *)primaryColor + withFontName:(NSString *)fontName + withFontSize:(CGFloat)fontSize + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISegmentedControl appearance] setTintColor:primaryColor]; + [[UISegmentedControl appearanceWhenContainedIn:[UINavigationBar class], nil] + setTintColor:contentColor]; + [[UISegmentedControl appearanceWhenContainedIn:[UIToolbar class], nil] + setTintColor:contentColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + if (font) { + [[UISegmentedControl appearance] setTitleTextAttributes:@{NSFontAttributeName:font} + forState:UIControlStateNormal]; + } +} + +#pragma mark - UISlider + ++ (void)customizeSliderWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISlider appearance] setMinimumTrackTintColor:primaryColor]; + [[UISlider appearanceWhenContainedIn:[UINavigationBar class], nil] setMinimumTrackTintColor:contentColor]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setMinimumTrackTintColor:contentColor]; + [[UISlider appearance] setMaximumTrackTintColor:[UIColor lightGrayColor]]; + [[UISlider appearanceWhenContainedIn:[UINavigationBar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + + [[UISlider appearance] setThumbTintColor:primaryColor]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setThumbTintColor:contentColor]; +} + ++ (void)customizeSliderWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UISlider appearance] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[UINavigationBar class], nil] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearance] setMaximumTrackTintColor:[UIColor lightGrayColor]]; + [[UISlider appearanceWhenContainedIn:[UINavigationBar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + + [[UISlider appearance] setThumbTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[UIToolbar class], nil] setThumbTintColor:ContrastColor(primaryColor, NO)]; +} + +#pragma mark - UIStepper + ++ (void)customizeStepperWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIStepper appearance] setTintColor:primaryColor]; + [[UIStepper appearanceWhenContainedIn:[UINavigationBar class], nil] + setTintColor:contentColor]; + [[UIStepper appearanceWhenContainedIn:[UIToolbar class], nil] + setTintColor:contentColor]; +} + +#pragma mark - UISwitch + ++ (void)customizeSwitchWithPrimaryColor:(UIColor *)primaryColor { + + [[UISwitch appearance] setOnTintColor:primaryColor]; + [[UISwitch appearanceWhenContainedIn:[UINavigationBar class], nil] setOnTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISwitch appearanceWhenContainedIn:[UIToolbar class], nil] setOnTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + ++ (void)customizeSwitchWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UISwitch appearance] setOnTintColor:secondaryColor]; + [[UISwitch appearanceWhenContainedIn:[UINavigationBar class], nil] setOnTintColor:secondaryColor]; + [[UISwitch appearanceWhenContainedIn:[UIToolbar class], nil] setOnTintColor:secondaryColor]; +} + +#pragma mark - UITabBar + ++ (void)customizeTabBarWithBarTintColor:(UIColor *)barTintColor + andTintColor:(UIColor *)tintColor { + + [[UITabBar appearance] setBarTintColor:barTintColor]; + [[UITabBar appearance] setTintColor:tintColor]; +} + ++ (void)customizeTabBarWithBarTintColor:(UIColor *)barTintColor + tintColor:(UIColor *)tintColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize { + + [[UITabBar appearance] setBarTintColor:barTintColor]; + [[UITabBar appearance] setTintColor:tintColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + if (font) { + [[UITabBarItem appearance] setTitleTextAttributes:@{NSFontAttributeName:font} + forState:UIControlStateNormal]; + } +} + +#pragma mark - UIToolbar + ++ (void)customizeToolbarWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIToolbar appearance] setTintColor:contentColor]; + [[UIToolbar appearance] setBarTintColor:primaryColor]; + [[UIToolbar appearance] setClipsToBounds:YES]; +} + +#pragma GCC diagnostic pop + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.h new file mode 100755 index 0000000..b74c664 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.h @@ -0,0 +1,112 @@ + +// NSArray+Chameleon.h + +/* + + The MIT License (MIT) + + Copyright (c) 2014-2015 Vicc Alexander. + + 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 + +#pragma mark - Enums + +/** + * Color schemes with which to select colors using a specified color. + * + * @since 1.0 + */ +typedef NS_ENUM(NSInteger, ColorScheme){ + /** + * Analogous color schemes use colors that are next to each other on the color wheel. They usually match well and create serene and comfortable designs. Analogous color schemes are often found in nature and are harmonious and pleasing to the eye. Make sure you have enough contrast when choosing an analogous color scheme. Choose one color to dominate, a second to support. The third color is used (along with black, white or gray) as an accent. + * + * @since 1.0 + */ + ColorSchemeAnalogous, + /** + * Colors that are opposite each other on the color wheel are considered to be complementary colors (example: red and green). The high contrast of complementary colors creates a vibrant look especially when used at full saturation. This color scheme must be managed well so it is not jarring. Complementary colors are tricky to use in large doses, but work well when you want something to stand out. Complementary colors are really bad for text. + * + * @since 1.0 + */ + ColorSchemeTriadic, + /** + * A triadic color scheme uses colors that are evenly spaced around the color wheel. Triadic color harmonies tend to be quite vibrant, even if you use pale or unsaturated versions of your hues. To use a triadic harmony successfully, the colors should be carefully balanced - let one color dominate and use the two others for accent. + * + * @since 1.0 + */ + ColorSchemeComplementary +}; + +@interface NSArray (Chameleon) + +#pragma mark - Generating Color Schemes + +/** + * Generates and creates an array of 5 color objects in the HSB colorspace from the specified color. + * + * @param colorScheme The color scheme with which to select colors using a specified color. + * @param color The specified color which the color scheme is built around. + * @param isFlatScheme Pass YES to return flat color objects. + * + * @return An array of 5 color objects in the HSB colorspace. + * + * @since 2.0 + */ ++ (NSArray *)arrayOfColorsWithColorScheme:(ColorScheme)colorScheme + usingColor:(UIColor *)color + withFlatScheme:(BOOL)isFlatScheme; + +/** + * Generates and creates an array of 5 color objects in the HSB colorspace that appear most often in a specified image. + * + * @param image The specified image which the color scheme is built around. + * @param isFlatScheme Pass YES to return flat color objects. + * + * @return An array of 5 color objects in the HSB colorspace. + * + * @since 2.0 + */ ++ (NSArray *)arrayOfColorsFromImage:(UIImage *)image + withFlatScheme:(BOOL)isFlatScheme; + +#pragma mark - Deprecated Methods + +/** + * Generates and creates an array of 5 color objects in the HSB colorspace from the specified color. + * + * @param colorScheme The color scheme with which to select colors using a specified color. + * + * @param color The specified color which the color scheme is built around. + * + * @param isFlatScheme Pass YES to return flat color objects. + * + * @return An array of 5 color objects in the HSB colorspace. + * + * @since 1.1.2 + */ ++ (NSArray *)arrayOfColorsWithColorScheme:(ColorScheme)colorScheme + with:(UIColor *)color + flatScheme:(BOOL)isFlatScheme __attribute((deprecated(" Use -arrayOfColorsWithColorScheme:usingColor:withFlatScheme: instead (First deprecated in Chameleon 2.0)."))); + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.m new file mode 100755 index 0000000..1e1dbe0 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.m @@ -0,0 +1,736 @@ + +// NSArray+Chameleon.m + +/* + + The MIT License (MIT) + + Copyright (c) 2014-2015 Vicc Alexander. + + 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 "NSArray+Chameleon.h" +#import "ChameleonMacros.h" +#import "UIColor+ChameleonPrivate.h" +#import "UIImage+ChameleonPrivate.h" + +@interface ChameleonCountedColor : NSObject + +@property (assign) NSUInteger count; +@property (strong) UIColor *color; + +- (id)initWithColor:(UIColor *)color count:(NSUInteger)count; + +@end + +@implementation NSArray (Chameleon) + +#pragma mark - Deprecated 2.0 + ++ (NSArray *)arrayOfColorsWithColorScheme:(ColorScheme)colorScheme with:(UIColor *)color flatScheme:(BOOL)isFlatScheme { + + //Extract HSB values from input color + CGFloat h, s, b, a; + [color getHue:&h saturation:&s brightness:&b alpha:&a]; + + //Multiply our values by the max value to convert + h *= 360; + s *= 100; + b *= 100; + + //Choose Between Schemes + switch (colorScheme) { + case ColorSchemeAnalogous: + return [self analogousColorSchemeFromHue:h Saturation:s Brightness:b flat:isFlatScheme]; + case ColorSchemeComplementary: + return [self complementaryColorSchemeFromHue:h Saturation:s Brightness:b flat:isFlatScheme]; + case ColorSchemeTriadic: + return [self triadicColorSchemeFromHue:h Saturation:s Brightness:b flat:isFlatScheme]; + default: + NSAssert(0, @"Oops! Unrecognized color scheme provided as random color."); + } +} + +#pragma mark - Chameleon - Public Color Scheme Methods + ++ (NSArray *)arrayOfColorsWithColorScheme:(ColorScheme)colorScheme usingColor:(UIColor *)color withFlatScheme:(BOOL)isFlatScheme { + + //Extract HSB values from input color + CGFloat h, s, b, a; + [color getHue:&h saturation:&s brightness:&b alpha:&a]; + + //Multiply our values by the max value to convert + h *= 360; + s *= 100; + b *= 100; + + //Choose Between Schemes + switch (colorScheme) { + case ColorSchemeAnalogous: + if (isFlatScheme) return [self analogousColorSchemeFromHue:h Saturation:s Brightness:b flat:YES]; + else return [self analogousColorSchemeFromHue:h Saturation:s Brightness:b flat:NO]; + case ColorSchemeComplementary: + if (isFlatScheme) return [self complementaryColorSchemeFromHue:h Saturation:s Brightness:b flat:YES]; + else return [self complementaryColorSchemeFromHue:h Saturation:s Brightness:b flat:NO]; + case ColorSchemeTriadic: + if (isFlatScheme) return [self triadicColorSchemeFromHue:h Saturation:s Brightness:b flat:YES]; + else return [self triadicColorSchemeFromHue:h Saturation:s Brightness:b flat:NO]; + default: + NSAssert(0, @"Oops! Unrecognized color scheme provided as random color."); + } +} + ++ (NSArray *)arrayOfColorsFromImage:(UIImage *)image withFlatScheme:(BOOL)isFlatScheme { + + //Quick return in case we don't have an image + if (!image) { + + //Make sure we return some colors to prevent exception + NSMutableArray *emptyColors = [NSMutableArray array]; + while (emptyColors.count < 5) { + [emptyColors addObject:[UIColor whiteColor]]; + } + + return emptyColors; + } + + //Scale image + UIImage *scaledImage = [UIImage imageWithImage:image scaledToSize:CGSizeMake(image.size.width/8, image.size.height/8)]; + NSMutableArray *finalColors = [NSMutableArray array]; + + //Find colors in image ********************************* + + //Get dimensions of image in pixels + size_t width = CGImageGetWidth(scaledImage.CGImage); + size_t height = CGImageGetHeight(scaledImage.CGImage); + + //Initialize a counted set with the correct capacity + NSCountedSet *imageColors = [[NSCountedSet alloc] initWithCapacity:(width * height)]; + + //Loop through each column + for (NSUInteger x = 0; x < width; x++) { + + //Loop through each row + for (NSUInteger y = 0; y < height; y++) { + + //Get color at a specific point + UIColor *color = [UIColor colorFromImage:scaledImage atPoint:CGPointMake(x, y)]; + + //Add color to our list of all pixel colors + [imageColors addObject:color]; + } + } + + //Setup up an enumerator object + NSEnumerator *enumerator = [imageColors objectEnumerator]; + UIColor *currentColor; + NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:imageColors.count]; + NSMutableArray *resultColors = [NSMutableArray array]; + + //Enumerate through each object once + while ((currentColor = [enumerator nextObject]) != nil) { + + //Set a minimum allowed saturation + currentColor = [currentColor colorWithMinimumSaturation:0.15f]; + + //Get color count + NSUInteger colorCount = [imageColors countForObject:currentColor]; + + //Add them to our sortedColors array + [sortedColors addObject:[[ChameleonCountedColor alloc] initWithColor:currentColor count:colorCount]]; + } + + //Sort Colors + [sortedColors sortUsingSelector:@selector(compare:)]; + + //Loop through our sorted colors + for (ChameleonCountedColor *countedColor in sortedColors) { + + //Define our current color + currentColor = countedColor.color; + + //Setup a flag to see if we should continue counting + BOOL continueFlag = NO; + + //Loop through our colors + for (UIColor *otherColor in resultColors) { + + //If our current color differs from our last, break the loop + if (![currentColor isDistinct:otherColor]) { + continueFlag = YES; + break; + } + } + + //Continue + if (continueFlag) { + continue; + } + + //If we can still add more colors, do so + if (resultColors.count < 5) { + + //Check if we should flatten our color + if (isFlatScheme) { + [resultColors addObject:[currentColor flatten]]; + } else { + [resultColors addObject:currentColor]; + } + + } else { + break; + } + + } + + //Get colors from image + [finalColors addObjectsFromArray:[NSArray arrayWithArray:resultColors]]; + + // ***************************************************** + + //Make sure we add white colors in case we're missing colors + while (finalColors.count < 5) { + [finalColors addObject:[UIColor whiteColor]]; + } + + //Return array of colors + return [NSArray arrayWithArray:finalColors]; +} + +- (NSArray *)findColorsOfImage:(UIImage *)image imageColors:(NSCountedSet * __autoreleasing *)colors { + + //Get dimensions of image in pixels + size_t width = CGImageGetWidth(image.CGImage); + size_t height = CGImageGetHeight(image.CGImage); + + //Initialize a counted set with the correct capacity + NSCountedSet *imageColors = [[NSCountedSet alloc] initWithCapacity:(width * height)]; + + //Loop through each column + for (NSUInteger x = 0; x < width; x++) { + + //Loop through each row + for (NSUInteger y = 0; y < height; y++) { + + //Get color at a specific point + UIColor *color = [UIColor colorFromImage:image atPoint:CGPointMake(x, y)]; + + //Add color to our list of all pixel colors + [imageColors addObject:color]; + } + } + + //Assign imageColors to colors + *colors = imageColors; + + //Setup up an enumerator object + NSEnumerator *enumerator = [imageColors objectEnumerator]; + UIColor *currentColor; + NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:imageColors.count]; + NSMutableArray *resultColors = [NSMutableArray array]; + + //Enumerate through each object once + while ((currentColor = [enumerator nextObject]) != nil) { + + //Set a minimum allowed saturation + currentColor = [currentColor colorWithMinimumSaturation:0.15f]; + + //Get color count + NSUInteger colorCount = [imageColors countForObject:currentColor]; + + //Add them to our sortedColors array + [sortedColors addObject:[[ChameleonCountedColor alloc] initWithColor:currentColor count:colorCount]]; + } + + //Sort Colors + [sortedColors sortUsingSelector:@selector(compare:)]; + + //Loop through our sorted colors + for (ChameleonCountedColor *countedColor in sortedColors) { + + //Define our current color + currentColor = countedColor.color; + + //Setup a flag to see if we should continue counting + BOOL continueFlag = NO; + + //Loop through our colors + for (UIColor *c in resultColors) { + + //If our current color differs from our last, break the loop + if (![currentColor isDistinct:c]) { + continueFlag = YES; + break; + } + } + + //Continue + if (continueFlag) { + continue; + } + + //If we can still add more colors, do so + if (resultColors.count < self.count) { + + [resultColors addObject:currentColor]; + + } else { + break; + } + + } + + //Return our colors + return [NSArray arrayWithArray:resultColors]; +} + + +#pragma mark - Chameleon - Internal Color Scheme Methods + +//Creates an array with 2 analagous colors on each side of the predefined color ++ (NSArray *)analogousColorSchemeFromHue:(CGFloat)h Saturation:(CGFloat)s Brightness:(CGFloat)b flat:(BOOL)isFlat { + + UIColor *firstColor = [UIColor colorWithHue:([[self class] add:-32 to:h])/360 + saturation:(s+5)/100 + brightness:(b+5)/100 + alpha:1.0]; + + UIColor *secondColor = [UIColor colorWithHue:[[self class] add:-16 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0]; + + UIColor *thirdColor = [UIColor colorWithHue:h/360 + saturation:s/100 + brightness:b/100 + alpha:1.0]; + + UIColor *fourthColor = [UIColor colorWithHue:[[self class] add:16 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0]; + + UIColor *fifthColor = [UIColor colorWithHue:[[self class] add:32 to:h]/360 + saturation:(s+5)/100 + brightness:(b+5)/100 + alpha:1.0]; + + if (isFlat) { + + //Flatten colors + firstColor = [firstColor flatten]; + secondColor = [secondColor flatten]; + thirdColor = [thirdColor flatten]; + fourthColor = [fourthColor flatten]; + fifthColor = [fifthColor flatten]; + + //Make sure returned colors are unique + + //Inner Colors + if ([secondColor isEqual:thirdColor]) { + + secondColor = [[UIColor colorWithHue:[[self class] add:-48 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] flatten]; + } + + if ([thirdColor isEqual:fourthColor]) { + + fourthColor = [[UIColor colorWithHue:[[self class] add:32 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] flatten]; + } + + //Outer Colors + + if ([firstColor isEqual:secondColor]) { + + firstColor = [[UIColor colorWithHue:[[self class] add:-64 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] flatten]; + } + + if ([firstColor isEqual:thirdColor]) { + + firstColor = [[UIColor colorWithHue:[[self class] add:-96 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] darkenByPercentage:0.25]; + } + + if ([fourthColor isEqual:fifthColor]) { + fifthColor = [[UIColor colorWithHue:[[self class] add:64 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] flatten]; + } + + if ([thirdColor isEqual:fifthColor]) { + fifthColor = [[UIColor colorWithHue:[[self class] add:96 to:h]/360 + saturation:(s+5)/100 + brightness:(b+9)/100 + alpha:1.0] flatten]; + } + + } + + return @[firstColor, secondColor, thirdColor, fourthColor, fifthColor]; +} + +// Creates an array of 5 colors, both 90 degrees and 180 degrees away from the predefined colors on both sides ++ (NSArray *)complementaryColorSchemeFromHue:(CGFloat)h Saturation:(CGFloat)s Brightness:(CGFloat)b flat:(BOOL)isFlat { + + UIColor *firstColor = [UIColor colorWithHue:h/360 + saturation:(s+5)/100 + brightness:(b-30)/100 + alpha:1.0]; + + UIColor *secondColor = [UIColor colorWithHue:h/360 + saturation:(s-10)/100 + brightness:(b+9)/100 + alpha:1.0]; + + UIColor *thirdColor = [UIColor colorWithHue:h/360 + saturation:s/100 + brightness:b/100 + alpha:1.0]; + + UIColor *fourthColor = [UIColor colorWithHue:[[self class] add:180 to:h]/360 + saturation:s/100 + brightness:b/100 + alpha:1.0]; + + UIColor *fifthColor = [UIColor colorWithHue:[[self class] add:180 to:h]/360 + saturation:(s+20)/100 + brightness:(b-30)/100 + alpha:1.0]; + + + if (isFlat) { + + //Flatten colors + firstColor = [firstColor flatten]; + secondColor = [secondColor flatten]; + thirdColor = [thirdColor flatten]; + fourthColor = [fourthColor flatten]; + fifthColor = [fifthColor flatten]; + + //Make sure returned colors are unique + + //Inner Colors + if ([secondColor isEqual:thirdColor]) { + secondColor = [[secondColor darkenByPercentage:0.25] flatten]; + } + + if ([thirdColor isEqual:fourthColor]) { + fourthColor = [[fourthColor darkenByPercentage:0.25] flatten]; + } + + if ([firstColor isEqual:thirdColor]) { + firstColor = [[firstColor darkenByPercentage:0.25] flatten]; + } + + if ([fifthColor isEqual:thirdColor]) { + fifthColor = [[fifthColor darkenByPercentage:0.25] flatten]; + } + + //Outer Colors + + if ([firstColor isEqual:secondColor]) { + firstColor = [[firstColor darkenByPercentage:0.25] flatten]; + } + + + if ([fourthColor isEqual:fifthColor]) { + fifthColor = [[fifthColor darkenByPercentage:0.25] flatten]; + } + + } + + return @[firstColor, secondColor, thirdColor, fourthColor, fifthColor]; + +} + +// Creates an array of 5 colors, both 120 degrees and 240 degrees away from the predefined colors on both sides ++ (NSArray *)triadicColorSchemeFromHue:(CGFloat)h Saturation:(CGFloat)s Brightness:(CGFloat)b flat:(BOOL)isFlat { + + UIColor *firstColor = [UIColor colorWithHue:[[self class] add:120 to:h]/360 + saturation:(7*s/6)/100 + brightness:(b-5)/100 + alpha:1.0]; + + UIColor *secondColor = [UIColor colorWithHue:[[self class] add:120 to:h]/360 + saturation:s/100 + brightness:(b+9)/100 + alpha:1.0]; + + UIColor *thirdColor = [UIColor colorWithHue:h/360 + saturation:s/100 + brightness:b/100 + alpha:1.0]; + + UIColor *fourthColor = [UIColor colorWithHue:[[self class] add:240 to:h]/360 + saturation:(7*s/6)/100 + brightness:(b-5)/100 + alpha:1.0]; + + UIColor *fifthColor = [UIColor colorWithHue:[[self class] add:240 to:h]/360 + saturation:s/100 + brightness:(b-30)/100 + alpha:1.0]; + + if (isFlat) { + + //Flatten colors + firstColor = [firstColor flatten]; + secondColor = [secondColor flatten]; + thirdColor = [thirdColor flatten]; + fourthColor = [fourthColor flatten]; + fifthColor = [fifthColor flatten]; + + //Make sure returned colors are unique + + //Inner Colors + if ([secondColor isEqual:thirdColor]) { + secondColor = [[secondColor darkenByPercentage:0.25] flatten]; + } + + if ([thirdColor isEqual:fourthColor]) { + fourthColor = [[fourthColor darkenByPercentage:0.25] flatten]; + } + + if ([firstColor isEqual:thirdColor]) { + firstColor = [[firstColor darkenByPercentage:0.25] flatten]; + } + + if ([fifthColor isEqual:thirdColor]) { + fifthColor = [[fifthColor darkenByPercentage:0.25] flatten]; + } + + //Outer Colors + + if ([firstColor isEqual:secondColor]) { + firstColor = [[firstColor darkenByPercentage:0.25] flatten]; + } + + + if ([fourthColor isEqual:fifthColor]) { + fifthColor = [[fifthColor darkenByPercentage:0.25] flatten]; + } + + } + + return @[firstColor, secondColor, thirdColor, fourthColor, fifthColor]; +} + +#pragma mark - Helper Methods for Color Schemes + ++ (float)add:(float)newValue to:(float)currentValue { + + currentValue += newValue; + + //Check if currentValue exceeds 360 degrees + if (currentValue > 360) { + float offset = currentValue - 360; + return offset; + } + + else if (currentValue < 0) { + return -1 * currentValue; + } + + else { + return currentValue; + } +} + ++ (UIColor *)colorWithFlatVersionOf:(UIColor *)color { + + //Create CGFloats to hold our color values + CGFloat L, A, B, alpha; + + //Get LAB values for our color + [color getLightness:&L valueForA:&A valueForB:&B alpha:&alpha]; + + //Find the nearest flat color + return [self nearestFlatColorForL:L A:A B:B alpha:1.0]; +} + +//Array of all our colors ++ (NSArray *)flatColors { + + return @[FlatBlack, FlatBlackDark, FlatBlue, FlatBlueDark, FlatBrown, FlatBrownDark, FlatCoffee, FlatCoffeeDark, FlatForestGreen, FlatForestGreenDark, FlatGray, FlatGrayDark, FlatGreen, FlatGreenDark, FlatLime, FlatLimeDark, FlatMagenta, FlatMagentaDark, FlatMaroon, FlatMaroonDark, FlatMint, FlatMintDark, FlatNavyBlue, FlatNavyBlueDark, FlatOrange, FlatOrangeDark, FlatPink, FlatPinkDark, FlatPlum, FlatPlumDark, FlatPowderBlue, FlatPowderBlueDark, FlatPurple, FlatPurpleDark, FlatRed, FlatRedDark, FlatSand, FlatSandDark, FlatSkyBlue, FlatSkyBlueDark, FlatTeal, FlatTealDark, FlatWatermelon, FlatWatermelonDark, FlatWhite, FlatWhiteDark, FlatYellow, FlatYellowDark]; +} + +//Calculate the total sum of differences - Euclidian distance +//Chameleon is now using the CIEDE2000 formula to calculate distances between 2 colors. +//More info: http://en.wikipedia.org/wiki/Color_difference ++ (float)totalSumOfDifferencesFroml1:(CGFloat)L1 l2:(CGFloat)L2 a1:(CGFloat)A1 + a2:(CGFloat)A2 b1:(CGFloat)B1 b2:(CGFloat)B2 { + + //Get C Values in LCH from LAB Values + CGFloat C1 = sqrt(pow(A1, 2) + pow(B1, 2)); + CGFloat C2 = sqrt(pow(A2, 2) + pow(B2, 2)); + + //CIE Weights + CGFloat KL = 1; + CGFloat KC = 1; + CGFloat KH = 1; + + //Variables specifically set for CIE:2000 + CGFloat DeltaPrimeL = L2 - L1; + CGFloat MeanL = ((L1 + L2) / 2); + CGFloat MeanC = ((C1 + C2) / 2); + CGFloat A1Prime = A1 + A1 / 2 * (1 - sqrt(pow(MeanC, 7.0) / (pow(MeanC, 7.0) + pow(25.0, 7.0)))); + CGFloat A2Prime = A2 + A2 / 2 * (1 - sqrt(pow(MeanC, 7.0) / (pow(MeanC, 7.0) + pow(25.0, 7.0)))); + CGFloat C1Prime = sqrt(pow(A1Prime, 2) + pow(B1, 2)); + CGFloat C2Prime = sqrt(pow(A2Prime, 2) + pow(B2, 2)); + CGFloat DeltaPrimeC = C1Prime - C2Prime; + CGFloat DeltaC = C1 - C2; + CGFloat MeanCPrime = (C1Prime + C2Prime) / 2; + CGFloat H1Prime = fmodf(atan2(B1, A1Prime), (360.0 * M_PI/180)); + CGFloat H2Prime = fmodf(atan2(B2, A2Prime), (360.0 * M_PI/180)); + + //Run everything through our △H' Function + CGFloat hDeltaPrime = 0; + if (fabs(H1Prime - H2Prime) <= (180.0 * M_PI/180)) { + + hDeltaPrime = H2Prime - H1Prime; + + } else if (H2Prime <= H1Prime) { + + hDeltaPrime = (H2Prime - H1Prime) + ((360.0 * M_PI/180)); + + } else { + + hDeltaPrime = (H2Prime - H1Prime) - ((360.0 * M_PI/180)); + } + + CGFloat deltaHPrime = 2 * (sqrt(C1Prime*C2Prime)) * sin(hDeltaPrime/2); + + //Get Mean H' Value + CGFloat MeanHPrime = 0; + if (fabs(H1Prime-H2Prime) > (180.0 * M_PI/180)) { + + MeanHPrime = (H1Prime + H2Prime + (360.0 * M_PI/180)) / 2; + + } else { + + MeanHPrime = (H1Prime + H2Prime) / 2; + } + + CGFloat T = 1 - 0.17 * cos(MeanHPrime - (30.0 * M_PI/180)) + 0.24 * cos(2 * MeanHPrime)+0.32 * cos(3 * MeanHPrime + (6.0 * M_PI/180)) - 0.20 * cos(4 * MeanHPrime - (63.0 * M_PI/180)); + + CGFloat SL = 1 + (0.015 * pow((MeanL - 50), 2))/sqrt(20 + pow((MeanL - 50), 2)); + CGFloat SC = 1 + 0.045 * MeanCPrime; + CGFloat SH = 1 + 0.015 * MeanCPrime * T; + + CGFloat RT = -2 * sqrt(pow(MeanCPrime, 7) / (pow(MeanCPrime, 7) + pow(25.0, 7))) * sin((60.0 * M_PI/180)* exp(-1 * pow((MeanCPrime - (275.0 * M_PI/180)) / (25.0 * M_PI/180), 2))); + + + //Get total difference + CGFloat TotalDifference = sqrt(pow((DeltaPrimeL / (KL * SL)), 2) + pow((DeltaPrimeC / (KC * SC)), 2) + pow((deltaHPrime / (KH * SH)), 2) + RT * (DeltaC / (KC * SC)) * (deltaHPrime / (KH * SH))); + + return TotalDifference; +} + ++ (UIColor *)nearestFlatColorForL:(CGFloat)l1 A:(CGFloat)a1 B:(CGFloat)b1 alpha:(CGFloat)alpha{ + + //Keep track of our index + int index = 0; + + //Start with a random big number to make sure the first comparison gets saved. + float smallestDistance = 1000000; + float previousDistance = 1000000; + float currentDistance; + + //Our values + CGFloat l2, a2, b2; + + //We're interested in the color with values returning the smallest sum of total differences so we need to cross reference our input color's values with every flat color's values + for (int i=0; i<[[self flatColors] count]; i++ ) { + + //Check that index is not zero + if (i!=0 ) { + //Extract LAB values from colors in array and store it as the previous index + [[self flatColors][i - 1] getLightness:&l2 valueForA:&a2 valueForB:&b2 alpha:nil]; + + previousDistance = [self totalSumOfDifferencesFroml1:l1 l2:l2 + a1:a1 a2:a2 + b1:b1 b2:b2]; + } + + //Extract LAB values from colors in array and store it as the current index + [[self flatColors][i] getLightness:&l2 valueForA:&a2 valueForB:&b2 alpha:nil]; + + currentDistance = [self totalSumOfDifferencesFroml1:l1 l2:l2 + a1:a1 a2:a2 + b1:b1 b2:b2]; + + //We're only interested in the smallest difference + if (currentDistance < previousDistance) { + if (currentDistance < smallestDistance) { + smallestDistance = currentDistance; + index = i; + } + } + } + + + //Collect the RGB Values of the color where the smallest difference was found + CGFloat red, green, blue; + [[self flatColors][index] getRed:&red green:&green blue:&blue alpha:nil]; + + //Return the closest flat color + return rgba(red*255, green*255, blue*255, alpha); +} + +@end + +@implementation ChameleonCountedColor + +- (id)initWithColor:(UIColor *)color count:(NSUInteger)count { + + if ((self = [super init])) { + self.color = color; + self.count = count; + } + + return self; +} + +- (NSComparisonResult)compare:(ChameleonCountedColor *)object { + + if ([object isKindOfClass:[ChameleonCountedColor class]]) { + if (self.count < object.count) + return NSOrderedDescending; + else if (self.count == object.count) + return NSOrderedSame; + } + + return NSOrderedAscending; +} + + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.h new file mode 100755 index 0000000..2d43ceb --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.h @@ -0,0 +1,17 @@ +// +// UIAppearance+Swift.h +// Chameleon +// +// Created by Vicc Alexander on 11/26/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import + +@interface UIView (UIViewAppearance_Swift) + +// @param containers An array of Class < UIAppearanceContainer > +// http://stackoverflow.com/a/28765193 ++ (instancetype)appearanceWhenContainedWithin:(NSArray *)containers; + +@end \ No newline at end of file diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.m new file mode 100755 index 0000000..d0854a1 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.m @@ -0,0 +1,32 @@ +// +// UIAppearance+Swift.m +// Chameleon +// +// Created by Vicc Alexander on 11/26/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import "UIAppearance+Swift.h" + +@implementation UIView (UIViewAppearance_Swift) + ++ (instancetype)appearanceWhenContainedWithin: (NSArray *)containers { + + NSUInteger count = containers.count; + NSAssert(count <= 10, @"The count of containers greater than 10 is not supported."); + + return [self appearanceWhenContainedIn: + count > 0 ? containers[0] : nil, + count > 1 ? containers[1] : nil, + count > 2 ? containers[2] : nil, + count > 3 ? containers[3] : nil, + count > 4 ? containers[4] : nil, + count > 5 ? containers[5] : nil, + count > 6 ? containers[6] : nil, + count > 7 ? containers[7] : nil, + count > 8 ? containers[8] : nil, + count > 9 ? containers[9] : nil, + nil]; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.h new file mode 100755 index 0000000..c0fecea --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.h @@ -0,0 +1,15 @@ +// +// UIButton+Chameleon.h +// Chameleon +// +// Created by Vicc Alexander on 9/20/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import + +@interface UIButton (Chameleon) + +- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.m new file mode 100755 index 0000000..a1e896f --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.m @@ -0,0 +1,23 @@ +// +// UIButton+Chameleon.m +// Chameleon +// +// Created by Vicc Alexander on 9/20/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import "UIButton+Chameleon.h" + +@implementation UIButton (Chameleon) + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR { + + self.font = [UIFont fontWithName:name size:self.font.pointSize]; +} + +#pragma GCC diagnostic pop + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.h new file mode 100755 index 0000000..22d9b7c --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.h @@ -0,0 +1,685 @@ + +// UIColor+Chameleon.h + +/* + + The MIT License (MIT) + + Copyright (c) 2014-2015 Vicc Alexander. + + 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 + +#pragma mark - Enums + +/** + * Defines the gradient style and direction of the gradient color. + * + * @since 1.0 + */ +typedef NS_ENUM (NSUInteger, UIGradientStyle) { + /** + * Returns a gradual blend between colors originating at the leftmost point of an object's frame, and ending at the rightmost point of the object's frame. + * + * @since 1.0 + */ + UIGradientStyleLeftToRight, + /** + * Returns a gradual blend between colors originating at the center of an object's frame, and ending at all edges of the object's frame. NOTE: Supports a Maximum of 2 Colors. + * + * @since 1.0 + */ + UIGradientStyleRadial, + /** + * Returns a gradual blend between colors originating at the topmost point of an object's frame, and ending at the bottommost point of the object's frame. + * + * @since 1.0 + */ + UIGradientStyleTopToBottom +}; + +/** + * Defines the shade of a any flat color. + * + * @since 1.0 + */ +typedef NS_ENUM (NSInteger, UIShadeStyle) { + /** + * Returns the light shade version of a flat color. + * + * @since 1.0 + */ + UIShadeStyleLight, + /** + * Returns the dark shade version of a flat color. + * + * @since 1.0 + */ + UIShadeStyleDark +}; + + +@interface UIColor (Chameleon) + +#pragma mark - Instance Variables + +/** + * Stores an object's UIColor image if the UIColor was created using colorWithPatternImage. + * + * @since 1.0 + */ + +@property (nonatomic, strong) UIImage *gradientImage; + +#pragma mark - Quick Shorthand Macros + +// Quick & Easy Shorthand for RGB x HSB Colors +// The reason we don't import our Macro file is to prevent naming conflicts. +#define rgba(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a] +#define rgb(r,g,b) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:1.0] +#define hsba(h,s,b,a) [UIColor colorWithHue:h/360.0f saturation:s/100.0f brightness:b/100.0f alpha:a] +#define hsb(h,s,b) [UIColor colorWithHue:h/360.0f saturation:s/100.0f brightness:b/100.0f alpha:1.0] + +#pragma mark - Light Shades + +/** + * Returns a flat color object whose HSB values are 0.0, 0.0, 0.17 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBlackColor; + +/** + * Returns a flat color object whose HSB values are 0.62, 0.50, 0.63 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBlueColor; + +/** + * Returns a flat color object whose HSB values are 0.07, 0.45, 0.37 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBrownColor; + +/** + * Returns a flat color object whose HSB values are 0.07, 0.31, 0.64 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatCoffeeColor; + +/** + * Returns a flat color object whose HSB values are 0.38, 0.45, 0.37 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatForestGreenColor; + +/** + * Returns a flat color object whose HSB values are 0.51, 0.10, 0.65 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatGrayColor; + +/** + * Returns a flat color object whose HSB values are 0.40, 0.77, 0.80 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatGreenColor; + +/** + * Returns a flat color object whose HSB values are 0.21, 0.70, 0.78 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatLimeColor; + +/** + * Returns a flat color object whose HSB values are 0.79, 0.51, 0.71 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMagentaColor; + +/** + * Returns a flat color object whose HSB values are 0.01, 0.65, 0.47 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMaroonColor; + +/** + * Returns a flat color object whose HSB values are 0.47, 0.86, 0.74 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMintColor; + +/** + * Returns a flat color object whose HSB values are 0.58, 0.45, 0.37 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatNavyBlueColor; + +/** + * Returns a flat color object whose HSB values are 0.08, 0.85, 0.90 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatOrangeColor; + +/** + * Returns a flat color object whose HSB values are 0.90, 0.49, 0.96 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPinkColor; + +/** + * Returns a flat color object whose HSB values are 0.83, 0.45, 0.37 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPlumColor; + +/** + * Returns a flat color object whose HSB values are 0.62, 0.24, 0.95 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPowderBlueColor; + +/** + * Returns a flat color object whose HSB values are 0.70, 0.52, 0.77 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPurpleColor; + +/** + * Returns a flat color object whose HSB values are 0.02, 0.74, 0.91 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatRedColor; + +/** + * Returns a flat color object whose HSB values are 0.12, 0.25, 0.94 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatSandColor; + +/** + * Returns a flat color object whose HSB values are 0.57, 0.76, 0.86 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatSkyBlueColor; + +/** + * Returns a flat color object whose HSB values are 0.54, 0.55, 0.51 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatTealColor; + +/** + * Returns a flat color object whose HSB values are 0.99, 0.53, 0.94 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatWatermelonColor; + +/** + * Returns a flat color object whose HSB values are 0.53, 0.02, 0.95 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatWhiteColor; + +/** + * Returns a flat color object whose HSB values are 0.13, 0.99, 1.00 and whose alpha value is 1.0. + * + * @return A flat UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatYellowColor; + +#pragma mark - Dark Shades + +/** + * Returns a flat color object whose HSB values are 0.00, 0.00, 0.15 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBlackColorDark; + +/** + * Returns a flat color object whose HSB values are 0.62, 0.56, 0.51 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBlueColorDark; + +/** + * Returns a flat color object whose HSB values are 0.07, 0.45, 0.31 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatBrownColorDark; + +/** + * Returns a flat color object whose HSB values are 0.07, 0.34, 0.56 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatCoffeeColorDark; + +/** + * Returns a flat color object whose HSB values are 0.38, 0.44, 0.31 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatForestGreenColorDark; + +/** + * Returns a flat color object whose HSB values are 0.51, 0.10, 0.55 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatGrayColorDark; + +/** + * Returns a flat color object whose HSB values are 0.40, 0.78, 0.68 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatGreenColorDark; + +/** + * Returns a flat color object whose HSB values are 0.21, 0.81, 0.69 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatLimeColorDark; + +/** + * Returns a flat color object whose HSB values are 0.78, 0.61, 0.68 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMagentaColorDark; + +/** + * Returns a flat color object whose HSB values are 0.01, 0.68, 0.40 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMaroonColorDark; + +/** + * Returns a flat color object whose HSB values are 0.47, 0.86, 0.63 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatMintColorDark; + +/** + * Returns a flat color object whose HSB values are 0.58, 0.45, 0.31 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatNavyBlueColorDark; + +/** + * Returns a flat color object whose HSB values are 0.07, 1.00, 0.83 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatOrangeColorDark; + +/** + * Returns a flat color object whose HSB values are 0.91, 0.57, 0.83 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPinkColorDark; + +/** + * Returns a flat color object whose HSB values are 0.83, 0.46, 0.31 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPlumColorDark; + +/** + * Returns a flat color object whose HSB values are 0.62, 0.28, 0.84 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPowderBlueColorDark; + +/** + * Returns a flat color object whose HSB values are 0.70, 0.56, 0.64 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatPurpleColorDark; + +/** + * Returns a flat color object whose HSB values are 0.02, 0.78, 0.75 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatRedColorDark; + +/** + * Returns a flat color object whose HSB values are 0.12, 0.30, 0.84 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatSandColorDark; + +/** + * Returns a flat color object whose HSB values are 0.57, 0.78, 0.73 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatSkyBlueColorDark; + +/** + * Returns a flat color object whose HSB values are 0.54, 0.54, 0.45 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatTealColorDark; + +/** + * Returns a flat color object whose HSB values are 0.99, 0.61, 0.85 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatWatermelonColorDark; + +/** + * Returns a flat color object whose HSB values are 0.57, 0.05, 0.78 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatWhiteColorDark; + +/** + * Returns a flat color object whose HSB values are 0.11, 1.00, 1.00 and whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + */ ++ (UIColor *)flatYellowColorDark; + +#pragma mark - Randomizing Colors + +/** + * Returns a randomly generated flat color object whose alpha value is 1.0. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 1.0 + */ + ++ (UIColor *)randomFlatColor; + +/** + * @author Vicc Alexander + * + * Returns a randomly generated flat color object NOT found in the specified array. + * + * @param excludedColors An array specifying which colors NOT to return. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 2.1.0 + */ ++ (UIColor *)colorWithRandomFlatColorExcludingColorsInArray:(NSArray *)colors; + +/** + * @author Vicc Alexander + * + * Returns a randomly generated color object found in the specified array. + * + * @param colors An array specifying which colors to return. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 2.1.0 + */ ++ (UIColor *)colorWithRandomColorInArray:(NSArray *)colors; + +/** + * Returns a randomly generated flat color object with an alpha value of 1.0 in either a light or dark shade. + * + * @param shadeStyle Specifies whether the randomly generated flat color should be a light or dark shade. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 1.0 + */ ++ (UIColor *)colorWithRandomFlatColorOfShadeStyle:(UIShadeStyle)shadeStyle; + +/** + * Returns a randomly generated flat color object in either a light or dark shade. + * + * @param shadeStyle Specifies whether the randomly generated flat color should be a light or dark shade. + * @param alpha The opacity. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 2.0 + */ ++ (UIColor *)colorWithRandomFlatColorOfShadeStyle:(UIShadeStyle)shadeStyle withAlpha:(CGFloat)alpha; + +#pragma mark - Averaging a Color + +/** + * Returns the average color generated by averaging the colors of a specified image. + * + * @param image A specified @c UIImage. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 2.0 + */ ++ (UIColor *)colorWithAverageColorFromImage:(UIImage *)image; + +/** + * Returns the average color generated by averaging the colors of a specified image. + * + * @param image A specified @c UIImage. + * @param alpha The opacity. + * + * @return A flat @c UIColor object in the HSB colorspace. + * + * @since 2.0 + */ ++ (UIColor *)colorWithAverageColorFromImage:(UIImage *)image withAlpha:(CGFloat)alpha; + +#pragma mark - Complementary Colors + +/** + * Creates and returns a complementary flat color object 180 degrees away in the HSB colorspace from the specified color. + * + * @param color The color whose complementary color is being requested. + * + * @return A flat UIColor object in the HSB colorspace. + * + * @since 1.0 + */ + ++ (UIColor *)colorWithComplementaryFlatColorOf:(UIColor *)color; + +/** + * Creates and returns a complementary flat color object 180 degrees away in the HSB colorspace from the specified color. + * + * @param color The color whose complementary color is being requested. + * @param alpha The opacity. + * + * @return A flat UIColor object in the HSB colorspace. + * + * @since 2.0 + */ + ++ (UIColor *)colorWithComplementaryFlatColorOf:(UIColor *)color withAlpha:(CGFloat)alpha; + +#pragma mark - Contrasting Colors + +/** + * Creates and returns either a black or white color object depending on which contrasts more with a specified color. + * + * @param color The specified color of the contrast color that is being requested. + * @param isFlat Pass YES to return flat color objects. + * + * @return A UIColor object in the HSB colorspace. + * + * @since 1.0 + */ + ++ (UIColor *)colorWithContrastingBlackOrWhiteColorOn:(UIColor *)backgroundColor isFlat:(BOOL)flat; + +/** + * Creates and returns either a black or white color object depending on which contrasts more with a specified color. + * + * @param color The specified color of the contrast color that is being requested. + * @param isFlat Pass YES to return flat color objects. + * @param alpha The opacity. + * + * @return A UIColor object in the HSB colorspace. + * + * @since 2.0 + */ ++ (UIColor *)colorWithContrastingBlackOrWhiteColorOn:(UIColor *)backgroundColor isFlat:(BOOL)flat alpha:(CGFloat)alpha; + +#pragma mark - Gradient Colors + +/** + * Creates and returns a gradient as a color object with an alpha value of 1.0 + * + * @param gradientStyle Specifies the style and direction of the gradual blend between colors. + * @param frame The frame rectangle, which describes the view’s location and size in its superview’s coordinate system. + * @param colors An array of color objects used to create a gradient. + * + * @return A @c UIColor object using colorWithPattern. + * + * @since 2.0 + */ ++ (UIColor *)colorWithGradientStyle:(UIGradientStyle)gradientStyle withFrame:(CGRect)frame andColors:(NSArray *)colors; + +#pragma mark - Colors from Hex Strings + +/** + * Creates and returns a @c UIColor object based on the specified hex string. + * + * @param string The hex string. + * + * @return A @c UIColor object in the RGB colorspace. + * + * + * @since 2.0 + */ ++ (UIColor *)colorWithHexString:(NSString *)string; + +/** + * Creates and returns a @c UIColor object based on the specified hex string. + * + * @param string The hex string. + * @param alpha The opacity. + * + * @return A @c UIColor object in the RGB colorspace. + * + * + * @since 2.0 + */ ++ (UIColor *)colorWithHexString:(NSString *)string withAlpha:(CGFloat)alpha; + +#pragma mark - Instance Methods + +/** + * Creates and returns a flat color object closest to the specified color in the LAB colorspace. + * + * @return A flat version of the specified @c UIColor. + * + * @since 2.0 + */ +- (UIColor *)flatten; + +/** + * Creates and returns a darker shade of a specified color in the HSB space. + * + * @param percentage The value with which to darken a specified color. + * + * @return A @c UIColor object in the HSB space. + * + * @since 2.0 + */ +- (UIColor *)darkenByPercentage:(CGFloat)percentage; + +/** + * @author Vicc Alexander + * + * Returns the hex string value for the specified color. + * + * @return An @NSString object. + * + * @since 2.1.0 + */ +- (NSString *)hexValue; + +/** + * Creates and returns a lighter shade of a specified color in the HSB space. + * + * @param percentage The value with which to lighten a specified color. + * + * @return A @c UIColor object in the HSB space. + * + * @since 2.0 + */ +- (UIColor *)lightenByPercentage:(CGFloat)percentage; + +#pragma mark - Deprecated Methods + +/** + * Creates and returns a flat color object closest to the specified color in the LAB colorspace. + * + * @param color The specified color of the flat color that is being requested. + * + * @return A flat UIColor object in the RGB colorspace. + * + * @since 1.0 + */ ++ (UIColor *)colorWithFlatVersionOf:(UIColor *)color __attribute((deprecated(" Use -flatten: instead (First deprecated in Chameleon 2.0)."))); + + +@end + diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.m new file mode 100755 index 0000000..934f760 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.m @@ -0,0 +1,1027 @@ + +// UIColor+Chameleon.m + +/* + + The MIT License (MIT) + + Copyright (c) 2014-2015 Vicc Alexander. + + 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 "UIColor+Chameleon.h" +#import "UIColor+ChameleonPrivate.h" +#import "ChameleonMacros.h" +#import + +@implementation UIColor (Chameleon) + +@dynamic gradientImage; + +#pragma mark - Chameleon - Getter & Setter Methods for Instance Variables + ++ (void)setGradientImage:(UIImage *)gradientImage { + + objc_setAssociatedObject(self, @selector(gradientImage), gradientImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (UIImage *)gradientImage { + + return objc_getAssociatedObject(self, @selector(gradientImage)); +} + +#pragma mark - Chameleon - Light Shades + ++ (UIColor *)flatBlackColor { + return hsb(0, 0, 17); +} + ++ (UIColor *)flatBlueColor { + return hsb(224, 50, 63); +} + ++ (UIColor *)flatBrownColor { + return hsb(24, 45, 37); +} + ++ (UIColor *)flatCoffeeColor { + return hsb(25, 31, 64); +} + ++ (UIColor *)flatForestGreenColor { + return hsb(138, 45, 37); +} + ++ (UIColor *)flatGrayColor { + return hsb(184, 10, 65); +} + ++ (UIColor *)flatGreenColor { + return hsb(145, 77, 80); +} + ++ (UIColor *)flatLimeColor { + return hsb(74, 70, 78); +} + ++ (UIColor *)flatMagentaColor { + return hsb(283, 51, 71); +} + ++ (UIColor *)flatMaroonColor { + return hsb(5, 65, 47); +} + ++ (UIColor *)flatMintColor { + return hsb(168, 86, 74); +} + ++ (UIColor *)flatNavyBlueColor { + return hsb(210, 45, 37); +} + ++ (UIColor *)flatOrangeColor { + return hsb(28, 85, 90); +} + ++ (UIColor *)flatPinkColor { + return hsb(324, 49, 96); +} + ++ (UIColor *)flatPlumColor { + return hsb(300, 45, 37); +} + ++ (UIColor *)flatPowderBlueColor { + return hsb(222, 24, 95); +} + ++ (UIColor *)flatPurpleColor { + return hsb(253, 52, 77); +} + ++ (UIColor *)flatRedColor { + return hsb(6, 74, 91); +} + ++ (UIColor *)flatSandColor { + return hsb(42, 25, 94); +} + ++ (UIColor *)flatSkyBlueColor { + return hsb(204, 76, 86); +} + ++ (UIColor *)flatTealColor { + return hsb(195, 55, 51); +} + ++ (UIColor *)flatWatermelonColor { + return hsb(356, 53, 94); +} + ++ (UIColor *)flatWhiteColor { + return hsb(192, 2, 95); +} + ++ (UIColor *)flatYellowColor { + return hsb(48, 99, 100); +} + +#pragma mark - Chameleon - Dark Shades + ++ (UIColor *)flatBlackColorDark { + return hsb(0, 0, 15); +} + ++ (UIColor *)flatBlueColorDark { + return hsb(224, 56, 51); +} + ++ (UIColor *)flatBrownColorDark { + return hsb(25, 45, 31); +} + ++ (UIColor *)flatCoffeeColorDark { + return hsb(25, 34, 56); +} + ++ (UIColor *)flatForestGreenColorDark { + return hsb(135, 44, 31); +} + ++ (UIColor *)flatGrayColorDark { + return hsb(184, 10, 55); +} + ++ (UIColor *)flatGreenColorDark { + return hsb(145, 78, 68); +} + ++ (UIColor *)flatLimeColorDark { + return hsb(74, 81, 69); +} + ++ (UIColor *)flatMagentaColorDark { + return hsb(282, 61, 68); +} + ++ (UIColor *)flatMaroonColorDark { + return hsb(4, 68, 40); +} + ++ (UIColor *)flatMintColorDark { + return hsb(168, 86, 63); +} + ++ (UIColor *)flatNavyBlueColorDark { + return hsb(210, 45, 31); +} + ++ (UIColor *)flatOrangeColorDark { + return hsb(24, 100, 83); +} + ++ (UIColor *)flatPinkColorDark { + return hsb(327, 57, 83); +} + ++ (UIColor *)flatPlumColorDark { + return hsb(300, 46, 31); +} + ++ (UIColor *)flatPowderBlueColorDark { + return hsb(222, 28, 84); +} + ++ (UIColor *)flatPurpleColorDark { + return hsb(253, 56, 64); +} + ++ (UIColor *)flatRedColorDark { + return hsb(6, 78, 75); +} + ++ (UIColor *)flatSandColorDark { + return hsb(42, 30, 84); +} + ++ (UIColor *)flatSkyBlueColorDark { + return hsb(204, 78, 73); +} + ++ (UIColor *)flatTealColorDark { + return hsb(196, 54, 45); +} + ++ (UIColor *)flatWatermelonColorDark { + return hsb(358, 61, 85); +} + ++ (UIColor *)flatWhiteColorDark { + return hsb(204, 5, 78); +} + ++ (UIColor *)flatYellowColorDark { + return hsb(40, 100, 100); +} + +#pragma mark - Chameleon - "Color With" Methods + ++ (UIColor *)colorWithAverageColorFromImage:(UIImage *)image { + + return [self colorWithAverageColorFromImage:image withAlpha:1.0]; +} + ++ (UIColor *)colorWithAverageColorFromImage:(UIImage *)image withAlpha:(CGFloat)alpha { + + //Work within the RGB colorspoace + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + unsigned char rgba[4]; + CGContextRef context = CGBitmapContextCreate(rgba, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + + //Draw our image down to 1x1 pixels + CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), image.CGImage); + CGColorSpaceRelease(colorSpace); + CGContextRelease(context); + + //Check if image alpha is 0 + if (rgba[3] == 0) { + + CGFloat imageAlpha = ((CGFloat)rgba[3])/255.0; + CGFloat multiplier = imageAlpha/255.0; + + UIColor *averageColor = [UIColor colorWithRed:((CGFloat)rgba[0])*multiplier green:((CGFloat)rgba[1])*multiplier blue:((CGFloat)rgba[2])*multiplier alpha:imageAlpha]; + + //Improve color + averageColor = [averageColor colorWithMinimumSaturation:0.15]; + + //Return average color + return averageColor; + } + + else { + + //Get average + UIColor *averageColor = [UIColor colorWithRed:((CGFloat)rgba[0])/255.0 green:((CGFloat)rgba[1])/255.0 blue:((CGFloat)rgba[2])/255.0 alpha:alpha]; + + //Improve color + averageColor = [averageColor colorWithMinimumSaturation:0.15]; + + //Return average color + return averageColor; + } +} + ++ (UIColor *)colorWithComplementaryFlatColorOf:(UIColor *)color { + + return [[self class] colorWithComplementaryFlatColorOf:color withAlpha:1.0]; +} + ++ (UIColor *)colorWithComplementaryFlatColorOf:(UIColor *)color withAlpha:(CGFloat)alpha { + + //Check if input UIColor is a gradient aka a pattern + if (CGColorGetPattern(color.CGColor)) { + + //Let's find the average color of the image and contrast against that. + CGSize size = {1, 1}; + + //Create a 1x1 bitmap context + UIGraphicsBeginImageContext(size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + //Set the interpolation quality to medium + CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium); + + //Draw image scaled down to this 1x1 pixel + [[self gradientImage] drawInRect:(CGRect){.size = size} blendMode:kCGBlendModeCopy alpha:1]; + + //Read the RGB values from the context's buffer + uint8_t *data = CGBitmapContextGetData(ctx); + color = [UIColor colorWithRed:data[2] / 255.0f + green:data[1] / 255.0f + blue:data[0] / 255.0f + alpha:1]; + UIGraphicsEndImageContext(); + } + + //Extract & Check to make sure we have an actual color to work with (Clear returns clear) + CGFloat hue, saturation, brightness, alpha1; + [color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha1]; + + //Check if color is transparent + if (alpha1 == 0) { + return [UIColor clearColor]; + } + + //Multiply our value by their max values to convert + hue *= 360; + saturation *= 100; + brightness *= 100; + + //Select a color with a hue 180 degrees away on the colorwheel (i.e. for 50 it would be 230). + hue += 180.0f; + if (hue > 360.f) { + hue -= 360.0f; + } + + //Round to the nearest whole number after multiplying + hue = roundf(hue); + saturation = roundf(saturation); + brightness = roundf(brightness); + + //Store complimentary nonflat color + UIColor *complimentaryNonFlatColor = [UIColor colorWithHue:hue/360.0 + saturation:saturation/100.0 + brightness:brightness/100.0 + alpha:alpha]; + + //Retrieve LAB values from our complimentary nonflat color & return nearest flat color + return [self colorWithFlatVersionOf:complimentaryNonFlatColor withAlpha:alpha]; +} + + ++ (UIColor *)colorWithFlatVersionOf:(UIColor *)color { + + //Return flat version with default alpha of 1.0 + return [[self class] colorWithFlatVersionOf:color withAlpha:1.0]; +} + ++ (UIColor *)colorWithFlatVersionFrom:(UIColor *)color { + CGFloat colorAlpha = 0; + [color getLightness:nil valueForA:nil valueForB:nil alpha:&colorAlpha]; + colorAlpha = colorAlpha > 0.0 ? colorAlpha : 1.0; + //Return flat version with default alpha of 1.0 + return [[self class] colorWithFlatVersionOf:color withAlpha:colorAlpha]; +} + ++ (UIColor *)colorWithFlatVersionOf:(UIColor *)color withAlpha:(CGFloat)alpha { + + //Check if input UIColor is a gradient aka a pattern + if (CGColorGetPattern(color.CGColor)) { + + //Let's find the average color of the image and contrast against that. + CGSize size = {1, 1}; + + //Create a 1x1 bitmap context + UIGraphicsBeginImageContext(size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + //Set the interpolation quality to medium + CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium); + + //Draw image scaled down to this 1x1 pixel + [[self gradientImage] drawInRect:(CGRect){.size = size} blendMode:kCGBlendModeCopy alpha:1]; + + //Read the RGB values from the context's buffer + uint8_t *data = CGBitmapContextGetData(ctx); + color = [UIColor colorWithRed:data[2] / 255.0f + green:data[1] / 255.0f + blue:data[0] / 255.0f + alpha:1]; + UIGraphicsEndImageContext(); + } + + //Create CGFloats to hold our color values + CGFloat L, A, B, alpha1; + + //Get LAB values for our color + [color getLightness:&L valueForA:&A valueForB:&B alpha:&alpha1]; + + if (![color getLightness:&L valueForA:&A valueForB:&B alpha:&alpha1]) { + return nil; + } + + //Find the nearest flat color + return [self nearestFlatColorForL:L A:A B:B alpha:alpha]; +} + ++ (UIColor *)colorWithContrastingBlackOrWhiteColorOn:(UIColor *)backgroundColor isFlat:(BOOL)flat { + + //Return color with default alpha value of 1.0 + return [[self class] colorWithContrastingBlackOrWhiteColorOn:backgroundColor isFlat:flat alpha:1.0]; +} + ++ (UIColor *)colorWithContrastingBlackOrWhiteColorOn:(UIColor *)backgroundColor + isFlat:(BOOL)flat + alpha:(CGFloat)alpha { + + //Check if UIColor is a gradient aka a pattern + if (CGColorGetPattern(backgroundColor.CGColor)) { + + //Let's find the average color of the image and contrast against that. + CGSize size = {1, 1}; + + //Create a 1x1 bitmap context + UIGraphicsBeginImageContext(size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + + //Set the interpolation quality to medium + CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium); + + //Draw image scaled down to this 1x1 pixel + [[self gradientImage] drawInRect:(CGRect){.size = size} blendMode:kCGBlendModeCopy alpha:1]; + + //Read the RGB values from the context's buffer + uint8_t *data = CGBitmapContextGetData(ctx); + backgroundColor = [UIColor colorWithRed:data[2] / 255.0f + green:data[1] / 255.0f + blue:data[0] / 255.0f + alpha:1]; + UIGraphicsEndImageContext(); + } + + //Calculate Luminance + CGFloat luminance; + CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha1 = 0.0; + [backgroundColor getRed:&red green:&green blue:&blue alpha:&alpha1]; + + //Check if color is transparent + if (alpha == 0) { + return [UIColor clearColor]; + } + + // Relative luminance in colorimetric spaces - http://en.wikipedia.org/wiki/Luminance_(relative) + red *= 0.2126f; green *= 0.7152f; blue *= 0.0722f; + luminance = red + green + blue; + + if (flat == NO) { + return (luminance > 0.6f) ? rgba(0, 0, 0, alpha) : rgba(255, 255, 255, alpha); + } else { + return (luminance > 0.6f) ? hsba(0, 0, 15, alpha) : hsba(192, 2, 95, alpha); + } +} + ++ (UIColor *)colorWithGradientStyle:(UIGradientStyle)gradientStyle withFrame:(CGRect)frame andColors:(NSArray *)colors; { + + //Create our background gradient layer + CAGradientLayer *backgroundGradientLayer = [CAGradientLayer layer]; + + //Set the frame to our object's bounds + backgroundGradientLayer.frame = frame; + + //To simplfy formatting, we'll iterate through our colors array and create a mutable array with their CG counterparts + NSMutableArray *cgColors = [[NSMutableArray alloc] init]; + for (UIColor *color in colors) { + [cgColors addObject:(id)[color CGColor]]; + } + + switch (gradientStyle) { + case UIGradientStyleLeftToRight: { + + //Set out gradient's colors + backgroundGradientLayer.colors = cgColors; + + //Specify the direction our gradient will take + [backgroundGradientLayer setStartPoint:CGPointMake(0.0, 0.5)]; + [backgroundGradientLayer setEndPoint:CGPointMake(1.0, 0.5)]; + + //Convert our CALayer to a UIImage object + UIGraphicsBeginImageContextWithOptions(backgroundGradientLayer.bounds.size,NO, [UIScreen mainScreen].scale); + [backgroundGradientLayer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *backgroundColorImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [self setGradientImage:backgroundColorImage]; + return [UIColor colorWithPatternImage:backgroundColorImage]; + } + + case UIGradientStyleRadial: { + UIGraphicsBeginImageContextWithOptions(frame.size,NO, [UIScreen mainScreen].scale); + + //Specific the spread of the gradient (For now this gradient only takes 2 locations) + CGFloat locations[2] = {0.0, 1.0}; + + //Default to the RGB Colorspace + CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB(); + CFArrayRef arrayRef = (__bridge CFArrayRef)cgColors; + + //Create our Fradient + CGGradientRef myGradient = CGGradientCreateWithColors(myColorspace, arrayRef, locations); + + + // Normalise the 0-1 ranged inputs to the width of the image + CGPoint myCentrePoint = CGPointMake(0.5 * frame.size.width, 0.5 * frame.size.height); + float myRadius = MIN(frame.size.width, frame.size.height) * 1.0; + + // Draw our Gradient + CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), myGradient, myCentrePoint, + 0, myCentrePoint, myRadius, + kCGGradientDrawsAfterEndLocation); + + // Grab it as an Image + UIImage *backgroundColorImage = UIGraphicsGetImageFromCurrentImageContext(); + + // Clean up + CGColorSpaceRelease(myColorspace); // Necessary? + CGGradientRelease(myGradient); // Necessary? + UIGraphicsEndImageContext(); + + [self setGradientImage:backgroundColorImage]; + return [UIColor colorWithPatternImage:backgroundColorImage]; + } + + case UIGradientStyleTopToBottom: + default: { + + //Set out gradient's colors + backgroundGradientLayer.colors = cgColors; + + //Convert our CALayer to a UIImage object + UIGraphicsBeginImageContextWithOptions(backgroundGradientLayer.bounds.size,NO, [UIScreen mainScreen].scale); + [backgroundGradientLayer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *backgroundColorImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [self setGradientImage:backgroundColorImage]; + return [UIColor colorWithPatternImage:backgroundColorImage]; + } + + } +} + ++ (UIColor *)colorWithHexString:(NSString *)string { + + //Color with string and a defualt alpha value of 1.0 + return [self colorWithHexString:string withAlpha:1.0]; +} + ++ (UIColor *)colorWithHexString:(NSString *)string withAlpha:(CGFloat)alpha { + + //Quick return in case string is empty + if (string.length == 0) { + return nil; + } + + //Check to see if we need to add a hashtag + if('#' != [string characterAtIndex:0]) { + string = [NSString stringWithFormat:@"#%@", string]; + } + + //Make sure we have a working string length + if (string.length != 7 && string.length != 4) { + + #ifdef DEBUG + NSLog(@"Unsupported string format: %@", string); + #endif + + return nil; + } + + //Check for short hex strings + if(string.length == 4) { + + //Convert to full length hex string + string = [NSString stringWithFormat:@"#%@%@%@%@%@%@", + [string substringWithRange:NSMakeRange(1, 1)],[string substringWithRange:NSMakeRange(1, 1)], + [string substringWithRange:NSMakeRange(2, 1)],[string substringWithRange:NSMakeRange(2, 1)], + [string substringWithRange:NSMakeRange(3, 1)],[string substringWithRange:NSMakeRange(3, 1)]]; + } + + NSString *redHex = [NSString stringWithFormat:@"0x%@", [string substringWithRange:NSMakeRange(1, 2)]]; + unsigned red = [[self class] hexValueToUnsigned:redHex]; + + NSString *greenHex = [NSString stringWithFormat:@"0x%@", [string substringWithRange:NSMakeRange(3, 2)]]; + unsigned green = [[self class] hexValueToUnsigned:greenHex]; + + NSString *blueHex = [NSString stringWithFormat:@"0x%@", [string substringWithRange:NSMakeRange(5, 2)]]; + unsigned blue = [[self class] hexValueToUnsigned:blueHex]; + + return [UIColor colorWithRed:(float)red/255 green:(float)green/255 blue:(float)blue/255 alpha:alpha]; +} + ++ (unsigned)hexValueToUnsigned:(NSString *)hexValue { + + //Define default unsigned value + unsigned value = 0; + + //Scan unsigned value + NSScanner *hexValueScanner = [NSScanner scannerWithString:hexValue]; + [hexValueScanner scanHexInt:&value]; + + //Return found value + return value; +} + +#pragma mark - Chameleon - Random Color Methods + ++ (NSInteger)generateRandomNumberWithMax:(NSInteger)max { + + //Choose a random number between 0 and our number of available colors + return arc4random_uniform((UInt32)max); +} + ++ (UIColor *)randomFlatColor { + + //Number of colors to choose from + const uint32_t numberOfPossibleColors = 48; + + //Chose one of those colors at random + NSInteger randomColorChosen = [[self class] generateRandomNumberWithMax:numberOfPossibleColors]; + + //Check if a previous random number exists + if (![[NSUserDefaults standardUserDefaults] integerForKey:@"previousRandomNumber"]) { + + //If no previous number exists, save it as such and find the matching color + [[NSUserDefaults standardUserDefaults] setInteger:randomColorChosen forKey:@"previousRandomNumber"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + } else { + + //Keep generating a random number until it is different than the one generated last time + while (randomColorChosen == [[NSUserDefaults standardUserDefaults] integerForKey:@"previousRandomNumber"]) { + randomColorChosen = [[self class] generateRandomNumberWithMax:numberOfPossibleColors]; + } + + //Once a new number has been generated then store it as the previous number for next time and proceed + [[NSUserDefaults standardUserDefaults] setInteger:randomColorChosen forKey:@"previousRandomNumber"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + return [self flatColors][randomColorChosen]; +} + ++ (UIColor *)colorWithRandomColorInArray:(NSArray *)colors { + + UIColor *randomColor; + if (colors.count) { + + //Pick a random index + NSInteger randomIndex = arc4random() % colors.count; + + //Return the color at the random index + randomColor = colors[randomIndex]; + + } else { + return nil; + } + + NSAssert([randomColor isKindOfClass:[UIColor class]], @"Hmm... one of your objects in your 'colors' array is not a UIColor object."); + + //Return + return randomColor; +} + ++ (UIColor *)colorWithRandomFlatColorExcludingColorsInArray:(NSArray *)colors { + + //Set random flat color + UIColor *randomColor = [[self class] randomFlatColor]; + + //If the selected color is blacklisted select a new color + while ([colors containsObject:randomColor]) { + randomColor = [[self class] randomFlatColor]; + } + + //Return + return randomColor; +} + ++ (UIColor *)colorWithRandomFlatColorOfShadeStyle:(UIShadeStyle)shadeStyle { + + //Return color with default alpha value of 1.0 + return [[self class] colorWithRandomFlatColorOfShadeStyle:shadeStyle withAlpha:1.0]; +} + ++ (UIColor *)colorWithRandomFlatColorOfShadeStyle:(UIShadeStyle)shadeStyle withAlpha:(CGFloat)alpha { + + //Number of colors to choose from + const NSInteger numberOfPossibleColors = 24; + + //Chose one of those colors at random + NSInteger randomColorChosen = [[self class] generateRandomNumberWithMax:numberOfPossibleColors]; + + //Check if a previous random number exists + if (![[NSUserDefaults standardUserDefaults] integerForKey:@"previousRandomNumber"]) { + + //If no previous number exists, save it as such and find the matching color + [[NSUserDefaults standardUserDefaults] setInteger:randomColorChosen forKey:@"previousRandomNumber"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + } else { + + //Keep generating a random number until it is different than the one generated last time + while (randomColorChosen == [[NSUserDefaults standardUserDefaults] integerForKey:@"previousRandomNumber"]) { + randomColorChosen = [[self class] generateRandomNumberWithMax:numberOfPossibleColors]; + } + + //Once a new number has been generated then store it as the previous number for next time and proceed + [[NSUserDefaults standardUserDefaults] setInteger:randomColorChosen forKey:@"previousRandomNumber"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + //Assign a random color based on randomColorChosen + UIColor *randomColor; + + //Return a color depending on the specified shade + switch (shadeStyle) { + case UIShadeStyleDark: { + + NSArray *darkColors = @[FlatBlackDark, FlatBlueDark, FlatBrownDark, FlatCoffeeDark, FlatForestGreenDark, FlatGrayDark, FlatGreenDark, FlatLimeDark, FlatMagentaDark, FlatMaroonDark, FlatMintDark, FlatNavyBlueDark, FlatOrangeDark, FlatPinkDark, FlatPlumDark, FlatPowderBlueDark, FlatPurpleDark, FlatRedDark, FlatSandDark, FlatSkyBlueDark, FlatTealDark, FlatWatermelonDark, FlatWhiteDark, FlatYellowDark]; + + randomColor = darkColors[randomColorChosen]; + break; + } + + case UIShadeStyleLight: + default: { + + NSArray *lightColors = @[FlatBlack, FlatBlue, FlatBrown, FlatCoffee, FlatForestGreen, FlatGray, FlatGreen, FlatLime, FlatMagenta, FlatMaroon, FlatMint, FlatNavyBlue, FlatOrange, FlatPink, FlatPlum, FlatPowderBlue, FlatPurple, FlatRed, FlatSand, FlatSkyBlue, FlatTeal, FlatWatermelon, FlatWhite, FlatYellow]; + + randomColor = lightColors[randomColorChosen]; + break; + } + } + + //Return color with correct alpha value + randomColor = [randomColor colorWithAlphaComponent:alpha]; + + return randomColor; +} + +#pragma mark - Chameleon Instance Methods + +- (UIColor *)flatten { + + return [UIColor colorWithFlatVersionFrom:self]; +} + +- (UIColor *)darkenByPercentage:(CGFloat)percentage { + + //Define HSBA values + CGFloat h, s, b, a; + + //Check if HSBA values exist + if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) { + + //Make sure our percentage is greater than 0 + if (percentage > 0) { + b = MIN(b - percentage, 1.0); + } + + //Return darker color + return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; + } + + return nil; +} + +- (NSString *)hexValue { + + UIColor *currentColor = self; + if (CGColorGetNumberOfComponents(self.CGColor) < 4) { + const CGFloat *components = CGColorGetComponents(self.CGColor); + currentColor = [UIColor colorWithRed:components[0] green:components[0] blue:components[0] alpha:components[1]]; + } + + if (CGColorSpaceGetModel(CGColorGetColorSpace(currentColor.CGColor)) != kCGColorSpaceModelRGB) { + return [NSString stringWithFormat:@"#FFFFFF"]; + } + + return [NSString stringWithFormat:@"#%02X%02X%02X", (int)((CGColorGetComponents(currentColor.CGColor))[0]*255.0), (int)((CGColorGetComponents(currentColor.CGColor))[1]*255.0), (int)((CGColorGetComponents(currentColor.CGColor))[2]*255.0)]; + +} + +- (UIColor *)lightenByPercentage:(CGFloat)percentage { + + //Define HSBA values + CGFloat h, s, b, a; + + //Check if HSBA values exist + if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) { + + //Make sure our percentage is greater than 0 + if (percentage > 0) { + b = MIN(b + percentage, 1.0); + } + + //Return lighter color + return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; + } + + return nil; +} + +- (BOOL)isEqualToColor:(UIColor *)color { + + //Check if both colors are in the Monochrome / RGB color space + if ([self isMonochromeOrRGB] && [color isMonochromeOrRGB]) { + + //Return comparison + return self.RGBAValue == color.RGBAValue; + } + + //Return comparison + return [self isEqual:color]; +} + +#pragma mark - Chameleon Internal Methods + +//Array of all our colors ++ (NSArray *)flatColors { + + return @[FlatBlack, FlatBlackDark, FlatBlue, FlatBlueDark, FlatBrown, FlatBrownDark, FlatCoffee, FlatCoffeeDark, FlatForestGreen, FlatForestGreenDark, FlatGray, FlatGrayDark, FlatGreen, FlatGreenDark, FlatLime, FlatLimeDark, FlatMagenta, FlatMagentaDark, FlatMaroon, FlatMaroonDark, FlatMint, FlatMintDark, FlatNavyBlue, FlatNavyBlueDark, FlatOrange, FlatOrangeDark, FlatPink, FlatPinkDark, FlatPlum, FlatPlumDark, FlatPowderBlue, FlatPowderBlueDark, FlatPurple, FlatPurpleDark, FlatRed, FlatRedDark, FlatSand, FlatSandDark, FlatSkyBlue, FlatSkyBlueDark, FlatTeal, FlatTealDark, FlatWatermelon, FlatWatermelonDark, FlatWhite, FlatWhiteDark, FlatYellow, FlatYellowDark]; +} + +- (uint32_t)RGBAValue { + + CGFloat rgba[4]; + [self getRGBAComponents:rgba]; + uint8_t red = rgba[0]*255; + uint8_t green = rgba[1]*255; + uint8_t blue = rgba[2]*255; + uint8_t alpha = rgba[3]*255; + + return (red << 24) + (green << 16) + (blue << 8) + alpha; +} + +- (void)getRGBAComponents:(CGFloat[4])rgba { + + CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor)); + const CGFloat *components = CGColorGetComponents(self.CGColor); + switch (model) { + + case kCGColorSpaceModelMonochrome: { + rgba[0] = components[0]; + rgba[1] = components[0]; + rgba[2] = components[0]; + rgba[3] = components[1]; + break; + } + + case kCGColorSpaceModelRGB: { + rgba[0] = components[0]; + rgba[1] = components[1]; + rgba[2] = components[2]; + rgba[3] = components[3]; + break; + } + + case kCGColorSpaceModelCMYK: + case kCGColorSpaceModelDeviceN: + case kCGColorSpaceModelIndexed: + case kCGColorSpaceModelLab: + case kCGColorSpaceModelPattern: + case kCGColorSpaceModelUnknown: { + + #ifdef DEBUG + NSLog(@"Unsupported color model: %i", model); + #endif + + rgba[0] = 0.0f; + rgba[1] = 0.0f; + rgba[2] = 0.0f; + rgba[3] = 1.0f; + break; + } + } +} + +//Check if color is in the monochrome or rgb color space +- (BOOL)isMonochromeOrRGB { + + CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor)); + return model == kCGColorSpaceModelMonochrome || model == kCGColorSpaceModelRGB; +} + +//Calculate the total sum of differences - Euclidian distance +//Chameleon is now using the CIEDE2000 formula to calculate distances between 2 colors. +//More info: http://en.wikipedia.org/wiki/Color_difference ++ (float)totalSumOfDifferencesFroml1:(CGFloat)L1 l2:(CGFloat)L2 a1:(CGFloat)A1 + a2:(CGFloat)A2 b1:(CGFloat)B1 b2:(CGFloat)B2 { + + //Get C Values in LCH from LAB Values + CGFloat C1 = sqrt(pow(A1, 2) + pow(B1, 2)); + CGFloat C2 = sqrt(pow(A2, 2) + pow(B2, 2)); + + //CIE Weights + CGFloat KL = 1; + CGFloat KC = 1; + CGFloat KH = 1; + + //Variables specifically set for CIE:2000 + CGFloat DeltaPrimeL = L2 - L1; + CGFloat MeanL = ((L1 + L2) / 2); + CGFloat MeanC = ((C1 + C2) / 2); + CGFloat A1Prime = A1 + A1 / 2 * (1 - sqrt(pow(MeanC, 7.0) / (pow(MeanC, 7.0) + pow(25.0, 7.0)))); + CGFloat A2Prime = A2 + A2 / 2 * (1 - sqrt(pow(MeanC, 7.0) / (pow(MeanC, 7.0) + pow(25.0, 7.0)))); + CGFloat C1Prime = sqrt(pow(A1Prime, 2) + pow(B1, 2)); + CGFloat C2Prime = sqrt(pow(A2Prime, 2) + pow(B2, 2)); + CGFloat DeltaPrimeC = C1Prime - C2Prime; + CGFloat DeltaC = C1 - C2; + CGFloat MeanCPrime = (C1Prime + C2Prime) / 2; + CGFloat H1Prime = fmodf(atan2(B1, A1Prime), (360.0 * M_PI/180)); + CGFloat H2Prime = fmodf(atan2(B2, A2Prime), (360.0 * M_PI/180)); + + //Run everything through our △H' Function + CGFloat hDeltaPrime = 0; + if (fabs(H1Prime - H2Prime) <= (180.0 * M_PI/180)) { + + hDeltaPrime = H2Prime - H1Prime; + + } else if (H2Prime <= H1Prime) { + + hDeltaPrime = (H2Prime - H1Prime) + ((360.0 * M_PI/180)); + + } else { + + hDeltaPrime = (H2Prime - H1Prime) - ((360.0 * M_PI/180)); + } + + CGFloat deltaHPrime = 2 * (sqrt(C1Prime*C2Prime)) * sin(hDeltaPrime/2); + + //Get Mean H' Value + CGFloat MeanHPrime = 0; + if (fabs(H1Prime-H2Prime) > (180.0 * M_PI/180)) { + + MeanHPrime = (H1Prime + H2Prime + (360.0 * M_PI/180)) / 2; + + } else { + + MeanHPrime = (H1Prime + H2Prime) / 2; + } + + CGFloat T = 1 - 0.17 * cos(MeanHPrime - (30.0 * M_PI/180)) + 0.24 * cos(2 * MeanHPrime)+0.32 * cos(3 * MeanHPrime + (6.0 * M_PI/180)) - 0.20 * cos(4 * MeanHPrime - (63.0 * M_PI/180)); + + CGFloat SL = 1 + (0.015 * pow((MeanL - 50), 2))/sqrt(20 + pow((MeanL - 50), 2)); + CGFloat SC = 1 + 0.045 * MeanCPrime; + CGFloat SH = 1 + 0.015 * MeanCPrime * T; + + CGFloat RT = -2 * sqrt(pow(MeanCPrime, 7) / (pow(MeanCPrime, 7) + pow(25.0, 7))) * sin((60.0 * M_PI/180)* exp(-1 * pow((MeanCPrime - (275.0 * M_PI/180)) / (25.0 * M_PI/180), 2))); + + + //Get total difference + CGFloat TotalDifference = sqrt(pow((DeltaPrimeL / (KL * SL)), 2) + pow((DeltaPrimeC / (KC * SC)), 2) + pow((deltaHPrime / (KH * SH)), 2) + RT * (DeltaC / (KC * SC)) * (deltaHPrime / (KH * SH))); + + return TotalDifference; +} + ++ (UIColor *)nearestFlatColorForL:(CGFloat)l1 A:(CGFloat)a1 B:(CGFloat)b1 alpha:(CGFloat)alpha{ + + //Keep track of our index + int index = 0; + + //Start with a random big number to make sure the first comparison gets saved. + float smallestDistance = 1000000; + float previousDistance = 1000000; + float currentDistance; + + //Our values + CGFloat l2, a2, b2; + + //We're interested in the color with values returning the smallest sum of total differences so we need to cross reference our input color's values with every flat color's values + for (int i=0; i<[[self flatColors] count]; i++ ) { + + //Check that index is not zero + if (i!=0 ) { + //Extract LAB values from colors in array and store it as the previous index + [[self flatColors][i - 1] getLightness:&l2 valueForA:&a2 valueForB:&b2 alpha:nil]; + + previousDistance = [self totalSumOfDifferencesFroml1:l1 l2:l2 + a1:a1 a2:a2 + b1:b1 b2:b2]; + } + + //Extract LAB values from colors in array and store it as the current index + [[self flatColors][i] getLightness:&l2 valueForA:&a2 valueForB:&b2 alpha:nil]; + + currentDistance = [self totalSumOfDifferencesFroml1:l1 l2:l2 + a1:a1 a2:a2 + b1:b1 b2:b2]; + + //We're only interested in the smallest difference + if (currentDistance < previousDistance) { + if (currentDistance < smallestDistance) { + smallestDistance = currentDistance; + index = i; + } + } + } + + + //Collect the RGB Values of the color where the smallest difference was found + CGFloat red, green, blue; + [[self flatColors][index] getRed:&red green:&green blue:&blue alpha:nil]; + + //Return the closest flat color + return rgba(red * 255, green * 255, blue * 255, alpha); +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h new file mode 100755 index 0000000..f81e69a --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h @@ -0,0 +1,36 @@ +// +// UIColor+ChameleonPrivate.h +// Chameleon +// +// Created by Vicc Alexander on 6/6/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import +#import + +@interface UIColor (ChameleonPrivate) + +@property (nonatomic, readwrite) NSUInteger count; + +#pragma mark - Class Methods + ++ (UIColor *)colorFromImage:(UIImage *)image atPoint:(CGPoint)point; + +- (UIColor *)colorWithMinimumSaturation:(CGFloat)saturation; + +#pragma mark - Instance Methods + +- (BOOL)isDistinct:(UIColor *)color; + +- (BOOL)getValueForX:(CGFloat *)X + valueForY:(CGFloat *)Y + valueForZ:(CGFloat *)Z + alpha:(CGFloat *)alpha; + +- (BOOL)getLightness:(CGFloat *)L + valueForA:(CGFloat *)A + valueForB:(CGFloat *)B + alpha:(CGFloat *)alpha; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.m new file mode 100755 index 0000000..c5c7d47 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.m @@ -0,0 +1,251 @@ +// +// UIColor+ChameleonPrivate.m +// Chameleon +// +// Created by Vicc Alexander on 6/6/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "UIColor+ChameleonPrivate.h" + +@implementation UIColor (ChameleonPrivate) + +@dynamic count; + +#pragma mark - Associated Objects Methods + +- (void)setCount:(NSUInteger)count { + objc_setAssociatedObject(self, @selector(count), [NSNumber numberWithUnsignedInteger:count], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSUInteger)count { + NSNumber *number = objc_getAssociatedObject(self, @selector(count)); + return [number unsignedIntegerValue]; +} + +#pragma mark - Class Methods + +// Would not have been possible without - http://stackoverflow.com/a/1262893 ++ (UIColor *)colorFromImage:(UIImage *)image atPoint:(CGPoint)point { + + //Encapsulate our image + CGImageRef imageRef = image.CGImage; + NSUInteger width = CGImageGetWidth(imageRef); + NSUInteger height = CGImageGetHeight(imageRef); + + //Specify the colorspace we're in + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + //Extract the data we need + unsigned char *rawData = calloc(height * width * 4, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, + colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + //Release colorspace + CGColorSpaceRelease(colorSpace); + + //Draw and release image + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + CGContextRelease(context); + + //rawData now contains the image data in RGBA8888 + NSInteger byteIndex = (bytesPerRow * point.y) + (point.x * bytesPerPixel); + + //Define our RGBA values + CGFloat red = (rawData[byteIndex] * 1.f) / 255.f; + CGFloat green = (rawData[byteIndex + 1] * 1.f) / 255.f; + CGFloat blue = (rawData[byteIndex + 2] * 1.f) / 255.f; + CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.f; + + //Free our rawData + free(rawData); + + //Return color + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +} + +- (UIColor *)colorWithMinimumSaturation:(CGFloat)saturation { + if (!self) + return nil; + + CGFloat h, s, b, a; + [self getHue:&h saturation:&s brightness:&b alpha:&a]; + + if (s < saturation) + return [UIColor colorWithHue:h saturation:saturation brightness:b alpha:a]; + + return self; +} + +#pragma mark - Instance Methods + +- (BOOL)isDistinct:(UIColor *)color { + + if (!self || !color) { + return NO; + } + + CGFloat r, g, b, a; + CGFloat rc, gc, bc, ac; + + [self getRed:&r green:&g blue:&b alpha:&a]; + [color getRed:&rc green:&gc blue:&bc alpha:&ac]; + + CGFloat threshold = 0.25f; + + if (fabs(r - rc) > threshold || fabs(g - gc) > threshold || + + fabs(b - bc) > threshold || fabs(a - ac) > threshold) { + + // Check for grays + if (fabs(r - g) < 0.03f && fabs(r - b) < 0.03f) { + + if (fabs(rc - gc) < 0.03f && (fabs(rc - bc) < 0.03f)) { + return NO; + } + + } + + return YES; + } + + return NO; +} + +- (BOOL)getValueForX:(CGFloat *)X valueForY:(CGFloat *)Y valueForZ:(CGFloat *)Z alpha:(CGFloat *)alpha{ + + if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) { + + //Get RGB values from the input color + CGFloat red = 0, green = 0, blue = 0, alpha1 = 0; + [self getRed:&red green:&green blue:&blue alpha:&alpha1]; + + //Run our input color's RGB values through the XYZ algorithm to convert them into XYZ values + NSArray *XYZValues = [self arrayOfXYZValuesForR:red G:green B:blue A:alpha1]; + *X = [XYZValues[0] floatValue]; + *Y = [XYZValues[1] floatValue]; + *Z = [XYZValues[2] floatValue]; + *alpha = [XYZValues[3] floatValue]; + + return YES; + } + + return NO; +} + +- (BOOL)getLightness:(CGFloat *)L valueForA:(CGFloat *)A valueForB:(CGFloat *)B alpha:(CGFloat *)alpha { + + if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) { + + //Get RGB values from the input color + CGFloat red = 0, green = 0, blue = 0, alpha1 = 0; + [self getRed:&red green:&green blue:&blue alpha:&alpha1]; + + //Run our input color's RGB values through the XYZ algorithm to convert them into XYZ values + NSArray *XYZValues = [self arrayOfXYZValuesForR:red G:green B:blue A:alpha1]; + CGFloat X = [XYZValues[0] floatValue]; + CGFloat Y = [XYZValues[1] floatValue]; + CGFloat Z = [XYZValues[2] floatValue]; + + if (L != nil && A != nil && B != nil) { + //Run our new XYZ values through our LAB algorithm to convert them into LAB values + NSArray *LABValues = [self arrayOfLABValuesForX:X Y:Y Z:Z alpha:alpha1]; + *L = [LABValues[0] floatValue]; + *A = [LABValues[1] floatValue]; + *B = [LABValues[2] floatValue]; + } + + return YES; + } + + return NO; +} + +#pragma mark - Internal Helper Methods + +- (NSArray *)arrayOfXYZValuesForR:(CGFloat)red G:(CGFloat)green B:(CGFloat)blue A:(CGFloat)alpha { + + /* + Let's begin by converting from RGB to sRGB. + We're going to use the Reverse Transformation Equation. + http://en.wikipedia.org/wiki/SRGB + */ + + void (^sRGB)(CGFloat *C); + sRGB = ^(CGFloat *C) { + if (*C > 0.04045) { + *C = pow(((*C + 0.055)/ (1 + 0.055)), 2.40); + } else { + *C /= 12.92; + } + }; + + sRGB(&red); + sRGB(&green); + sRGB(&blue); + + /* + Now we're going to convert to XYZ values, using a matrix multiplication of the linear values + http://upload.wikimedia.org/math/4/3/3/433376fc18cccd887758beffb7e7c625.png + */ + + CGFloat X = (red * 0.4124) + (green * 0.3576) + (blue * 0.1805); + CGFloat Y = (red * 0.2126) + (green * 0.7152) + (blue * 0.0722); + CGFloat Z = (red * 0.0193) + (green * 0.1192) + (blue * 0.9505); + + X *= 100; + Y *= 100; + Z *= 100; + + return @[@(X), @(Y), @(Z), @(alpha)]; +} + +- (NSArray *)arrayOfLABValuesForX:(CGFloat)X Y:(CGFloat)Y Z:(CGFloat)Z alpha:(CGFloat)alpha { + + /* + The corresponding original XYZ values are such that white is D65 with unit luminance (X,Y,Z = 0.9505, 1.0000, 1.0890). + Calculations are also to assume the 2° standard colorimetric observer. + D65: http://en.wikipedia.org/wiki/CIE_Standard_Illuminant_D65 + Standard Colorimetric Observer: http://en.wikipedia.org/wiki/Standard_colorimetric_observer#CIE_standard_observer + + Since we mutiplied our XYZ values by 100 to produce a percentage we should also multiply our unit luminance values by 100. + */ + + X /= (0.9505 * 100); + Y /= (1.0000 * 100); + Z /= (1.0890 * 100); + + /* + Next we need to use the forward transformation function for CIELAB-CIEXYZ conversions + Function: http://upload.wikimedia.org/math/e/5/1/e513d25d50d406bfffb6ed3c854bd8a4.png + */ + + void (^XYZtoLAB)(CGFloat *f); + XYZtoLAB = ^(CGFloat *f) { + if ((*f > pow((6.0/29.0), 3.0)) ) { + *f = pow(*f, 1.0/3.0); + } else { + *f = (1/3)*pow((29.0/6.0), 2.0) * *f + 4/29.0; + } + }; + + XYZtoLAB(&X); + XYZtoLAB(&Y); + XYZtoLAB(&Z); + + /* + Next we get our LAB values using the following equations and the results from the function above + http://upload.wikimedia.org/math/0/0/6/006164b74314e2fdcdc34ac9d0aa6fe4.png + */ + + CGFloat L = (116 * Y) - 16; + CGFloat A = 500 * (X - Y); + CGFloat B = 200 * (Y - Z); + + + return @[@(L), @(A), @(B), @(alpha)]; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h new file mode 100755 index 0000000..91fdc45 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h @@ -0,0 +1,21 @@ +// +// UIImage+ChameleonPrivate.h +// Chameleon +// +// Created by Vicc Alexander on 6/8/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import + +@interface UIImage (ChameleonPrivate) + +#pragma mark - Class Methods + ++ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize; + +#pragma mark - Instance Methods + +- (UIImage *)imageScaledToSize:(CGSize)newSize; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.m new file mode 100755 index 0000000..707c64a --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.m @@ -0,0 +1,74 @@ +// +// UIImage+ChameleonPrivate.m +// Chameleon +// +// Created by Vicc Alexander on 6/8/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "UIImage+ChameleonPrivate.h" + +@implementation UIImage (ChameleonPrivate) + +// Would not have been possible without - http://stackoverflow.com/a/1262893 ++ (UIColor *)colorFromImage:(UIImage *)image atPoint:(CGPoint)point { + + //Encapsulate our image + CGImageRef imageRef = image.CGImage; + NSUInteger width = CGImageGetWidth(imageRef); + NSUInteger height = CGImageGetHeight(imageRef); + + //Specify the colorspace we're in + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + //Extract the data we need + unsigned char *rawData = calloc(height * width * 4, sizeof(unsigned char)); + NSUInteger bytesPerPixel = 4; + NSUInteger bytesPerRow = bytesPerPixel * width; + NSUInteger bitsPerComponent = 8; + CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, + colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); + //Release colorspace + CGColorSpaceRelease(colorSpace); + + //Draw and release image + CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); + CGContextRelease(context); + + //rawData now contains the image data in RGBA8888 + NSInteger byteIndex = (bytesPerRow * point.y) + (point.x * bytesPerPixel); + + //Define our RGBA values + CGFloat red = (rawData[byteIndex] * 1.f) / 255.f; + CGFloat green = (rawData[byteIndex + 1] * 1.f) / 255.f; + CGFloat blue = (rawData[byteIndex + 2] * 1.f) / 255.f; + CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.f; + + //Free our rawData + free(rawData); + + //Return color + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; +} + ++ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { + + UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0); + [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} + +#pragma mark - Instance Methods + +- (UIImage *)imageScaledToSize:(CGSize)newSize { + + UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0); + [self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.h new file mode 100755 index 0000000..6bc8e22 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.h @@ -0,0 +1,15 @@ +// +// UILabel+Chameleon.h +// Chameleon +// +// Created by Vicc Alexander on 9/20/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import + +@interface UILabel (Chameleon) + +- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.m new file mode 100755 index 0000000..bb28af0 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.m @@ -0,0 +1,18 @@ +// +// UILabel+Chameleon.m +// Chameleon +// +// Created by Vicc Alexander on 9/20/15. +// Copyright © 2015 Vicc Alexander. All rights reserved. +// + +#import "UILabel+Chameleon.h" + +@implementation UILabel (Chameleon) + +- (void)setSubstituteFontName:(NSString *)name UI_APPEARANCE_SELECTOR { + + self.font = [UIFont fontWithName:name size:self.font.pointSize]; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.h new file mode 100755 index 0000000..6913d49 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.h @@ -0,0 +1,32 @@ +// +// UINavigationController+Chameleon.h +// ChameleonDemo +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import +#import "ChameleonEnums.h" + +@interface UINavigationController (Chameleon) + +/** + * Sets the status bar style for the specified @c UINavigationController and all its child controllers. + * + * @param statusBarStyle The style of the device's status bar. + * + * @note Chameleon introduces a new @c statusBarStyle called @c UIStatusBarStyleContrast. + * + * @since 2.0 + */ +- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle; + +/** + * Hides the hairline view at the bottom of a navigation bar. The default value is @c NO. + * + * @since 2.0.3 + */ +@property (nonatomic, assign) BOOL hidesNavigationBarHairline; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.m new file mode 100755 index 0000000..1599815 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.m @@ -0,0 +1,214 @@ +// +// UINavigationController+Chameleon.m +// ChameleonDemo +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "UINavigationController+Chameleon.h" +#import + +#import "ChameleonConstants.h" +#import "ChameleonEnums.h" +#import "ChameleonMacros.h" + +#import "NSArray+Chameleon.h" +#import "UIColor+Chameleon.h" +#import "UIViewController+Chameleon.h" + +@interface UINavigationController () + +@property (readwrite) BOOL shouldContrast; +@property (readwrite) BOOL shouldUseLightContent; + +@end + +@implementation UINavigationController (Chameleon) + +@dynamic hidesNavigationBarHairline; + +#pragma mark - Swizzling + ++ (void)load { + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + Class class = [self class]; + + SEL originalSelector = @selector(viewDidLoad); + SEL swizzledSelector = @selector(chameleon_viewDidLoad); + + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + BOOL didAddMethod = + class_addMethod(class, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(class, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + }); +} + +- (void)chameleon_viewDidLoad { + + [self chameleon_viewDidLoad]; + + UIView *hairlineImageView = [self findHairlineImageViewUnder:self.navigationBar]; + + if (hairlineImageView) { + + if (self.hidesNavigationBarHairline) { + hairlineImageView.hidden = YES; + + } else { + hairlineImageView.hidden = NO; + } + } +} + +- (UIImageView *)findHairlineImageViewUnder:(UIView *)view { + + if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) { + return (UIImageView *)view; + } + + for (UIView *subview in view.subviews) { + UIImageView *imageView = [self findHairlineImageViewUnder:subview]; + if (imageView) { + return imageView; + } + } + + return nil; +} + +#pragma mark - Runtime + +- (void)setShouldContrast:(BOOL)contrast { + + NSNumber *number = [NSNumber numberWithBool:contrast]; + objc_setAssociatedObject(self, @selector(shouldContrast), number, OBJC_ASSOCIATION_RETAIN); +} + +- (BOOL)shouldContrast { + + NSNumber *number = objc_getAssociatedObject(self, @selector(shouldContrast)); + return [number boolValue]; +} + +- (void)setShouldUseLightContent:(BOOL)shouldUseLightContent { + + NSNumber *number = [NSNumber numberWithBool:shouldUseLightContent]; + objc_setAssociatedObject(self, @selector(shouldUseLightContent), number, OBJC_ASSOCIATION_RETAIN); +} + +- (BOOL)shouldUseLightContent { + + NSNumber *number = objc_getAssociatedObject(self, @selector(shouldUseLightContent)); + return [number boolValue]; +} + +- (void)setHidesNavigationBarHairline:(BOOL)hidesNavigationBarHairline { + + NSNumber *number = [NSNumber numberWithBool:hidesNavigationBarHairline]; + objc_setAssociatedObject(self, @selector(hidesNavigationBarHairline), number, OBJC_ASSOCIATION_RETAIN); + + //Find Hairline Image + UIView *hairlineImageView = [self findHairlineImageViewUnder:self.navigationBar]; + + //Check if it exists + if (hairlineImageView) { + + //Check if we should hide it or not + if (hidesNavigationBarHairline) { + hairlineImageView.hidden = YES; + + } else { + hairlineImageView.hidden = NO; + } + } +} + +- (BOOL)hidesNavigationBarHairline { + + NSNumber *number = objc_getAssociatedObject(self, @selector(hidesNavigationBarHairline)); + return [number boolValue]; +} + + +#pragma mark - Public Methods + +- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle { + + if (statusBarStyle == UIStatusBarStyleContrast) { + + self.shouldContrast = YES; + + } else { + + if (statusBarStyle == UIStatusBarStyleLightContent) { + + self.shouldUseLightContent = YES; + + } else { + + self.shouldUseLightContent = NO; + } + + } +} + +#pragma mark - Private Methods + +- (UIStatusBarStyle)preferredStatusBarStyle { + + [super preferredStatusBarStyle]; + + if (self.shouldContrast) { + + return [self contrastingStatusBarStyleForColor:self.navigationBar.barTintColor]; + + } else { + + if (self.shouldUseLightContent) { + + return UIStatusBarStyleLightContent; + + } else { + + return UIStatusBarStyleDefault; + } + } +} + +- (UIStatusBarStyle)contrastingStatusBarStyleForColor:(UIColor *)backgroundColor { + + //Calculate Luminance + CGFloat luminance; + CGFloat red, green, blue; + + //Check for clear or uncalculatable color and assume white + if (![backgroundColor getRed:&red green:&green blue:&blue alpha:nil]) { + return UIStatusBarStyleDefault; + } + + //Relative luminance in colorimetric spaces - http://en.wikipedia.org/wiki/Luminance_(relative) + red *= 0.2126f; green *= 0.7152f; blue *= 0.0722f; + luminance = red + green + blue; + + return (luminance > 0.6f) ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.h new file mode 100755 index 0000000..cc3b6ce --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.h @@ -0,0 +1,16 @@ +// +// UIView+ChameleonPrivate.h +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import + +@interface UIView (ChameleonPrivate) + +- (BOOL)isTopViewInWindow; +- (UIView *)findTopMostViewForPoint:(CGPoint)point; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.m new file mode 100755 index 0000000..bc5203d --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.m @@ -0,0 +1,46 @@ +// +// UIView+ChameleonPrivate.m +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "UIView+ChameleonPrivate.h" + +@implementation UIView (ChameleonPrivate) + +- (BOOL)isTopViewInWindow { + + if (!self.window) { + return NO; + } + + CGPoint centerPointInSelf = CGPointMake(CGRectGetMidX(self.bounds), + CGRectGetMidY(self.bounds)); + + CGPoint centerPointOfSelfInWindow = [self convertPoint:centerPointInSelf + toView:self.window]; + + UIView *view = [self.window findTopMostViewForPoint:centerPointOfSelfInWindow]; + BOOL isTopMost = view == self || [view isDescendantOfView:self]; + + return isTopMost; +} + +- (UIView *)findTopMostViewForPoint:(CGPoint)point { + + for (int i = (int)self.subviews.count - 1; i >= 0; i--) { + + UIView *subview = self.subviews[i]; + + if (!subview.hidden && CGRectContainsPoint(subview.frame, point) && subview.alpha > 0.01) { + CGPoint pointConverted = [self convertPoint:point toView:subview]; + return [subview findTopMostViewForPoint:pointConverted]; + } + } + + return self; +} + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.h b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.h new file mode 100755 index 0000000..f84b945 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.h @@ -0,0 +1,64 @@ +// +// UIViewController+Chameleon.h +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import +#import "ChameleonEnums.h" + +@interface UIViewController (Chameleon) + +/** + * Sets the color theme for the specified view controller using a primary color and the specified content style. + * + * @param primaryColor The primary color. + * @param contentStyle The contentStyle. + * + * @since 2.0 + */ +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle; + +/** + * Sets the color theme for the specified view controller using a primary color, secondary color, and the specified content style. + * + * @param primaryColor The primary color. + * @param secondaryColor The secondary color. + * @param contentStyle The contentStyle. + * + * @since 2.0 + */ +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + andContentStyle:(UIContentStyle)contentStyle; + +/** + * Sets the color theme for the specified view controller using a primary color, secondary color, font name, and the specified content style. + * + * @param primaryColor The primary color. + * @param secondaryColor The secondary color. + * @param fontName The main font to use for all text-based elements. + * @param contentStyle The contentStyle. + * + * @since 2.0 + */ +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + usingFontName:(NSString *)fontName + andContentStyle:(UIContentStyle)contentStyle; + +/** + * Sets the status bar style for the specified @c UIViewController and all its child controllers. + * + * @param statusBarStyle The style of the device's status bar. + * + * @note Chameleon introduces a new @c statusBarStyle called @c UIStatusBarStyleContrast. + * + * @since 2.0 + */ +- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle; + +@end diff --git a/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.m b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.m new file mode 100755 index 0000000..b6b4691 --- /dev/null +++ b/Pods/ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.m @@ -0,0 +1,900 @@ +// +// UIViewController+Chameleon.m +// Chameleon +// +// Created by Vicc Alexander on 6/4/15. +// Copyright (c) 2015 Vicc Alexander. All rights reserved. +// + +#import "UIViewController+Chameleon.h" +#import + +#import "ChameleonConstants.h" +#import "ChameleonEnums.h" +#import "ChameleonMacros.h" + +#import "NSArray+Chameleon.h" +#import "UIColor+Chameleon.h" +#import "UIViewController+Chameleon.h" +#import "UIView+ChameleonPrivate.h" +#import "UILabel+Chameleon.h" +#import "UIButton+Chameleon.h" +#import "UIAppearance+Swift.h" + +@interface UIViewController () + +@property (readwrite) BOOL shouldContrast; +@property (readwrite) BOOL shouldUseLightContent; + +@end + +@implementation UIViewController (Chameleon) + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#pragma mark - Runtime + +- (void)setShouldContrast:(BOOL)contrast { + + NSNumber *number = [NSNumber numberWithBool:contrast]; + objc_setAssociatedObject(self, @selector(shouldContrast), number, OBJC_ASSOCIATION_RETAIN); +} + +- (BOOL)shouldContrast { + + NSNumber *number = objc_getAssociatedObject(self, @selector(shouldContrast)); + return [number boolValue]; +} + +- (void)setShouldUseLightContent:(BOOL)shouldUseLightContent { + + NSNumber *number = [NSNumber numberWithBool:shouldUseLightContent]; + objc_setAssociatedObject(self, @selector(shouldUseLightContent), number, OBJC_ASSOCIATION_RETAIN); +} + +- (BOOL)shouldUseLightContent { + + NSNumber *number = objc_getAssociatedObject(self, @selector(shouldUseLightContent)); + return [number boolValue]; +} + +#pragma mark - Swizzling + ++ (void)load { + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + Class class = [self class]; + + SEL originalSelector = @selector(preferredStatusBarStyle); + SEL swizzledSelector = @selector(chameleon_preferredStatusBarStyle); + + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + BOOL didAddMethod = + class_addMethod(class, + originalSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(class, + swizzledSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + }); +} + +#pragma mark - Methods + + +- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle { + + if (statusBarStyle == UIStatusBarStyleContrast) { + + [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate) withObject:nil afterDelay:0.01]; + self.shouldContrast = YES; + + } else { + + if (statusBarStyle == UIStatusBarStyleLightContent) { + + [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate) withObject:nil afterDelay:0.01]; + self.shouldUseLightContent = YES; + + } else { + + [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate) withObject:nil afterDelay:0.01]; + self.shouldUseLightContent = NO; + } + } + + [self preferredStatusBarStyle]; +} + +- (UIStatusBarStyle)chameleon_preferredStatusBarStyle { + + [self chameleon_preferredStatusBarStyle]; + + if (self.shouldContrast) { + + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + UIView *topView = [self.view findTopMostViewForPoint:CGPointMake(CGRectGetMidX(statusBarFrame), 2)]; + + return [self contrastingStatusBarStyleForColor:topView.backgroundColor]; + + } else { + + if (self.shouldUseLightContent) { + return UIStatusBarStyleLightContent; + + } else { + return [self chameleon_preferredStatusBarStyle]; + } + } +} + +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor contentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeNavigationBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; +} + +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + andContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor contentStyle:contentStyle]; + [[self class] customizeButtonWithPrimaryColor:primaryColor secondaryColor:secondaryColor withContentStyle:contentStyle]; + [[self class] customizeNavigationBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; +} + +- (void)setThemeUsingPrimaryColor:(UIColor *)primaryColor + withSecondaryColor:(UIColor *)secondaryColor + usingFontName:(NSString *)fontName + andContentStyle:(UIContentStyle)contentStyle { + + if (contentStyle == UIContentStyleContrast) { + + if ([ContrastColor(primaryColor, YES) isEqual:FlatWhite]) { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + } else { + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + } else if (contentStyle == UIContentStyleLight) { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; + + } else { + + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; + } + + [[UILabel appearance] setSubstituteFontName:fontName]; + [[UIButton appearance] setSubstituteFontName:fontName]; + + [[self class] customizeButtonWithPrimaryColor:primaryColor secondaryColor:secondaryColor withContentStyle:contentStyle]; + [[self class] customizeBarButtonItemWithPrimaryColor:primaryColor fontName:fontName fontSize:18 contentStyle:contentStyle]; + [[self class] customizeNavigationBarWithBarColor:primaryColor textColor:ContrastColor(primaryColor, YES) fontName:fontName fontSize:20 buttonColor:ContrastColor(primaryColor, YES)]; + [[self class] customizePageControlWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeProgressViewWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeSearchBarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSegmentedControlWithPrimaryColor:primaryColor withFontName:fontName withFontSize:14 withContentStyle:contentStyle]; + [[self class] customizeSliderWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeStepperWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeSwitchWithPrimaryColor:primaryColor andSecondaryColor:secondaryColor]; + [[self class] customizeTabBarWithBarTintColor:FlatWhite andTintColor:primaryColor]; + [[self class] customizeToolbarWithPrimaryColor:primaryColor withContentStyle:contentStyle]; + [[self class] customizeImagePickerControllerWithPrimaryColor:primaryColor withContentStyle:contentStyle]; +} + +#pragma mark - UIBarButtonItem + ++ (void)customizeBarButtonItemWithPrimaryColor:(UIColor *)primaryColor + contentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIBarButtonItem appearance] setTintColor:primaryColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; +} + ++ (void)customizeBarButtonItemWithPrimaryColor:(UIColor *)primaryColor + fontName:(NSString *)fontName + fontSize:(float)fontSize + contentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIBarButtonItem appearance] setTintColor:primaryColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + + + if ([UIFont fontWithName:fontName size:fontSize]) { + [[UIBarButtonItem appearance] setTitleTextAttributes:@{ NSForegroundColorAttributeName:contentColor, NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize]} forState:UIControlStateNormal]; + } +} + +#pragma mark - UIButton + ++ (void)customizeButtonWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIButton appearance] setTintColor:contentColor]; + [[UIButton appearance] setBackgroundColor:primaryColor]; + + + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setTintColor:primaryColor]; + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearance] setTitleShadowColor:ClearColor forState:UIControlStateNormal]; + +} + ++ (void)customizeButtonWithPrimaryColor:(UIColor *)primaryColor + secondaryColor:(UIColor *)secondaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + UIColor *secondaryContentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + secondaryContentColor = ContrastColor(secondaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + secondaryContentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + secondaryContentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + secondaryContentColor = ContrastColor(secondaryColor, NO); + break; + } + } + + [[UIButton appearance] setTintColor:secondaryContentColor]; + [[UIButton appearance] setBackgroundColor:secondaryColor]; + + + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UINavigationBar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedIn:[UIToolbar class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setTintColor:primaryColor]; + [[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundColor:ClearColor]; + + [[UIButton appearance] setTitleShadowColor:ClearColor forState:UIControlStateNormal]; + +} + +#pragma mark - UIImagePickerController + ++ (void)customizeImagePickerControllerWithPrimaryColor:(UIColor *)primaryColor withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + //Workaround for Swift http://stackoverflow.com/a/28765193 + [[UIButton appearanceWhenContainedWithin:@[[UIView class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UIView class],[UIImagePickerController class]]] setTintColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UINavigationBar class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + [[UIButton appearanceWhenContainedWithin:@[[UINavigationBar class],[UIImagePickerController class]]] setTintColor:contentColor]; + [[UIButton appearanceWhenContainedWithin:@[[UITableViewCell class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UIView class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UIView class],[UIImagePickerController class]]] setTintColor:contentColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class],[UIImagePickerController class]]] setTintColor:contentColor]; + //[[UIButton appearanceWhenContainedInInstancesOfClasses:@[[UITableViewCell class],[UIImagePickerController class]]] setBackgroundColor:ClearColor]; +} + +#pragma mark - UILabel + ++ (void)customizeLabelWithPrimaryColor:(UIColor *)primaryColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UILabel appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setTextColor:contentColor]; + [[UILabel appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setTextColor:contentColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + + if (font) { + [[UILabel appearanceWhenContainedIn:[self class], nil] setFont:[UIFont fontWithName:fontName size:fontSize]]; + [[UILabel appearanceWhenContainedIn:[self class], [UITextField class], nil] setFont:[UIFont fontWithName:fontName size:14]]; + [[UILabel appearanceWhenContainedIn:[self class], [UIButton class], nil] setFont:[UIFont fontWithName:fontName size:18]]; + } +} + +#pragma mark - UINavigationBar + ++ (void)customizeNavigationBarWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:primaryColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTintColor:contentColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTitleTextAttributes:@{NSForegroundColorAttributeName:contentColor}]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setShadowImage:[UIImage new]]; +// [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; +} + ++ (void)customizeNavigationBarWithBarColor:(UIColor *)barColor + textColor:(UIColor *)textColor + buttonColor:(UIColor *)buttonColor { + + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:barColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTintColor:buttonColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTitleTextAttributes:@{NSForegroundColorAttributeName:textColor}]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setShadowImage:[UIImage new]]; +// [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; +} + ++ (void)customizeNavigationBarWithBarColor:(UIColor *)barColor + textColor:(UIColor *)textColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize + buttonColor:(UIColor *)buttonColor { + + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:barColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTintColor:buttonColor]; + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setShadowImage:[UIImage new]]; +// [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; + + if ([UIFont fontWithName:fontName size:fontSize]) { + [[UINavigationBar appearanceWhenContainedIn:[self class], nil] setTitleTextAttributes:@{ NSForegroundColorAttributeName:textColor, NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize] }]; + } +} + +#pragma mark - UIPageControl + ++ (void)customizePageControlWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIPageControl appearanceWhenContainedIn:[self class], nil] setCurrentPageIndicatorTintColor:primaryColor]; + [[UIPageControl appearanceWhenContainedIn:[self class], nil] setPageIndicatorTintColor:[primaryColor colorWithAlphaComponent:0.4]]; + [[UIPageControl appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setCurrentPageIndicatorTintColor:contentColor]; + [[UIPageControl appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setPageIndicatorTintColor:[contentColor colorWithAlphaComponent:0.4]]; + [[UIPageControl appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setCurrentPageIndicatorTintColor:contentColor]; + [[UIPageControl appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setPageIndicatorTintColor:[contentColor colorWithAlphaComponent:0.4]]; +} + +#pragma mark - UIProgressView + ++ (void)customizeProgressViewWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIProgressView appearanceWhenContainedIn:[self class], nil] setProgressTintColor:primaryColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setProgressTintColor:contentColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setProgressTintColor:contentColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], nil] setTrackTintColor:[UIColor lightGrayColor]]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + ++ (void)customizeProgressViewWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UIProgressView appearanceWhenContainedIn:[self class], nil] setProgressTintColor:secondaryColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setProgressTintColor:secondaryColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setProgressTintColor:secondaryColor]; + [[UIProgressView appearanceWhenContainedIn:[self class], nil] setTrackTintColor:[UIColor lightGrayColor]]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UIProgressView appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + +#pragma mark - UISearchBar + ++ (void)customizeSearchBarWithPrimaryColor:(UIColor *)primaryColor withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISearchBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:primaryColor]; + [[UISearchBar appearanceWhenContainedIn:[self class], nil] setBackgroundColor:primaryColor]; + [[UISearchBar appearanceWhenContainedIn:[self class], nil] setTintColor:contentColor]; + [[UISearchBar appearanceWhenContainedIn:[self class], nil] setBackgroundImage:[UIImage new] forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault]; +} + +#pragma mark - UISegmentedControl + ++ (void)customizeSegmentedControlWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISegmentedControl appearanceWhenContainedIn:[self class], nil] setTintColor:primaryColor]; + [[UISegmentedControl appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] + setTintColor:contentColor]; + [[UISegmentedControl appearanceWhenContainedIn:[self class], [UIToolbar class], nil] + setTintColor:contentColor]; +} + ++ (void)customizeSegmentedControlWithPrimaryColor:(UIColor *)primaryColor + withFontName:(NSString *)fontName + withFontSize:(CGFloat)fontSize + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISegmentedControl appearanceWhenContainedIn:[self class], nil] setTintColor:primaryColor]; + [[UISegmentedControl appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] + setTintColor:contentColor]; + [[UISegmentedControl appearanceWhenContainedIn:[self class], [UIToolbar class], nil] + setTintColor:contentColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + if (font) { + [[UISegmentedControl appearanceWhenContainedIn:[self class], nil] setTitleTextAttributes:@{NSFontAttributeName:font} + forState:UIControlStateNormal]; + } +} + +#pragma mark - UISlider + ++ (void)customizeSliderWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UISlider appearanceWhenContainedIn:[self class], nil] setMinimumTrackTintColor:primaryColor]; + [[UISlider appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setMinimumTrackTintColor:contentColor]; + [[UISlider appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setMinimumTrackTintColor:contentColor]; + [[UISlider appearanceWhenContainedIn:[self class], nil] setMaximumTrackTintColor:[UIColor lightGrayColor]]; + [[UISlider appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISlider appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + ++ (void)customizeSliderWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UISlider appearanceWhenContainedIn:[self class], nil] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setMinimumTrackTintColor:secondaryColor]; + [[UISlider appearanceWhenContainedIn:[self class], nil] setMaximumTrackTintColor:[UIColor lightGrayColor]]; + [[UISlider appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISlider appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setMaximumTrackTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + +#pragma mark - UIStepper + ++ (void)customizeStepperWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIStepper appearanceWhenContainedIn:[self class], nil] setTintColor:primaryColor]; + [[UIStepper appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] + setTintColor:contentColor]; + [[UIStepper appearanceWhenContainedIn:[self class], [UIToolbar class], nil] + setTintColor:contentColor]; +} + +#pragma mark - UISwitch + ++ (void)customizeSwitchWithPrimaryColor:(UIColor *)primaryColor { + + [[UISwitch appearanceWhenContainedIn:[self class], nil] setOnTintColor:primaryColor]; + [[UISwitch appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setOnTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; + [[UISwitch appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setOnTintColor:[[primaryColor darkenByPercentage:0.25] flatten]]; +} + ++ (void)customizeSwitchWithPrimaryColor:(UIColor *)primaryColor + andSecondaryColor:(UIColor *)secondaryColor { + + [[UISwitch appearanceWhenContainedIn:[self class], nil] setOnTintColor:secondaryColor]; + [[UISwitch appearanceWhenContainedIn:[self class], [UINavigationBar class], nil] setOnTintColor:secondaryColor]; + [[UISwitch appearanceWhenContainedIn:[self class], [UIToolbar class], nil] setOnTintColor:secondaryColor]; +} + +#pragma mark - UITabBar + ++ (void)customizeTabBarWithBarTintColor:(UIColor *)barTintColor + andTintColor:(UIColor *)tintColor { + + [[UITabBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:barTintColor]; + [[UITabBar appearanceWhenContainedIn:[self class], nil] setTintColor:tintColor]; +} + ++ (void)customizeTabBarWithBarTintColor:(UIColor *)barTintColor + tintColor:(UIColor *)tintColor + fontName:(NSString *)fontName + fontSize:(CGFloat)fontSize { + + [[UITabBar appearanceWhenContainedIn:[self class], nil] setBarTintColor:barTintColor]; + [[UITabBar appearanceWhenContainedIn:[self class], nil] setTintColor:tintColor]; + + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + if (font) { + [[UITabBarItem appearanceWhenContainedIn:[self class], nil] setTitleTextAttributes:@{NSFontAttributeName:font} + forState:UIControlStateNormal]; + } +} + +#pragma mark - UIToolbar + ++ (void)customizeToolbarWithPrimaryColor:(UIColor *)primaryColor + withContentStyle:(UIContentStyle)contentStyle { + + UIColor *contentColor; + switch (contentStyle) { + case UIContentStyleContrast: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + case UIContentStyleLight: { + contentColor = [UIColor whiteColor]; + break; + } + case UIContentStyleDark: { + contentColor = FlatBlackDark; + break; + } + default: { + contentColor = ContrastColor(primaryColor, NO); + break; + } + } + + [[UIToolbar appearanceWhenContainedIn:[self class], nil] setTintColor:contentColor]; + [[UIToolbar appearanceWhenContainedIn:[self class], nil] setBarTintColor:primaryColor]; + [[UIToolbar appearanceWhenContainedIn:[self class], nil] setClipsToBounds:YES]; +} + + +#pragma mark - Private Methods + +- (UIStatusBarStyle)contrastingStatusBarStyleForColor:(UIColor *)backgroundColor { + + //Calculate Luminance + CGFloat luminance; + CGFloat red, green, blue; + + //Check for clear or uncalculatable color and assume white + if (![backgroundColor getRed:&red green:&green blue:&blue alpha:nil]) { + return UIStatusBarStyleDefault; + } + + //Relative luminance in colorimetric spaces - http://en.wikipedia.org/wiki/Luminance_(relative) + red *= 0.2126f; green *= 0.7152f; blue *= 0.0722f; + luminance = red + green + blue; + + return (luminance > 0.6f) ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent; +} + +#pragma GCC diagnostic pop + +@end diff --git a/Pods/ChameleonFramework/README.md b/Pods/ChameleonFramework/README.md new file mode 100644 index 0000000..2dbbdf0 --- /dev/null +++ b/Pods/ChameleonFramework/README.md @@ -0,0 +1,768 @@ +

+ Chameleon by Vicc Alexander +

+ +

+ Downloads + Platform: iOS 8+ + Language: Swift 2 + Carthage compatible + Cocoapods compatible + License: MIT

+ Donate +

+ +## Introduction + +**Chameleon** is a lightweight, yet powerful, color framework for iOS (Objective-C & Swift). It is built on the idea that software applications should function effortlessly while simultaneously maintaining their beautiful interfaces. + +With Chameleon, you can easily stop tinkering with RGB values, wasting hours figuring out the right color combinations to use in your app, and worrying about whether your text will be readable on the various background colors of your app. + +### Features + +

+ Features +

+ +### App Showcase ![New](http://i.imgur.com/BX3b9ES.png) + +###### In an upcoming update we'll begin showcasing some of the best apps and companies making use of Chameleon. If you'd like to see your app featured in this section, make sure to add it [here](https://airtable.com/shrr1WK6dLQBZfXV0). + +## Table of Contents +[● Product Features](https://github.com/ViccAlexander/Chameleon#-product-features) +[● Requirements](https://github.com/ViccAlexander/Chameleon#%EF%B8%8F-requirements) +[● License](https://github.com/ViccAlexander/Chameleon#-license) +[● Contributions](https://github.com/ViccAlexander/Chameleon#-contributions) +[● Documentation](https://github.com/ViccAlexander/Chameleon#-documentation) +[● Storyboard Add-On](https://github.com/ViccAlexander/Chameleon#storyboard-add-on) +[● Author](https://github.com/ViccAlexander/Chameleon#-author) +[● Special Thanks](https://github.com/ViccAlexander/Chameleon#-special-thanks) +[● To Do List](https://github.com/ViccAlexander/Chameleon#-to-do-list) +[● Change Log](https://github.com/ViccAlexander/Chameleon#-change-log) + + + +## 🌟 Product Features + +### 100% Flat & Gorgeous + +Chameleon features over 24 hand-picked colors that come in both light and dark shades. + +

+ Swatches +

+ +### Flat Color Schemes + +Chameleon equips you with 3 different classes of flat color schemes that can be generated from a flat or non-flat color. *In the examples below, the white stars indicate the color used to generate the schemes.* + +###### Analogous Flat Color Scheme + +

+ Analogous Scheme +

+ +###### Complementary Flat Color Scheme +

+ Complementary Scheme +

+ +###### Triadic Flat Color Scheme +

+ Triadic Scheme +

+ +### Contrasting Text +With a plethora of color choices available for text, it's difficult to choose one that all users will appreciate and be able to read. Whether you're in doubt of your text and tint color choices, or afraid to let users customize their profile colors because it may disturb the legibility or usability of the app, you no longer have to worry. With Chameleon, you can ensure that all text stands out independent of the background color. + +Oh... Chameleon works with the status bar as well. : ) + +

+ Status Bar +

+ +### Themes ![Beta](http://i.imgur.com/JyYiUJq.png) + +Chameleon now allows you easily theme your app with as little as **one line of code**. You can set a theme for all your views, and for specific views as well. + +

+ Themes +

+ +### Colors From Images + +Chameleon allows you to seamlessly extract non-flat or flat color schemes from images without hassle. You can also generate the average color from an image with ease. You can now mold the UI colors of a profile, or product based on an image! + +

+ Colors from images +

+ +### Gradient Colors +With iOS 7 & 8, Apple mainstreamed flat colors. Now, with the release of iOS 9, Chameleon strives to elevate the game once more. Say hello to gradient colors. Using one line of code, you can easily set any object's color properties to a gradient (background colors, text colors, tint colors, etc). Other features, like Chameleon's contrasting feature, can also be applied to create a seamless product. Experimentation is encouraged, and gutsiness is applauded! + +

+ Gradients +

+ +![](http://i.imgur.com/2jN72eh.png) + +### Xcode Quick Help Documentation + +Chameleon's documentation, while written as clearly and concisely as possible may still render some slightly confused. But don't fret! Staying true to our vision of simplifying the entire color process, we added Xcode Quick Help's Documentation Support! Simply highlight a Chameleon method or tap it with three fingers to find out more about what it is and what it does! + +

+ Xcode Quick Help Documentation +

+ +### Storyboard Palette + +If you're like me and love to use storyboard, Chameleon's got you covered. We've provided you with a quick and easy way to access Chameleon colors right from Storyboard, and any other app that uses the color picker (i.e. TextEdit). + +

+ Chameleon Palette +

+ +## ⚠️ Requirements + +* Objective-C or Swift +* Requires a minimum of iOS 7.0 for Objective-C (No active development for anything earlier, but may work with 6.0) and a minimum of iOS 8.0 for Swift. +* Requires Xcode 6.3 for use in any iOS Project + +## 🔑 License +Chameleon is released and distributed under the terms and conditions of the [MIT license](https://github.com/ViccAlexander/Chameleon/blob/master/LICENSE.md). + +## 👥 Contributions +If you run into problems, please open up an issue. We also actively welcome pull requests. By contributing to Chameleon you agree that your contributions will be licensed under its MIT license. + +If you use Chameleon in your app we would love to hear about it! Drop Vicc a line on [twitter](http://twitter.com/viccsmind). + +## 📗 Documentation +All methods, properties, and types available in Chameleon are documented below. + +#####Documentation Table of Contents +[● Installation](https://github.com/ViccAlexander/Chameleon#installation) +[● Storyboard-Add On](https://github.com/ViccAlexander/Chameleon#storyboard-add-on-) +[● Usage](https://github.com/ViccAlexander/Chameleon#usage) +[● UIColor Methods](https://github.com/ViccAlexander/Chameleon#uicolor-methods) +[● Colors From Images](https://github.com/ViccAlexander/Chameleon#colors-from-images--1) +[● UIStatusBarStyle Methods](https://github.com/ViccAlexander/Chameleon#uistatusbarstyle-methods) +[● Color Scheme Methods](https://github.com/ViccAlexander/Chameleon#color-schemes-methods) +[● Theme Methods](https://github.com/ViccAlexander/Chameleon#theme-methods-) + +###Installation +####CocoaPods Installation +Chameleon is now available on [CocoaPods](http://cocoapods.org). Simply add the following to your project Podfile, and you'll be good to go. + +######Objective-C +```ruby +use_frameworks! + +pod 'ChameleonFramework' +``` +######Swift +```ruby +use_frameworks! + +pod 'ChameleonFramework/Swift' +``` + +###### **Note:** Swift support for Chameleon 2.0 is almost complete. + +======= +####Carthage Installation +Add this to your Cartfile: +```ruby +github "ViccAlexander/Chameleon" +``` + +======= +####Manual Installation +If you rather install this framework manually, just drag and drop the Chameleon folder into your project, and make sure you check the following boxes. Note: Don't forget to manually import the *QuartzCore* & *CoreGraphics* framework if you plan on using gradient colors! + +

+ Manual Installation +

+ +If you're working with Swift and are manually installing Chameleon, there's an additional step. Make sure to download and drag the following file, [ChameleonShorthand.swift](https://github.com/ViccAlexander/Chameleon/blob/master/Pod/Classes/Swift/ChameleonShorthand.swift), into your project, and you'll be good to go. + +####Storyboard Add-On +Using Chameleon's awesome palette in Storyboard is easy! Simply download and install [Chameleon Palette](https://github.com/ViccAlexander/Chameleon/blob/master/Extras/Chameleon.dmg?raw=true). + +Once installed, make sure to restart XCode. You'll find all of Chameleon's colors in the Palette Color Picker whenever they're needed! :) + +

+ Chameleon Palette +

+ +

+ Chameleon Palette +

+ +###Usage +To use the myriad of features in Chameleon, include the following import: + +###### If you installed Chameleon using cocoapods: + +######Objective-C + +``` objective-c +#import +``` + +######Swift: +``` swift +import ChameleonFramework +``` + +###### If you installed Chameleon manually: +``` objective-c +#import "Chameleon.h" +``` +###UIColor Methods +[● Flat Colors](https://github.com/ViccAlexander/Chameleon#flat-colors) +[● Random Colors](https://github.com/ViccAlexander/Chameleon#random-colors) +[● Complementary Colors](https://github.com/ViccAlexander/Chameleon#complementary-colors) +[● Contrasting Colors](https://github.com/ViccAlexander/Chameleon#contrasting-colors) +[● Flattening Non-Flat Colors](https://github.com/ViccAlexander/Chameleon#flattening-non-flat-colors) +[● Gradient Colors](https://github.com/ViccAlexander/Chameleon#gradient-colors-1) +[● Hex Colors](https://github.com/ViccAlexander/Chameleon#hex-colors-) +[● Lighter & Darker Colors](https://github.com/ViccAlexander/Chameleon#lighter-and-darker-colors-) + +####Flat Colors +Using a flat color is as easy as adding any other color in your app (if not easier). For example, to set a view's background property to a flat color with a dark shade, you simply have to do the following: + +#####Normal Convention: + +######Objective-C +``` objective-c +self.view.backgroundColor = [UIColor flatGreenColorDark]; +``` +######Swift +``` swift +view.backgroundColor = UIColor.flatGreenColorDark() +``` + +#####Chameleon Shorthand: + +######Objective-C +``` objective-c +self.view.backgroundColor = FlatGreenDark; +``` +######Swift +``` swift +view.backgroundColor = FlatGreenDark() +``` + +Setting the color for a light shade is the same, except without adding the *Dark* suffix. (By default, all colors without a *Dark* suffix are light shades). For example: + +#####Normal Convention: +######Objective-C +``` objective-c +self.view.backgroundColor = [UIColor flatGreenColor]; +``` +######Swift +``` swift +view.backgroundColor = UIColor.flatGreenColor() +``` + +#####Chameleon Shorthand: + +######Objective-C +``` objective-c +self.view.backgroundColor = FlatGreen; +``` +######Swift +``` swift +view.backgroundColor = FlatGreen() +``` + +####Random Colors +There are four ways to generate a random flat color. If you have no preference as to whether you want a light shade or a dark shade, you can do the following: + +#####Normal Convention: +######Objective-C +``` objective-c +self.view.backgroundColor = [UIColor randomFlatColor]; +``` +######Swift +``` swift +view.backgroundColor = UIColor.randomFlatColor() +``` + +#####Chameleon Shorthand: +###### Objective-C +``` objective-c +self.view.backgroundColor = RandomFlatColor; +``` + +######Swift +``` swift +view.backgroundColor = RandomFlatColor() +``` + +Otherwise, you can perform the following method call to specify whether it should return either a light or dark shade: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithRandomFlatColorOfShadeStyle:UIShadeStyleLight]; +``` + +######Swift +``` swift +UIColor(randomFlatColorOfShadeStyle:.Light) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +RandomFlatColorWithShade(UIShadeStyleLight); +``` +######Swift +``` swift +RandomFlatColorWithShade(.Light) +``` + +**UIShadeStyles:** +- `UIShadeStyleLight` (`UIShadeStyle.Light` in Swift) +- `UIShadeStyleDark` (`UIShadeStyle.Dark` in Swift) + +##### Choosing A Random Color From a List of Colors ![New](http://i.imgur.com/BX3b9ES.png) + +If you need to be a bit more selective and only display a random color from a set list of colors, you can use the following method: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithRandomColorInArray:@[FlatWhite, FlatRed, FlatBlue]]; +``` + +######Swift +``` swift +TBA +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +RandomFlatColorInArray(@[FlatWhite, FlatRed, FlatBlue]) +``` +######Swift +``` swift +TBA +``` + +##### Choosing A Random Flat Color But Excluding A Few ![New](http://i.imgur.com/BX3b9ES.png) + +Last but certainly not least, you can also choose form the list of random colors and exclude the ones you don't want. For example say you want to randomly select a flat color for a user's profile, but don't want to use any blacks, grays, or whites. You can simply do: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithRandomFlatColorExcludingColorsInArray:@[FlatBlack, FlatBlackDark, FlatGray, FlatGrayDark, FlatWhite, FlatWhiteDark]]; +``` + +######Swift +``` swift +TBA +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +RandomFlatColorExcluding(@[FlatBlack, FlatBlackDark, FlatGray, FlatGrayDark, FlatWhite, FlatWhiteDark]) +``` +######Swift +``` swift +TBA +``` + +####Complementary Colors +To generate a complementary color, perform the following method call, remembering to specify the color whose complement you want: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithComplementaryFlatColorOf:(UIColor *)someUIColor]; +``` + +######Swift +``` swift +UIColor(complementaryFlatColorOf:someUIColor) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +ComplementaryFlatColorOf(color); +``` + +######Swift +``` swift +ComplementaryFlatColorOf(color) +``` + +####Contrasting Colors +The contrasting color feature returns either a dark color a light color depending on what the Chameleon algorithm believes is a better choice. You can specify whether the dark or light colors are flat: *`([UIColor flatWhiteColor]` & `[UIColor flatBlackColorDark]`)* or non-flat *(`[UIColor whiteColor]` & `[UIColor blackColor]`).* + +If you're trying to set a `UILabel's textColor` property, make sure you provide the `UILabel's backgroundColor`. If your label has a clear `backgroundColor`, just provide the `backgroundColor` property of the object directly behind the `UILabel`. + +Here's an example: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithContrastingBlackOrWhiteColorOn:(UIColor *)backgroundColor isFlat:(BOOL)flat]; +``` + +######Swift +``` swift +UIColor(contrastingBlackOrWhiteColorOn:UIColor!, isFlat:Bool) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +ContrastColor(backgroundColor, isFlat); +``` + +######Swift +``` swift +ContrastColor(backgroundColor, isFlat) +``` + +####Flattening Non-Flat Colors +As mentioned previously, this feature is unique to Chameleon. While this feature is in its early stages of operation and can be improved, it is accurate in finding the nearest flat version of any color in the spectrum, and very simple to use: + +#####Normal Convention: +######Objective-C +``` objective-c +[(UIColor *)color flatten]; +``` + +######Swift +``` swift +UIColor.pinkColor().flatten() +``` + +#### Gradient Colors +Using a gradient to color an object usually requires a couple of lines of code plus many more lines to superimpose smart contrasting text. Thankfully, Chameleon takes care of that for you. We've introduced a new way to have multicolored objects, and that's with gradients! + +#####Gradient Styles +Chameleon provides three simple gradient styles. Gradients can be created from any number of colors you desire as long as at least two colors are provided. Don't forget that the contrasting text feature is also compatible with gradient colors! + +**UIGradientStyles:** +* `UIGradientStyleLeftToRight` (UIGradientStyle.LeftToRight in Swift) +* `UIGradientStyleTopToBottom` (UIGradientStyle.TopToBottom in Swift) +* `UIGradientStyleRadial` (UIGradientStyle.Radial in Swift) + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithGradientStyle:(UIGradientStyle)gradientStyle withFrame:(CGRect)frame andColors:(NSArray *)colors]; +``` + +######Swift +``` swift +UIColor(gradientStyle:UIGradientStyle, withFrame:CGRect, andColors:[UIColor]) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +GradientColor(gradientStyle, frame, colors); +``` + +######Swift +``` swift +GradientColor(gradientStyle, frame, colors) +``` + +**Objective-C Note**: If you use the Chameleon Shorthand, and use the `NSArray` literal ```@[]``` to set the array of colors, make sure you add parenthesis around it, or else you'll get an error. + +Note: `UIGradientStyleRadial` only uses a maximum of 2 colors at the moment. So if more colors are provided, they will not show. + +#### Hex Colors + +One of the most requested features, *hex colors*, is now available. You can simply provide a hex string with or without a *#* sign: + +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithHexString:(NSString *)string]; +``` + +######Swift +``` swift +UIColor(hexString:string) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +HexColor(hexString) +``` + +######Swift +``` swift +HexColor(hexString) +``` +#### Hex Values ![New](http://i.imgur.com/BX3b9ES.png) + +Retrieving the `hexValue` of a UIColor is just as easy. + +######Objective-C +``` objective-c +[FlatGreen hexValue]; //Returns @"2ecc71" +``` + +######Swift +``` swift +FlatGreen.hexValue //Returns @"2ecc71" +``` + +#### Lighter and Darker Colors + +Sometimes all you need is a color a shade lighter or a shade darker. Well for those rare, but crucial moments, Chameleon's got you covered. You can now lighten any color the following way: + +#####Normal Convention: +######Objective-C +``` objective-c +[color lightenByPercentage:(CGFloat)percentage]; +``` + +######Swift +``` swift +color.lightenByPercentage(percentage: CGFloat) +``` + +You can also generate a darker version of a color: + +#####Normal Convention: +######Objective-C +``` objective-c +[color darkenByPercentage:(CGFloat)percentage]; +``` + +######Swift +``` swift +color.darkenByPercentage(percentage: CGFloat) +``` + +### Colors From Images + +Chameleon now supports the extraction of colors from images. You can either generate both flat and non-flat color schemes from an image, or easily extract the average color. + +To generate a color scheme simply do the following: +#####Normal Convention: +######Objective-C +``` objective-c +[NSArray arrayOfColorsFromImage:(UIImage *)image withFlatScheme:(BOOL)flatScheme]; +``` + +######Swift (**Array extension missing**) +``` swift +NSArray(ofColorsFromImage: UIImage, withFlatScheme: Bool) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +ColorsFromImage(image, isFlatScheme) +``` + +######Swift +``` swift +ColorsFromImage(image, isFlatScheme) +``` + +To extract the average color from an image, you can also do: +#####Normal Convention: +######Objective-C +``` objective-c +[UIColor colorWithAverageColorFromImage:(UIImage *)image]; +``` + +######Swift +``` swift +UIColor(averageColorFromImage: UIImage) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +AverageColorFromImage(image) +``` + +######Swift +``` swift +AverageColorFromImage(image) +``` + +###UIStatusBarStyle Methods +####Contrasting UIStatusBarStyle +Many apps on the market, even the most popular ones, overlook this aspect of a beautiful app: the status bar style. Chameleon has done something no other framework has... it has created a new status bar style: `UIStatusBarStyleContrast`. Whether you have a `ViewController` embedded in a `NavigationController`, or not, you can do the following: + +#####Normal Convention: +######Objective-C +``` objective-c +[self setStatusBarStyle:UIStatusBarStyleContrast]; +``` + +######Swift +``` swift +self.setStatusBarStyle(UIStatusBarStyleContrast) +``` +######**Note**: Make sure that the key *View controller-based status bar appearance* in **Info.plist** is set to `YES`. + +###Color Schemes Methods +######**Note**: *Due to the limited number of flat colors currently available, color schemes may return results that reuse certain flat colors. Because of this redundancy, we have provided an option to return either a flat color scheme or a non-flat color scheme until more flat colors are added to the inventory.* + +The initial color can be either a non-flat color or flat color. Chameleon will return an `NSArray` of 5 `UIColors` in which the original color will be the third object of the scheme. This allows for Chameleon to designate the colors of the color scheme (2 colors counter-clockwise and 2 clockwise from the initial color), and thus, the chosen colors are aligned specifically in that order. + +####Analogous Color Scheme +An analogous color scheme uses three adjacent colors on the color wheel. According to Wikipedia, it’s best used with either warm or cool colors, creating a cohesive collection with certain temperature qualities as well as proper color harmony; however, this particular scheme lacks contrast and is less vibrant than complementary schemes. Within the scheme, choose one color to dominate and two to support. The remaining two colors should be used (along with black, white or gray) as accents. + +####Complementary Color Scheme +A complementary color scheme uses opposite colors on the color wheel. To put into slightly more technical terms, they are two colors that, when combined, produce a neutral color. Complementary colors are tricky to use extensively, but work well when you want a point of emphasis. Complementary colors are generally not favorable to use for text. + +####Triadic Color Scheme +A triadic scheme uses evenly spaced colors on the color wheel. The colors tend to be richly vivid and offer a higher degree of contrast while, at the same time, retain color harmony. Let one color dominate and use the two others for accent. + +####Getting Colors in a Color Scheme +To retrieve an array of colors, first make sure to initialize an NSMutableArray (in case you want to use the same array to replace with different colors later): + +#####Normal Convention: +######Objective-C +``` objective-c +NSMutableArray *colorArray = [NSMutableArray alloc] initWithArray:[NSArray arrayOfColorsWithColorScheme:(ColorScheme)colorScheme + with:(UIColor *)color + flatScheme:(BOOL)isFlatScheme]]; +``` + +######Swift +``` swift +var colorArray = NSArray(ofColorsWithColorScheme:ColorScheme, with:UIColor!, flatScheme:Bool) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +NSMutableArray *colorArray = [[NSMutableArray alloc] initWithArray:ColorScheme(colorSchemeType, color, isFlatScheme)]; +``` + +######Swift +``` swift +var colorArray = ColorSchemeOf(colorSchemeType, color, isFlatScheme) +``` + +#####Example: +Assuming you want to generate an analogous color scheme for the light shade of Flat Red, perform the following method call: + +#####Normal Convention: +######Objective-C +``` objective-c +NSMutableArray *colorArray = [NSMutableArray alloc] initWithArray:[NSArray arrayOfColorsWithColorScheme:ColorSchemeAnalogous + with:[UIColor flatRedColor] + flatScheme:YES]]; +``` + +######Swift +``` swift +var colorArray = NSArray(ofColorsWithColorScheme:ColorScheme.Analogous, with:UIColor.flatRedColor(), flatScheme:true) +``` + +#####Chameleon Shorthand: +######Objective-C +``` objective-c +NSMutableArray *colorArray = [[NSMutableArray alloc] initWithArray:ColorScheme(ColorSchemeAnalogous, FlatRed, YES)]; +``` + +######Swift +``` swift +var colorArray = ColorSchemeOf(ColorScheme.Analogous, FlatRed(), true) +``` + +You can then retrieve each individual color the same way you would normally retrieve any object from an array: + +######Objective-C +```objective-c +UIColor *firstColor = colorArray[0]; +``` + +######Swift +``` swift +var firstColor = colorArray[0] as! UIColor +``` + +###Theme Methods + +With Chameleon, you can now specify a global color theme with simply one line of code (It even takes care of dealing with the status bar style as well)! Here's one of three methods to get you started. `ContentStyle` allows you to decide whether text and a few other elements should be white, black, or whichever contrasts more over any UI element's `backgroundColor`. + +To set a global theme, you can do the following in your app delegate: + +#####Normal Convention: +######Objective-C +``` objective-c +[Chameleon setGlobalThemeUsingPrimaryColor:(UIColor *)color withContentStyle:(UIContentStyle)contentStyle]; +``` + +But what if you want a different theme for a specific `UIViewController?` No problem, Chameleon allows you to override the global theme in any `UIViewController` and `UINavigationController`, by simply doing the following: + +#####Normal Convention: +######Objective-C +```objective-c +//This would go in the controller you specifically want to theme differently +[self setThemeUsingPrimaryColor:FlatMint withSecondaryColor:FlatBlue andContentStyle:UIContentStyleContrast]; +``` + +###### **Note:** In order for the status bar style to automatically be set using a theme, you need to make sure that the *View controller-based status bar appearance* key in **Info.plist** is set to `NO`. + +#### Navigation Bar Hairline + +![No Hairline](http://i.imgur.com/tjwx53y.png) + +As of `2.0.3` the navigation bar hairline view is no longer hidden by default. However, if you're seeking a true flat look (like the image above), you can hide the hairline at the bottom of the navigation bar by doing the following: + +######Objective-C +```objective-c +[self.navigationController setHidesNavigationBarHairline:YES]; + +//or + +self.navigationController.hidesNavigationBarHairline = YES; +``` + +## 👑 Author +Chameleon was developed by **Vicc Alexander** [(@ViccsMind)](https://twitter.com/viccsmind) in 2014. Currently, it is being maintained by [@ViccAlexander](https://github.com/ViccAlexander) and [@Bre7](https://github.com/bre7). + +##### Support ☕️ +If you enjoy Chameleon and would like to buy us a coffee we'd appreciate it. Donate + +## 📝 To Do List +* ~~Cocoapods Support~~ ![1.0.1](http://i.imgur.com/8Li5aRR.png) +* ~~Table of Contents~~ ![1.0.1](http://i.imgur.com/8Li5aRR.png) +* ~~Storyboard Color Picker Add-On~~ ![1.1.0](http://i.imgur.com/Py4QvaK.png) +* ~~Xcode In-App Documentation~~ ![1.1.0](http://i.imgur.com/Py4QvaK.png) +* ~~Switch from RGB values over to HSB and LAB~~ ![1.1.0](http://i.imgur.com/Py4QvaK.png) +* ~~Gradient Colors~~ ![1.1.0](http://i.imgur.com/Py4QvaK.png) +* ~~Update GradientStyle & ShadeStyle Syntax~~ ![1.1.1](http://i.imgur.com/AHxj8Rb.png) +* ~~Add Radial Gradient Support~~ ![1.1.1](http://i.imgur.com/AHxj8Rb.png) +* ~~Fix Swift Conflict with `initWithArray:for:flatScheme:` method~~ ![1.1.12](http://i.imgur.com/7NrZ7yx.png) +* ~~Swift Support~~ ![1.1.3](http://i.imgur.com/WgpBlLo.png) +* ~~Color Scheme From Images~~ ![2.0.0](http://i.imgur.com/HdE8kjQ.png) +* ~~UIAppearance Convenience Methods~~ ![2.0.0](http://i.imgur.com/HdE8kjQ.png) +* ~~Add option to hide `NavigationBar` hairline~~ ![2.0.3](http://i.imgur.com/DmlOKPJ.png) +* Allow Gradient Colors to Adapt To Frame Changes + +## 📄 Change Log + +### See [Changelog.md](https://github.com/ViccAlexander/Chameleon/blob/master/CHANGELOG.md) 👀 + diff --git a/Pods/Crashlytics/Crashlytics.framework/README b/Pods/Crashlytics/Crashlytics.framework/README new file mode 100644 index 0000000..3ebf767 --- /dev/null +++ b/Pods/Crashlytics/Crashlytics.framework/README @@ -0,0 +1 @@ +We've now combined all our supported platforms into a single podspec. As a result, we moved our submit script to a new location for Cocoapods projects: ${PODS_ROOT}/Crashlytics/submit. To avoid breaking functionality that references the old location of the submit, we've placed this dummy script that calls to the correct location, while providing a helpful warning if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning! diff --git a/Pods/Crashlytics/Crashlytics.framework/submit b/Pods/Crashlytics/Crashlytics.framework/submit new file mode 100755 index 0000000..b7de4e3 --- /dev/null +++ b/Pods/Crashlytics/Crashlytics.framework/submit @@ -0,0 +1,6 @@ +if [[ -z $PODS_ROOT ]]; then +echo "error: The submit binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Crashlytics/submit'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the submit binary to fix this issue." +else +echo "warning: The submit script is now located at '$"{"PODS_ROOT"}"/Crashlytics/submit'. To remove this warning, update your path to point to this new location." +sh "${PODS_ROOT}/Crashlytics/submit" "$@" +fi diff --git a/Pods/Crashlytics/README.md b/Pods/Crashlytics/README.md new file mode 100644 index 0000000..db7883d --- /dev/null +++ b/Pods/Crashlytics/README.md @@ -0,0 +1,40 @@ +![Crashlytics Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-crashlytics-header.png) + +Part of [Twitter Fabric](https://www.fabric.io), [Crashlytics](http://try.crashlytics.com/) offers the most powerful, yet lightest weight crash reporting solution for iOS. Crashlytics also provides real-time analytics through [Answers](https://answers.io/) and app distributions to testers using [Beta](http://try.crashlytics.com/beta/). + +## Setup + +1. Visit [https://fabric.io/sign_up](https://fabric.io/sign_up) to create your Fabric account and to download Fabric.app. + +1. Open Fabric.app, login and select the Crashlytics SDK. + + ![Fabric Plugin](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-fabric-plugin.png) + +1. The Fabric app automatically detects when a project uses CocoaPods and gives you the option to install via the Podfile or Xcode. + + ![Fabric Installation Options](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-pod-installation-option.png) + +1. Select the Podfile option and follow the installation instructions to update your Podfile. **Note:** the Crashlytics Pod includes Answers. If you have Answers included as a separate Pod it should be removed from your Podfile to avoid duplicate symbol errors. + + ``` + pod 'Fabric' + pod 'Crashlytics' + ``` + +1. Run `pod install` + +1. Add a Run Script Build Phase and build your app. + + ![Fabric Run Script Build Phase](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-rsbp.png) + +1. Initialize the SDK by inserting code outlined in the Fabric.app. + +1. Run your app to finish the installation. + +## Resources + +* [Documentation](https://docs.fabric.io/ios/crashlytics/index.html) +* [Forums](https://twittercommunity.com/c/fabric/crashlytics) +* [Website](http://try.crashlytics.com/) +* Follow us on Twitter: [@fabric](https://twitter.com/fabric) and [@crashlytics](https://twitter.com/crashlytics) +* Follow us on Periscope: [Fabric](https://periscope.tv/fabric) and [TwitterDev](https://periscope.tv/twitterdev) diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics b/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics new file mode 100755 index 0000000..eaa3b05 Binary files /dev/null and b/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics differ diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h new file mode 100644 index 0000000..6ec011d --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h @@ -0,0 +1,31 @@ +// +// ANSCompatibility.h +// AnswersKit +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// + +#pragma once + +#if !__has_feature(nullability) +#define nonnull +#define nullable +#define _Nullable +#define _Nonnull +#endif + +#ifndef NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_BEGIN +#endif + +#ifndef NS_ASSUME_NONNULL_END +#define NS_ASSUME_NONNULL_END +#endif + +#if __has_feature(objc_generics) +#define ANS_GENERIC_NSARRAY(type) NSArray +#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary +#else +#define ANS_GENERIC_NSARRAY(type) NSArray +#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary +#endif diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h new file mode 100644 index 0000000..710eb50 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h @@ -0,0 +1,210 @@ +// +// Answers.h +// Crashlytics +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// + +#import +#import "ANSCompatibility.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class exposes the Answers Events API, allowing you to track key + * user user actions and metrics in your app. + */ +@interface Answers : NSObject + +/** + * Log a Sign Up event to see users signing up for your app in real-time, understand how + * many users are signing up with different methods and their success rate signing up. + * + * @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits. + * @param signUpSucceededOrNil The ultimate success or failure of the login + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil + success:(nullable NSNumber *)signUpSucceededOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log an Log In event to see users logging into your app in real-time, understand how many + * users are logging in with different methods and their success rate logging into your app. + * + * @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits. + * @param loginSucceededOrNil The ultimate success or failure of the login + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil + success:(nullable NSNumber *)loginSucceededOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Share event to see users sharing from your app in real-time, letting you + * understand what content they're sharing from the type or genre down to the specific id. + * + * @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS. + * @param contentNameOrNil The human readable name for this piece of content. + * @param contentTypeOrNil The type of content shared. + * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil + contentName:(nullable NSString *)contentNameOrNil + contentType:(nullable NSString *)contentTypeOrNil + contentId:(nullable NSString *)contentIdOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log an Invite Event to track how users are inviting other users into + * your application. + * + * @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which + * items are most popular, and track plenty of other important purchase-related metrics. + * + * @param itemPriceOrNil The purchased item's price. + * @param currencyOrNil The ISO4217 currency code. Example: USD + * @param purchaseSucceededOrNil Was the purchase succesful or unsuccesful + * @param itemNameOrNil The human-readable form of the item's name. Example: + * @param itemTypeOrNil The type, or genre of the item. Example: Song + * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU + * @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase. + */ ++ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil + currency:(nullable NSString *)currencyOrNil + success:(nullable NSNumber *)purchaseSucceededOrNil + itemName:(nullable NSString *)itemNameOrNil + itemType:(nullable NSString *)itemTypeOrNil + itemId:(nullable NSString *)itemIdOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Level Start Event to track where users are in your game. + * + * @param levelNameOrNil The level name + * @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event. + */ ++ (void)logLevelStart:(nullable NSString *)levelNameOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Level End event to track how users are completing levels in your game. + * + * @param levelNameOrNil The name of the level completed, E.G. "1" or "Training" + * @param scoreOrNil The score the user completed the level with. + * @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed succesfully. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logLevelEnd:(nullable NSString *)levelNameOrNil + score:(nullable NSNumber *)scoreOrNil + success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how + * many users start the purchase flow, see which items are most popular, and track plenty of other important + * purchase-related metrics. + * + * @param itemPriceOrNil The purchased item's price. + * @param currencyOrNil The ISO4217 currency code. Example: USD + * @param itemNameOrNil The human-readable form of the item's name. Example: + * @param itemTypeOrNil The type, or genre of the item. Example: Song + * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil + currency:(nullable NSString *)currencyOrNil + itemName:(nullable NSString *)itemNameOrNil + itemType:(nullable NSString *)itemTypeOrNil + itemId:(nullable NSString *)itemIdOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many + * users are doing this and how much they're spending per checkout, and see how it related to other important + * purchase-related metrics. + * + * @param totalPriceOrNil The total price of the cart. + * @param currencyOrNil The ISO4217 currency code. Example: USD + * @param itemCountOrNil The number of items in the cart. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil + currency:(nullable NSString *)currencyOrNil + itemCount:(nullable NSNumber *)itemCountOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Rating event to see users rating content within your app in real-time and understand what + * content is most engaging, from the type or genre down to the specific id. + * + * @param ratingOrNil The integer rating given by the user. + * @param contentNameOrNil The human readable name for this piece of content. + * @param contentTypeOrNil The type of content shared. + * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logRating:(nullable NSNumber *)ratingOrNil + contentName:(nullable NSString *)contentNameOrNil + contentType:(nullable NSString *)contentTypeOrNil + contentId:(nullable NSString *)contentIdOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Content View event to see users viewing content within your app in real-time and + * understand what content is most engaging, from the type or genre down to the specific id. + * + * @param contentNameOrNil The human readable name for this piece of content. + * @param contentTypeOrNil The type of content shared. + * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil + contentType:(nullable NSString *)contentTypeOrNil + contentId:(nullable NSString *)contentIdOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Search event allows you to see users searching within your app in real-time and understand + * exactly what they're searching for. + * + * @param queryOrNil The user's query. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. + */ ++ (void)logSearchWithQuery:(nullable NSString *)queryOrNil + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +/** + * Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often + * they're performing these actions with breakdowns by different categories you add. Use a human-readable name for + * the name of the event, since this is how the event will appear in Answers. + * + * @param eventName The human-readable name for the event. + * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys + * must be NSString and and values must be NSNumber or NSString. + * @discussion How we treat NSNumbers: + * We will provide information about the distribution of values over time. + * + * How we treat NSStrings: + * NSStrings are used as categorical data, allowing comparison across different category values. + * Strings are limited to a maximum length of 100 characters, attributes over this length will be + * truncated. + * + * When tracking the Tweet views to better understand user engagement, sending the tweet's length + * and the type of media present in the tweet allows you to track how tweet length and the type of media influence + * engagement. + */ ++ (void)logCustomEventWithName:(NSString *)eventName + customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h new file mode 100644 index 0000000..1526b0d --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h @@ -0,0 +1,33 @@ +// +// CLSAttributes.h +// Crashlytics +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// + +#pragma once + +#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x))) + +#if !__has_feature(nullability) + #define nonnull + #define nullable + #define _Nullable + #define _Nonnull +#endif + +#ifndef NS_ASSUME_NONNULL_BEGIN + #define NS_ASSUME_NONNULL_BEGIN +#endif + +#ifndef NS_ASSUME_NONNULL_END + #define NS_ASSUME_NONNULL_END +#endif + +#if __has_feature(objc_generics) + #define CLS_GENERIC_NSARRAY(type) NSArray + #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary +#else + #define CLS_GENERIC_NSARRAY(type) NSArray + #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary +#endif diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h new file mode 100644 index 0000000..59590d5 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h @@ -0,0 +1,64 @@ +// +// CLSLogging.h +// Crashlytics +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// +#ifdef __OBJC__ +#import "CLSAttributes.h" +#import + +NS_ASSUME_NONNULL_BEGIN +#endif + + + +/** + * + * The CLS_LOG macro provides as easy way to gather more information in your log messages that are + * sent with your crash data. CLS_LOG prepends your custom log message with the function name and + * line number where the macro was used. If your app was built with the DEBUG preprocessor macro + * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog. + * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only. + * + * Example output: + * -[AppDelegate login:] line 134 $ login start + * + * If you would like to change this macro, create a new header file, unset our define and then define + * your own version. Make sure this new header file is imported after the Crashlytics header file. + * + * #undef CLS_LOG + * #define CLS_LOG(__FORMAT__, ...) CLSNSLog... + * + **/ +#ifdef __OBJC__ +#ifdef DEBUG +#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) +#else +#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) +#endif +#endif + +/** + * + * Add logging that will be sent with your crash data. This logging will not show up in the system.log + * and will only be visible in your Crashlytics dashboard. + * + **/ + +#ifdef __OBJC__ +OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); +OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); + +/** + * + * Add logging that will be sent with your crash data. This logging will show up in the system.log + * and your Crashlytics dashboard. It is not recommended for Release builds. + * + **/ +OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); +OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); + + +NS_ASSUME_NONNULL_END +#endif diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h new file mode 100644 index 0000000..6e3a515 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h @@ -0,0 +1,103 @@ +// +// CLSReport.h +// Crashlytics +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// + +#import +#import "CLSAttributes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details. + **/ +@protocol CLSCrashReport + +@property (nonatomic, copy, readonly) NSString *identifier; +@property (nonatomic, copy, readonly) NSDictionary *customKeys; +@property (nonatomic, copy, readonly) NSString *bundleVersion; +@property (nonatomic, copy, readonly) NSString *bundleShortVersionString; +@property (nonatomic, copy, readonly) NSDate *crashedOnDate; +@property (nonatomic, copy, readonly) NSString *OSVersion; +@property (nonatomic, copy, readonly) NSString *OSBuildVersion; + +@end + +/** + * The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can + * use this class to get information about the event, and can also set some values after the + * event has occured. + **/ +@interface CLSReport : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Returns the session identifier for the report. + **/ +@property (nonatomic, copy, readonly) NSString *identifier; + +/** + * Returns the custom key value data for the report. + **/ +@property (nonatomic, copy, readonly) NSDictionary *customKeys; + +/** + * Returns the CFBundleVersion of the application that generated the report. + **/ +@property (nonatomic, copy, readonly) NSString *bundleVersion; + +/** + * Returns the CFBundleShortVersionString of the application that generated the report. + **/ +@property (nonatomic, copy, readonly) NSString *bundleShortVersionString; + +/** + * Returns the date that the report was created. + **/ +@property (nonatomic, copy, readonly) NSDate *dateCreated; + +/** + * Returns the os version that the application crashed on. + **/ +@property (nonatomic, copy, readonly) NSString *OSVersion; + +/** + * Returns the os build version that the application crashed on. + **/ +@property (nonatomic, copy, readonly) NSString *OSBuildVersion; + +/** + * Returns YES if the report contains any crash information, otherwise returns NO. + **/ +@property (nonatomic, assign, readonly) BOOL isCrash; + +/** + * You can use this method to set, after the event, additional custom keys. The rules + * and semantics for this method are the same as those documented in Crashlytics.h. Be aware + * that the maximum size and count of custom keys is still enforced, and you can overwrite keys + * and/or cause excess keys to be deleted by using this method. + **/ +- (void)setObjectValue:(nullable id)value forKey:(NSString *)key; + +/** + * Record an application-specific user identifier. See Crashlytics.h for details. + **/ +@property (nonatomic, copy, nullable) NSString * userIdentifier; + +/** + * Record a user name. See Crashlytics.h for details. + **/ +@property (nonatomic, copy, nullable) NSString * userName; + +/** + * Record a user email. See Crashlytics.h for details. + **/ +@property (nonatomic, copy, nullable) NSString * userEmail; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h new file mode 100644 index 0000000..cdb5596 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h @@ -0,0 +1,38 @@ +// +// CLSStackFrame.h +// Crashlytics +// +// Copyright 2015 Crashlytics, Inc. All rights reserved. +// + +#import +#import "CLSAttributes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * + * This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to + * record information about non-ObjC/C++ exceptions. All information included here will be displayed + * in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the + * address property. If set, Crashlytics will attempt symbolication and could overwrite other properities + * in the process. + * + **/ +@interface CLSStackFrame : NSObject + ++ (instancetype)stackFrame; ++ (instancetype)stackFrameWithAddress:(NSUInteger)address; ++ (instancetype)stackFrameWithSymbol:(NSString *)symbol; + +@property (nonatomic, copy, nullable) NSString *symbol; +@property (nonatomic, copy, nullable) NSString *rawSymbol; +@property (nonatomic, copy, nullable) NSString *library; +@property (nonatomic, copy, nullable) NSString *fileName; +@property (nonatomic, assign) uint32_t lineNumber; +@property (nonatomic, assign) uint64_t offset; +@property (nonatomic, assign) uint64_t address; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h new file mode 100644 index 0000000..34288f1 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h @@ -0,0 +1,266 @@ +// +// Crashlytics.h +// Crashlytics +// +// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. +// + +#import + +#import "CLSAttributes.h" +#import "CLSLogging.h" +#import "CLSReport.h" +#import "CLSStackFrame.h" +#import "Answers.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol CrashlyticsDelegate; + +/** + * Crashlytics. Handles configuration and initialization of Crashlytics. + * + * Note: The Crashlytics class cannot be subclassed. If this is causing you pain for + * testing, we suggest using either a wrapper class or a protocol extension. + */ +@interface Crashlytics : NSObject + +@property (nonatomic, readonly, copy) NSString *APIKey; +@property (nonatomic, readonly, copy) NSString *version; +@property (nonatomic, assign) BOOL debugMode; + +/** + * + * The delegate can be used to influence decisions on reporting and behavior, as well as reacting + * to previous crashes. + * + * Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or + * via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur + * synchronously during start. + * + **/ +@property (nonatomic, assign, nullable) id delegate; + +/** + * The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey: + * in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching: + * method. + * + * Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter + * is no longer required to throttle submissions on launch, performance will be great without it. + * + * @param apiKey The Crashlytics API Key for this app + * + * @return The singleton Crashlytics instance + */ ++ (Crashlytics *)startWithAPIKey:(NSString *)apiKey; ++ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead."); + +/** + * If you need the functionality provided by the CrashlyticsDelegate protocol, you can use + * these convenience methods to activate the framework and set the delegate in one call. + * + * @param apiKey The Crashlytics API Key for this app + * @param delegate A delegate object which conforms to CrashlyticsDelegate. + * + * @return The singleton Crashlytics instance + */ ++ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate; ++ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead."); + +/** + * Access the singleton Crashlytics instance. + * + * @return The singleton Crashlytics instance + */ ++ (Crashlytics *)sharedInstance; + +/** + * The easiest way to cause a crash - great for testing! + */ +- (void)crash; + +/** + * The easiest way to cause a crash with an exception - great for testing. + */ +- (void)throwException; + +/** + * Specify a user identifier which will be visible in the Crashlytics UI. + * + * Many of our customers have requested the ability to tie crashes to specific end-users of their + * application in order to facilitate responses to support requests or permit the ability to reach + * out for more information. We allow you to specify up to three separate values for display within + * the Crashlytics UI - but please be mindful of your end-user's privacy. + * + * We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record + * in your system. This could be a database id, hash, or other value that is meaningless to a + * third-party observer but can be indexed and queried by you. + * + * Optionally, you may also specify the end-user's name or username, as well as email address if you + * do not have a system that works well with obscured identifiers. + * + * Pursuant to our EULA, this data is transferred securely throughout our system and we will not + * disseminate end-user data unless required to by law. That said, if you choose to provide end-user + * contact information, we strongly recommend that you disclose this in your application's privacy + * policy. Data privacy is of our utmost concern. + * + * @param identifier An arbitrary user identifier string which ties an end-user to a record in your system. + */ +- (void)setUserIdentifier:(nullable NSString *)identifier; + +/** + * Specify a user name which will be visible in the Crashlytics UI. + * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs. + * @see setUserIdentifier: + * + * @param name An end user's name. + */ +- (void)setUserName:(nullable NSString *)name; + +/** + * Specify a user email which will be visible in the Crashlytics UI. + * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs. + * + * @see setUserIdentifier: + * + * @param email An end user's email address. + */ +- (void)setUserEmail:(nullable NSString *)email; + ++ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance"); ++ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance"); ++ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance"); + +/** + * Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI. + * When setting an object value, the object is converted to a string. This is typically done by calling + * -[NSObject description]. + * + * @param value The object to be associated with the key + * @param key The key with which to associate the value + */ +- (void)setObjectValue:(nullable id)value forKey:(NSString *)key; + +/** + * Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI. + * + * @param value The integer value to be set + * @param key The key with which to associate the value + */ +- (void)setIntValue:(int)value forKey:(NSString *)key; + +/** + * Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI. + * + * @param value The BOOL value to be set + * @param key The key with which to associate the value + */ +- (void)setBoolValue:(BOOL)value forKey:(NSString *)key; + +/** + * Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI. + * + * @param value The float value to be set + * @param key The key with which to associate the value + */ +- (void)setFloatValue:(float)value forKey:(NSString *)key; + ++ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); ++ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); ++ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); ++ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); + +/** + * This method can be used to record a single exception structure in a report. This is particularly useful + * when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be + * expensive and should only be used shortly before process termination. This API is not intended be to used + * to log NSException objects. All safely-reportable NSExceptions are automatically captured by + * Crashlytics. + * + * @param name The name of the custom exception + * @param reason The reason this exception occured + * @param frameArray An array of CLSStackFrame objects + */ +- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray; + +/** + * + * This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and + * displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of + * NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the + * buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch + * of your application. + * + * You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented + * by the NSError instance itself. + * + **/ +- (void)recordError:(NSError *)error; +- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo; + +- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); +- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); ++ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); ++ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); + +@end + +/** + * + * The CrashlyticsDelegate protocol provides a mechanism for your application to take + * action on events that occur in the Crashlytics crash reporting system. You can make + * use of these calls by assigning an object to the Crashlytics' delegate property directly, + * or through the convenience +startWithAPIKey:delegate: method. + * + */ +@protocol CrashlyticsDelegate +@optional + + +- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:"); +- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id )crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:"); + +/** + * + * Called when a Crashlytics instance has determined that the last execution of the + * application ended in a crash. This is called synchronously on Crashlytics + * initialization. Your delegate must invoke the completionHandler, but does not need to do so + * synchronously, or even on the main thread. Invoking completionHandler with NO will cause the + * detected report to be deleted and not submitted to Crashlytics. This is useful for + * implementing permission prompts, or other more-complex forms of logic around submitting crashes. + * + * @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out. + * + * @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can + * impact the reliability of reporting crashes very early in application launch. + * + * @param report The CLSReport object representing the last detected crash + * @param completionHandler The completion handler to call when your logic has completed. + * + */ +- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler; + +/** + * If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit + * most reports using out-of-process background networking operations. This results in a significant + * improvement in reliability of reporting, as well as power and performance wins for your users. + * If you don't want this functionality, you can disable by returning NO from this method. + * + * @warning Background submission is not supported for extensions on iOS or OS X. + * + * @param crashlytics The Crashlytics singleton instance + * + * @return Return NO if you don't want out-of-process background network operations. + * + */ +- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics; + +@end + +/** + * `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance() + */ +#define CrashlyticsKit [Crashlytics sharedInstance] + +NS_ASSUME_NONNULL_END diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist b/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist new file mode 100644 index 0000000..eda42e9 Binary files /dev/null and b/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist differ diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap b/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap new file mode 100644 index 0000000..da0845e --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap @@ -0,0 +1,14 @@ +framework module Crashlytics { + header "Crashlytics.h" + header "Answers.h" + header "ANSCompatibility.h" + header "CLSLogging.h" + header "CLSReport.h" + header "CLSStackFrame.h" + header "CLSAttributes.h" + + export * + + link "z" + link "c++" +} diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/run b/Pods/Crashlytics/iOS/Crashlytics.framework/run new file mode 100755 index 0000000..9058ea6 --- /dev/null +++ b/Pods/Crashlytics/iOS/Crashlytics.framework/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# run +# +# Copyright (c) 2015 Crashlytics. All rights reserved. + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# Quote path in case of spaces or special chars +DIR="\"${DIR}" + +PATH_SEP="/" +VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" +UPLOAD_COMMAND="uploadDSYM\" $@ run-script" + +# Ensure params are as expected, run in sync mode to validate +eval $DIR$PATH_SEP$VALIDATE_COMMAND +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, upload dSYM in background to prevent Xcode from waiting +# Note: Validation is performed again before upload. +# Output can still be found in Console.app +eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/submit b/Pods/Crashlytics/iOS/Crashlytics.framework/submit new file mode 100755 index 0000000..c05b8e9 Binary files /dev/null and b/Pods/Crashlytics/iOS/Crashlytics.framework/submit differ diff --git a/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM b/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM new file mode 100755 index 0000000..6edd046 Binary files /dev/null and b/Pods/Crashlytics/iOS/Crashlytics.framework/uploadDSYM differ diff --git a/Pods/Crashlytics/submit b/Pods/Crashlytics/submit new file mode 100755 index 0000000..c05b8e9 Binary files /dev/null and b/Pods/Crashlytics/submit differ diff --git a/Pods/FCAlertView/FCAlertView/Assets/alert-round.png b/Pods/FCAlertView/FCAlertView/Assets/alert-round.png new file mode 100644 index 0000000..704db46 Binary files /dev/null and b/Pods/FCAlertView/FCAlertView/Assets/alert-round.png differ diff --git a/Pods/FCAlertView/FCAlertView/Assets/checkmark-round.png b/Pods/FCAlertView/FCAlertView/Assets/checkmark-round.png new file mode 100755 index 0000000..bf6361e Binary files /dev/null and b/Pods/FCAlertView/FCAlertView/Assets/checkmark-round.png differ diff --git a/Pods/FCAlertView/FCAlertView/Assets/close-round.png b/Pods/FCAlertView/FCAlertView/Assets/close-round.png new file mode 100755 index 0000000..81aa367 Binary files /dev/null and b/Pods/FCAlertView/FCAlertView/Assets/close-round.png differ diff --git a/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.h b/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.h new file mode 100644 index 0000000..4fe9fd7 --- /dev/null +++ b/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.h @@ -0,0 +1,153 @@ +// +// FCAlertView.h +// ShiftRide +// +// Created by Nima Tahami on 2016-07-10. +// Copyright © 2016 Nima Tahami. All rights reserved. +// + +#import +#import + +#import +#import + +@protocol FCAlertViewDelegate; + +@interface FCAlertView : UIView { + + // Blur + + UIVisualEffectView *backgroundVisualEffectView; + + // Default UI adjustments + + CGFloat defaultHeight; + CGFloat defaultSpacing; + + // AlertView & Contents + + UIView *alertView; + UIView *alertViewContents; + CAShapeLayer *circleLayer; + + // Customizations made to UI + + NSMutableArray *alertButtons; + NSMutableArray *alertTextFields; + NSInteger alertViewWithVector; + NSString *doneTitle; + UIImage *vectorImage; + NSString *alertType; + + // Frames + + CGRect alertViewFrame; + CGRect currentAVCFrames; + CGRect descriptionLabelFrames; + + // Alert AudioPlayer + + AVAudioPlayer *player; + +} + +// Delegate + +@property (nonatomic, weak) id delegate; + +// AlertView Title & Subtitle Text + +@property (nonatomic, retain) NSString *title; +@property (nonatomic, retain) NSString *subTitle; + +// AlertView Background + +@property (nonatomic, retain) UIView *alertBackground; + +// AlertView TextView + +@property (nonatomic, retain) UITextField *textField; + +// AlertView Customizations + +@property CGFloat customHeight; +@property CGFloat customSpacing; + +@property NSInteger numberOfButtons; +@property NSInteger autoHideSeconds; +@property CGFloat cornerRadius; + +@property BOOL dismissOnOutsideTouch; +@property BOOL hideAllButtons; +@property BOOL hideDoneButton; +@property BOOL avoidCustomImageTint; +@property BOOL blurBackground; +@property BOOL bounceAnimations; + +// Default Types of Alerts + +- (void) makeAlertTypeWarning; +- (void) makeAlertTypeCaution; +- (void) makeAlertTypeSuccess; + +// Play Sound with Alert + +- (void) setAlertSoundWithFileName:(NSString *)filename; + +// Presenting AlertView + +- (void) showAlertInView:(UIViewController *)view withTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons; + +- (void) showAlertInWindow:(UIWindow *)window withTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons; + +- (void) showAlertWithTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons; + +- (void)setAlertViewAttributes:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons; + +// Dismissing AlertView + +- (void) dismissAlertView; + +// Alert Action Block Method + +typedef void (^FCActionBlock)(void); +@property (nonatomic, copy) FCActionBlock actionBlock; +@property (nonatomic, copy) FCActionBlock doneBlock; +- (void)addButton:(NSString *)title withActionBlock:(FCActionBlock)action; +- (void)doneActionBlock:(FCActionBlock)action; + +// Alert TextField Block Method + +typedef void (^FCTextReturnBlock)(NSString *text); +@property (nonatomic, copy) FCTextReturnBlock textReturnBlock; +- (void)addTextFieldWithPlaceholder:(NSString *)placeholder andTextReturnBlock:(FCTextReturnBlock)textReturn; + +// Color Schemes + +@property (nonatomic, retain) UIColor * colorScheme; +@property (nonatomic, retain) UIColor * titleColor; +@property (nonatomic, retain) UIColor * subTitleColor; + +// Preset Flat Colors + +@property (nonatomic, retain) UIColor * flatTurquoise; +@property (nonatomic, retain) UIColor * flatGreen; +@property (nonatomic, retain) UIColor * flatBlue; +@property (nonatomic, retain) UIColor * flatMidnight; +@property (nonatomic, retain) UIColor * flatPurple; +@property (nonatomic, retain) UIColor * flatOrange; +@property (nonatomic, retain) UIColor * flatRed; +@property (nonatomic, retain) UIColor * flatSilver; +@property (nonatomic, retain) UIColor * flatGray; + +@end + +@protocol FCAlertViewDelegate +@optional +- (void)FCAlertView:( FCAlertView *)alertView clickedButtonIndex:(NSInteger)index buttonTitle:(NSString *)title; +- (void)FCAlertViewDismissed:(FCAlertView *)alertView; +- (void)FCAlertViewWillAppear:(FCAlertView *)alertView; +- (void)FCAlertDoneButtonClicked:(FCAlertView *)alertView; + +@end diff --git a/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.m b/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.m new file mode 100644 index 0000000..ea1033e --- /dev/null +++ b/Pods/FCAlertView/FCAlertView/Classes/FCAlertView.m @@ -0,0 +1,997 @@ +// +// FCAlertView.m +// ShiftRide +// +// Created by Nima Tahami on 2016-07-10. +// Copyright © 2016 Nima Tahami. All rights reserved. +// + +#import "FCAlertView.h" + +@implementation FCAlertView + +- (id)init +{ + self = [super init]; + if (self) { + + // INITIALIZATIONS - Setting Up Basic UI Adjustments + + CGSize result = [[UIScreen mainScreen] bounds].size; + + self.frame = CGRectMake(0, + 0, + result.width, + result.height); + + self.backgroundColor = [UIColor clearColor]; + + // ALERTVIEW BACKGROUND - Setting up Background View + + _alertBackground = [[UIView alloc] init]; + _alertBackground.frame = CGRectMake(0, + 0, + result.width, + result.height); + _alertBackground.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.35]; // set color as you want. + + [self addSubview:_alertBackground]; + + // PRESET FLAT COLORS - Setting up RGB of Flat Colors - Put in another file? REMOVE + + _flatTurquoise = [UIColor colorWithRed:26.0/255.0f green:188.0/255.0f blue:156.0/255.0f alpha:1.0]; + _flatGreen = [UIColor colorWithRed:39.0/255.0f green:174.0/255.0f blue:96.0/255.0f alpha:1.0]; + _flatBlue = [UIColor colorWithRed:41.0/255.0f green:128.0/255.0f blue:185.0/255.0f alpha:1.0]; + _flatMidnight = [UIColor colorWithRed:44.0/255.0f green:62.0/255.0f blue:80.0/255.0f alpha:1.0]; + _flatPurple = [UIColor colorWithRed:142.0/255.0f green:68.0/255.0f blue:173.0/255.0f alpha:1.0]; + _flatOrange = [UIColor colorWithRed:243.0/255.0f green:156.0/255.0f blue:18.0/255.0f alpha:1.0]; + _flatRed = [UIColor colorWithRed:192.0/255.0f green:57.0/255.0f blue:43.0/255.0f alpha:1.0]; + _flatSilver = [UIColor colorWithRed:189.0/255.0f green:195.0/255.0f blue:199.0/255.0f alpha:1.0]; + _flatGray = [UIColor colorWithRed:127.0/255.0f green:140.0/255.0f blue:141.0/255.0f alpha:1.0]; + + // CUSTOMIZATIONS - Setting Default Customization Settings & Checks + + alertButtons = [[NSMutableArray alloc] init]; + alertTextFields = [[NSMutableArray alloc] init]; + + _numberOfButtons = 0; + _autoHideSeconds = 0; + _cornerRadius = 18.0f; + + _dismissOnOutsideTouch = NO; + _hideAllButtons = NO; + _hideDoneButton = NO; + + defaultSpacing = [self configureAVWidth]; + defaultHeight = [self configureAVHeight]; + + [self checkCustomizationValid]; + + } + + return self; + +} + +#pragma mark - Frame Configuration + +- (CGFloat) configureAVWidth { + + if (_customSpacing == 0) { + if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) + { + CGSize result = [[UIScreen mainScreen] bounds].size; + if(result.height == 480) + { + // iPhone Classic + return 55.0f; + + } + if(result.height == 568) + { + // iPhone 5 + return 65.0f; + + } + if (result.height == 736) + { + // iPhone 6/7 Plus + return 130.0f; + } + else + { + return 105.0f; + } + + } + } else { + return _customSpacing; + } + +} + +- (CGFloat) configureAVHeight { + + if (_customHeight == 0) { + return 200.0f; + } else { + return _customHeight; + } +} + +#pragma mark - FCAlertView Checks +#pragma mark - Customization Data Checkpoint + +- (void) checkCustomizationValid { + + if (_subTitle == nil || [_subTitle isEqualToString:@""]) + if (_title == nil || [_title isEqualToString:@""]) + _subTitle = @"You need to have a title or subtitle to use FCAlertView 😀"; + + if (doneTitle == nil || [doneTitle isEqualToString:@""]) { + doneTitle = @"OK"; + } + + if (_cornerRadius == 0.0f) + _cornerRadius = 18.0f; + + if (vectorImage != nil) + alertViewWithVector = 1; + +} + +#pragma mark - Safety Close Check + +- (void) safetyCloseCheck { + + if (_hideDoneButton || _hideAllButtons) { + + if (_autoHideSeconds == 0) { + + _dismissOnOutsideTouch = YES; + + NSLog(@"Forced Dismiss on Outside Touch"); + + } + + } + +} + +#pragma mark - Touch Events + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + + UITouch *touch = [touches anyObject]; + + CGPoint touchPoint = [touch locationInView:_alertBackground]; + CGPoint touchPoint2 = [touch locationInView:alertViewContents]; + + BOOL isPointInsideBackview = [_alertBackground pointInside:touchPoint withEvent:nil]; + BOOL isPointInsideAlertView = [alertViewContents pointInside:touchPoint2 withEvent:nil]; + + if (_dismissOnOutsideTouch && isPointInsideBackview && !isPointInsideAlertView) + [self dismissAlertView]; + + if (alertTextFields.count > 0 && isPointInsideBackview && !isPointInsideAlertView) + [self endEditing:YES]; + +} + +#pragma mark - Drawing AlertView + +- (void)drawRect:(CGRect)rect { + + defaultSpacing = [self configureAVWidth]; + defaultHeight = [self configureAVHeight]; + + CGSize result = [[UIScreen mainScreen] bounds].size; + + CGRect alertViewFrame; + + self.alpha = 0; + + // Adjusting AlertView Frames + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + defaultSpacing += 350; + + if (alertViewWithVector) // Frames for when AlertView contains an image + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - (200.0f/2), + result.width - defaultSpacing, + defaultHeight); + else + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - (170.0f/2), + result.width - defaultSpacing, + defaultHeight - 30); + + if (self.title == nil) // Frames for when AlertView doesn't contain a title + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50)/2), + result.width - defaultSpacing, + alertViewFrame.size.height - 10); + + if (_hideAllButtons) { // Frames for when AlertView has hidden all buttons + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50)/2), + result.width - defaultSpacing, + alertViewFrame.size.height - 45); + + } else { + if (_hideDoneButton && _numberOfButtons == 0) { // Frames for when AlertView has hidden the DONE/DISMISS button + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50)/2), + result.width - defaultSpacing, + alertViewFrame.size.height - 45); + } + if (!_hideDoneButton && _numberOfButtons >= 2) // Frames for AlertView with 2 added buttons (vertical buttons) + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50 + 140)/2), + result.width - defaultSpacing, + alertViewFrame.size.height - 50 + 140); + } + + if (alertTextFields.count > 0) + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50 + 140)/2), + result.width - defaultSpacing, + alertViewFrame.size.height + 40); + else + alertViewFrame = CGRectMake(self.frame.size.width/2 - ((result.width - defaultSpacing)/2), + self.frame.size.height/2 - ((alertViewFrame.size.height - 50 + 140)/2), + result.width - defaultSpacing, + alertViewFrame.size.height); + + // Setting Up Contents of AlertView + + alertViewContents = [[UIView alloc] initWithFrame:alertViewFrame]; + [self addSubview:alertViewContents]; + + alertView = [[UIView alloc] initWithFrame:CGRectMake(0, + 0, + alertViewFrame.size.width, + alertViewFrame.size.height)]; + + // Setting Background Color of AlertView + + if (alertViewWithVector) + alertView.backgroundColor = [UIColor clearColor]; + else + alertView.backgroundColor = [UIColor whiteColor]; + + [alertViewContents addSubview:alertView]; + + // CREATING ALERTVIEW + // CUSTOM SHAPING - Displaying Cut out circle for Vector Type Alerts + + int radius = alertView.frame.size.width; + UIBezierPath *rectPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, + 0, + self.frame.size.width, + alertView.bounds.size.height) + cornerRadius:0]; + UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(alertViewFrame.size.width/2 - 33.75f, + -33.75f, + 67.5f, + 67.5f) + cornerRadius:radius]; + [rectPath appendPath:circlePath]; + [rectPath setUsesEvenOddFillRule:YES]; + + CAShapeLayer *fillLayer = [CAShapeLayer layer]; + fillLayer.path = rectPath.CGPath; + fillLayer.fillRule = kCAFillRuleEvenOdd; + fillLayer.fillColor = [UIColor whiteColor].CGColor; + fillLayer.opacity = 1.0; + + if (alertViewWithVector) + [alertView.layer addSublayer:fillLayer]; + + // HEADER VIEW - With Title & Subtitle + + UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15.0f, + 20.0f + (alertViewWithVector * 30), + alertViewFrame.size.width - 30.0f, + 20.0f)]; + titleLabel.font = [UIFont systemFontOfSize:18.0f weight:UIFontWeightMedium]; + titleLabel.numberOfLines = 1; + titleLabel.textColor = self.titleColor; + titleLabel.text = self.title; + titleLabel.textAlignment = NSTextAlignmentCenter; + + NSInteger descriptionLevel = 45.0f; + + if (_title == nil) + descriptionLevel = 25.0f; + + UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(25.0f, + descriptionLevel + (alertViewWithVector * 30), + alertViewFrame.size.width - 50.0f, + 60.0f)]; + descriptionLabel.font = [UIFont systemFontOfSize:15.0f weight:UIFontWeightLight]; + descriptionLabel.numberOfLines = 4; + descriptionLabel.textColor = self.subTitleColor; + descriptionLabel.text = self.subTitle; + descriptionLabel.textAlignment = NSTextAlignmentCenter; + descriptionLabel.adjustsFontSizeToFitWidth = YES; + + descriptionLabelFrames = descriptionLabel.frame; + + if (_title == nil) { + descriptionLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightRegular]; + } + + // SEPARATOR LINE - Seperating Header View with Button View + + UIView* separatorLineView = [[UIView alloc] initWithFrame:CGRectMake(0, + alertViewFrame.size.height - 47, + alertViewFrame.size.width, + 2)]; + separatorLineView.backgroundColor = [UIColor colorWithWhite:100.0f/255.0f alpha:1.0]; // set color as you want. + + // TEXTFIELD VIEW - Section with TextField + + if (alertTextFields.count > 0) { + + _textField = [[UITextField alloc] initWithFrame:CGRectMake(12.5, descriptionLabel.frame.size.height + descriptionLabel.frame.origin.y + 3, alertViewFrame.size.width - 25, 40)]; + + _textField.layer.cornerRadius = 3.0f; + _textField.layer.masksToBounds = YES; + _textField.layer.borderColor = [[UIColor colorWithWhite:217.0f/255.0f alpha:1.0] CGColor]; + _textField.layer.borderWidth = 1.0f; + _textField.delegate = self; + _textField.placeholder = [[alertTextFields firstObject] objectForKey:@"placeholder"]; + + UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)]; + _textField.leftView = paddingView; + _textField.leftViewMode = UITextFieldViewModeAlways; + + [alertView addSubview:_textField]; + + } + + // BUTTON(S) VIEW - Section containing all Buttons + + if (_numberOfButtons == 0) { // View only contains DONE/DISMISS Button + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + if (_colorScheme == nil) + doneButton.backgroundColor = [UIColor whiteColor]; + else + doneButton.backgroundColor = _colorScheme; + doneButton.frame = CGRectMake(0, + alertViewFrame.size.height - 45, + alertViewFrame.size.width, + 45); + [doneButton setTitle:doneTitle forState:UIControlStateNormal]; + [doneButton addTarget:self action:@selector(donePressed) forControlEvents:UIControlEventTouchUpInside]; + [doneButton addTarget:self action:@selector(btnTouched) forControlEvents:UIControlEventTouchDown]; + [doneButton addTarget:self action:@selector(btnReleased) forControlEvents:UIControlEventTouchDragExit]; + doneButton.titleLabel.font = [UIFont systemFontOfSize:18.0f weight:UIFontWeightMedium]; + if (_colorScheme != nil) + doneButton.tintColor = [UIColor whiteColor]; + + if (!_hideAllButtons && !_hideDoneButton) + [alertView addSubview:doneButton]; + + } else if (_numberOfButtons == 1) { // View also contains OTHER (One) Button + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + if (_colorScheme == nil) + doneButton.backgroundColor = [UIColor whiteColor]; + else + doneButton.backgroundColor = _colorScheme; + doneButton.frame = CGRectMake(alertViewFrame.size.width/2, + alertViewFrame.size.height - 45, + alertViewFrame.size.width/2, + 45); + [doneButton setTitle:doneTitle forState:UIControlStateNormal]; + [doneButton addTarget:self action:@selector(donePressed) forControlEvents:UIControlEventTouchUpInside]; + [doneButton addTarget:self action:@selector(btnTouched) forControlEvents:UIControlEventTouchDown]; + [doneButton addTarget:self action:@selector(btnReleased) forControlEvents:UIControlEventTouchDragExit]; + doneButton.titleLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightMedium]; + if (_colorScheme != nil) + doneButton.tintColor = [UIColor whiteColor]; + + UIButton *otherButton = [UIButton buttonWithType:UIButtonTypeSystem]; + otherButton.backgroundColor = [UIColor whiteColor]; + otherButton.frame = CGRectMake(0, + alertViewFrame.size.height - 45, + alertViewFrame.size.width/2, + 45); + if (_hideDoneButton) + otherButton.frame = CGRectMake(0, + alertViewFrame.size.height - 45, + alertViewFrame.size.width, + 45); + [otherButton setTitle:[[alertButtons objectAtIndex:0] objectForKey:@"title"] forState:UIControlStateNormal]; + [otherButton addTarget:self action:@selector(handleButton:) forControlEvents:UIControlEventTouchUpInside]; + [otherButton addTarget:self action:@selector(btnTouched) forControlEvents:UIControlEventTouchDown]; + [otherButton addTarget:self action:@selector(btnReleased) forControlEvents:UIControlEventTouchDragExit]; + otherButton.titleLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightRegular]; + otherButton.tintColor = self.colorScheme; + otherButton.titleLabel.adjustsFontSizeToFitWidth = YES; + otherButton.titleLabel.minimumScaleFactor = 0.8; + + if (!_hideAllButtons) { + [alertView addSubview:otherButton]; + } + + if (!_hideAllButtons && !_hideDoneButton) + [alertView addSubview:doneButton]; + + UIView *horizontalSeparator = [[UIView alloc] initWithFrame:CGRectMake(alertViewFrame.size.width/2 - 1, + otherButton.frame.origin.y - 2, + 2, + 47)]; + + horizontalSeparator.backgroundColor = [UIColor colorWithWhite:100.0f/255.0f alpha:1.0]; // set color as you want. + + UIVisualEffect *blurEffect; + blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]; + + UIVisualEffectView *visualEffectView3; + visualEffectView3 = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + visualEffectView3.frame = horizontalSeparator.bounds; + visualEffectView3.userInteractionEnabled = NO; + [horizontalSeparator addSubview:visualEffectView3]; + + if (!_hideAllButtons && !_hideDoneButton) { + [alertView addSubview:horizontalSeparator]; + } + + } else if (_numberOfButtons >= 2) { // View contains TWO OTHER Buttons - First & Second Button + + UIButton *firstButton = [UIButton buttonWithType:UIButtonTypeSystem]; + firstButton.backgroundColor = [UIColor whiteColor]; + firstButton.frame = CGRectMake(0, + alertViewFrame.size.height - 135, + alertViewFrame.size.width, + 45); + if (_hideDoneButton) + firstButton.frame = CGRectMake(0, + alertViewFrame.size.height - 45, + alertViewFrame.size.width/2, + 45); + [firstButton setTitle:[[alertButtons objectAtIndex:0] objectForKey:@"title"] forState:UIControlStateNormal]; + [firstButton addTarget:self action:@selector(handleButton:) forControlEvents:UIControlEventTouchUpInside]; + [firstButton addTarget:self action:@selector(btnTouched) forControlEvents:UIControlEventTouchDown]; + [firstButton addTarget:self action:@selector(btnReleased) forControlEvents:UIControlEventTouchDragExit]; + firstButton.titleLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightRegular]; + firstButton.tintColor = self.colorScheme; + firstButton.titleLabel.adjustsFontSizeToFitWidth = YES; + firstButton.titleLabel.minimumScaleFactor = 0.8; + firstButton.tag = 0; + + UIButton *secondButton = [UIButton buttonWithType:UIButtonTypeSystem]; + secondButton.backgroundColor = [UIColor whiteColor]; + secondButton.frame = CGRectMake(0, + alertViewFrame.size.height - 90, + alertViewFrame.size.width, + 45); + if (_hideDoneButton) + secondButton.frame = CGRectMake(alertViewFrame.size.width/2, + alertViewFrame.size.height - 45, + alertViewFrame.size.width/2, + 45); + [secondButton setTitle:[[alertButtons objectAtIndex:1] objectForKey:@"title"] forState:UIControlStateNormal]; + [secondButton addTarget:self action:@selector(handleButton:) forControlEvents:UIControlEventTouchUpInside]; + [secondButton addTarget:self action:@selector(btnTouched) forControlEvents:UIControlEventTouchDown]; + [secondButton addTarget:self action:@selector(btnReleased) forControlEvents:UIControlEventTouchDragExit]; + secondButton.titleLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightRegular]; + secondButton.tintColor = self.colorScheme; + secondButton.titleLabel.adjustsFontSizeToFitWidth = YES; + secondButton.titleLabel.minimumScaleFactor = 0.8; + secondButton.tag = 1; + + UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + if (_colorScheme == nil) + doneButton.backgroundColor = [UIColor whiteColor]; + else + doneButton.backgroundColor = _colorScheme; + doneButton.frame = CGRectMake(0, + alertViewFrame.size.height - 45, + alertViewFrame.size.width, + 45); + [doneButton setTitle:doneTitle forState:UIControlStateNormal]; + [doneButton addTarget:self action:@selector(donePressed) forControlEvents:UIControlEventTouchUpInside]; + doneButton.titleLabel.font = [UIFont systemFontOfSize:18.0f weight:UIFontWeightMedium]; + if (_colorScheme != nil) + doneButton.tintColor = [UIColor whiteColor]; + + if (!_hideAllButtons) { + [alertView addSubview:firstButton]; + [alertView addSubview:secondButton]; + } + + if (!_hideAllButtons && !_hideDoneButton) + [alertView addSubview:doneButton]; + + UIView *firstSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, + firstButton.frame.origin.y - 2, + alertViewFrame.size.width, + 2)]; + firstSeparator.backgroundColor = [UIColor colorWithWhite:100.0f/255.0f alpha:1.0]; // set color as you want. + + UIView *secondSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, + secondButton.frame.origin.y - 2, + alertViewFrame.size.width, + 2)]; + if (_hideDoneButton) + secondSeparator.frame = CGRectMake(alertViewFrame.size.width/2 - 1, + secondButton.frame.origin.y, + 2, + 45); + secondSeparator.backgroundColor = [UIColor colorWithWhite:100.0f/255.0f alpha:1.0]; // set color as you want. + + UIVisualEffect *blurEffect; + blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]; + + UIVisualEffectView *visualEffectView; + visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + visualEffectView.frame = firstSeparator.bounds; + visualEffectView.userInteractionEnabled = NO; + [firstSeparator addSubview:visualEffectView]; + + UIVisualEffectView *visualEffectView2; + visualEffectView2 = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + visualEffectView2.frame = secondSeparator.bounds; + visualEffectView2.userInteractionEnabled = NO; + [secondSeparator addSubview:visualEffectView2]; + + if (!_hideAllButtons) { + [alertView addSubview:firstSeparator]; + [alertView addSubview:secondSeparator]; + } + + } + + UIVisualEffect *blurEffect; + blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]; + UIVisualEffectView *visualEffectView; + visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + visualEffectView.frame = separatorLineView.bounds; + visualEffectView.userInteractionEnabled = NO; + [separatorLineView addSubview:visualEffectView]; + + circleLayer = [CAShapeLayer layer]; + [circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(alertViewContents.frame.size.width/2 - 30.0f, -30.0f, 60.0f, 60.0f)] CGPath]]; + [circleLayer setFillColor:[UIColor whiteColor].CGColor]; + + UIButton *alertViewVector; + + if (_avoidCustomImageTint && alertType.length == 0) + alertViewVector = [UIButton buttonWithType:UIButtonTypeCustom]; + else + alertViewVector = [UIButton buttonWithType:UIButtonTypeSystem]; + + alertViewVector.frame = CGRectMake(alertViewContents.frame.size.width/2 - 15.0f, + -15.0f, + 30.0f, + 30.0f); + [alertViewVector setImage:vectorImage forState:UIControlStateNormal]; + alertViewVector.userInteractionEnabled = 0; + alertViewVector.tintColor = _colorScheme; + + // VIEW BORDER - Rounding Corners of AlertView + + alertView.layer.cornerRadius = self.cornerRadius; + alertView.layer.masksToBounds = YES; + + // ADDING CONTENTS - Contained in Header and Separator Views + + [alertViewContents addSubview:titleLabel]; + [alertViewContents addSubview:descriptionLabel]; + + if (!_hideAllButtons) { + if (_numberOfButtons == 1) + [alertViewContents addSubview:separatorLineView]; + else if (!_hideDoneButton) + [alertViewContents addSubview:separatorLineView]; + } + + if (alertViewWithVector) { + [alertViewContents.layer addSublayer:circleLayer]; + [alertViewContents addSubview:alertViewVector]; + } + + // SCALING ALERTVIEW - Before Animation + + alertViewContents.transform = CGAffineTransformMakeScale(1.15, 1.15); + + // APPLYING SHADOW + + [self.layer setShadowColor:[UIColor blackColor].CGColor]; + [self.layer setShadowOpacity:0.10f]; + [self.layer setShadowRadius:10.0f]; + [self.layer setShadowOffset:CGSizeMake(0.0f, 0.0f)]; + + if (_bounceAnimations) { + + UIInterpolatingMotionEffect *horizontalMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + horizontalMotionEffect.minimumRelativeValue = @(-22.5); + horizontalMotionEffect.maximumRelativeValue = @(22.5); + + UIInterpolatingMotionEffect *verticalMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + verticalMotionEffect.minimumRelativeValue = @(-22.5); + verticalMotionEffect.maximumRelativeValue = @(22.5); + + [alertViewContents addMotionEffect:horizontalMotionEffect]; + [alertViewContents addMotionEffect:verticalMotionEffect]; + + } + + [self showAlertView]; + +} + ++(NSBundle *)getResourcesBundle +{ + NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"FCAlertView" withExtension:@"bundle"]]; + return bundle; +} + +-(UIImage *)loadImageFromResourceBundle:(NSString *)imageName +{ + UIImage *icon = [UIImage imageNamed:imageName]; + + CGImageRef cgref = [icon CGImage]; + CIImage *cim = [icon CIImage]; + + if (cim == nil && cgref == NULL) + { + NSBundle *bundle = [FCAlertView getResourcesBundle]; + NSString *imageFileName = [NSString stringWithFormat:@"%@.png",imageName]; + UIImage *image = [UIImage imageNamed:imageFileName inBundle:bundle compatibleWithTraitCollection:nil]; + return image; + } + + return icon; +} + +#pragma mark - Default Types of Alerts + +- (void) makeAlertTypeWarning { + vectorImage = [self loadImageFromResourceBundle:@"close-round.png"]; + alertViewWithVector = 1; + self.colorScheme = self.flatRed; + alertType = @"Warning"; +} + +- (void) makeAlertTypeCaution { + vectorImage = [self loadImageFromResourceBundle:@"alert-round.png"]; + alertViewWithVector = 1; + self.colorScheme = self.flatOrange; + alertType = @"Caution"; +} + +- (void) makeAlertTypeSuccess { + vectorImage = [self loadImageFromResourceBundle:@"checkmark-round.png"]; + alertViewWithVector = 1; + self.colorScheme = self.flatGreen; + alertType = @"Success"; +} + +#pragma mark - Play Sound with Alert + +- (void) setAlertSoundWithFileName:(NSString *)filename { + + NSString *soundFilePath = [NSString stringWithFormat:@"%@/%@", + [[NSBundle mainBundle] resourcePath], filename]; + NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath]; + player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL + error:nil]; + player.numberOfLoops = 0; + +} + +#pragma mark - Presenting AlertView + +- (void) showAlertInView:(UIViewController *)view withTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons { + + // Blur Effect + + if (_blurBackground && NSClassFromString(@"UIVisualEffectView") != nil) { + UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + backgroundVisualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + backgroundVisualEffectView.frame = view.view.bounds; + _alertBackground.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5]; + [view.view addSubview:backgroundVisualEffectView]; + } + + // Adding Alert + + [self setAlertViewAttributes:title withSubtitle:subTitle withCustomImage:image withDoneButtonTitle:done andButtons:buttons]; + [view.view.window addSubview:self]; + +} + +- (void) showAlertInWindow:(UIWindow *)window withTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons { + + // Blur Effect + + if (_blurBackground && NSClassFromString(@"UIVisualEffectView") != nil) { + UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + backgroundVisualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + backgroundVisualEffectView.frame = window.bounds; + _alertBackground.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5]; + [window addSubview:backgroundVisualEffectView]; + } + + // Adding Alert + + [self setAlertViewAttributes:title withSubtitle:subTitle withCustomImage:image withDoneButtonTitle:done andButtons:buttons]; + [window addSubview:self]; + +} + +- (void) showAlertWithTitle:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons{ + + [self setAlertViewAttributes:title withSubtitle:subTitle withCustomImage:image withDoneButtonTitle:done andButtons:buttons]; + UIWindow *window = [UIApplication sharedApplication].windows.lastObject; + + // Blur Effect + if (_blurBackground && NSClassFromString(@"UIVisualEffectView") != nil) { + UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + backgroundVisualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; + backgroundVisualEffectView.frame = window.bounds; + _alertBackground.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.5]; + [window addSubview:backgroundVisualEffectView]; + } + + // Adding Alert + + [window addSubview:self]; + [window bringSubviewToFront:self]; + +} + +- (void)setAlertViewAttributes:(NSString *)title withSubtitle:(NSString *)subTitle withCustomImage:(UIImage *)image withDoneButtonTitle:(NSString *)done andButtons:(NSArray *)buttons{ + + self.title = title; + self.subTitle = subTitle; + + for (int i = 0; i < buttons.count; i++) { + NSDictionary *btnDict = @{@"title" : [buttons objectAtIndex:i], + @"action" : @0}; + [alertButtons addObject:btnDict]; + } + + _numberOfButtons = alertButtons.count; + doneTitle = done; + + if (!alertViewWithVector) + vectorImage = image; + + // Checks prior to presenting View + + [self checkCustomizationValid]; + [self safetyCloseCheck]; + +} + +#pragma mark - Showing and Hiding AlertView + +- (void) showAlertView { + + id strongDelegate = self.delegate; + + if ([strongDelegate respondsToSelector:@selector(FCAlertViewWillAppear:)]) { + [strongDelegate FCAlertViewWillAppear:self]; + } + + [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.alpha = 1; + if (_bounceAnimations) + alertViewContents.transform = CGAffineTransformMakeScale(0.95, 0.95); + else + alertViewContents.transform = CGAffineTransformMakeScale(1.0, 1.0); + } completion:^(BOOL finished) { + if (_bounceAnimations) + [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + alertViewContents.transform = CGAffineTransformMakeScale(1.00, 1.00); + } completion:nil]; + if (self.autoHideSeconds != 0) { + [self performSelector:@selector(dismissAlertView) withObject:nil afterDelay:self.autoHideSeconds]; + } + }]; + + // Playing Sound for Alert (when there is one) + + [player play]; + +} + +- (void) dismissAlertView { + + [UIView animateWithDuration:0.175 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.alpha = 0; + backgroundVisualEffectView.alpha = 0; + alertViewContents.transform = CGAffineTransformMakeScale(0.9, 0.9); + } completion:^(BOOL finished) { + + id strongDelegate = self.delegate; + + if ([strongDelegate respondsToSelector:@selector(FCAlertViewDismissed:)]) { + [strongDelegate FCAlertViewDismissed:self]; + } + + [backgroundVisualEffectView removeFromSuperview]; + [self removeFromSuperview]; + + }]; + +} + +#pragma mark - Action Block Methods + +- (void)addButton:(NSString *)title withActionBlock:(FCActionBlock)action { + + if (alertButtons.count < 2) { + if (action != nil) + [alertButtons addObject:@{@"title" : title, + @"action" : action}]; + else + [alertButtons addObject:@{@"title" : title, + @"action" : @0}]; + } + + _numberOfButtons = alertButtons.count; + +} + +- (void)doneActionBlock:(FCActionBlock)action { + + if (action != nil) + self.doneBlock = action; + +} + +#pragma mark - ACTIONS +#pragma mark Button Selection + +- (void)handleButton:(id)sender { + + id strongDelegate = self.delegate; + + UIButton *clickedButton = (UIButton*)sender; + + NSDictionary *btnDict = [alertButtons objectAtIndex:[sender tag]]; + + if (btnDict != nil) { + if ([btnDict objectForKey:@"action"] != nil && ![[btnDict objectForKey:@"action"] isEqual:@0]) { + FCActionBlock block = [btnDict objectForKey:@"action"]; + if (block) + block(); + } + } + + if ([strongDelegate respondsToSelector:@selector(FCAlertView:clickedButtonIndex:buttonTitle:)]) { + [strongDelegate FCAlertView:self clickedButtonIndex:[sender tag] buttonTitle:clickedButton.titleLabel.text]; + } + + // Rertun Text from TextField to Block + + FCTextReturnBlock textReturnBlock = [[alertTextFields firstObject] objectForKey:@"action"]; + if (textReturnBlock) + textReturnBlock(_textField.text); + + [self dismissAlertView]; + +} + +#pragma mark - Button Actions + +- (void) btnTouched { + + if (_bounceAnimations) { + + [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + self.alpha = 1; + if (_bounceAnimations) + alertViewContents.transform = CGAffineTransformMakeScale(0.95, 0.95); + }completion:nil]; + + } + +} + +- (void) donePressed { + + if (self.doneBlock) + self.doneBlock(); + + id strongDelegate = self.delegate; + + if ([strongDelegate respondsToSelector:@selector(FCAlertDoneButtonClicked:)]) { + [strongDelegate FCAlertDoneButtonClicked:self]; + } + + FCTextReturnBlock textReturnBlock = [[alertTextFields firstObject] objectForKey:@"action"]; + NSString *textF = _textField.text; + if (textReturnBlock) + textReturnBlock(_textField.text); + + [self dismissAlertView]; + +} + +- (void) btnReleased { + + if (_bounceAnimations) { + + [UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + alertViewContents.transform = CGAffineTransformMakeScale(1.05, 1.05); + }completion:^(BOOL finished) { + [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + alertViewContents.transform = CGAffineTransformMakeScale(1.00, 1.00); + }completion:nil]; + }]; + + } + +} + +#pragma mark - TEXT FIELD METHODS +#pragma mark - Text Field Begin Editing + +#pragma mark - Adding Alert TextField Block Method + +- (void)addTextFieldWithPlaceholder:(NSString *)placeholder andTextReturnBlock:(FCTextReturnBlock)textReturn { + + if (textReturn != nil) + [alertTextFields addObject:@{@"placeholder" : placeholder, + @"action" : textReturn}]; + else + [alertTextFields addObject:@{@"placeholder" : placeholder, + @"action" : @0}]; + +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + + currentAVCFrames = alertViewContents.frame; + + [UIView animateWithDuration:0.30 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + alertViewContents.frame = CGRectMake(currentAVCFrames.origin.x, + currentAVCFrames.origin.y - 80, + currentAVCFrames.size.width, + currentAVCFrames.size.height); + } completion:nil]; + +} + +#pragma mark - Text Field End Editing + +- (void)textFieldDidEndEditing:(UITextField *)textField { + + NSString *text = textField.text; + + [UIView animateWithDuration:0.30 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ + alertViewContents.frame = CGRectMake(currentAVCFrames.origin.x, + currentAVCFrames.origin.y, + currentAVCFrames.size.width, + currentAVCFrames.size.height); + } completion:nil]; + +} + +#pragma mark - Text Field Returned + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + + [textField endEditing:YES]; + +} + +#pragma mark - Text Field Changed + +-(void)textChanged:(UITextField *)textField +{ + + +} + +@end diff --git a/Pods/FCAlertView/LICENSE b/Pods/FCAlertView/LICENSE new file mode 100644 index 0000000..ba64e8c --- /dev/null +++ b/Pods/FCAlertView/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Nima Tahami + +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/FCAlertView/README.md b/Pods/FCAlertView/README.md new file mode 100644 index 0000000..b4889b5 --- /dev/null +++ b/Pods/FCAlertView/README.md @@ -0,0 +1,413 @@ +FCAlertView +============ + +FCAlertView is a Flat Customizable AlertView, written in Objective C + +[![CI Status](http://img.shields.io/travis/Nima Tahami/FCAlertView.svg?style=flat)](https://travis-ci.org/Nima Tahami/FCAlertView) +[![Version](https://img.shields.io/cocoapods/v/FCAlertView.svg?style=flat)](http://cocoapods.org/pods/FCAlertView) +[![License](https://img.shields.io/cocoapods/l/FCAlertView.svg?style=flat)](http://cocoapods.org/pods/FCAlertView) +[![Platform](https://img.shields.io/cocoapods/p/FCAlertView.svg?style=flat)](http://cocoapods.org/pods/FCAlertView) + +![Logo](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/RepoLogo2.png) + +![BackgroundImage](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot.png) +![BackgroundImage](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot2.png) +![BackgroundImage](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot3.png) +![BackgroundImage](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot4.png) +![BackgroundImage](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot5.png) + +#Swift + +For the swift version of FCAlertView, [Click Here](https://github.com/k9101/FCAlertView). Credits to [Kris Penney](https://github.com/k9101) for writting the swift library. + +#Installation + +### Using CocoaPods + +FCAlertView is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod 'FCAlertView' +``` + +### Manually + +Clone or Download this Repo. Then simply drag the folder ```FCAlertView``` to your Xcode project. Please make sure to drag the whole folder, which includes assets needed for some alert types. + +# Example + +FCAlertView comes with an example app that you can use to try out all of the customizations below. It's recommended that you go through all of the docs before using the example app. To use the example app, clone or download FCAlertView, open and run ```Example/FCAlertView.xcworkspace```. + +#Adding FCAlertView + +Start by adding the following to your desired View Controller: + +```Objective-C +#import "FCAlertView.h" +``` + +### Presenting an FCAlertView + +```Objective-C +FCAlertView *alert = [[FCAlertView alloc] init]; + +[alert showAlertInView:self + withTitle:@"Alert Title" + withSubtitle:@"This is your alert's subtitle. Keep it short and concise. 😜👌" + withCustomImage:nil + withDoneButtonTitle:nil + andButtons:nil]; + +``` + +#### Showing Options + +You can also present your FCAlertView using the following: + +##### By Selecting a specific UIWindow + +```Objective-C +[alert showAlertInWidnow:self.view.window + withTitle:@"Alert Title" + withSubtitle:@"This is your alert's subtitle. Keep it short and concise. 😜👌" + withCustomImage:nil + withDoneButtonTitle:nil + andButtons:nil]; +``` + +##### Or just by presenting it on the current UIApplication Window (this will also bring your alert to the front so that keyboard or any other element don't cover it) + +```Objective-C +[alert showAlertWithTitle:@"Alert Title" + withSubtitle:@"This is your alert's subtitle. Keep it short and concise. 😜👌" + withCustomImage:nil + withDoneButtonTitle:nil + andButtons:nil]; +``` + +# Base Customizations + +- **Title (NSString):** You can leave the Title as ```nil``` or Give it an ```NSString```. + +- **Subtitle (NSString):** FCAlertView always requires a subtitle, even if you want just a few words, add it here instead of the title (then leave the title as nil). *Take a look at [Screenshot 2](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot2.png) for an example*. + +- **CustomImage (UIImage):** You can leave this image as ```nil``` or Give it a ```UIImage``` which will show at the top of the alert. *Take a look at [Screenshot 4](https://github.com/nimati/FCAlertView/blob/master/Images/ScreenShots/ScreenShot4.png) for an example*. + +- **DoneButtonTitle (NSString):** You can leave this as ```nil``` to show "Ok" as the dismiss button for the AlertView, or Give it an ```NSString```. + +- **Buttons (NSArray of NSStrings):** If you want to add buttons to your alert, simply add an array of 1 or 2 button titles as ```NSString``` here, anything more will be ignored as 2 is the max custom buttons you can add (aside from the done button). Read more about buttons and actions further down. + +## Adding Buttons With Action Blocks + +Alternatively, you can add buttons to FCAlertView with action block like so: + +```Objective-C +[alert addButton:@"Button" withActionBlock:^{ + // Put your action here +}]; +``` + +### Action Block for Done Button + +```Objective-C +[alert doneActionBlock:^{ + // Put your action here +}]; +``` + +# Extra Customizations + +This section includes all the tiny details that you can customize your alert with, which makes FCAlertView very customizable. Or leave it as is and enjoy the simplicity. + +### Color Scheme + +By default, FCAlertView doesn't include a color scheme, much like UIAlertView, but you can add one by adding this line: + +```Objective-C +alert.colorScheme = [UIColor colorWithRed:150.0f/255.0f green:150.0f/255.0f blue:150.0f/255.0f alpha:1.0]; +``` + +If you add a custom image to your alert, it will be tinted with the color scheme by default. To keep this from happening, add this: + +```Objective-C +alert.avoidCustomImageTint = 1; // Off by default +``` + +FCAlertView also comes with a set of pre-made colors that you can use: + +![alt text](https://github.com/nimati/FCAlertView/blob/master/Images/FlatColors.png?raw=true "Flat Colors") + +#####*Credit goes to [flatuicolors.com](http://flatuicolors.com) for the Beautiful Palette of Flat Colors* + +Simply choose the color you'd like to use for your AlertView, and add: + +```Objective-C +alert.colorScheme = alert.flatBlue; // Replace "Blue" with your preferred color from the image above +``` + +### Title and Subtitle Colors + +#### Change Title Color by Adding + +```Objective-C +alert.titleColor = alertView.flatPurple; +``` + +#### Change SubTitle Color by Adding + +```Objective-C +alert.subTitleColor = alertView.flatBlue; +``` + +### AlertView Rounded Corners + +Change the Rounding of the FCAlertView's corners as desired using: + +```Objective-C +alert.cornerRadius = 4; // Replace 4 with your desired corner radius amount (Set to 0.1 if you don't want rounding) + +``` + +### Alert Types + +FCAlertView comes with 3 pre-designed custom alert types. Success, Caution, or Warning, simply add the one line after initializing FCAlertView. + +#### Success + +```Objective-C +[alert makeAlertTypeSuccess]; +``` + +#### Caution + +```Objective-C +[alert makeAlertTypeCaution]; +``` + +#### Warning + +```Objective-C +[alert makeAlertTypeWarning]; +``` + +### Dismissing FCAlertView + +There are multiple ways you can dismiss an FCAlertView + +#### Close on Outside Touch + +When the user taps anywhere outside the alert, you can dismiss it by adding this line: + +```Objective-C +alert.dismissOnOutsideTouch = YES; +``` + +#### Auto-Close the Alert + +Dismiss the AlertView when a certain time has elapsed after the AlertView is presented, by adding this line: + +```Objective-C +alert.autoHideSeconds = 5; // Replace 5 with the number of Seconds you'd like the view to appear for before dismissing itself +``` + +#### Done Button or Any Custom Buttons + +All Buttons including the Done/Dismiss Button will make the FCAlertView dismiss. + +#### Dismissing it yourself + +If you'd like to dismiss the AlertView yourself, simply add the following line to where you need it: + +```Objective-C +[alert dismissAlertView]; +``` + +### Hiding Done/Dismiss Button + +If you'd like to have no buttons on your AlertView (to simply display a notification or approval of something) or you want all your buttons to be a custom one which you've added yourself. Simply hide the Done buttons by adding this line: + +```Objective-C +alert.hideDoneButton = YES; +``` + +### Hiding All Buttons + +If you'd like to simply hide all buttons from your alert, you can do so by adding this line: + +```Objective-C +alert.hideAllButtons = YES; +``` + +Please note that hiding Done/Dismiss Button and/or Hiding All Buttons would trigger a safety close mechanism by forcing Close on Outside Touch to stay ON. + +## New Customizations (after V1.1.0) +### Blur Background + +Simply adds a blur to the background of the window/view behind the alertview: + +```Objective-C +alert.blurBackground = 1; +``` + +### Bounce/Natural Animations + +Adds more natural animations to the alertview, such as reactive bounce buttons and more. Add this line: + +```Objective-C +alert.bounceAnimations = 1; +``` +### Adding TextFields + +Simply add a single textfield to your alert, by adding this line and get the returned text when any of the AlertView's buttons are pressed: + +```Objective-C +[alert addTextFieldWithPlaceholder:@"Email Address" andTextReturnBlock:^(NSString *text) { + NSLog(@"The Email Address is: %@", text); // Do what you'd like with the text returned from the field +}]; +``` + +### Sounds + +Add the following line to play an audio when the alert opens, simply pass it the name of your audiofile: + +```Objective-C +[alert setAlertSoundWithFileName:@"Ding.mp3"]; +``` + +**Note:** It's best to add these Frameworks to your project for this to work: ```AVFoundation``` and ```AudioToolbox```. + +# Button Actions + +To add actions to your buttons, if you're not adding buttons with action blocks, you have to first delegate your FCAlertView with your view, and then add a helper method which will detect button touches. Here's how you can add an alert with buttons and perform actions: + +First add ```FCAlertViewDelegate``` to your View Controller's ```@interface``` as such: + +```Objective-C +#import +#import "FCAlertView.h" + +@interface ViewController : UIViewController + +@end +``` + +Now add your FCAlertView with Buttons where you need to present it: + +```Objective-C + FCAlertView *alert = [[FCAlertView alloc] init]; + + alert.delegate = self; + + [alert showAlertInView:self + withTitle:@"Alert Title" + withSubtitle:@"This is your alert's subtitle. Keep it short and concise. 😜👌" + withCustomImage:nil + withDoneButtonTitle:nil + andButtons:@[@"Button 1", @"Button 2"]]; // Set your button titles here + +``` + +After adding your FCAlertView, you can detect button touches by adding this method to your class: + +```Objective-C +- (void) FCAlertView:(FCAlertView *)alertView clickedButtonIndex:(NSInteger)index buttonTitle:(NSString *)title { + if ([title isEqualToString:@"Button 1"]) { // Change "Button 1" to the title of your first button + // Perform Action for Button 1 + } + + if ([title isEqualToString:@"Button 2"]) { + // Perform Action for Button 2 + } +} +``` + +#### Done Button Method + +If you'd also like to detect button touch for the Done/Dismiss button, simply add this method to your class: + +```Objective-C +- (void)FCAlertDoneButtonClicked:(FCAlertView *)alertView { + // Done Button was Pressed, Perform the Action you'd like here. +} +``` + +# Other Helper Methods + +Make sure to add ```FCAlertViewDelegate``` to your View Controller's ```@interface``` as such: + +```Objective-C +#import +#import "FCAlertView.h" + +@interface ViewController : UIViewController + +@end +``` + +and setting the delegate of your FCAlertView, as such: + +```Objective-C + FCAlertView *alert = [[FCAlertView alloc] init]; + alert.delegate = self; +``` + +### Detect when FCAlertView has been dismissed + +```Objective-C +- (void)FCAlertViewDismissed:(FCAlertView *)alertView { + // Your FCAlertView was Dismissed, Perform the Action you'd like here. +} +``` + +### Detect when FCAlertView is about to present + +```Objective-C +- (void)FCAlertViewWillAppear:(FCAlertView *)alertView { + // Your FCAlertView will be Presented, Perform the Action you'd like here. +} +``` + +# More Customizations + +FCAlertView is an ongoing project with the goal of becoming the most used custom AlertView for iOS. Improvements and changes are on the way, and here are some of the things that are coming soon with it: + +- Swift Friendly ✓ +- Adding TextFields ✓ +- Blur Background ✓ +- Frame Customizations ✓ +- Alert Sounds ✓ +- Landscape Orientation +- More Custom Animations +- More Types of Alerts (including Progress Types) +- iPad Friendly Alerts +- Improved Button Highlighting and Customizations +- Something Missing? Email your suggestion [here](mailto:nima6tahami@gmail.com) + +About FCAlertView +----------------- + +FCAlertView is a fully customizable and beautifully designed AlertView. I designed FCAlertView because I've always wanted to have access to change the different attributes of the default UIAlertView. Design wise, FCAlertView is similar looking to the default AlertView, however, as you start customizing it for your specific need, you realize it can do a lot more while looking flat and sharp. + +FCAlertView lets you do things such as specify the number of buttons, the color scheme of the view, adding a small image to it, hide the view after a certain time, and more. A full description of how to customize FCAlertView to fit your alert can be found on http://github.com/nimati/FCAlertView. + +The Vision for FC Libraries +--------------------------- + +My goal is to create a set of different libraries, each targetting a certain UI element of iOS, with the goal to improve the design and add more customizations. As such, FCAlertView is a more Flat/Customizable AlertView. With this mindset, I'd like to create more FC libraries, such as FCActionSheet, FCNotification (for quick, in app alerts), FCPopoverView, FCGuideView (for guiding your users around your app). If you also have a suggestion for an FC Library, please send it [here](mailto:nima6tahami@gmail.com). + +> Ultimately, FC Libraries is here to improve the look and feel of your app for your end users. So all improvements and suggestions are welcome. + +Cheers 🍻 + +### Author + +Created and designed by [Nima Tahami](http://nimatahami.com). + +Credits for the Beautiful Color Palette goes to [flatuicolors.com](http://flatuicolors.com). + +Credit for the Beautiful Icons go to [ionicons.com](http://ionicons.com). + +### License + +FCAlertView is available under the MIT license. See the LICENSE file for more info. diff --git a/Pods/Fabric/Fabric.framework/README b/Pods/Fabric/Fabric.framework/README new file mode 100644 index 0000000..3b1fbe2 --- /dev/null +++ b/Pods/Fabric/Fabric.framework/README @@ -0,0 +1 @@ +We've now combined all our supported platforms into a single podspec. As a result, we moved our run script to a new location for Cocoapods projects: ${PODS_ROOT}/Fabric/run. To avoid breaking builds that reference the old location of the run script, we've placed this dummy script that calls to the correct location, while providing a helpful warning in Xcode if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning! diff --git a/Pods/Fabric/Fabric.framework/run b/Pods/Fabric/Fabric.framework/run new file mode 100755 index 0000000..b9edd17 --- /dev/null +++ b/Pods/Fabric/Fabric.framework/run @@ -0,0 +1,6 @@ +if [[ -z $PODS_ROOT ]]; then + echo "error: The run binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Fabric/run'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the run binary to fix this issue." +else + echo "warning: The run script is now located at '$"{"PODS_ROOT"}"/Fabric/run'. To remove this warning, update your Run Script Build Phase to point to this new location." + sh "${PODS_ROOT}/Fabric/run" "$@" +fi diff --git a/Pods/Fabric/README.md b/Pods/Fabric/README.md new file mode 100644 index 0000000..1db8549 --- /dev/null +++ b/Pods/Fabric/README.md @@ -0,0 +1,43 @@ +![Fabric Header](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-fabric-header.png) + +# Fabric + +## Overview + +[Fabric](https://www.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Twitter and the team that built Crashlytics, Fabric provides an easy way to manage all your SDKs so that you’ll never have to worry about tedious configurations or juggling different accounts. We let you get right into coding and building the next big app. + +For a full list of SDK provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits). + +## Setup + +The Fabric Pod is a dependency for all Fabric SDKs and is included when installing any Fabric related Pods. General setup instructions are shown below; however, these vary depending on the selected SDK. + +1. Visit [https://fabric.io/sign_up](https://fabric.io/sign_up) to create your Fabric account and to download Fabric.app. + +1. Open Fabric.app, login and select an SDK to install. + + ![Fabric Plugin](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-fabric-plugin.png) + +1. The Fabric app automatically detects when a project uses CocoaPods and gives you the option to install via the Podfile or Xcode. + + ![Fabric Installation Options](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-pod-installation-option.png) + +1. Select the Podfile option and follow the installation instructions to update your Podfile. Note: the example below is for the Crashlytics SDK. The instructions will vary based on the selected SDK. + + ![Fabric Podfile Instructions](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-podfile-instructions.png) + +1. Add a Run Script Build Phase and build your app. + + ![Fabric Run Script Build Phase](https://docs.fabric.io/ios/cocoapod-readmes/cocoapods-rsbp.png) + +1. Initialize the SDK by inserting code outlined in Fabric.app. + +1. Run your app to finish the installation. + +## Resources + +* [Documentation](https://docs.fabric.io/) +* [Forums](https://twittercommunity.com/c/fabric) +* [Website](https://www.fabric.io) +* Follow us on Twitter: [@fabric](https://twitter.com/fabric) +* Follow us on Periscope: [Fabric](https://periscope.tv/fabric) and [TwitterDev](https://periscope.tv/twitterdev) diff --git a/Pods/Fabric/iOS/Fabric.framework/Fabric b/Pods/Fabric/iOS/Fabric.framework/Fabric new file mode 100755 index 0000000..c48028c Binary files /dev/null and b/Pods/Fabric/iOS/Fabric.framework/Fabric differ diff --git a/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h b/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h new file mode 100644 index 0000000..3a9355a --- /dev/null +++ b/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h @@ -0,0 +1,51 @@ +// +// FABAttributes.h +// Fabric +// +// Copyright (C) 2015 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#pragma once + +#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x))) + +#if !__has_feature(nullability) + #define nonnull + #define nullable + #define _Nullable + #define _Nonnull +#endif + +#ifndef NS_ASSUME_NONNULL_BEGIN + #define NS_ASSUME_NONNULL_BEGIN +#endif + +#ifndef NS_ASSUME_NONNULL_END + #define NS_ASSUME_NONNULL_END +#endif + + +/** + * The following macros are defined here to provide + * backwards compatability. If you are still using + * them you should migrate to the native nullability + * macros. + */ +#define fab_nullable nullable +#define fab_nonnull nonnull +#define FAB_NONNULL __fab_nonnull +#define FAB_NULLABLE __fab_nullable +#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN +#define FAB_END_NONNULL NS_ASSUME_NONNULL_END diff --git a/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h b/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h new file mode 100644 index 0000000..ecbdb53 --- /dev/null +++ b/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h @@ -0,0 +1,82 @@ +// +// Fabric.h +// Fabric +// +// Copyright (C) 2015 Twitter, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "FABAttributes.h" + +NS_ASSUME_NONNULL_BEGIN + +#if TARGET_OS_IPHONE +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + #error "Fabric's minimum iOS version is 6.0" +#endif +#else +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + #error "Fabric's minimum OS X version is 10.7" +#endif +#endif + +/** + * Fabric Base. Coordinates configuration and starts all provided kits. + */ +@interface Fabric : NSObject + +/** + * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. + * + * For example, in Objective-C: + * + * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` + * + * Swift: + * + * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` + * + * Only the first call to this method is honored. Subsequent calls are no-ops. + * + * @param kitClasses An array of kit Class objects + * + * @return Returns the shared Fabric instance. In most cases this can be ignored. + */ ++ (instancetype)with:(NSArray *)kitClasses; + +/** + * Returns the Fabric singleton object. + */ ++ (instancetype)sharedSDK; + +/** + * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. + */ +@property (nonatomic, assign) BOOL debug; + +/** + * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. + */ +- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); + +/** + * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. + */ ++ (instancetype)new FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Pods/Fabric/iOS/Fabric.framework/Info.plist b/Pods/Fabric/iOS/Fabric.framework/Info.plist new file mode 100644 index 0000000..4240ec4 Binary files /dev/null and b/Pods/Fabric/iOS/Fabric.framework/Info.plist differ diff --git a/Fabric.framework/Modules/module.modulemap b/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap similarity index 100% rename from Fabric.framework/Modules/module.modulemap rename to Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap diff --git a/Pods/Fabric/iOS/Fabric.framework/run b/Pods/Fabric/iOS/Fabric.framework/run new file mode 100755 index 0000000..9058ea6 --- /dev/null +++ b/Pods/Fabric/iOS/Fabric.framework/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# run +# +# Copyright (c) 2015 Crashlytics. All rights reserved. + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# Quote path in case of spaces or special chars +DIR="\"${DIR}" + +PATH_SEP="/" +VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" +UPLOAD_COMMAND="uploadDSYM\" $@ run-script" + +# Ensure params are as expected, run in sync mode to validate +eval $DIR$PATH_SEP$VALIDATE_COMMAND +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, upload dSYM in background to prevent Xcode from waiting +# Note: Validation is performed again before upload. +# Output can still be found in Console.app +eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & diff --git a/Pods/Fabric/iOS/Fabric.framework/uploadDSYM b/Pods/Fabric/iOS/Fabric.framework/uploadDSYM new file mode 100755 index 0000000..faefbb3 Binary files /dev/null and b/Pods/Fabric/iOS/Fabric.framework/uploadDSYM differ diff --git a/Pods/Fabric/run b/Pods/Fabric/run new file mode 100755 index 0000000..9058ea6 --- /dev/null +++ b/Pods/Fabric/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# run +# +# Copyright (c) 2015 Crashlytics. All rights reserved. + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# Quote path in case of spaces or special chars +DIR="\"${DIR}" + +PATH_SEP="/" +VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" +UPLOAD_COMMAND="uploadDSYM\" $@ run-script" + +# Ensure params are as expected, run in sync mode to validate +eval $DIR$PATH_SEP$VALIDATE_COMMAND +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, upload dSYM in background to prevent Xcode from waiting +# Note: Validation is performed again before upload. +# Output can still be found in Console.app +eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & diff --git a/Pods/Fabric/upload-symbols b/Pods/Fabric/upload-symbols new file mode 100755 index 0000000..3dcea45 Binary files /dev/null and b/Pods/Fabric/upload-symbols differ diff --git a/Pods/Fabric/uploadDSYM b/Pods/Fabric/uploadDSYM new file mode 100755 index 0000000..faefbb3 Binary files /dev/null and b/Pods/Fabric/uploadDSYM differ diff --git a/Pods/Firebase/Core/Sources/Firebase.h b/Pods/Firebase/Core/Sources/Firebase.h new file mode 100755 index 0000000..90798a6 --- /dev/null +++ b/Pods/Firebase/Core/Sources/Firebase.h @@ -0,0 +1,52 @@ +#import +#import + +#if !defined(__has_include) + #error "Firebase.h won't import anything if your compiler doesn't support __has_include. Please \ + import the headers individually." +#else + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + + #if __has_include() + #import + #endif + +#endif // defined(__has_include) diff --git a/Pods/Firebase/Core/Sources/module.modulemap b/Pods/Firebase/Core/Sources/module.modulemap new file mode 100755 index 0000000..3685b54 --- /dev/null +++ b/Pods/Firebase/Core/Sources/module.modulemap @@ -0,0 +1,4 @@ +module Firebase { + export * + header "Firebase.h" +} \ No newline at end of file diff --git a/Pods/Firebase/README.md b/Pods/Firebase/README.md new file mode 100755 index 0000000..bfc5419 --- /dev/null +++ b/Pods/Firebase/README.md @@ -0,0 +1,76 @@ +# Firebase APIs for iOS + +Simplify your iOS development, grow your user base, and monetize more +effectively with Firebase services. + +Much more information can be found at [https://firebase.google.com](https://firebase.google.com). + +## Install a Firebase SDK using CocoaPods + +Firebase distributes several iOS specific APIs and SDKs via CocoaPods. +You can install the CocoaPods tool on OS X by running the following command from +the terminal. Detailed information is available in the [Getting Started +guide](https://guides.cocoapods.org/using/getting-started.html#getting-started). + +``` +$ sudo gem install cocoapods +``` + +## Try out an SDK + +You can try any of the SDKs with `pod try`. Run the following command and select +the SDK you are interested in when prompted: + +``` +$ pod try Firebase +``` + +Note that some SDKs may require credentials. More information is available in +the SDK-specific documentation at [https://firebase.google.com/docs/](https://firebase.google.com/docs/). + +## Add a Firebase SDK to your iOS app + +CocoaPods is used to install and manage dependencies in existing Xcode projects. + +1. Create an Xcode project, and save it to your local machine. +2. Create a file named `Podfile` in your project directory. This file defines + your project's dependencies, and is commonly referred to as a Podspec. +3. Open `Podfile`, and add your dependencies. A simple Podspec is shown here: + + ``` + platform :ios, '7.0' + pod 'Firebase' + ``` + +4. Save the file. +5. Open a terminal and `cd` to the directory containing the Podfile. + + ``` + $ cd /project/ + ``` + +6. Run the `pod install` command. This will install the SDKs specified in the + Podspec, along with any dependencies they may have. + + ``` + $ pod install + ``` + +7. Open your app's `.xcworkspace` file to launch Xcode. + Use this file for all development on your app. +8. You can also install other Firebase SDKs by adding the subspecs in the + Podfile. + + ``` + pod 'Firebase/AdMob' + pod 'Firebase/Analytics' + pod 'Firebase/AppIndexing' + pod 'Firebase/Auth' + pod 'Firebase/Crash' + pod 'Firebase/Database' + pod 'Firebase/DynamicLinks' + pod 'Firebase/Invites' + pod 'Firebase/Messaging' + pod 'Firebase/RemoteConfig' + pod 'Firebase/Storage' + ``` diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/FirebaseAnalytics new file mode 100755 index 0000000..048c8d1 Binary files /dev/null and b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h new file mode 100755 index 0000000..e3ff4c1 --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h @@ -0,0 +1,57 @@ +#import + +#import "FIRAnalytics.h" + +/** + * Provides App Delegate handlers to be used in your App Delegate. + * + * To save time integrating Firebase Analytics in an application, Firebase Analytics does not + * require delegation implementation from the AppDelegate. Instead this is automatically done by + * Firebase Analytics. Should you choose instead to delegate manually, you can turn off the App + * Delegate Proxy by adding FirebaseAppDelegateProxyEnabled into your app's Info.plist and setting + * it to NO, and adding the methods in this category to corresponding delegation handlers. + * + * To handle Universal Links, you must return YES in + * [UIApplicationDelegate application:didFinishLaunchingWithOptions:]. + */ +@interface FIRAnalytics (AppDelegate) + +/** + * Handles events related to a URL session that are waiting to be processed. + * + * For optimal use of Firebase Analytics, call this method from the + * [UIApplicationDelegate application:handleEventsForBackgroundURLSession:completionHandler] + * method of the app delegate in your app. + * + * @param identifier The identifier of the URL session requiring attention. + * @param completionHandler The completion handler to call when you finish processing the events. + * Calling this completion handler lets the system know that your app's user interface is + * updated and a new snapshot can be taken. + */ ++ (void)handleEventsForBackgroundURLSession:(NSString *)identifier + completionHandler:(void (^)(void))completionHandler; + +/** + * Handles the event when the app is launched by a URL. + * + * Call this method from [UIApplicationDelegate application:openURL:options:] (on iOS 9.0 and + * above), or [UIApplicationDelegate application:openURL:sourceApplication:annotation:] (on iOS 8.x + * and below) in your app. + * + * @param url The URL resource to open. This resource can be a network resource or a file. + */ ++ (void)handleOpenURL:(NSURL *)url; + +/** + * Handles the event when the app receives data associated with user activity that includes a + * Universal Link (on iOS 9.0 and above). + * + * Call this method from [UIApplication continueUserActivity:restorationHandler:] in your app + * delegate (on iOS 9.0 and above). + * + * @param userActivity The activity object containing the data associated with the task the user + * was performing. + */ ++ (void)handleUserActivity:(id)userActivity; + +@end diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h new file mode 100755 index 0000000..43ee86e --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h @@ -0,0 +1,71 @@ +#import + +#import "FIREventNames.h" +#import "FIRParameterNames.h" +#import "FIRUserPropertyNames.h" + +/// The top level Firebase Analytics singleton that provides methods for logging events and setting +/// user properties. See the developer guides for general +/// information on using Firebase Analytics in your apps. +@interface FIRAnalytics : NSObject + +/// Logs an app event. The event can have up to 25 parameters. Events with the same name must have +/// the same parameters. Up to 500 event names are supported. Using predefined events and/or +/// parameters is recommended for optimal reporting. +/// +/// The following event names are reserved and cannot be used: +///
    +///
  • app_clear_data
  • +///
  • app_remove
  • +///
  • app_update
  • +///
  • error
  • +///
  • first_open
  • +///
  • in_app_purchase
  • +///
  • notification_dismiss
  • +///
  • notification_foreground
  • +///
  • notification_open
  • +///
  • notification_receive
  • +///
  • os_update
  • +///
  • session_start
  • +///
  • user_engagement
  • +///
+/// +/// @param name The name of the event. Should contain 1 to 32 alphanumeric characters or +/// underscores. The name must start with an alphabetic character. Some event names are +/// reserved. See FIREventNames.h for the list of reserved event names. The "firebase_" prefix +/// is reserved and should not be used. Note that event names are case-sensitive and that +/// logging two events whose names differ only in case will result in two distinct events. +/// @param parameters The dictionary of event parameters. Passing nil indicates that the event has +/// no parameters. Parameter names can be up to 24 characters long and must start with an +/// alphabetic character and contain only alphanumeric characters and underscores. Only NSString +/// and NSNumber (signed 64-bit integer and 64-bit floating-point number) parameter types are +/// supported. NSString parameter values can be up to 36 characters long. The "firebase_" prefix +/// is reserved and should not be used for parameter names. ++ (void)logEventWithName:(nonnull NSString *)name + parameters:(nullable NSDictionary *)parameters; + +/// Sets a user property to a given value. Up to 25 user property names are supported. Once set, +/// user property values persist throughout the app lifecycle and across sessions. +/// +/// The following user property names are reserved and cannot be used: +///
    +///
  • first_open_time
  • +///
  • last_deep_link_referrer
  • +///
  • user_id
  • +///
+/// +/// @param value The value of the user property. Values can be up to 36 characters long. Setting the +/// value to nil removes the user property. +/// @param name The name of the user property to set. Should contain 1 to 24 alphanumeric characters +/// or underscores and must start with an alphabetic character. The "firebase_" prefix is +/// reserved and should not be used for user property names. ++ (void)setUserPropertyString:(nullable NSString *)value forName:(nonnull NSString *)name; + +/// Sets the user ID property. This feature must be used in accordance with +/// Google's Privacy Policy +/// +/// @param userID The user ID to ascribe to the user of this app on this device, which must be +/// non-empty and no more than 36 characters long. Setting userID to nil removes the user ID. ++ (void)setUserID:(nullable NSString *)userID; + +@end diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h new file mode 100755 index 0000000..dc227a4 --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h @@ -0,0 +1 @@ +#import diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRApp.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRApp.h new file mode 100755 index 0000000..de24da1 --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRApp.h @@ -0,0 +1 @@ +#import diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRConfiguration.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRConfiguration.h new file mode 100755 index 0000000..be2ff7b --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRConfiguration.h @@ -0,0 +1 @@ +#import diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h new file mode 100755 index 0000000..f68967b --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h @@ -0,0 +1,321 @@ +/// @file FIREventNames.h +/// +/// Predefined event names. +/// +/// An Event is an important occurrence in your app that you want to measure. You can report up to +/// 500 different types of Events per app and you can associate up to 25 unique parameters with each +/// Event type. Some common events are suggested below, but you may also choose to specify custom +/// Event types that are associated with your specific app. Each event type is identified by a +/// unique name. Event names can be up to 32 characters long, may only contain alphanumeric +/// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_" +/// prefix is reserved and should not be used. + +/// Add Payment Info event. This event signifies that a user has submitted their payment information +/// to your app. +static NSString *const kFIREventAddPaymentInfo = @"add_payment_info"; + +/// E-Commerce Add To Cart event. This event signifies that an item was added to a cart for +/// purchase. Add this event to a funnel with kFIREventEcommercePurchase to gauge the effectiveness +/// of your checkout process. Note: If you supply the {@link kFIRParameterValue} parameter, you must +/// also supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed +/// accurately. Params: +/// +///
    +///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
  • {@link kFIRParameterItemName} (NSString)
  • +///
  • {@link kFIRParameterItemCategory} (NSString)
  • +///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • +///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • +///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • +///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • +///
+static NSString *const kFIREventAddToCart = @"add_to_cart"; + +/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist. +/// Use this event to identify popular gift items in your app. Note: If you supply the +/// {@link kFIRParameterValue} parameter, you must also supply the {@link kFIRParameterCurrency} +/// parameter so that revenue metrics can be computed accurately. Params: +/// +///
    +///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
  • {@link kFIRParameterItemName} (NSString)
  • +///
  • {@link kFIRParameterItemCategory} (NSString)
  • +///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • +///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
+static NSString *const kFIREventAddToWishlist = @"add_to_wishlist"; + +/// App Open event. By logging this event when an App is moved to the foreground, developers can +/// understand how often users leave and return during the course of a Session. Although Sessions +/// are automatically reported, this event can provide further clarification around the continuous +/// engagement of app-users. +static NSString *const kFIREventAppOpen = @"app_open"; + +/// E-Commerce Begin Checkout event. This event signifies that a user has begun the process of +/// checking out. Add this event to a funnel with your kFIREventEcommercePurchase event to gauge the +/// effectiveness of your checkout process. Note: If you supply the {@link kFIRParameterValue} +/// parameter, you must also supply the {@link kFIRParameterCurrency} parameter so that revenue +/// metrics can be computed accurately. Params: +/// +///
    +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • +///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) +/// for travel bookings
  • +///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • +///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • +///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • +///
+static NSString *const kFIREventBeginCheckout = @"begin_checkout"; + +/// Earn Virtual Currency event. This event tracks the awarding of virtual currency in your app. Log +/// this along with {@link kFIREventSpendVirtualCurrency} to better understand your virtual economy. +/// Params: +/// +///
    +///
  • {@link kFIRParameterVirtualCurrencyName} (NSString)
  • +///
  • {@link kFIRParameterValue} (signed 64-bit integer or double as NSNumber)
  • +///
+static NSString *const kFIREventEarnVirtualCurrency = @"earn_virtual_currency"; + +/// E-Commerce Purchase event. This event signifies that an item was purchased by a user. Note: +/// This is different from the in-app purchase event, which is reported automatically for App +/// Store-based apps. Note: If you supply the {@link kFIRParameterValue} parameter, you must also +/// supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed +/// accurately. Params: +/// +///
    +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • +///
  • {@link kFIRParameterTax} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterShipping} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCoupon} (NSString) (optional)
  • +///
  • {@link kFIRParameterLocation} (NSString) (optional)
  • +///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) +/// for travel bookings
  • +///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • +///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • +///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • +///
+static NSString *const kFIREventEcommercePurchase = @"ecommerce_purchase"; + +/// Generate Lead event. Log this event when a lead has been generated in the app to understand the +/// efficacy of your install and re-engagement campaigns. Note: If you supply the +/// {@link kFIRParameterValue} parameter, you must also supply the {@link kFIRParameterCurrency} +/// parameter so that revenue metrics can be computed accurately. Params: +/// +///
    +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
+static NSString *const kFIREventGenerateLead = @"generate_lead"; + +/// Join Group event. Log this event when a user joins a group such as a guild, team or family. Use +/// this event to analyze how popular certain groups or social features are in your app. Params: +/// +///
    +///
  • {@link kFIRParameterGroupID} (NSString)
  • +///
+static NSString *const kFIREventJoinGroup = @"join_group"; + +/// Level Up event. This event signifies that a player has leveled up in your gaming app. It can +/// help you gauge the level distribution of your userbase and help you identify certain levels that +/// are difficult to pass. Params: +/// +///
    +///
  • {@link kFIRParameterLevel} (signed 64-bit integer as NSNumber)
  • +///
  • {@link kFIRParameterCharacter} (NSString) (optional)
  • +///
+static NSString *const kFIREventLevelUp = @"level_up"; + +/// Login event. Apps with a login feature can report this event to signify that a user has logged +/// in. +static NSString *const kFIREventLogin = @"login"; + +/// Post Score event. Log this event when the user posts a score in your gaming app. This event can +/// help you understand how users are actually performing in your game and it can help you correlate +/// high scores with certain audiences or behaviors. Params: +/// +///
    +///
  • {@link kFIRParameterScore} (signed 64-bit integer as NSNumber)
  • +///
  • {@link kFIRParameterLevel} (signed 64-bit integer as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCharacter} (NSString) (optional)
  • +///
+static NSString *const kFIREventPostScore = @"post_score"; + +/// Present Offer event. This event signifies that the app has presented a purchase offer to a user. +/// Add this event to a funnel with the kFIREventAddToCart and kFIREventEcommercePurchase to gauge +/// your conversion process. Note: If you supply the {@link kFIRParameterValue} parameter, you must +/// also supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed +/// accurately. Params: +/// +///
    +///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
  • {@link kFIRParameterItemName} (NSString)
  • +///
  • {@link kFIRParameterItemCategory} (NSString)
  • +///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • +///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
+static NSString *const kFIREventPresentOffer = @"present_offer"; + +/// E-Commerce Purchase Refund event. This event signifies that an item purchase was refunded. +/// Note: If you supply the {@link kFIRParameterValue} parameter, you must also supply the +/// {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed accurately. +/// Params: +/// +///
    +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • +///
+static NSString *const kFIREventPurchaseRefund = @"purchase_refund"; + +/// Search event. Apps that support search features can use this event to contextualize search +/// operations by supplying the appropriate, corresponding parameters. This event can help you +/// identify the most popular content in your app. Params: +/// +///
    +///
  • {@link kFIRParameterSearchTerm} (NSString)
  • +///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for +/// hotel bookings
  • +///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) +/// for travel bookings
  • +///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • +///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • +///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • +///
+static NSString *const kFIREventSearch = @"search"; + +/// Select Content event. This general purpose event signifies that a user has selected some content +/// of a certain type in an app. The content can be any object in your app. This event can help you +/// identify popular content and categories of content in your app. Params: +/// +///
    +///
  • {@link kFIRParameterContentType} (NSString)
  • +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
+static NSString *const kFIREventSelectContent = @"select_content"; + +/// Share event. Apps with social features can log the Share event to identify the most viral +/// content. Params: +/// +///
    +///
  • {@link kFIRParameterContentType} (NSString)
  • +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
+static NSString *const kFIREventShare = @"share"; + +/// Sign Up event. This event indicates that a user has signed up for an account in your app. The +/// parameter signifies the method by which the user signed up. Use this event to understand the +/// different behaviors between logged in and logged out users. Params: +/// +///
    +///
  • {@link kFIRParameterSignUpMethod} (NSString)
  • +///
+static NSString *const kFIREventSignUp = @"sign_up"; + +/// Spend Virtual Currency event. This event tracks the sale of virtual goods in your app and can +/// help you identify which virtual goods are the most popular objects of purchase. Params: +/// +///
    +///
  • {@link kFIRParameterItemName} (NSString)
  • +///
  • {@link kFIRParameterVirtualCurrencyName} (NSString)
  • +///
  • {@link kFIRParameterValue} (signed 64-bit integer or double as NSNumber)
  • +///
+static NSString *const kFIREventSpendVirtualCurrency = @"spend_virtual_currency"; + +/// Tutorial Begin event. This event signifies the start of the on-boarding process in your app. Use +/// this in a funnel with kFIREventTutorialComplete to understand how many users complete this +/// process and move on to the full app experience. +static NSString *const kFIREventTutorialBegin = @"tutorial_begin"; + +/// Tutorial End event. Use this event to signify the user's completion of your app's on-boarding +/// process. Add this to a funnel with kFIREventTutorialBegin to gauge the completion rate of your +/// on-boarding process. +static NSString *const kFIREventTutorialComplete = @"tutorial_complete"; + +/// Unlock Achievement event. Log this event when the user has unlocked an achievement in your +/// game. Since achievements generally represent the breadth of a gaming experience, this event can +/// help you understand how many users are experiencing all that your game has to offer. Params: +/// +///
    +///
  • {@link kFIRParameterAchievementID} (NSString)
  • +///
+static NSString *const kFIREventUnlockAchievement = @"unlock_achievement"; + +/// View Item event. This event signifies that some content was shown to the user. This content may +/// be a product, a webpage or just a simple image or text. Use the appropriate parameters to +/// contextualize the event. Use this event to discover the most popular items viewed in your app. +/// Note: If you supply the {@link kFIRParameterValue} parameter, you must also supply the +/// {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed accurately. +/// Params: +/// +///
    +///
  • {@link kFIRParameterItemID} (NSString)
  • +///
  • {@link kFIRParameterItemName} (NSString)
  • +///
  • {@link kFIRParameterItemCategory} (NSString)
  • +///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • +///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber) (optional)
  • +///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • +///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • +///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • +///
  • {@link kFIRParameterFlightNumber} (NSString) (optional) for travel bookings
  • +///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) +/// for travel bookings
  • +///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for +/// travel bookings
  • +///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for +/// travel bookings
  • +///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • +///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • +///
  • {@link kFIRParameterSearchTerm} (NSString) (optional) for travel bookings
  • +///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • +///
+static NSString *const kFIREventViewItem = @"view_item"; + +/// View Item List event. Log this event when the user has been presented with a list of items of a +/// certain category. Params: +/// +///
    +///
  • {@link kFIRParameterItemCategory} (NSString)
  • +///
+static NSString *const kFIREventViewItemList = @"view_item_list"; + +/// View Search Results event. Log this event when the user has been presented with the results of a +/// search. Params: +/// +///
    +///
  • {@link kFIRParameterSearchTerm} (NSString)
  • +///
+static NSString *const kFIREventViewSearchResults = @"view_search_results"; diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIROptions.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIROptions.h new file mode 100755 index 0000000..126824b --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIROptions.h @@ -0,0 +1 @@ +#import diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h new file mode 100755 index 0000000..42c0c5e --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h @@ -0,0 +1,304 @@ +/// @file FIRParameterNames.h +/// +/// Predefined event parameter names. +/// +/// Params supply information that contextualize Events. You can associate up to 25 unique Params +/// with each Event type. Some Params are suggested below for certain common Events, but you are +/// not limited to these. You may supply extra Params for suggested Events or custom Params for +/// Custom events. Param names can be up to 24 characters long, may only contain alphanumeric +/// characters and underscores ("_"), and must start with an alphabetic character. Param values can +/// be up to 36 characters long. The "firebase_" prefix is reserved and should not be used. + +/// Game achievement ID (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterAchievementID : @"10_matches_won",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterAchievementID = @"achievement_id"; + +/// Character used in game (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterCharacter : @"beat_boss",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterCharacter = @"character"; + +/// Type of content selected (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterContentType : @"news article",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterContentType = @"content_type"; + +/// Coupon code for a purchasable item (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterCoupon : @"zz123",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterCoupon = @"coupon"; + +/// Purchase currency in 3-letter +/// ISO_4217 format (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterCurrency : @"USD",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterCurrency = @"currency"; + +/// Flight or Travel destination (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterDestination : @"Mountain View, CA",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterDestination = @"destination"; + +/// The arrival date, check-out date or rental end date for the item. This should be in +/// YYYY-MM-DD format (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterEndDate : @"2015-09-14",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterEndDate = @"end_date"; + +/// Flight number for travel events (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterFlightNumber : @"ZZ800",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterFlightNumber = @"flight_number"; + +/// Group/clan/guild ID (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterGroupID : @"g1",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterGroupID = @"group_id"; + +/// Item category (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterItemCategory : @"t-shirts",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterItemCategory = @"item_category"; + +/// Item ID (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterItemID : @"p7654",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterItemID = @"item_id"; + +/// The Google Place ID (NSString) that +/// corresponds to the associated item. Alternatively, you can supply your own custom Location ID. +///
+///     NSDictionary *params = @{
+///       kFIRParameterItemLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterItemLocationID = @"item_location_id"; + +/// Item name (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterItemName : @"abc",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterItemName = @"item_name"; + +/// Level in game (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterLevel : @(42),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterLevel = @"level"; + +/// Location (NSString). The Google Place ID +/// that corresponds to the associated event. Alternatively, you can supply your own custom +/// Location ID. +///
+///     NSDictionary *params = @{
+///       kFIRParameterLocation : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterLocation = @"location"; + +/// Number of nights staying at hotel (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfNights : @(3),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterNumberOfNights = @"number_of_nights"; + +/// Number of passengers traveling (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfPassengers : @(11),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterNumberOfPassengers = @"number_of_passengers"; + +/// Number of rooms for travel events (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterNumberOfRooms : @(2),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterNumberOfRooms = @"number_of_rooms"; + +/// Flight or Travel origin (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterOrigin : @"Mountain View, CA",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterOrigin = @"origin"; + +/// Purchase price (double as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterPrice : @(1.0),
+///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterPrice = @"price"; + +/// Purchase quantity (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterQuantity : @(1),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterQuantity = @"quantity"; + +/// Score in game (signed 64-bit integer as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterScore : @(4200),
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterScore = @"score"; + +/// The search string/keywords used (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterSearchTerm : @"periodic table",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterSearchTerm = @"search_term"; + +/// Shipping cost (double as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterShipping : @(9.50),
+///       kFIRParameterCurrency : @"USD",  // e.g. $9.50 USD
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterShipping = @"shipping"; + +/// Sign up method (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterSignUpMethod : @"google",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterSignUpMethod = @"sign_up_method"; + +/// The departure date, check-in date or rental start date for the item. This should be in +/// YYYY-MM-DD format (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterStartDate : @"2015-09-14",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterStartDate = @"start_date"; + +/// Tax amount (double as NSNumber). +///
+///     NSDictionary *params = @{
+///       kFIRParameterTax : @(1.0),
+///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterTax = @"tax"; + +/// A single ID for a ecommerce group transaction (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterTransactionID : @"ab7236dd9823",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterTransactionID = @"transaction_id"; + +/// Travel class (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterTravelClass : @"business",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterTravelClass = @"travel_class"; + +/// A context-specific numeric value which is accumulated automatically for each event type. This is +/// a general purpose parameter that is useful for accumulating a key metric that pertains to an +/// event. Examples include revenue, distance, time and points. Value should be specified as signed +/// 64-bit integer or double as NSNumber. Notes: Currency-related values should be supplied using +/// double as NSNumber and must be accompanied by a {@link kFIRParameterCurrency} parameter. The +/// valid range of accumulated values is [-9,223,372,036,854.77, 9,223,372,036,854.77]. +///
+///     NSDictionary *params = @{
+///       kFIRParameterValue : @(3.99),
+///       kFIRParameterCurrency : @"USD",  // e.g. $3.99 USD
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterValue = @"value"; + +/// Name of virtual currency type (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterVirtualCurrencyName : @"virtual_currency_name",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRParameterVirtualCurrencyName = @"virtual_currency_name"; diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h new file mode 100755 index 0000000..54cf1c2 --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h @@ -0,0 +1,13 @@ +/// @file FIRUserPropertyNames.h +/// +/// Predefined user property names. +/// +/// A UserProperty is an attribute that describes the app-user. By supplying UserProperties, you can +/// later analyze different behaviors of various segments of your userbase. You may supply up to 25 +/// unique UserProperties per app, and you can use the name and value of your choosing for each one. +/// UserProperty names can be up to 24 characters long, may only contain alphanumeric characters and +/// underscores ("_"), and must start with an alphabetic character. UserProperty values can be up to +/// 36 characters long. The "firebase_" prefix is reserved and should not be used. + +/// The method used to sign in. For example, "google", "facebook" or "twitter". +static NSString *const kFIRUserPropertySignUpMethod = @"sign_up_method"; diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h new file mode 100755 index 0000000..02667b3 --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h @@ -0,0 +1,11 @@ +// Generated umbrella header for FirebaseAnalytics. + +#import "FIRAnalytics+AppDelegate.h" +#import "FIRAnalytics.h" +#import "FIRAnalyticsConfiguration.h" +#import "FIRApp.h" +#import "FIRConfiguration.h" +#import "FIREventNames.h" +#import "FIROptions.h" +#import "FIRParameterNames.h" +#import "FIRUserPropertyNames.h" diff --git a/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Modules/module.modulemap b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Modules/module.modulemap new file mode 100755 index 0000000..e54b5ea --- /dev/null +++ b/Pods/FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Modules/module.modulemap @@ -0,0 +1,23 @@ +framework module FirebaseAnalytics { + + export * + + umbrella header "FirebaseAnalytics.h" + + header "FIRAnalytics+AppDelegate.h" + header "FIRAnalytics.h" + header "FIRAnalyticsConfiguration.h" + header "FIRApp.h" + header "FIRConfiguration.h" + header "FIREventNames.h" + header "FIROptions.h" + header "FIRParameterNames.h" + header "FIRUserPropertyNames.h" + + link framework "AddressBook" + link framework "StoreKit" + + link "c++" + link "sqlite3" + link "z" +} diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/FirebaseCore b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/FirebaseCore new file mode 100755 index 0000000..5f3dcec Binary files /dev/null and b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/FirebaseCore differ diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h new file mode 100755 index 0000000..667d5a4 --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h @@ -0,0 +1,38 @@ +#import + +/** + * This class provides configuration fields for Firebase Analytics. + */ +@interface FIRAnalyticsConfiguration : NSObject + +/** + * Returns the shared instance of FIRAnalyticsConfiguration. + */ ++ (FIRAnalyticsConfiguration *)sharedInstance; + +/** + * Sets the minimum engagement time in seconds required to start a new session. The default value + * is 10 seconds. + */ +- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval; + +/** + * Sets the interval of inactivity in seconds that terminates the current session. The default + * value is 1800 seconds (30 minutes). + */ +- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval; + +/** + * Sets whether analytics collection is enabled for this app on this device. This setting is + * persisted across app sessions. By default it is enabled. + */ +- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled; + +/** + * Deprecated. Sets whether measurement and reporting are enabled for this app on this device. By + * default they are enabled. + */ +- (void)setIsEnabled:(BOOL)isEnabled + DEPRECATED_MSG_ATTRIBUTE("Use setAnalyticsCollectionEnabled: instead."); + +@end diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRApp.h b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRApp.h new file mode 100755 index 0000000..45b2388 --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRApp.h @@ -0,0 +1,94 @@ +#import +#import + +@class FIROptions; + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^FIRAppVoidBoolCallback)(BOOL success); + +/** + * The entry point of Firebase SDKs. + * + * Initialize and configure FIRApp using [FIRApp configure]; + * Or other customized ways as shown below. + * + * The logging system has two modes: default mode and debug mode. In default mode, only logs with + * log level Notice, Warning and Error will be sent to device. In debug mode, all logs will be sent + * to device. The log levels that Firebase uses are consistent with the ASL log levels. + * + * Enable debug mode by passing the -FIRDebugEnabled argument to the application. You can add this + * argument in the application's Xcode scheme. When debug mode is enabled via -FIRDebugEnabled, + * further executions of the application will also be in debug mode. In order to return to default + * mode, you must explicitly disable the debug mode with the application argument -FIRDebugDisabled. + */ +@interface FIRApp : NSObject + +/** + * Configures a default Firebase app. Raises an exception if any configuration step fails. The + * default app is named "__FIRAPP_DEFAULT". This method should be called after the app is launched + * and before using Firebase services. This method is thread safe. + */ ++ (void)configure; + +/** + * Configures the default Firebase app with the provided options. The default app is named + * "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method is thread + * safe. + * + * @param options The Firebase application options used to configure the service. + */ ++ (void)configureWithOptions:(FIROptions *)options; + +/** + * Configures a Firebase app with the given name and options. Raises an exception if any + * configuration step fails. This method is thread safe. + * + * @param name The application's name given by the developer. The name should should only contain + Letters, Numbers and Underscore. + * @param options The Firebase application options used to configure the services. + */ ++ (void)configureWithName:(NSString *)name options:(FIROptions *)options; + +/** + * Returns the default app, or nil if the default app does not exist. + */ ++ (nullable FIRApp *)defaultApp NS_SWIFT_NAME(defaultApp()); + +/** + * Returns a previously created FIRApp instance with the given name, or nil if no such app exists. + * This method is thread safe. + */ ++ (nullable FIRApp *)appNamed:(NSString *)name; + +/** + * Returns the set of all extant FIRApp instances, or nil if there is no FIRApp instance. This + * method is thread safe. + */ ++ (nullable NSDictionary *)allApps; + +/** + * Cleans up the current FIRApp, freeing associated data and returning its name to the pool for + * future use. This method is thread safe in class level. + */ +- (void)deleteApp:(FIRAppVoidBoolCallback)completion; + +/** + * FIRFirebaseApp instances should not be initialized directly. Call |FIRApp configure|, or + * |FIRApp configureWithOptions:|, or |FIRApp configureWithNames:options| directly. + */ +- (nullable instancetype)init NS_UNAVAILABLE; + +/** + * Gets the name of this app. + */ +@property(nonatomic, copy, readonly) NSString *name; + +/** + * Gets the options for this app. + */ +@property(nonatomic, readonly) FIROptions *options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRConfiguration.h b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRConfiguration.h new file mode 100755 index 0000000..e85ea8b --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRConfiguration.h @@ -0,0 +1,33 @@ +#import + +#import "FIRAnalyticsConfiguration.h" + +/** + * The log levels used by FIRConfiguration. + */ +typedef NS_ENUM(NSInteger, FIRLogLevel) { + kFIRLogLevelError __deprecated = 0, + kFIRLogLevelWarning __deprecated, + kFIRLogLevelInfo __deprecated, + kFIRLogLevelDebug __deprecated, + kFIRLogLevelAssert __deprecated, + kFIRLogLevelMax __deprecated = kFIRLogLevelAssert +} DEPRECATED_MSG_ATTRIBUTE( + "Use -FIRDebugEnabled and -FIRDebugDisabled. See FIRApp.h for more details."); + +/** + * This interface provides global level properties that the developer can tweak, and the singleton + * of the Firebase Analytics configuration class. + */ +@interface FIRConfiguration : NSObject + ++ (FIRConfiguration *)sharedInstance; + +// The configuration class for Firebase Analytics. +@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration; + +// Global log level. Defaults to kFIRLogLevelError. +@property(nonatomic, readwrite, assign) FIRLogLevel logLevel DEPRECATED_MSG_ATTRIBUTE( + "Use -FIRDebugEnabled and -FIRDebugDisabled. See FIRApp.h for more details."); + +@end diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIROptions.h b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIROptions.h new file mode 100755 index 0000000..5ab20c6 --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIROptions.h @@ -0,0 +1,87 @@ +#import + +/** + * This class provides constant fields of Google APIs. + */ +@interface FIROptions : NSObject + +/** + * Returns the default options. + */ ++ (FIROptions *)defaultOptions; + +/** + * An iOS API key used for authenticating requests from your app, e.g. + * @"AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", used to identify your app to Google servers. + */ +@property(nonatomic, readonly, copy) NSString *APIKey; + +/** + * The OAuth2 client ID for iOS application used to authenticate Google users, for example + * @"12345.apps.googleusercontent.com", used for signing in with Google. + */ +@property(nonatomic, readonly, copy) NSString *clientID; + +/** + * The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure Google Analytics. + */ +@property(nonatomic, readonly, copy) NSString *trackingID; + +/** + * The Project Number from the Google Developer's console, for example @"012345678901", used to + * configure Google Cloud Messaging. + */ +@property(nonatomic, readonly, copy) NSString *GCMSenderID; + +/** + * The Android client ID used in Google AppInvite when an iOS app has its Android version, for + * example @"12345.apps.googleusercontent.com". + */ +@property(nonatomic, readonly, copy) NSString *androidClientID; + +/** + * The Google App ID that is used to uniquely identify an instance of an app. + */ +@property(nonatomic, readonly, copy) NSString *googleAppID; + +/** + * The database root URL, e.g. @"http://abc-xyz-123.firebaseio.com". + */ +@property(nonatomic, readonly, copy) NSString *databaseURL; + +/** + * The URL scheme used to set up Durable Deep Link service. + */ +@property(nonatomic, readwrite, copy) NSString *deepLinkURLScheme; + +/** + * The Google Cloud Storage bucket name, e.g. @"abc-xyz-123.storage.firebase.com". + */ +@property(nonatomic, readonly, copy) NSString *storageBucket; + +/** + * Initializes a customized instance of FIROptions with keys. googleAppID, bundleID and GCMSenderID + * are required. Other keys may required for configuring specific services. + */ +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + bundleID:(NSString *)bundleID + GCMSenderID:(NSString *)GCMSenderID + APIKey:(NSString *)APIKey + clientID:(NSString *)clientID + trackingID:(NSString *)trackingID + androidClientID:(NSString *)androidClientID + databaseURL:(NSString *)databaseURL + storageBucket:(NSString *)storageBucket + deepLinkURLScheme:(NSString *)deepLinkURLScheme; + +/** + * Initializes a customized instance of FIROptions from the file at the given plist file path. + * For example, + * NSString *filePath = + * [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; + * FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath]; + * Returns nil if the plist file does not exist or is invalid. + */ +- (instancetype)initWithContentsOfFile:(NSString *)plistPath; + +@end diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FirebaseCore.h b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FirebaseCore.h new file mode 100755 index 0000000..d144758 --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FirebaseCore.h @@ -0,0 +1,6 @@ +// Generated umbrella header for FirebaseCore. + +#import "FIRAnalyticsConfiguration.h" +#import "FIRApp.h" +#import "FIRConfiguration.h" +#import "FIROptions.h" diff --git a/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Modules/module.modulemap b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Modules/module.modulemap new file mode 100755 index 0000000..82d14eb --- /dev/null +++ b/Pods/FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Modules/module.modulemap @@ -0,0 +1,15 @@ +framework module FirebaseCore { + + export * + + umbrella header "FirebaseCore.h" + + header "FIRAnalyticsConfiguration.h" + header "FIRApp.h" + header "FIRConfiguration.h" + header "FIROptions.h" + + link framework "SystemConfiguration" + + link "c++" +} diff --git a/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/FirebaseInstanceID b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/FirebaseInstanceID new file mode 100755 index 0000000..5d67d05 Binary files /dev/null and b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/FirebaseInstanceID differ diff --git a/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h new file mode 100755 index 0000000..717e290 --- /dev/null +++ b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h @@ -0,0 +1,245 @@ +#import + +/** + * @memberof FIRInstanceID + * + * The scope to be used when fetching/deleting a token for Firebase Messaging. + */ +FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDScopeFirebaseMessaging; + +/** + * Called when the system determines that tokens need to be refreshed. + * This method is also called if Instance ID has been reset in which + * case, tokens and FCM topic subscriptions also need to be refreshed. + * + * Instance ID service will throttle the refresh event across all devices + * to control the rate of token updates on application servers. + */ +FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDTokenRefreshNotification; + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the InstanceID token returns. If + * the call fails we return the appropriate `error code` as described below. + * + * @param token The valid token as returned by InstanceID backend. + * + * @param error The error describing why generating a new token + * failed. See the error codes below for a more detailed + * description. + */ +typedef void(^FIRInstanceIDTokenHandler)( NSString * __nullable token, NSError * __nullable error); + + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the InstanceID `deleteToken` returns. If + * the call fails we return the appropriate `error code` as described below + * + * @param error The error describing why deleting the token failed. + * See the error codes below for a more detailed description. + */ +typedef void(^FIRInstanceIDDeleteTokenHandler)(NSError * __nullable error); + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the app identity is created. If the + * identity wasn't created for some reason we return the appropriate error code. + * + * @param identity A valid identity for the app instance, nil if there was an error + * while creating an identity. + * @param error The error if fetching the identity fails else nil. + */ +typedef void(^FIRInstanceIDHandler)(NSString * __nullable identity, NSError * __nullable error); + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the app identity and all the tokens associated + * with it are deleted. Returns a valid error object in case of failure else nil. + * + * @param error The error if deleting the identity and all the tokens associated with + * it fails else nil. + */ +typedef void(^FIRInstanceIDDeleteHandler)(NSError * __nullable error); + +/** + * @enum FIRInstanceIDError + */ +typedef NS_ENUM(NSUInteger, FIRInstanceIDError) { + // Http related errors. + + /// Unknown error. + FIRInstanceIDErrorUnknown = 0, + + /// Auth Error -- GCM couldn't validate request from this client. + FIRInstanceIDErrorAuthentication = 1, + + /// NoAccess -- InstanceID service cannot be accessed. + FIRInstanceIDErrorNoAccess = 2, + + /// Timeout -- Request to InstanceID backend timed out. + FIRInstanceIDErrorTimeout = 3, + + /// Network -- No network available to reach the servers. + FIRInstanceIDErrorNetwork = 4, + + /// OperationInProgress -- Another similar operation in progress, + /// bailing this one. + FIRInstanceIDErrorOperationInProgress = 5, + + /// InvalidRequest -- Some parameters of the request were invalid. + FIRInstanceIDErrorInvalidRequest = 7, +}; + +/** + * The APNS token type for the app. If the token type is set to `UNKNOWN` + * InstanceID will implicitly try to figure out what the actual token type + * is from the provisioning profile. + */ +typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) { + /// Unknown token type. + FIRInstanceIDAPNSTokenTypeUnknown, + /// Sandbox token type. + FIRInstanceIDAPNSTokenTypeSandbox, + /// Production token type. + FIRInstanceIDAPNSTokenTypeProd, +}; + +/** + * Instance ID provides a unique identifier for each app instance and a mechanism + * to authenticate and authorize actions (for example, sending a GCM message). + * + * Instance ID is long lived but, may be reset if the device is not used for + * a long time or the Instance ID service detects a problem. + * If Instance ID is reset, the app will be notified with a `com.firebase.iid.token-refresh` + * notification. + * + * If the Instance ID has become invalid, the app can request a new one and + * send it to the app server. + * To prove ownership of Instance ID and to allow servers to access data or + * services associated with the app, call + * `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. + */ +@interface FIRInstanceID : NSObject + +/** + * FIRInstanceID. + * + * @return A shared instance of FIRInstanceID. + */ ++ (nonnull instancetype)instanceID NS_SWIFT_NAME(instanceID()); + +/** + * Unavailable. Use +instanceID instead. + */ +- (nonnull instancetype)init __attribute__((unavailable("Use +instanceID instead."))); + +/** + * Set APNS token for the application. This APNS token will be used to register + * with Firebase Messaging using `token` or + * `tokenWithAuthorizedEntity:scope:options:handler`. If the token type is set to + * `FIRInstanceIDAPNSTokenTypeUnknown` InstanceID will read the provisioning profile + * to find out the token type. + * + * @param token The APNS token for the application. + * @param type The APNS token type for the above token. + */ +- (void)setAPNSToken:(nonnull NSData *)token + type:(FIRInstanceIDAPNSTokenType)type; + +#pragma mark - Tokens + +/** + * Returns a Firebase Messaging scoped token for the firebase app. + * + * @return Null Returns null if the device has not yet been registerd with + * Firebase Message else returns a valid token. + */ +- (nullable NSString *)token; + +/** + * Returns a token that authorizes an Entity (example: cloud service) to perform + * an action on behalf of the application identified by Instance ID. + * + * This is similar to an OAuth2 token except, it applies to the + * application instance instead of a user. + * + * This is an asynchronous call. If the token fetching fails for some reason + * we invoke the completion callback with nil `token` and the appropriate + * error. + * + * Note, you can only have one `token` or `deleteToken` call for a given + * authorizedEntity and scope at any point of time. Making another such call with the + * same authorizedEntity and scope before the last one finishes will result in an + * error with code `OperationInProgress`. + * + * @see FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler: + * + * @param authorizedEntity Entity authorized by the token. + * @param scope Action authorized for authorizedEntity. + * @param options The extra options to be sent with your token request. The + * value for the `apns_token` should be the NSData object + * passed to UIApplication's + * `didRegisterForRemoteNotificationsWithDeviceToken` method. + * All other keys and values in the options dict need to be + * instances of NSString or else they will be discarded. Bundle + * keys starting with 'GCM.' and 'GOOGLE.' are reserved. + * @param handler The callback handler which is invoked when the token is + * successfully fetched. In case of success a valid `token` and + * `nil` error are returned. In case of any error the `token` + * is nil and a valid `error` is returned. The valid error + * codes have been documented above. + */ +- (void)tokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity + scope:(nonnull NSString *)scope + options:(nullable NSDictionary *)options + handler:(nonnull FIRInstanceIDTokenHandler)handler; + +/** + * Revokes access to a scope (action) for an entity previously + * authorized by `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. + * + * This is an asynchronous call. Call this on the main thread since InstanceID lib + * is not thread safe. In case token deletion fails for some reason we invoke the + * `handler` callback passed in with the appropriate error code. + * + * Note, you can only have one `token` or `deleteToken` call for a given + * authorizedEntity and scope at a point of time. Making another such call with the + * same authorizedEntity and scope before the last one finishes will result in an error + * with code `OperationInProgress`. + * + * @param authorizedEntity Entity that must no longer have access. + * @param scope Action that entity is no longer authorized to perform. + * @param handler The handler that is invoked once the unsubscribe call ends. + * In case of error an appropriate error object is returned + * else error is nil. + */ +- (void)deleteTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity + scope:(nonnull NSString *)scope + handler:(nonnull FIRInstanceIDDeleteTokenHandler)handler; + +#pragma mark - Identity + +/** + * Asynchronously fetch a stable identifier that uniquely identifies the app + * instance. If the identifier has been revoked or has expired, this method will + * return a new identifier. + * + * + * @param handler The handler to invoke once the identifier has been fetched. + * In case of error an appropriate error object is returned else + * a valid identifier is returned and a valid identifier for the + * application instance. + */ +- (void)getIDWithHandler:(nonnull FIRInstanceIDHandler)handler; + +/** + * Resets Instance ID and revokes all tokens. + */ +- (void)deleteIDWithHandler:(nonnull FIRInstanceIDDeleteHandler)handler; + +@end diff --git a/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h new file mode 100755 index 0000000..053ec2b --- /dev/null +++ b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h @@ -0,0 +1 @@ +#import "FIRInstanceID.h" diff --git a/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Modules/module.modulemap b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Modules/module.modulemap new file mode 100755 index 0000000..b4a5b5e --- /dev/null +++ b/Pods/FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module FirebaseInstanceID { + + export * + + umbrella header "FirebaseInstanceID.h" + + header "FIRInstanceID.h" +} diff --git a/Pods/FirebaseInstanceID/Sources/FIRInstanceID.h b/Pods/FirebaseInstanceID/Sources/FIRInstanceID.h new file mode 100755 index 0000000..e3782f8 --- /dev/null +++ b/Pods/FirebaseInstanceID/Sources/FIRInstanceID.h @@ -0,0 +1,237 @@ +#import + +/** + * @memberof FIRInstanceID + * + * The scope to be used when fetching/deleting a token for Firebase Messaging. + */ +FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDScopeFirebaseMessaging; + +/** + * Called when the system determines that tokens need to be refreshed. + * This method is also called if Instance ID has been reset in which + * case, tokens and FCM topic subscriptions also need to be refreshed. + * + * Instance ID service will throttle the refresh event across all devices + * to control the rate of token updates on application servers. + */ +FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDTokenRefreshNotification; + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the InstanceID token returns. If + * the call fails we return the appropriate `error code` as described below. + * + * @param token The valid token as returned by InstanceID backend. + * + * @param error The error describing why generating a new token + * failed. See the error codes below for a more detailed + * description. + */ +typedef void(^FIRInstanceIDTokenHandler)(NSString * __nullable token, NSError * __nullable error); + + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the InstanceID `deleteToken` returns. If + * the call fails we return the appropriate `error code` as described below + * + * @param error The error describing why deleting the token failed. + * See the error codes below for a more detailed description. + */ +typedef void(^FIRInstanceIDDeleteTokenHandler)(NSError * __nullable error); + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the app identity is created. If the + * identity wasn't created for some reason we return the appropriate error code. + * + * @param identity A valid identity for the app instance, nil if there was an error + * while creating an identity. + * @param error The error if fetching the identity fails else nil. + */ +typedef void(^FIRInstanceIDHandler)(NSString * __nullable identity, NSError * __nullable error); + +/** + * @related FIRInstanceID + * + * The completion handler invoked when the app identity and all the tokens associated + * with it are deleted. Returns a valid error object in case of failure else nil. + * + * @param error The error if deleting the identity and all the tokens associated with + * it fails else nil. + */ +typedef void(^FIRInstanceIDDeleteHandler)(NSError * __nullable error); + +/** + * @enum FIRInstanceIDError + */ +typedef NS_ENUM(NSUInteger, FIRInstanceIDError) { + /// Unknown error. + FIRInstanceIDErrorUnknown = 0, + + /// Auth Error -- FCM couldn't validate request from this client. + FIRInstanceIDErrorAuthentication = 1, + + /// NoAccess -- InstanceID service cannot be accessed. + FIRInstanceIDErrorNoAccess = 2, + + /// Timeout -- Request to InstanceID backend timed out. + FIRInstanceIDErrorTimeout = 3, + + /// Network -- No network available to reach the servers. + FIRInstanceIDErrorNetwork = 4, + + /// OperationInProgress -- Another similar operation in progress, + /// bailing this one. + FIRInstanceIDErrorOperationInProgress = 5, + + /// InvalidRequest -- Some parameters of the request were invalid. + FIRInstanceIDErrorInvalidRequest = 7, +}; + +/** + * The APNS token type for the app. If the token type is set to `UNKNOWN` + * InstanceID will implicitly try to figure out what the actual token type + * is from the provisioning profile. + */ +typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) { + /// Unknown token type. + FIRInstanceIDAPNSTokenTypeUnknown, + /// Sandbox token type. + FIRInstanceIDAPNSTokenTypeSandbox, + /// Production token type. + FIRInstanceIDAPNSTokenTypeProd, +}; + +/** + * Instance ID provides a unique identifier for each app instance and a mechanism + * to authenticate and authorize actions (for example, sending a GCM message). + * + * Instance ID is long lived but, may be reset if the device is not used for + * a long time or the Instance ID service detects a problem. + * If Instance ID is reset, the app will be notified with a `com.firebase.iid.token-refresh` + * notification. + * + * If the Instance ID has become invalid, the app can request a new one and + * send it to the app server. + * To prove ownership of Instance ID and to allow servers to access data or + * services associated with the app, call + * `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. + */ +@interface FIRInstanceID : NSObject + +/** + * FIRInstanceID. + * + * @return A shared instance of FIRInstanceID. + */ ++ (nonnull instancetype)instanceID; + +/** + * Set APNS token for the application. This APNS token will be used to register + * with Firebase Messaging using `token` or + * `tokenWithAuthorizedEntity:scope:options:handler`. If the token type is set to + * `FIRInstanceIDAPNSTokenTypeUnknown` InstanceID will read the provisioning profile + * to find out the token type. + * + * @param token The APNS token for the application. + * @param type The APNS token type for the above token. + */ +- (void)setAPNSToken:(nonnull NSData *)token type:(FIRInstanceIDAPNSTokenType)type; + +#pragma mark - Tokens + +/** + * Returns a Firebase Messaging scoped token for the firebase app. + * + * @return Null Returns null if the device has not yet been registerd with + * Firebase Message else returns a valid token. + */ +- (nullable NSString *)token; + +/** + * Returns a token that authorizes an Entity (example: cloud service) to perform + * an action on behalf of the application identified by Instance ID. + * + * This is similar to an OAuth2 token except, it applies to the + * application instance instead of a user. + * + * This is an asynchronous call. If the token fetching fails for some reason + * we invoke the completion callback with nil `token` and the appropriate + * error. + * + * Note, you can only have one `token` or `deleteToken` call for a given + * authorizedEntity and scope at any point of time. Making another such call with the + * same authorizedEntity and scope before the last one finishes will result in an + * error with code `OperationInProgress`. + * + * @see FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler: + * + * @param authorizedEntity Entity authorized by the token. + * @param scope Action authorized for authorizedEntity. + * @param options The extra options to be sent with your token request. The + * value for the `apns_token` should be the NSData object + * passed to UIApplication's + * `didRegisterForRemoteNotificationsWithDeviceToken` method. + * All other keys and values in the options dict need to be + * instances of NSString or else they will be discarded. Bundle + * keys starting with 'GCM.' and 'GOOGLE.' are reserved. + * @param handler The callback handler which is invoked when the token is + * successfully fetched. In case of success a valid `token` and + * `nil` error are returned. In case of any error the `token` + * is nil and a valid `error` is returned. The valid error + * codes have been documented above. + */ +- (void)tokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity + scope:(nonnull NSString *)scope + options:(nonnull NSDictionary *)options + handler:(nonnull FIRInstanceIDTokenHandler)handler; + +/** + * Revokes access to a scope (action) for an entity previously + * authorized by `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. + * + * This is an asynchronous call. Call this on the main thread since InstanceID lib + * is not thread safe. In case token deletion fails for some reason we invoke the + * `handler` callback passed in with the appropriate error code. + * + * Note, you can only have one `token` or `deleteToken` call for a given + * authorizedEntity and scope at a point of time. Making another such call with the + * same authorizedEntity and scope before the last one finishes will result in an error + * with code `OperationInProgress`. + * + * @param authorizedEntity Entity that must no longer have access. + * @param scope Action that entity is no longer authorized to perform. + * @param handler The handler that is invoked once the unsubscribe call ends. + * In case of error an appropriate error object is returned + * else error is nil. + */ +- (void)deleteTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity + scope:(nonnull NSString *)scope + handler:(nonnull FIRInstanceIDDeleteTokenHandler)handler; + +#pragma mark - Identity + +/** + * Asynchronously fetch a stable identifier that uniquely identifies the app + * instance. If the identifier has been revoked or has expired, this method will + * return a new identifier. + * + * + * @param handler The handler to invoke once the identifier has been fetched. + * In case of error an appropriate error object is returned else + * a valid identifier is returned and a valid identifier for the + * application instance. + */ +- (void)getIDWithHandler:(nonnull FIRInstanceIDHandler)handler; + +/** + * Resets Instance ID and revokes all tokens. + */ +- (void)deleteIDWithHandler:(nonnull FIRInstanceIDDeleteHandler)handler; + +@end diff --git a/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/FirebaseMessaging b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/FirebaseMessaging new file mode 100755 index 0000000..de75366 Binary files /dev/null and b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/FirebaseMessaging differ diff --git a/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FIRMessaging.h b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FIRMessaging.h new file mode 100755 index 0000000..04ea927 --- /dev/null +++ b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FIRMessaging.h @@ -0,0 +1,236 @@ +#import + +/** + * The completion handler invoked once the data connection with FIRMessaging is + * established. The data connection is used to send a continous stream of + * data and all the FIRMessaging data notifications arrive through this connection. + * Once the connection is established we invoke the callback with `nil` error. + * Correspondingly if we get an error while trying to establish a connection + * we invoke the handler with an appropriate error object and do an + * exponential backoff to try and connect again unless successful. + * + * @param error The error object if any describing why the data connection + * to FIRMessaging failed. + */ +typedef void(^FIRMessagingConnectCompletion)(NSError * __nullable error); + +/** + * Notification sent when the upstream message has been delivered + * successfully to the server. The notification object will be the messageID + * of the successfully delivered message. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendSuccessNotification; + +/** + * Notification sent when the upstream message was failed to be sent to the + * server. The notification object will be the messageID of the failed + * message. The userInfo dictionary will contain the relevant error + * information for the failure. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendErrorNotification; + +/** + * Notification sent when the Firebase messaging server deletes pending + * messages due to exceeded storage limits. This may occur, for example, when + * the device cannot be reached for an extended period of time. + * + * It is recommended to retrieve any missing messages directly from the + * server. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingMessagesDeletedNotification; + +/** + * @enum FIRMessagingError + */ +typedef NS_ENUM(NSUInteger, FIRMessagingError) { + // Unknown error. + FIRMessagingErrorUnknown = 0, + + // Auth Error -- FIRMessaging couldn't validate request from this client. + FIRMessagingErrorAuthentication = 1, + + // NoAccess -- InstanceID service cannot be accessed. + FIRMessagingErrorNoAccess = 2, + + // Timeout -- Request to InstanceID backend timed out. + FIRMessagingErrorTimeout = 3, + + // Network -- No network available to reach the servers. + FIRMessagingErrorNetwork = 4, + + // OperationInProgress -- Another similar operation in progress, + // bailing this one. + FIRMessagingErrorOperationInProgress = 5, + + // InvalidRequest -- Some parameters of the request were invalid. + FIRMessagingErrorInvalidRequest = 7, +}; + +/// Status for the downstream message received by the app. +typedef NS_ENUM(NSInteger, FIRMessagingMessageStatus) { + FIRMessagingMessageStatusUnknown, + /// New downstream message received by the app. + FIRMessagingMessageStatusNew, +}; + +/// Information about a downstream message received by the app. +@interface FIRMessagingMessageInfo : NSObject + +@property(nonatomic, readonly, assign) FIRMessagingMessageStatus status; + +@end + +/** + * A remote data message received by the app via FCM (not just the APNs interface). + * + * This is only for devices running iOS 10 or above. To support devices running iOS 9 or below, use + * the local and remote notifications handlers defined in UIApplicationDelegate protocol. + */ +@interface FIRMessagingRemoteMessage : NSObject + +/// The downstream message received by the application. +@property(nonatomic, readonly, strong, nonnull) NSDictionary *appData; + +@end + +/** + * A protocol to receive data message via FCM for devices running iOS 10 or above. + * + * To support devices running iOS 9 or below, use the local and remote notifications handlers + * defined in UIApplicationDelegate protocol. + */ +@protocol FIRMessagingDelegate + +/// The callback to handle data message received via FCM for devices running iOS 10 or above. +- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage; + +@end + +/** + * Firebase Messaging enables apps to communicate with their app servers + * using simple messages. + * + * To send or receive messages, the app must get a + * registration token from GGLInstanceID, which authorizes an + * app server to send messages to an app instance. Pass your sender ID and + * `kGGLInstanceIDScopeFIRMessaging` as parameters to the method. + * + * A sender ID is a project number created when you configure your API project. + * It is labeled "Project Number" in the Google Developers Console. + * + * In order to receive FIRMessaging messages, declare application:didReceiveRemoteNotification: + * + * Client apps can send upstream messages back to the app server using the XMPP-based + * Cloud Connection Server, + * + */ +@interface FIRMessaging : NSObject + +/** + * Delegate to handle remote data messages received via FCM for devices running iOS 10 or above. + */ +@property(nonatomic, weak, nullable) id remoteMessageDelegate; + +/** + * FIRMessaging + * + * @return An instance of FIRMessaging. + */ ++ (nonnull instancetype)messaging NS_SWIFT_NAME(messaging()); + +/** + * Unavailable. Use +messaging instead. + */ +- (nonnull instancetype)init __attribute__((unavailable("Use +messaging instead."))); + +#pragma mark - Connect + +/** + * Create a FIRMessaging data connection which will be used to send the data notifications + * send by your server. It will also be used to send ACKS and other messages based + * on the FIRMessaging ACKS and other messages based on the FIRMessaging protocol. + * + * Use the `disconnect` method to disconnect the connection. + * + * @see FIRMessagingService disconnect + * + * @param handler The handler to be invoked once the connection is established. + * If the connection fails we invoke the handler with an + * appropriate error code letting you know why it failed. At + * the same time, FIRMessaging performs exponential backoff to retry + * establishing a connection and invoke the handler when successful. + */ +- (void)connectWithCompletion:(nonnull FIRMessagingConnectCompletion)handler; + +/** + * Disconnect the current FIRMessaging data connection. This stops any attempts to + * connect to FIRMessaging. Calling this on an already disconnected client is a no-op. + * + * Call this before `teardown` when your app is going to the background. + * Since the FIRMessaging connection won't be allowed to live when in background it is + * prudent to close the connection. + */ +- (void)disconnect; + +#pragma mark - Topics + +/** + * Asynchronously subscribes to a topic. + * + * @param topic The name of the topic, for example @"sports". + */ +- (void)subscribeToTopic:(nonnull NSString *)topic; + +/** + * Asynchronously unsubscribe to a topic. + * + * @param topic The name of the topic, for example @"sports". + */ +- (void)unsubscribeFromTopic:(nonnull NSString *)topic; + +#pragma mark - Upstream + +/** + * Sends an upstream ("device to cloud") message. + * + * The message will be queued if we don't have an active connection. + * You can only use the upstream feature if your GCM implementation + * uses the XMPP-based Cloud Connection Server. + * + * @param message Key/Value pairs to be sent. Values must be String, any + * other type will be ignored. + * @param to A string identifying the receiver of the message. For GCM + * project IDs the value is `SENDER_ID@gcm.googleapis.com`. + * @param messageID The ID of the message. This is generated by the application. It + * must be unique for each message generated by this application. + * It allows error callbacks and debugging, to uniquely identify + * each message. + * @param ttl The time to live for the message. In case we aren't able to + * send the message before the TTL expires we will send you a + * callback. If 0, we'll attempt to send immediately and return + * an error if we're not connected. Otherwise, the message will + * be queued. As for server-side messages, we don't return an error + * if the message has been dropped because of TTL; this can happen + * on the server side, and it would require extra communication. + */ +- (void)sendMessage:(nonnull NSDictionary *)message + to:(nonnull NSString *)receiver + withMessageID:(nonnull NSString *)messageID + timeToLive:(int64_t)ttl; + +#pragma mark - Analytics + +/** + * Call this when the app received a downstream message. Used to track message + * delivery and analytics for messages. You don't need to call this if you + * don't set the `FIRMessagingAutoSetupEnabled` flag in your Info.plist. In the + * latter case the library will call this implicitly to track relevant + * messages. + * + * @param message The downstream message received by the application. + * + * @return Information about the downstream message. + */ +- (nonnull FIRMessagingMessageInfo *)appDidReceiveMessage:(nonnull NSDictionary *)message; + +@end diff --git a/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FirebaseMessaging.h b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FirebaseMessaging.h new file mode 100755 index 0000000..ef49e7f --- /dev/null +++ b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FirebaseMessaging.h @@ -0,0 +1 @@ +#import "FIRMessaging.h" diff --git a/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Modules/module.modulemap b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Modules/module.modulemap new file mode 100755 index 0000000..a390f11 --- /dev/null +++ b/Pods/FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Modules/module.modulemap @@ -0,0 +1,13 @@ +framework module FirebaseMessaging { + + export * + + umbrella header "FirebaseMessaging.h" + + header "FIRMessaging.h" + + link framework "AddressBook" + link framework "SystemConfiguration" + + link "sqlite3" +} diff --git a/Pods/FirebaseMessaging/Sources/FIRMessaging.h b/Pods/FirebaseMessaging/Sources/FIRMessaging.h new file mode 100755 index 0000000..326d075 --- /dev/null +++ b/Pods/FirebaseMessaging/Sources/FIRMessaging.h @@ -0,0 +1,179 @@ +/** + * The completion handler invoked once the data connection with FIRMessaging is + * established. The data connection is used to send a continous stream of + * data and all the FIRMessaging data notifications arrive through this connection. + * Once the connection is established we invoke the callback with `nil` error. + * Correspondingly if we get an error while trying to establish a connection + * we invoke the handler with an appropriate error object and do an + * exponential backoff to try and connect again unless successful. + * + * @param error The error object if any describing why the data connection + * to FIRMessaging failed. + */ +typedef void(^FIRMessagingConnectCompletion)(NSError * __nullable error); + +/** + * Notification sent when the upstream message has been delivered + * successfully to the server. The notification object will be the messageID + * of the successfully delivered message. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendSuccessNotification; + +/** + * Notification sent when the upstream message was failed to be sent to the + * server. The notification object will be the messageID of the failed + * message. The userInfo dictionary will contain the relevant error + * information for the failure. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendErrorNotification; + +/** + * Notification sent when the Firebase messaging server deletes pending + * messages due to exceeded storage limits. This may occur, for example, when + * the device cannot be reached for an extended period of time. It is recommended + * to retrieve any missing messages directly from the server. + */ +FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingMessagesDeletedNotification; + +/** + * @enum FIRMessagingError + */ +typedef NS_ENUM(NSUInteger, FIRMessagingError) { + /// Unknown error. + FIRMessagingErrorUnknown = 0, + + /// Auth Error -- FIRMessaging couldn't validate request from this client. + FIRMessagingErrorAuthentication = 1, + + /// NoAccess -- InstanceID service cannot be accessed. + FIRMessagingErrorNoAccess = 2, + + /// Timeout -- Request to InstanceID backend timed out. + FIRMessagingErrorTimeout = 3, + + /// Network -- No network available to reach the servers. + FIRMessagingErrorNetwork = 4, + + /// OperationInProgress -- Another similar operation in progress, + /// bailing this one. + FIRMessagingErrorOperationInProgress = 5, + + /// InvalidRequest -- Some parameters of the request were invalid. + FIRMessagingErrorInvalidRequest = 7, +}; + +/** + * Firebase Messaging enables apps to communicate with their app servers + * using simple messages. + * + * To send or receive messages, the app must get a + * registration token from FirebaseInstanceID, which authorizes an + * app server to send messages to an app instance. Pass your sender ID and + * `kFIRInstanceIDScopeFIRMessaging` as parameters to the method. + * + * A sender ID is a project number created when you configure your API project. + * It is labeled "Project Number" in the Google Developers Console. + * + * In order to receive FIRMessaging messages, declare application:didReceiveRemoteNotification: + * + * Client apps can send upstream messages back to the app server using the XMPP-based + * Cloud Connection Server, + * + */ +@interface FIRMessaging : NSObject + +/** + * FIRMessaging + * + * @return An instance of FIRMessaging. + */ ++ (nonnull instancetype)messaging; + +#pragma mark - Connect + +/** + * Create a FIRMessaging data connection which will be used to send the data notifications + * send by your server. It will also be used to send ACKS and other messages based + * on the FIRMessaging ACKS and other messages based on the FIRMessaging protocol. + * + * Use the `disconnect` method to disconnect the connection. + * + * @see FIRMessagingService disconnect + * + * @param handler The handler to be invoked once the connection is established. + * If the connection fails we invoke the handler with an + * appropriate error code letting you know why it failed. At + * the same time, FIRMessaging performs exponential backoff to retry + * establishing a connection and invoke the handler when successful. + */ +- (void)connectWithCompletion:(nonnull FIRMessagingConnectCompletion)handler; + +/** + * Disconnect the current FIRMessaging data connection. This stops any attempts to + * connect to FIRMessaging. Calling this on an already disconnected client is a no-op. + * + * Call this before `teardown` when your app is going to the background. + * Since the FIRMessaging connection won't be allowed to live when in background it is + * prudent to close the connection. + */ +- (void)disconnect; + +#pragma mark - Topics + +/** + * Asynchronously subscribes to a topic. + * + * @param topic The name of the topic, for example @"sports". + */ +- (void)subscribeToTopic:(nonnull NSString *)topic; + +/** + * Asynchronously unsubscribe to a topic. + * + * @param topic The name of the topic, for example @"sports". + */ +- (void)unsubscribeFromTopic:(nonnull NSString *)topic; + +#pragma mark - Upstream + +/** + * Sends an upstream ("device to cloud") message. + * + * The message will be queued if we don't have an active connection. + * You can only use the upstream feature if your Firebase Messaging implementation + * uses the XMPP-based Cloud Connection Server. + * + * @param message Key/Value pairs to be sent. Values must be String, any + * other type will be ignored. + * @param to A string identifying the receiver of the message. For GCM + * project IDs the value is `SENDER_ID@gcm.googleapis.com`. + * @param messageID The ID of the message. This is generated by the application. It + * must be unique for each message generated by this application. + * It allows error callbacks and debugging, to uniquely identify + * each message. + * @param ttl The time to live for the message. In case we aren't able to + * send the message before the TTL expires we will send you a + * callback. If 0, we'll attempt to send immediately and return + * an error if we're not connected. Otherwise, the message will + * be queued. As for server-side messages, we don't return an error + * if the message has been dropped because of TTL; this can happen + * on the server side, and it would require extra communication. + */ +- (void)sendMessage:(nonnull NSDictionary *)message + to:(nonnull NSString *)receiver + withMessageID:(nonnull NSString *)messageID + timeToLive:(int64_t)ttl; + +#pragma mark - Analytics + +/** + * Call this when the app received a downstream message. Used to track message + * delivery and analytics for messages. You don't need to call this if you + * don't set the `FIRMessagingAutoSetupEnabled` flag in your Info.plist. In the + * latter case the library will call this implicitly to track relevant messages. + * + * @param message The downstream message received by the application. + */ +- (void)appDidReceiveMessage:(nonnull NSDictionary *)message; + +@end diff --git a/Pods/Google/Headers/GGLAnalytics/Public/Analytics.h b/Pods/Google/Headers/GGLAnalytics/Public/Analytics.h new file mode 100644 index 0000000..105d74b --- /dev/null +++ b/Pods/Google/Headers/GGLAnalytics/Public/Analytics.h @@ -0,0 +1,14 @@ +#import "Core.h" + +#import "GAI.h" +#import "GAIDictionaryBuilder.h" +#import "GAIEcommerceFields.h" +#import "GAIEcommerceProduct.h" +#import "GAIEcommerceProductAction.h" +#import "GAIEcommercePromotion.h" +#import "GAIFields.h" +#import "GAILogger.h" +#import "GAITrackedViewController.h" +#import "GAITracker.h" +#import "GGLContext+Analytics.h" + diff --git a/Pods/Google/Headers/GGLAnalytics/Public/GGLContext+Analytics.h b/Pods/Google/Headers/GGLAnalytics/Public/GGLContext+Analytics.h new file mode 100644 index 0000000..4e721ac --- /dev/null +++ b/Pods/Google/Headers/GGLAnalytics/Public/GGLContext+Analytics.h @@ -0,0 +1,38 @@ +#import "GAITracker.h" +#import "GGLContext.h" + +/** + * The Google Analytic dispatch time interval in seconds when using a simulator. Setting it to one + * second by default means the tracking information will be automatically dispatched every second + * when using an iPhone simulator. + * + * If running on a device, uses the default time interval set by Google + * Analytics, which is two minutes. + */ +extern const NSTimeInterval kSimulatorDispatchIntervalInSeconds; + +/** + * This category extends |GGLContext| with the analytics service. Import + * GGLContext+Analytics to use Google Analytics in your app. + * + * [GAI sharedInstance] and [[GAI sharedInstance] defaultTracker] should be ready to use after + * -[[GGLContext sharedInstance] configureWithError:] is called. The defaultTracker can also be + * fetched here through [GGLContext sharedInstance].tracker. The tracking ID of the tracker is the + * one defined in GoogleService-Info.plist. + * + * @see GGLContext + */ +@interface GGLContext (Analytics) + +/** + * Retrieve a configured GAITracker instance. + * + * Note that [[GAI sharedInstance] defaultTracker] is the first initialized tracker. If a developer + * initializes a tracker before calling -[[GGLContext sharedInstance] configureWithError:], + * -[[GAI sharedInstance] defaultTracker] is the one initialized first. The one initialized through + * GGLContext can be accessed by either [[GAI sharedInstance] trackerWithTrackingId:@"UA-XXXX-Y"] + * or [GGLContext sharedInstance].tracker. + */ +@property(nonatomic, readonly, strong) id tracker; + +@end diff --git a/Pods/Google/Headers/GGLCore/Public/Core.h b/Pods/Google/Headers/GGLCore/Public/Core.h new file mode 100644 index 0000000..98b3973 --- /dev/null +++ b/Pods/Google/Headers/GGLCore/Public/Core.h @@ -0,0 +1,6 @@ +#import +#import + +#import "GGLConfiguration.h" +#import "GGLContext.h" +#import "GGLErrorCode.h" diff --git a/Pods/Google/Headers/GGLCore/Public/GGLConfiguration.h b/Pods/Google/Headers/GGLCore/Public/GGLConfiguration.h new file mode 100644 index 0000000..3464e64 --- /dev/null +++ b/Pods/Google/Headers/GGLCore/Public/GGLConfiguration.h @@ -0,0 +1,99 @@ +@import Foundation; + +/** + * This class provides configuration details to Google APIs. + */ +@interface GGLConfiguration : NSObject + +/** + * Initializes the configuration with provided details. + * + * This is the designated initializer. + * + * @param apiKey A secret iOS API key used for authenticating requests from your app, e.g. + * @"AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", used to identify your app to Google + * servers. + * @param clientID The OAuth2 client ID used to authenticate Google users, for example + * @"12345.apps.googleusercontent.com", used for signing in with Google. + * @param identityProviders The set of providers used for authentication when presenting or + * configuring authentication options. See |kGGLGoogleIdentityProvider| + * and |kGGLFacebookIdentityProvider|. + * @param trackingID The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure + * Google Analytics. + * @param widgetURL Where the GITkit JavaScript widget is hosted. It should be defined in REDIRECT + * URIS of the Client ID for web application. + * @param bannerAdUnitID Mobile Ads' Ad Unit ID for a banner view, for example + @"ca-app-pub-1234567890", used for displaying ads view. + * @param interstitialAdUnitID Mobile Ads' Ad Unit ID for an interstitial view, for example + * @"ca-app-pub-1234567890", used for displaying ads view. + * @param gcmSenderID The Project Number from the Google Developer's console, + * for example @"012345678901", used to configure Google Cloud Messaging. + * @param androidClientID The Android client ID used in Google AppInvite when an iOS app has its + * Android version, for example @"12345.apps.googleusercontent.com". + */ +- (instancetype)initWithAPIKey:(NSString *)apiKey + clientID:(NSString *)clientID + identityProviders:(NSArray *)identityProviders + trackingID:(NSString *)trackingID + widgetURL:(NSString *)widgetURL + bannerAdUnitID:(NSString *)bannerAdUnitID + interstitialAdUnitID:(NSString *)interstitialAdUnitID + gcmSenderID:(NSString *)gcmSenderID + androidClientID:(NSString *)androidClientID; + +/** + * A secret iOS API key used for authenticating requests from your app, e.g. + * @"AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", used to identify your app to Google servers. + */ +@property(nonatomic, readonly, copy) NSString *apiKey; + +/** + * The OAuth2 client ID for iOS application used to authenticate Google users, for example + * @"12345.apps.googleusercontent.com", used for signing in with Google. + */ +@property(nonatomic, readonly, copy) NSString *clientID; + +/** + * The set of providers used for authentication when presenting or configuring authentication + * options. See |kGGLGoogleIdentityProvider| and |kGGLFacebookIdentityProvider|. + */ +@property(nonatomic, readonly, copy) NSArray *identityProviders; + +/** + * The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure Google Analytics. + */ +@property(nonatomic, readonly, copy) NSString *trackingID; + +/** + * Where the GITkit JavaScript widget is hosted. It should be defined in REDIRECT URIS of the Client + * ID for web application. The widget is used for operations like password recovery and email + * change. It should be hosted in the developer's backend server. It can be nil if such a server + * does not exist. + */ +@property(nonatomic, readonly, copy) NSString *widgetURL; + +/** + * Mobile Ads' Ad Unit ID for a banner view, for example @"ca-app-pub-1234567890", used for + * displaying ads view. + */ +@property(nonatomic, readonly, copy) NSString *bannerAdUnitID; + +/** + * Mobile Ads' Ad Unit ID for an interstitial view, for example @"ca-app-pub-1234567890", used for + * displaying ads view. + */ +@property(nonatomic, readonly, copy) NSString *interstitialAdUnitID; + +/** + * The Project Number from the Google Developer's console, for example @"012345678901", used to + * configure Google Cloud Messaging. + */ +@property(nonatomic, readonly, copy) NSString *gcmSenderID; + +/** + * The Android client ID used in Google AppInvite when an iOS app has its Android version, for + * example @"12345.apps.googleusercontent.com". + */ +@property(nonatomic, readonly, copy) NSString *androidClientID; + +@end diff --git a/Pods/Google/Headers/GGLCore/Public/GGLContext.h b/Pods/Google/Headers/GGLCore/Public/GGLContext.h new file mode 100644 index 0000000..c25ae47 --- /dev/null +++ b/Pods/Google/Headers/GGLCore/Public/GGLContext.h @@ -0,0 +1,55 @@ +@import Foundation; + +@class GGLConfiguration; +@class GGLIdentity; + +/** + * Main entry point for Google API core configuration. Developers should call + * -[[GGLContext sharedInstance] configureWithError:] to configure the integrated services such as + * AdMob, Analytics, AppInvite, CloudMessaging, SignIn, etc. See GGLContext+ServiceName + * for details on the individual APIs. Generally, you will import those files + * directly rather than import the GGLContext.h header itself. + * + * Once the appropriate categories are imported, you can configure all services via the + * |configureWithError:| method, for example: + * + * NSError* configureError; + * [[GGLContext sharedInstance] configureWithError: &configureError]; + * if (configureError != nil) { + * NSLog(@"Error configuring the Google context: %@", configureError); + * } + * + * @see GGLContext (AdMob) + * @see GGLContext (Analytics) + * @see GGLContext (AppInvite) + * @see GGLContext (CloudMessaging) + * @see GGLContext (SignIn) + */ +@interface GGLContext : NSObject + +/** + * The configuration details for various Google APIs. + */ +@property(nonatomic, readonly, strong) GGLConfiguration *configuration; + +/** + * Get the shared instance of the GGLContext. + * @return the shared instance + */ ++ (instancetype)sharedInstance; + +/** + * Configures all the Google services integrated. This method should be called after the app is + * launched and before using other Google services. The services will be available in categories + * that extend this class, such as GGLContext+AdMob. + * + * @param error Pointer to an NSError that can be used an out param to report the status of this + * operation. After the call the error object will be nil if the operation is succesful, + * otherwise contains an appropriate NSError value. Parameter cannot be passed as nil + * + * @warning error must not be nil. + * + */ +- (void)configureWithError:(NSError **)error; + +@end diff --git a/Pods/Google/Headers/GGLCore/Public/GGLErrorCode.h b/Pods/Google/Headers/GGLCore/Public/GGLErrorCode.h new file mode 100644 index 0000000..ed19d31 --- /dev/null +++ b/Pods/Google/Headers/GGLCore/Public/GGLErrorCode.h @@ -0,0 +1,74 @@ +/** Error codes in Greenhouse error domain. */ +typedef enum { + /** + * Operation succeeded. + */ + kGGLErrorCodeSucceeded = 0, + /** + * Default failure error code. This is a catch all error and indicates something has gone very + * wrong. There is no remediation for this case. + **/ + kGGLErrorCodeUnknownFailure = -1, + + /** + * Indicates that the calling method did not do anything in response to the call. This occurs in + * situations where the caller asked state to be mutated into its current state or selector wasn't + * present but it isn't considered a critical failure.. + */ + kGGLErrorCodeNoOp = -2, + + // 100 series codes are for GGLIdentity + /** + * Sign In was cancelled before completion. This indicates that the user cancelled login. If + * non-login users are supported receiving this error code should start that flow. + **/ + kGGLErrorCodeSignInCancelled = -100, + + /** + * Sign in failed due to the e-mail returned by the provider did not match the one inputted by + * the user. This indicates some sort of issue with the identity provider. Possibly the user is + * using an alias for the email that they created the account with. This is likely a permanent + * failure without remediation by the identity provider. The provided UI does not indicate the + * failure to the user, so it is the responsibility of the caller to communicate this reason for + * failure to the user. + */ + kGGLErrorCodeEmailMismatch = -101, + + /** + * Sign in failed due to the user attempting to create a new account with an e-mail that is + * already associated with an account. The provided UI does not indicate the failure to the user, + * so it is the responsibility of the caller to communicate this reason for failure to the user. + */ + kGGLErrorCodeEmailExists = -102, + + /** + * Sign in failed due to an unexpected response during sign in process. This may or may not be a + * transitory error, so on first occurrence it is reasonable to attempt again. If it is + * consistently occurring it may indicate that the identity provider is not available, check the + * internet connection. + */ + kGGLErrorCodeUnexpectedResponse = -103, + + /** + * Indicates the session is not stored locally. + */ + kGGLErrorCodeSessionNotStored = -104, + + /** + * The user cannot sign in silently without a user interface. + */ + kGGLErrorCodeCannotSignInSilently = -105, + + // 200 series error codes are for GGLContext + /** + * Configuration of one of the requested subspecs failed. Additional details on what failed and + * why it failed should appear in the log. + */ + kGGLErrorCodeFeatureSubspecConfigFailed = -200, + + /** + * Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should + * not be ignored. Further calls to the API will fail and/or possibly cause crashes. + */ + kGGLErrorCodeInvalidPlistFile = -200 +} GGLErrorCode; diff --git a/Pods/Google/Libraries/libGGLAnalytics.a b/Pods/Google/Libraries/libGGLAnalytics.a new file mode 100644 index 0000000..91eeef8 Binary files /dev/null and b/Pods/Google/Libraries/libGGLAnalytics.a differ diff --git a/Pods/Google/Libraries/libGGLCore.a b/Pods/Google/Libraries/libGGLCore.a new file mode 100644 index 0000000..9313eb2 Binary files /dev/null and b/Pods/Google/Libraries/libGGLCore.a differ diff --git a/Pods/GoogleAnalytics/Headers/Public/GAI.h b/Pods/GoogleAnalytics/Headers/Public/GAI.h new file mode 100644 index 0000000..09c6bfd --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAI.h @@ -0,0 +1,192 @@ +/*! + @header GAI.h + @abstract Google Analytics iOS SDK Header + @version 3.13 + @copyright Copyright 2015 Google Inc. All rights reserved. + */ + +#import + +#import "GAILogger.h" +#import "GAITrackedViewController.h" +#import "GAITracker.h" + +typedef NS_ENUM(NSUInteger, GAIDispatchResult) { + kGAIDispatchNoData, + kGAIDispatchGood, + kGAIDispatchError +}; + +/*! Google Analytics product string. */ +extern NSString *const kGAIProduct; + +/*! Google Analytics version string. */ +extern NSString *const kGAIVersion; + +/*! + NSError objects returned by the Google Analytics SDK may have this error domain + to indicate that the error originated in the Google Analytics SDK. + */ +extern NSString *const kGAIErrorDomain; + +/*! Google Analytics error codes. */ +typedef enum { + // This error code indicates that there was no error. Never used. + kGAINoError = 0, + + // This error code indicates that there was a database-related error. + kGAIDatabaseError, + + // This error code indicates that there was a network-related error. + kGAINetworkError, +} GAIErrorCode; + +/*! + Google Analytics iOS top-level class. Provides facilities to create trackers + and set behaviorial flags. + */ +@interface GAI : NSObject + +/*! + For convenience, this class exposes a default tracker instance. + This is initialized to `nil` and will be set to the first tracker that is + instantiated in trackerWithTrackingId:. It may be overridden as desired. + + The GAITrackedViewController class will, by default, use this tracker instance. + */ +@property(nonatomic, assign) id defaultTracker; + +/*! + The GAILogger to use. + */ +@property(nonatomic, retain) id logger; + +/*! + When this is true, no tracking information will be gathered; tracking calls + will effectively become no-ops. When set to true, all tracking information that + has not yet been submitted. The value of this flag will be persisted + automatically by the SDK. Developers can optionally use this flag to implement + an opt-out setting in the app to allows users to opt out of Google Analytics + tracking. + + This is set to `NO` the first time the Google Analytics SDK is used on a + device, and is persisted thereafter. + */ +@property(nonatomic, assign) BOOL optOut; + +/*! + If this value is positive, tracking information will be automatically + dispatched every dispatchInterval seconds. Otherwise, tracking information must + be sent manually by calling dispatch. + + By default, this is set to `120`, which indicates tracking information should + be dispatched automatically every 120 seconds. + */ +@property(nonatomic, assign) NSTimeInterval dispatchInterval; + +/*! + When set to true, the SDK will record the currently registered uncaught + exception handler, and then register an uncaught exception handler which tracks + the exceptions that occurred using defaultTracker. If defaultTracker is not + `nil`, this function will track the exception on the tracker and attempt to + dispatch any outstanding tracking information for 5 seconds. It will then call + the previously registered exception handler, if any. When set back to false, + the previously registered uncaught exception handler will be restored. + */ +@property(nonatomic, assign) BOOL trackUncaughtExceptions; + +/*! + When this is 'YES', no tracking information will be sent. Defaults to 'NO'. + */ +@property(nonatomic, assign) BOOL dryRun; + +/*! Get the shared instance of the Google Analytics for iOS class. */ ++ (GAI *)sharedInstance; + +/*! + Creates or retrieves a GAITracker implementation with the specified name and + tracking ID. If the tracker for the specified name does not already exist, then + it will be created and returned; otherwise, the existing tracker will be + returned. If the existing tracker for the respective name has a different + tracking ID, that tracking ID is not changed by this method. If defaultTracker + is not set, it will be set to the tracker instance returned here. + + @param name The name of this tracker. Must not be `nil` or empty. + + @param trackingID The tracking ID to use for this tracker. It should be of + the form `UA-xxxxx-y`. + + @return A GAITracker associated with the specified name. The tracker + can be used to send tracking data to Google Analytics. The first time this + method is called with a particular name, the tracker for that name will be + returned, and subsequent calls with the same name will return the same + instance. It is not necessary to retain the tracker because the tracker will be + retained internally by the library. + + If an error occurs or the name is not valid, this method will return + `nil`. + */ +- (id)trackerWithName:(NSString *)name + trackingId:(NSString *)trackingId; + +/*! + Creates or retrieves a GAITracker implementation with name equal to + the specified tracking ID. If the tracker for the respective name does not + already exist, it is created, has it's tracking ID set to |trackingId|, + and is returned; otherwise, the existing tracker is returned. If the existing + tracker for the respective name has a different tracking ID, that tracking ID + is not changed by this method. If defaultTracker is not set, it is set to the + tracker instance returned here. + + @param trackingID The tracking ID to use for this tracker. It should be of + the form `UA-xxxxx-y`. The name of the tracker will be the same as trackingID. + + @return A GAITracker associated with the specified trackingID. The tracker + can be used to send tracking data to Google Analytics. The first time this + method is called with a particular trackingID, the tracker for the respective + name will be returned, and subsequent calls with the same trackingID + will return the same instance. It is not necessary to retain the tracker + because the tracker will be retained internally by the library. + + If an error occurs or the trackingId is not valid, this method will return + `nil`. + */ +- (id)trackerWithTrackingId:(NSString *)trackingId; + +/*! + Remove a tracker from the trackers dictionary. If it is the default tracker, + clears the default tracker as well. + + @param name The name of the tracker. + */ +- (void)removeTrackerByName:(NSString *)name; + +/*! + Dispatches any pending tracking information. + + Note that this does not have any effect on dispatchInterval, and can be used in + conjunction with periodic dispatch. */ +- (void)dispatch; + +/*! + Dispatches the next tracking beacon in the queue, calling completionHandler when + the tracking beacon has either been sent (returning kGAIDispatchGood) or an error has resulted + (returning kGAIDispatchError). If there is no network connection or there is no data to send, + kGAIDispatchNoData is returned. + + Note that calling this method with a non-nil completionHandler disables periodic dispatch. + Periodic dispatch can be reenabled by setting the dispatchInterval to a positive number when + the app resumes from the background. + + Calling this method with a nil completionHandler is the same as calling the dispatch + above. + + This method can be used for background data fetching in iOS 7.0 or later. It would be wise to + call this when the application is exiting to initiate the submission of any unsubmitted + tracking information. + + @param completionHandler The block to run after a single dispatch request. The GAIDispatchResult + param indicates whether the dispatch succeeded, had an error, or had no hits to dispatch. + */ +- (void)dispatchWithCompletionHandler:(void (^)(GAIDispatchResult result))completionHandler; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIDictionaryBuilder.h b/Pods/GoogleAnalytics/Headers/Public/GAIDictionaryBuilder.h new file mode 100644 index 0000000..28418cb --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIDictionaryBuilder.h @@ -0,0 +1,217 @@ +/*! + @header GAIDictionaryBuilder.h + @abstract Google Analytics iOS SDK Hit Format Header + @copyright Copyright 2013 Google Inc. All rights reserved. + */ + +#import + +#import "GAIEcommerceProduct.h" +#import "GAIEcommerceProductAction.h" +#import "GAIEcommercePromotion.h" + +/*! + * Helper class to build a dictionary of hit parameters and values. + *
+ * Examples: + * + * id t = // get a tracker. + * [t send:[[[GAIDictionaryBuilder createEventWithCategory:@"EventCategory" + * action:@"EventAction" + * label:nil + * value:nil] + * set:@"dimension1" forKey:[GAIFields customDimensionForIndex:1]] build]]; + * + * This will send an event hit type with the specified parameters + * and a custom dimension parameter. + *
+ * If you want to send a parameter with all hits, set it on GAITracker directly. + * + * [t set:kGAIScreenName value:@"Home"]; + * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" + * action:@"PlusOne" + * target:@"SOME_URL"] build]]; + * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" + * action:@"Share" + * target:@"SOME_POST"] build]]; + * [t send:[[GAIDictionaryBuilder createSocialWithNetwork:@"Google+" + * action:@"HangOut" + * target:@"SOME_CIRCLE"] + * build]]; + * + * You can override a value set on the tracker by adding it to the dictionary. + * + * [t set:kGAIScreenName value:@"Home"]; + * [t send:...]; + * [t send[[[GAIDictionaryBuilder createEventWithCategory:@"click" + * action:@"popup" + * label:nil + * value:nil] + * set:@"popup title" forKey:kGAIScreenName] build]]; + * + * The values set via [GAIDictionaryBuilder set] or + * [GAIDictionaryBuilder setAll] will override any existing values in the + * GAIDictionaryBuilder object (i.e. initialized by + * [GAIDictionaryBuilder createXYZ]). e.g. + * + * GAIDictionaryBuilder *m = + * GAIDictionaryBuilder createTimingWithCategory:@"category" + * interval:@0 + * name:@"name" + * label:nil]; + * [t send:[m.set:@"10" forKey:kGAITimingVar] build]; + * [t send:[m.set:@"20" forKey:kGAITimingVar] build]; + * + */ +@interface GAIDictionaryBuilder : NSObject + +- (GAIDictionaryBuilder *)set:(NSString *)value + forKey:(NSString *)key; + +/*! + * Copies all the name-value pairs from params into this object, ignoring any + * keys that are not NSString and any values that are neither NSString or + * NSNull. + */ +- (GAIDictionaryBuilder *)setAll:(NSDictionary *)params; + +/*! + * Returns the value for the input parameter paramName, or nil if paramName + * is not present. + */ +- (NSString *)get:(NSString *)paramName; + +/*! + * Return an NSMutableDictionary object with all the parameters set in this + */ +- (NSMutableDictionary *)build; + +/*! + * Parses and translates utm campaign parameters to analytics campaign param + * and returns them as a map. + * + * @param params url containing utm campaign parameters. + * + * Valid campaign parameters are: + *
    + *
  • utm_id
  • + *
  • utm_campaign
  • + *
  • utm_content
  • + *
  • utm_medium
  • + *
  • utm_source
  • + *
  • utm_term
  • + *
  • dclid
  • + *
  • gclid
  • + *
  • gmob_t
  • + *
  • aclid
  • + *
  • anid
  • + *
+ *

+ * Example: + * http://my.site.com/index.html?utm_campaign=wow&utm_source=source + * utm_campaign=wow&utm_source=source. + *

+ * For more information on auto-tagging, see + * http://support.google.com/googleanalytics/bin/answer.py?hl=en&answer=55590 + *

+ * For more information on manual tagging, see + * http://support.google.com/googleanalytics/bin/answer.py?hl=en&answer=55518 + */ +- (GAIDictionaryBuilder *)setCampaignParametersFromUrl:(NSString *)urlString; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to an appview + hit. + + Note that using this method will not set the screen name for followon hits. To + do that you need to call set:kGAIDescription value: on the + GAITracker instance. + + This method is deprecated. Use createScreenView instead. + */ ++ (GAIDictionaryBuilder *)createAppView DEPRECATED_MSG_ATTRIBUTE("Use createScreenView instead."); + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to a screenview + hit. + + Note that using this method will not set the screen name for followon hits. To + do that you need to call set:kGAIDescription value: on the + GAITracker instance. + */ ++ (GAIDictionaryBuilder *)createScreenView; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to an event hit. + */ ++ (GAIDictionaryBuilder *)createEventWithCategory:(NSString *)category + action:(NSString *)action + label:(NSString *)label + value:(NSNumber *)value; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to an exception + hit. + */ ++ (GAIDictionaryBuilder *)createExceptionWithDescription:(NSString *)description + withFatal:(NSNumber *)fatal; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to an item hit. + */ ++ (GAIDictionaryBuilder *)createItemWithTransactionId:(NSString *)transactionId + name:(NSString *)name + sku:(NSString *)sku + category:(NSString *)category + price:(NSNumber *)price + quantity:(NSNumber *)quantity + currencyCode:(NSString *)currencyCode; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to a social hit. + */ ++ (GAIDictionaryBuilder *)createSocialWithNetwork:(NSString *)network + action:(NSString *)action + target:(NSString *)target; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to a timing hit. + */ ++ (GAIDictionaryBuilder *)createTimingWithCategory:(NSString *)category + interval:(NSNumber *)intervalMillis + name:(NSString *)name + label:(NSString *)label; + +/*! + Returns a GAIDictionaryBuilder object with parameters specific to a transaction + hit. + */ ++ (GAIDictionaryBuilder *)createTransactionWithId:(NSString *)transactionId + affiliation:(NSString *)affiliation + revenue:(NSNumber *)revenue + tax:(NSNumber *)tax + shipping:(NSNumber *)shipping + currencyCode:(NSString *)currencyCode; + +/*! + Set the product action field for this hit. + */ +- (GAIDictionaryBuilder *)setProductAction:(GAIEcommerceProductAction *)productAction; + +/*! + Adds a product to this hit. + */ +- (GAIDictionaryBuilder *)addProduct:(GAIEcommerceProduct *)product; + +/*! + Add a product impression to this hit. + */ +- (GAIDictionaryBuilder *)addProductImpression:(GAIEcommerceProduct *)product + impressionList:(NSString *)name + impressionSource:(NSString *)source; + +/*! + Add a promotion to this hit. + */ +- (GAIDictionaryBuilder *)addPromotion:(GAIEcommercePromotion *)promotion; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceFields.h b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceFields.h new file mode 100644 index 0000000..b3ba60a --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceFields.h @@ -0,0 +1,124 @@ +/*! + @header GAIEcommerceFields.h + @abstract Google Analytics iOS SDK Ecommerce Hit Format Header + @copyright Copyright 2014 Google Inc. All rights reserved. + */ + +#import + +/*! + This class provides several fields and methods useful as wire format parameters for + Enhanced Ecommerce. See the online developer guides for Enhanced Ecommerce for details + on how to use the Enhanced Ecommerce features. + */ + +// Enhanced Ecommerce Product fields +extern NSString *const kGAIProductId; +extern NSString *const kGAIProductName; +extern NSString *const kGAIProductBrand; +extern NSString *const kGAIProductCategory; +extern NSString *const kGAIProductVariant; +extern NSString *const kGAIProductPrice; +extern NSString *const kGAIProductQuantity; +extern NSString *const kGAIProductCouponCode; +extern NSString *const kGAIProductPosition; + +extern NSString *const kGAIProductAction; + +// product action values +extern NSString *const kGAIPADetail; +extern NSString *const kGAIPAClick; +extern NSString *const kGAIPAAdd; +extern NSString *const kGAIPARemove; +extern NSString *const kGAIPACheckout; +extern NSString *const kGAIPACheckoutOption; +extern NSString *const kGAIPAPurchase; +extern NSString *const kGAIPARefund; + +// product action fields +// used for 'purchase' and 'refund' actions +extern NSString *const kGAIPATransactionId; +extern NSString *const kGAIPAAffiliation; +extern NSString *const kGAIPARevenue; +extern NSString *const kGAIPATax; +extern NSString *const kGAIPAShipping; +extern NSString *const kGAIPACouponCode; +// used for 'checkout' action +extern NSString *const kGAICheckoutStep; +extern NSString *const kGAICheckoutOption; +// used for 'detail' and 'click' actions +extern NSString *const kGAIProductActionList; +extern NSString *const kGAIProductListSource; + +// Enhanced Ecommerce Impressions fields +extern NSString *const kGAIImpressionName; +extern NSString *const kGAIImpressionListSource; +extern NSString *const kGAIImpressionProduct; +extern NSString *const kGAIImpressionProductId; +extern NSString *const kGAIImpressionProductName; +extern NSString *const kGAIImpressionProductBrand; +extern NSString *const kGAIImpressionProductCategory; +extern NSString *const kGAIImpressionProductVariant; +extern NSString *const kGAIImpressionProductPosition; +extern NSString *const kGAIImpressionProductPrice; + +// Enhanced Ecommerce Promotions fields +extern NSString *const kGAIPromotionId; +extern NSString *const kGAIPromotionName; +extern NSString *const kGAIPromotionCreative; +extern NSString *const kGAIPromotionPosition; + +// Promotion actions +extern NSString *const kGAIPromotionAction; +extern NSString *const kGAIPromotionView; +extern NSString *const kGAIPromotionClick; + +@interface GAIEcommerceFields : NSObject + +/*! + Generates an enhanced ecommerce product field. Note that field names generated by + customDimensionForIndex and customMetricForIndex can be used as suffixes. + + @param index the index of the product + @param suffix the product field suffix (such as kGAIProductPrice). + + @return an NSString representing the product field parameter + */ ++ (NSString *)productFieldForIndex:(NSUInteger)index suffix:(NSString *)suffix; + +/*! + Genrates an enhanced ecommerce impression list field name with an index. The return value of + this method should also be used as input to the productImpressionForList method below. + + @param index the index of the impression list + + @return an NSString representing the impression list parameter + */ ++ (NSString *)impressionListForIndex:(NSUInteger)index; + +/*! + Generates an enhanced ecommerce product impression field with the impression list, product index + and product suffix as parameters. The output of the method impressionListForIndex above should be + used as the input list for this method. The output of customDimensionForIndex and + customMetricForIndex can be used as suffixes. + + @param list the impression list for this product impression + @param index the index of this product in the impression list + @param suffix the product impression suffix for this field + + @return an NSString representing this product impression field parameter + */ ++ (NSString *)productImpressionForList:(NSString *)list + index:(NSUInteger)index + suffix:(NSString *)Suffix; + +/*! + Generates an enhanced ecommerce promotion field with an index and suffix. + + @param index the index of the promotion + @param suffix the promotion suffix (such as kGAIPromotionId) + + @return an NSString representing this promotion field paramter + */ ++ (NSString *)promotionForIndex:(NSUInteger)index suffix:(NSString *)suffix; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProduct.h b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProduct.h new file mode 100644 index 0000000..029f763 --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProduct.h @@ -0,0 +1,102 @@ +/*! + @header GAIEcommerceProduct.h + @abstract Google Analytics iOS SDK Hit Format Header + @copyright Copyright 2014 Google Inc. All rights reserved. + */ + +#import + +/*! + * Class to construct product related information for a Google Analytics beacon. Use this class to + * report information about products sold by merchants or impressions of products seen by users. + * Instances of this class can be associated with both Product Actions and Product + * Impression Lists. + *
+ * Typical usage: + * + * [tracker set:kGAIScreenName value:@"MyScreen"]; + * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; + * GAIEcommerceProduct *product = [[GAIEcommerceProduct alloc] init]; + * [product setId:@""PID-1234""]; + * [product setName:@"Space Monkeys!"]; + * [product setPrice:@100]; + * [product setQuantity:@2]; + * [builder addProductImpression:product impressionList:@"listName"]; + * [tracker send:[builder build]]; + * + */ +@interface GAIEcommerceProduct : NSObject + +/*! + Sets the id that is used to identify a product in GA reports. + */ +- (GAIEcommerceProduct *)setId:(NSString *)productId; + +/*! + Sets the name that is used to indentify the product in GA reports. + */ +- (GAIEcommerceProduct *)setName:(NSString *)productName; + +/*! + Sets the brand associated with the product in GA reports. + */ +- (GAIEcommerceProduct *)setBrand:(NSString *)productBrand; + +/*! + Sets the category associated with the product in GA reports. + */ +- (GAIEcommerceProduct *)setCategory:(NSString *)productCategory; + +/*! + Sets the variant of the product. + */ +- (GAIEcommerceProduct *)setVariant:(NSString *)productVariant; + +/*! + Sets the price of the product. + */ +- (GAIEcommerceProduct *)setPrice:(NSNumber *)productPrice; + +/*! + Sets the quantity of the product. This field is usually not used with product impressions. + */ +- (GAIEcommerceProduct *)setQuantity:(NSNumber *)productQuantity; + +/*! + Sets the coupon code associated with the product. This field is usually not used with product + impressions. + */ +- (GAIEcommerceProduct *)setCouponCode:(NSString *)productCouponCode; + +/*! + Sets the position of the product on the screen/product impression list, etc. + */ +- (GAIEcommerceProduct *)setPosition:(NSNumber *)productPosition; + +/*! + Sets the custom dimension associated with this product. + */ +- (GAIEcommerceProduct *)setCustomDimension:(NSUInteger)index value:(NSString *)value; + +/*! + Sets the custom metric associated with this product. + */ +- (GAIEcommerceProduct *)setCustomMetric:(NSUInteger)index value:(NSNumber *)value; + +/*! + Builds an NSDictionary of fields stored in this instance suitable for a product action. The + index parameter is the index of this product in the product action list. +
+ Normally, users will have no need to call this method. + */ +- (NSDictionary *)buildWithIndex:(NSUInteger)index; + +/*! + Builds an NSDictionary of fields stored in this instance suitable for an impression list. The + lIndex parameter is the index of the product impression list while the index parameter is the + index of this product in that impression list. +
+ Normally, users will have no need to call this method. + */ +- (NSDictionary *)buildWithListIndex:(NSUInteger)lIndex index:(NSUInteger)index; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProductAction.h b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProductAction.h new file mode 100644 index 0000000..e3da1c1 --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIEcommerceProductAction.h @@ -0,0 +1,107 @@ +/*! + @header GAIProductAction.h + @abstract Google Analytics iOS SDK Hit Format Header + @copyright Copyright 2014 Google Inc. All rights reserved. + */ + +#import + +/*! + * Class to construct transaction/checkout or other product interaction related information for a + * Google Analytics hit. Use this class to report information about products sold, viewed or + * refunded. This class is intended to be used with GAIDictionaryBuilder. + *
+ * Typical usage: + * + * [tracker set:kGAIScreenName value:@"MyScreen"]; + * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; + * GAIEcommerceProductAction *action = [[GAIEcommerceProductAction alloc] init]; + * [action setAction:kGAIPAPurchase]; + * [action setTransactionId:@"TT-1234"]; + * [action setRevenue:@3.14]; + * [action setCouponCode:@"EXTRA100"]; + * [builder setProductAction:action]; + * GAIEcommerceProduct *product = [[GAIEcommerceProduct alloc] init]; + * [product setId:@""PID-1234""]; + * [product setName:@"Space Monkeys!"]; + * [product setPrice:@100]; + * [product setQuantity:@2]; + * [builder addProduct:product]; + * [tracker send:[builder build]]; + * + */ +@interface GAIEcommerceProductAction : NSObject + +/*! + Sets the product action field for this product action. Valid values can be found in + GAIEcommerceFields.h under "product action values". + */ +- (GAIEcommerceProductAction *)setAction:(NSString *)productAction; + +/*! + The unique id associated with the transaction. This value is used for kGAIPAPurchase and + kGAIPARefund product actions. + */ +- (GAIEcommerceProductAction *)setTransactionId:(NSString *)transactionId; + +/*! + Sets the transaction's affiliation value. This value is used for kGAIPAPurchase and + kGAIPARefund product actions. + */ +- (GAIEcommerceProductAction *)setAffiliation:(NSString *)affiliation; + +/*! + Sets the transaction's total revenue. This value is used for kGAIPAPurchase and kGAIPARefund + product actions. + */ +- (GAIEcommerceProductAction *)setRevenue:(NSNumber *)revenue; + +/*! + Sets the transaction's total tax. This value is used for kGAIPAPurchase and kGAIPARefund + product actions. + */ +- (GAIEcommerceProductAction *)setTax:(NSNumber *)tax; + +/*! + Sets the transaction's total shipping costs. This value is used for kGAIPAPurchase and + kGAIPARefund product actions. + */ +- (GAIEcommerceProductAction *)setShipping:(NSNumber *)shipping; + +/*! + Sets the coupon code used in this transaction. This value is used for kGAIPAPurchase and + kGAIPARefund product actions. + */ +- (GAIEcommerceProductAction *)setCouponCode:(NSString *)couponCode; + +/*! + Sets the checkout process's progress. This value is used for kGAICheckout and + kGAICheckoutOptions product actions. + */ +- (GAIEcommerceProductAction *)setCheckoutStep:(NSNumber *)checkoutStep; + +/*! + Sets the option associated with the checkout. This value is used for kGAICheckout and + kGAICheckoutOptions product actions. + */ +- (GAIEcommerceProductAction *)setCheckoutOption:(NSString *)checkoutOption; + +/*! + Sets the list name associated with the products in Google Analytics beacons. This value is + used in kGAIPADetail and kGAIPAClick product actions. + */ +- (GAIEcommerceProductAction *)setProductActionList:(NSString *)productActionList; + +/*! + Sets the list source name associated with the products in Google Analytics beacons. This value + is used in kGAIPADetail and kGAIPAClick product actions. + */ +- (GAIEcommerceProductAction *)setProductListSource:(NSString *)productListSource; + +/*! + Builds an NSDictionary of fields stored in this instance representing this product action. +
+ Normally, users will have no need to call this method. + */ +- (NSDictionary *)build; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIEcommercePromotion.h b/Pods/GoogleAnalytics/Headers/Public/GAIEcommercePromotion.h new file mode 100644 index 0000000..c7bf25a --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIEcommercePromotion.h @@ -0,0 +1,54 @@ +/*! + @header GAIEcommercePromotion.h + @abstract Google Analytics iOS SDK Hit Format Header + @copyright Copyright 2014 Google Inc. All rights reserved. + */ + +#import + +/*! + * Class to construct promotion related fields for Google Analytics hits. The fields from this class + * can be used to represent internal promotions that run within an app, such as banners, banner ads + * etc. + * + * Typical usage: + * + * GAIDictionaryBuilder *builder = [GAIDictionaryBuilder createScreenView]; + * GAIEcommercePromotion *promotion = [[GAIEcommercePromotion alloc] init]; + * [promotion setId:@"PROMO-ID1234"]; + * [promotion setName:@"Home screen banner"]; + * [builder set:kGAIPromotionClick forKey:kGAIPromotionAction]; + * [builder addPromotion:promotion]; + * [tracker send:builder.build]]; + * + */ +@interface GAIEcommercePromotion : NSObject + +/*! + Sets the id that is used to identify a promotion in GA reports. + */ +- (GAIEcommercePromotion *)setId:(NSString *)pid; + +/*! + Sets the name that is used to identify a promotion in GA reports. + */ +- (GAIEcommercePromotion *)setName:(NSString *)name; + +/*! + Sets the name of the creative associated with the promotion. + */ +- (GAIEcommercePromotion *)setCreative:(NSString *)creative; + +/*! + Sets the position of the promotion. + */ +- (GAIEcommercePromotion *)setPosition:(NSString *)position; + +/*! + Builds an NSDictionary of fields stored in this instance. The index parameter is the + index of this promotion in that promotion list. +
+ Normally, users will have no need to call this method. + */ +- (NSDictionary *)buildWithIndex:(NSUInteger)index; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAIFields.h b/Pods/GoogleAnalytics/Headers/Public/GAIFields.h new file mode 100644 index 0000000..e54cef0 --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAIFields.h @@ -0,0 +1,133 @@ +/*! + @header GAIFields.h + @abstract Google Analytics iOS SDK Hit Format Header + @copyright Copyright 2013 Google Inc. All rights reserved. + */ + +#import + +/*! + These fields can be used for the wire format parameter names required by + the |GAITracker| get, set and send methods as well as the set methods in the + |GAIDictionaryBuilder| class. + */ +extern NSString *const kGAIUseSecure; + +extern NSString *const kGAIHitType; +extern NSString *const kGAITrackingId; +extern NSString *const kGAIClientId; +extern NSString *const kGAIDataSource; +extern NSString *const kGAIAnonymizeIp; +extern NSString *const kGAISessionControl; +extern NSString *const kGAIDeviceModelVersion; +extern NSString *const kGAIScreenResolution; +extern NSString *const kGAIViewportSize; +extern NSString *const kGAIEncoding; +extern NSString *const kGAIScreenColors; +extern NSString *const kGAILanguage; +extern NSString *const kGAIJavaEnabled; +extern NSString *const kGAIFlashVersion; +extern NSString *const kGAINonInteraction; +extern NSString *const kGAIReferrer; +extern NSString *const kGAILocation; +extern NSString *const kGAIHostname; +extern NSString *const kGAIPage; +extern NSString *const kGAIDescription; // synonym for kGAIScreenName +extern NSString *const kGAIScreenName; // synonym for kGAIDescription +extern NSString *const kGAITitle; +extern NSString *const kGAIAdMobHitId; +extern NSString *const kGAIAppName; +extern NSString *const kGAIAppVersion; +extern NSString *const kGAIAppId; +extern NSString *const kGAIAppInstallerId; +extern NSString *const kGAIUserId; + +extern NSString *const kGAIEventCategory; +extern NSString *const kGAIEventAction; +extern NSString *const kGAIEventLabel; +extern NSString *const kGAIEventValue; + +extern NSString *const kGAISocialNetwork; +extern NSString *const kGAISocialAction; +extern NSString *const kGAISocialTarget; + +extern NSString *const kGAITransactionId; +extern NSString *const kGAITransactionAffiliation; +extern NSString *const kGAITransactionRevenue; +extern NSString *const kGAITransactionShipping; +extern NSString *const kGAITransactionTax; +extern NSString *const kGAICurrencyCode; + +extern NSString *const kGAIItemPrice; +extern NSString *const kGAIItemQuantity; +extern NSString *const kGAIItemSku; +extern NSString *const kGAIItemName; +extern NSString *const kGAIItemCategory; + +extern NSString *const kGAICampaignSource; +extern NSString *const kGAICampaignMedium; +extern NSString *const kGAICampaignName; +extern NSString *const kGAICampaignKeyword; +extern NSString *const kGAICampaignContent; +extern NSString *const kGAICampaignId; +extern NSString *const kGAICampaignAdNetworkClickId; +extern NSString *const kGAICampaignAdNetworkId; + +extern NSString *const kGAITimingCategory; +extern NSString *const kGAITimingVar; +extern NSString *const kGAITimingValue; +extern NSString *const kGAITimingLabel; + +extern NSString *const kGAIExDescription; +extern NSString *const kGAIExFatal; + +extern NSString *const kGAISampleRate; + +extern NSString *const kGAIIdfa; +extern NSString *const kGAIAdTargetingEnabled; + +// hit types +extern NSString *const kGAIAppView DEPRECATED_MSG_ATTRIBUTE("Use kGAIScreenView instead."); +extern NSString *const kGAIScreenView; +extern NSString *const kGAIEvent; +extern NSString *const kGAISocial; +extern NSString *const kGAITransaction; +extern NSString *const kGAIItem; +extern NSString *const kGAIException; +extern NSString *const kGAITiming; + +/*! + This class provides several fields and methods useful as wire format parameter + names. The methods are used for wire format parameter names that are indexed. + */ + +@interface GAIFields : NSObject + +/*! + Generates the correct parameter name for a content group with an index. + + @param index the index of the content group. + + @return an NSString representing the content group parameter for the index. + */ ++ (NSString *)contentGroupForIndex:(NSUInteger)index; + +/*! + Generates the correct parameter name for a custon dimension with an index. + + @param index the index of the custom dimension. + + @return an NSString representing the custom dimension parameter for the index. + */ ++ (NSString *)customDimensionForIndex:(NSUInteger)index; + +/*! + Generates the correct parameter name for a custom metric with an index. + + @param index the index of the custom metric. + + @return an NSString representing the custom metric parameter for the index. + */ ++ (NSString *)customMetricForIndex:(NSUInteger)index; + +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAILogger.h b/Pods/GoogleAnalytics/Headers/Public/GAILogger.h new file mode 100644 index 0000000..06291f2 --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAILogger.h @@ -0,0 +1,49 @@ +/*! + @header GAILogger.h + @abstract Google Analytics iOS SDK Source + @copyright Copyright 2011 Google Inc. All rights reserved. + */ + +#import + +typedef NS_ENUM(NSUInteger, GAILogLevel) { + kGAILogLevelNone = 0, + kGAILogLevelError = 1, + kGAILogLevelWarning = 2, + kGAILogLevelInfo = 3, + kGAILogLevelVerbose = 4 +}; + +/*! + Protocol to be used for logging debug and informational messages from the SDK. + Implementations of this protocol can be provided to the |GAI| class, + to be used as the logger by the SDK. See the |logger| property in GAI.h. + */ +@protocol GAILogger +@required + +/*! + Only messages of |logLevel| and below are logged. + */ +@property (nonatomic, assign) GAILogLevel logLevel; + +/*! + Logs message with log level |kGAILogLevelVerbose|. + */ +- (void)verbose:(NSString *)message; + +/*! + Logs message with log level |kGAILogLevelInfo|. + */ +- (void)info:(NSString *)message; + +/*! + Logs message with log level |kGAILogLevelWarning|. + */ +- (void)warning:(NSString *)message; + +/*! + Logs message with log level |kGAILogLevelError|. + */ +- (void)error:(NSString *)message; +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAITrackedViewController.h b/Pods/GoogleAnalytics/Headers/Public/GAITrackedViewController.h new file mode 100644 index 0000000..de19def --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAITrackedViewController.h @@ -0,0 +1,33 @@ +/*! + @header GAITrackedViewController.h + @abstract Google Analytics for iOS Tracked View Controller Header + @copyright Copyright 2012 Google Inc. All rights reserved. + */ + +#import +#import + +@protocol GAITracker; + +/*! + Extends UIViewController to generate Google Analytics screenview calls + whenever the view appears; this is done by overriding the `viewDidAppear:` + method. The screen name must be set for any tracking calls to be made. + + By default, this will use [GAI defaultTracker] for tracking calls, but one can + override this by setting the tracker property. + */ +@interface GAITrackedViewController : UIViewController + +/*! + The tracker on which view tracking calls are be made, or `nil`, in which case + [GAI defaultTracker] will be used. + */ +@property(nonatomic, assign) id tracker; +/*! + The screen name, for purposes of Google Analytics tracking. If this is `nil`, + no tracking calls will be made. + */ +@property(nonatomic, copy) NSString *screenName; + +@end diff --git a/Pods/GoogleAnalytics/Headers/Public/GAITracker.h b/Pods/GoogleAnalytics/Headers/Public/GAITracker.h new file mode 100644 index 0000000..fdc5c5d --- /dev/null +++ b/Pods/GoogleAnalytics/Headers/Public/GAITracker.h @@ -0,0 +1,57 @@ +/*! + @header GAITracker.h + @abstract Google Analytics iOS SDK Tracker Header + @copyright Copyright 2013 Google Inc. All rights reserved. +*/ + +#import + +/*! + Google Analytics tracking interface. Obtain instances of this interface from + [GAI trackerWithTrackingId:] to track screens, events, transactions, timing, + and exceptions. The implementation of this interface is thread-safe, and no + calls are expected to block or take a long time. All network and disk activity + will take place in the background. + */ +@protocol GAITracker + +/*! + Name of this tracker. + */ +@property(nonatomic, readonly) NSString *name; + +/*! + Allow collection of IDFA and related fields if set to true. Default is false. + */ +@property(nonatomic) BOOL allowIDFACollection; + +/*! + Set a tracking parameter. + + @param parameterName The parameter name. + + @param value The value to set for the parameter. If this is nil, the + value for the parameter will be cleared. + */ +- (void)set:(NSString *)parameterName + value:(NSString *)value; + +/*! + Get a tracking parameter. + + @param parameterName The parameter name. + + @returns The parameter value, or nil if no value for the given parameter is + set. + */ +- (NSString *)get:(NSString *)parameterName; + +/*! + Queue tracking information with the given parameter values. + + @param parameters A map from parameter names to parameter values which will be + set just for this piece of tracking information, or nil for none. + */ +- (void)send:(NSDictionary *)parameters; + +@end diff --git a/Pods/GoogleAnalytics/Libraries/libGoogleAnalytics.a b/Pods/GoogleAnalytics/Libraries/libGoogleAnalytics.a new file mode 100644 index 0000000..d8f40f9 Binary files /dev/null and b/Pods/GoogleAnalytics/Libraries/libGoogleAnalytics.a differ diff --git a/Pods/GoogleIPhoneUtilities/Frameworks/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities b/Pods/GoogleIPhoneUtilities/Frameworks/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities new file mode 100644 index 0000000..7ab2cf2 Binary files /dev/null and b/Pods/GoogleIPhoneUtilities/Frameworks/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities differ diff --git a/Pods/GoogleInterchangeUtilities/Frameworks/frameworks/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities b/Pods/GoogleInterchangeUtilities/Frameworks/frameworks/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities new file mode 100755 index 0000000..de40424 Binary files /dev/null and b/Pods/GoogleInterchangeUtilities/Frameworks/frameworks/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities differ diff --git a/Pods/GoogleMaps/CHANGELOG b/Pods/GoogleMaps/CHANGELOG new file mode 100644 index 0000000..0680323 --- /dev/null +++ b/Pods/GoogleMaps/CHANGELOG @@ -0,0 +1,656 @@ +Version 2.1.0 - September 2016 +============================== +Features: + - This release introduces custom styling of the base map. You can use a JSON + style declaration to create a `GMSMapStyle` instance and pass it to the + `mapStyle` property, to change the visual display of features like roads, + parks, businesses, and other points of interest. You can use styling to + emphasize particular components of the map, or make the map complement the + style of your app. Styling works only on the `kGMSTypeNormal` map type. + - It is now possible to apply "night mode" to maps, by applying custom styles. + - Business points of interest (POIs) now appear by default on the map, + provided that the map type is `kGMSTypeNormal`. Prior to this release, + local POIs appeared on the map, but not business POIs). Business POIs + represent businesses such as shops, restaurants, hotels, and more. + - You can disable POIs on a map by using map styling. + - With the addition of business points of interest, there is a new optional + method `didTapPOIWithPlaceID` on `GMSMapViewDelegate` which provides + notification when a POI has been tapped. + +Resolved Issues: + - Renamed the `canGoOffline` selector internally, to avoid triggering false + positives during submission to the Apple app store. + - The `iconView` property of `GMSMarker` is now correctly marked as nullable. + - The renderer has been updated to avoid a race condition which would + cause rendering to not stop in time when an app transitions to background. + - `GMSPath` `initFromEncodedPath` now returns nil for invalid input + (previously invalid input could result in a crash). + - An additional method was added to `GMSMapView`, which compares two camera + positions to determine whether they are close enough to render identically. + - `GMSCircle` will now always draw at least a dot for small radius circles, + including radius 0. + +Version 2.0.1 - July 2016 +========================= + +Resolved Issues: + - Array properties are now correctly typed when accessed from Swift. + +Version 2.0.0 - July 2016 +========================= + +Improvements: + - This release splits the Places API from the Maps SDK. Previously, if you + wanted to use the Places API you had to include all of GoogleMaps. As a + result of the split, the final size of the Places API binary is 70% smaller + than the previous combined binary. If you are using only the Maps SDK you + will not be affected unless you have pinned your dependency on GoogleMaps + to a version earlier than 2.0. In this case, you should update this + restriction in your Podfile. If you are using the Places API, see the + migration guide online for more details. + +Resolved Issues: + - The GoogleMaps binary has been reduced to less than 100MB to avoid + exceeding GitHub's file size limit. + - GMSGroundOverlays now correctly respond to touch input when rotated. + - Marker info windows now render consistently. + - Info windows created using the return value of mapView:markerInfoContents: + will now correctly respect the height of the returned content rather than + always being square based on the width. + - Fixed an issue where texture cache limit is exceeded on devices supporting + @3x sized images. + +Version 1.13.2 - May 2016 +========================= +Resolved Issues: + - Added a workaround to avoid the false positive for the non-public API + imageWithName: selector. + +Version 1.13.1 - May 2016 +========================= +Resolved Issues: + - Fixed an application hang when using a UIView with autolayout enabled as an info + window or as a marker iconView. + - Changed lookUpPlaceID to not call its callback twice in error scenarios. + +Version 1.13.0 - March 2016 +=========================== +Features: + - UIView based markers. Marker content can now show advanced animations by + providing a custom view hierarchy to be displayed through the iconView property. + - Info windows can now have their custom views animated. Set tracksInfoWindowChanges + on the associated marker to YES to enable real-time updates. + - Map rendering now defaults to 60fps on modern iOS devices. Set the new + preferredFrameRate property on GMSMapView to revert to the old behavior + (Conservative) or a new low frame rate option to save battery (PowerSave). + - Added mapViewSnapshotReady: to GMSMapViewDelegate which fires when map content, + including markers and other overlays, has been fully rendered. + - Autocomplete widgets in the Places API now provide options for custom styling. + +Resolved Issues: + - GMSCoordinateBounds initWithVisibleRegion: now chooses correct bounds for large + viewports. + - Added a workaround to avoid a graphical glitch in snapshots taken using pre iOS 7 + methods when the My Location button is disabled. + - GMSAutocompleteViewController now works when used in a storyboard. + - Added missing Place Type constants. + +Version 1.12.3 - February 2016 +============================== +Resolved Issues: + - Fixed corruption in included bitcode that caused Xcode archive action to fail. + - Workaround limitation in Xcode 6.4 which was failing to compile included headers. + +Version 1.12.2 - February 2016 +============================== +Features: + - Added place photos to the Places API. + - Added structured address components to GMSPlace objects. + - SDK method signatures have been updated with generics and nullability annotations. + +Resolved Issues: + - GMSPlace objects now contain rating and price level where available. + - Minor bugfixes for the autocomplete widget UI. + - panoramaView:didMoveCamera: is no longer raised during the panoramaView delegate + setter. + - Old unused logo files have been removed from the SDK. + - Tap events on polygons near the anti-meridian are more reliable. + - Resolved an issue resulting in unrecognized selector crashes when calling + class methods on categories. + +Version 1.12.1 - February 2016 +============================== + +This version is exactly the same as 1.11.1. It was released to replace the +removed 1.12.0 release. + +Version 1.12.0 - February 2016 +============================== + +This version was removed because of errors in the framework and should not be +used. + +Version 1.11.1 - December 2015 +============================== +Resolved Issues: + - Modally presented Place Autocomplete widgets now correctly respect + UINavigationBar appearance proxy settings. + - Resolved minor UI issues with the Place Autocomplete widgets. + - Updated GoogleMaps.bundle info.plist to avoid triggering checks in + pre-submission verification. + +Version 1.11.0 - December 2015 +============================== +Features: + - Bitcode is now included in the SDK binary for all device architectures. + - Added Place Autocomplete widget classes. + - New events for long press on an info window, and closing an info window, have + been added to GMSMapViewDelegate. + - GMSMapViewDelegate has new events to indicate when map tiles and labels are + pending and finished rendering. + - GMSPanoramaViewDelegate has new events to indicate when panorama tiles are + pending and finished rendering. + - GMSGroundOverlay now supports an alpha multiplier via the opacity property. + - Added a holes property to GMSPolygon to allow for the subtraction away from + the filled area in order to create more complex shapes. + - At zoom levels greater than 14, the maximum tilt has been increased. + - Added an autocomplete screen to the Place Picker. + - Split autocomplete predictions into primary and secondary text fields. + - Added a country filter option to GMSAutocompleteFilter. + - Added a viewport field to GMSPlace. + +Resolved Issues: + - Correct handling of taps on overlapping markers. + - Address a race condition dependent crash which can happen when an application + enters and leaves the background while showing a map. + - Fix blank maps which can happen when launching an app into the background. + - Workaround issues with core animation that caused markers to jump. + - Updated to avoid subtle conflicts with applications which use + google-toolbox-for-mac. + - Use the iPhone language instead of the region formatting language for + Places API results. + +Notes: + - Setting GMSMapView selectedMarker to a marker not on the map is now ignored, + always set the marker's map property before attempting to select it. + +Version 1.10.5 - October 2015 +============================= +Resolved Issues: + - Workaround an issue in the Swift compiler's handling of umbrella header + module entries. + +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. diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos.xcodeproj/project.pbxproj b/Pods/GoogleMaps/Example/GoogleMapsDemos.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6dbcb61 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos.xcodeproj/project.pbxproj @@ -0,0 +1,638 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 04A6E32691762C9B82741DD3 /* AnimatedUIViewMarkerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2EB950AA302B77F16CF18088 /* AnimatedUIViewMarkerViewController.m */; }; + 06A6DB21EBC3BEE5EFC18F63 /* DemoAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C7AD415580B4A6B35532456A /* DemoAppDelegate.m */; }; + 098A6903B793226E41655E9A /* australia-large.png in Resources */ = {isa = PBXBuildFile; fileRef = 9F4CB893BCACB294FBCEC850 /* australia-large.png */; }; + 0A6ABA75278F49BBE253AB14 /* Samples.m in Sources */ = {isa = PBXBuildFile; fileRef = 115963CCE082409262D179F3 /* Samples.m */; }; + 0A993DA21C49FA93BD34D9AB /* VisibleRegionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A0518A8C9E543FFBA7F8305E /* VisibleRegionViewController.m */; }; + 0BFB1CE395345832444BB754 /* TrafficMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 611E9F57BD5274878732C0BE /* TrafficMapViewController.m */; }; + 0FA2ABEFE1C97FBAC51862A4 /* IndoorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DA013030BC14C994AEA2428 /* IndoorViewController.m */; }; + 1356F8E5750C793966437D77 /* StyledMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3408578DDDF6D8B183D9B91E /* StyledMapViewController.m */; }; + 15C05007C9E2A96F24EA9F5E /* newark_nj_1922.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 2B0884C720D5AE0BDCC1903D /* newark_nj_1922.jpg */; }; + 19FE865B8D874541ADF29D86 /* MarkerLayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E84ED0331DFB2A7082CA36 /* MarkerLayerViewController.m */; }; + 1A3DA97F99AF723B7107AF35 /* step1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C6A156792608F06437E5931 /* step1@2x.png */; }; + 20298941EFC09F43D63DCE5A /* CustomMarkersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC77B503E93E88F6E254A170 /* CustomMarkersViewController.m */; }; + 20D8B040CD85902D32D3FF1F /* step4@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 60944672BB9D460BE315ECB8 /* step4@2x.png */; }; + 22F37937FCB1678CA41A3B9C /* h1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 16C4B08486C9905568685983 /* h1@2x.png */; }; + 2697C9A8D582C19EB30D5FD6 /* MapsDemoAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B53B4C2097A6AD4A98746495 /* MapsDemoAssets.xcassets */; }; + 2A2DEEBBC313603053606408 /* argentina-large.png in Resources */ = {isa = PBXBuildFile; fileRef = 4D8E4A10394122CD9C9CD157 /* argentina-large.png */; }; + 2C99845EF56A93DE151A312C /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EE1D504741871952487BB5BE /* MasterViewController.m */; }; + 2D468948D33349CAA702223C /* FixedPanoramaViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C762E469D796AFF83D3516EF /* FixedPanoramaViewController.m */; }; + 378986C8682C3E9502BF3AB1 /* aeroplane.png in Resources */ = {isa = PBXBuildFile; fileRef = 937B2FC602E80ADC11CF364F /* aeroplane.png */; }; + 3A03EFC97500E90F5BA4B154 /* step2.png in Resources */ = {isa = PBXBuildFile; fileRef = BB85B60A794D982F001E5D1D /* step2.png */; }; + 3B5BFC9FF8920B3C0094CB2C /* botswana.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C186E84CB691DD6239C5E57 /* botswana.png */; }; + 3BBAD9FD0A0306B2F3A0017B /* step7@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D0E3C8118FBF6DF1D4996693 /* step7@2x.png */; }; + 3F1CD8E1AF755A78EB905F52 /* popup_santa.png in Resources */ = {isa = PBXBuildFile; fileRef = EF01EFEF0FC77447C02FE86C /* popup_santa.png */; }; + 42873FDCAF070F7C97FA6CD8 /* step7.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B0B37F0669CF34418A263F9 /* step7.png */; }; + 4619B918B8B9F159C1E96881 /* australia.png in Resources */ = {isa = PBXBuildFile; fileRef = 304A2B780A4A9B2D32261355 /* australia.png */; }; + 4A15D118D86D82C11D071B2C /* StructuredGeocoderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AAFE928C9FDAA1B5703AE31 /* StructuredGeocoderViewController.m */; }; + 4AF30BA2FC6298BE86F69993 /* mapstyle-night.json in Resources */ = {isa = PBXBuildFile; fileRef = FFEED4DDB04F69C84A1924F1 /* mapstyle-night.json */; }; + 51B83EFF06E04A24A563001A /* IndoorMuseumNavigationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1318E0A2BA2A41F2E8759CFE /* IndoorMuseumNavigationViewController.m */; }; + 5659C71D0D7318D593860C3F /* MapZoomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B3D673FA7036FA5D07CB287 /* MapZoomViewController.m */; }; + 59060F253A6002024FC080CD /* track.json in Resources */ = {isa = PBXBuildFile; fileRef = AD6B1AF49A87CC079E9CA862 /* track.json */; }; + 59EE98AF87BFE6B4535EC9A6 /* PanoramaViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9ECA1CA359738BB8B17657F /* PanoramaViewController.m */; }; + 5B8111397A7EABE0DCC77622 /* step5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AB708C911CD9B99ADBB07D08 /* step5@2x.png */; }; + 5C7670767D52307133D1BCFE /* DoubleMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DFCC9D1D1F70D492149D65 /* DoubleMapViewController.m */; }; + 64984B84B09FD0DF72308C07 /* CameraViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 09A36E26A0818D4F0DF21051 /* CameraViewController.m */; }; + 68679F97011184371A35D7C7 /* bulgaria-large.png in Resources */ = {isa = PBXBuildFile; fileRef = DA6BF21C0287E5828A770C09 /* bulgaria-large.png */; }; + 6CC9DED8E8F3CE56B8E94872 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 21267D205F7EC280F26D97ED /* main.m */; }; + 6D1C9B07CAEDF524673ED31E /* BasicMapViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FC5AD93CD72DE8F5B004A621 /* BasicMapViewController.m */; }; + 703FCE6348C17E69ED669A78 /* GroundOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8038AEC0AF9CBD2EBB975F54 /* GroundOverlayViewController.m */; }; + 74F05FB9E466D95B85A15A58 /* MarkerInfoWindowViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 89DFB3350D98DA617A2ECE85 /* MarkerInfoWindowViewController.m */; }; + 7AE0FCC85B7BBE162025751C /* step8.png in Resources */ = {isa = PBXBuildFile; fileRef = B7B684AAFC3CA2232721C6D0 /* step8.png */; }; + 7D2186B159E9848AA6C7386C /* step6@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C0E7585025B57F969D8C3CCD /* step6@2x.png */; }; + 8491B4753D6795A5CB69AF74 /* TileLayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CEA86A42848E5BAA7B523FB /* TileLayerViewController.m */; }; + 853063CF351F34F9F1DBD8CC /* step4.png in Resources */ = {isa = PBXBuildFile; fileRef = 99C6A64731FB5249BA53255D /* step4.png */; }; + 8852EA6A02F703F08F9458F3 /* x29.png in Resources */ = {isa = PBXBuildFile; fileRef = 77E4A5AC4E17EB252D617792 /* x29.png */; }; + 8950E155E20CB2893D279F3D /* AnimatedCurrentLocationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C37F2216D5D94A3F8B698AF2 /* AnimatedCurrentLocationViewController.m */; }; + 8EFDA781EBA17E7CD7301272 /* glow-marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 430314741C873551A75A6748 /* glow-marker@2x.png */; }; + 8F9917C4115CD02B657B331A /* spitfire.png in Resources */ = {isa = PBXBuildFile; fileRef = 91BCD28A8D5451665C5B3FDE /* spitfire.png */; }; + 8FE940554B1218140ABEE6BB /* PolylinesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4145303F340FC3675AABA5 /* PolylinesViewController.m */; }; + 91A98DD42AE05DDD991B8788 /* glow-marker.png in Resources */ = {isa = PBXBuildFile; fileRef = 57D0C4A29B66857C926387F0 /* glow-marker.png */; }; + 925BF76B710DA288319498D5 /* argentina.png in Resources */ = {isa = PBXBuildFile; fileRef = A6B80BDCF78F436799462F76 /* argentina.png */; }; + 93B36402D287634E247E5631 /* GeocoderViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A77C64C5A8B8109E6AFC41F /* GeocoderViewController.m */; }; + 9594F950A37D8665796B334D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F4C119D42CF37317FA0DFA9 /* UIKit.framework */; }; + 9D84E6619020CE531E5823CD /* botswana-large.png in Resources */ = {isa = PBXBuildFile; fileRef = 440513A7769094565EA82DD2 /* botswana-large.png */; }; + A04BBCC3FF015903AF7CD93F /* museum-exhibits.json in Resources */ = {isa = PBXBuildFile; fileRef = 346BE94E21267BA7E00E1B94 /* museum-exhibits.json */; }; + A137149150C011207E2F8EE1 /* MyLocationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BF0878372A9A7274B74BCBE0 /* MyLocationViewController.m */; }; + A1F2D1C33D98B28D454849A2 /* step2@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4F955F74654756E7F7DBD9C1 /* step2@2x.png */; }; + A35320D9B69272ACF3B9139F /* SnapshotReadyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E621C7788D69B720052601 /* SnapshotReadyViewController.m */; }; + A85222E1796CDBCBB8F98B6B /* CustomIndoorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5563ED82E4E77A76707DAF12 /* CustomIndoorViewController.m */; }; + B646C0BAD2A245557B519931 /* step1.png in Resources */ = {isa = PBXBuildFile; fileRef = D66EF243066A58A0D10280D4 /* step1.png */; }; + BA2D028BE5ED8042FEB05D8F /* bulgaria.png in Resources */ = {isa = PBXBuildFile; fileRef = 2BC67BDB51522BC85EBFED8E /* bulgaria.png */; }; + C0210C5B8B348A0348D92DA9 /* step8@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D5E5AF68BBC86BA530755A20 /* step8@2x.png */; }; + C24A5F52692CDC51864F522C /* FrameRateViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB963F858613E50A64CFAE6 /* FrameRateViewController.m */; }; + C2BC30E26D4D2515FDB15848 /* arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 6614BE28012C7A7508437047 /* arrow.png */; }; + C3BC945D74D0E19BCCA532DE /* voyager.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B3B4C44092471CA56ACFD4C /* voyager.png */; }; + C665CD379C7D3EA34F31733E /* MarkersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D4CFDA7D44E2CF33EB10072B /* MarkersViewController.m */; }; + C68C326EC8F06BD2844D239A /* MapLayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E7BBD64FA078FC4259FCBD2 /* MapLayerViewController.m */; }; + C72707078B04ECA88BB00B14 /* FitBoundsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 721936633CF79A85B5EF0E92 /* FitBoundsViewController.m */; }; + CEF41B4E3CA0239A4A40AD88 /* step3@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A3AE3B168663EA43D3F2F259 /* step3@2x.png */; }; + D2D3662C0F97C35BB54234A7 /* australia-large@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A1D521E4FF1F8F85E084ED60 /* australia-large@2x.png */; }; + D315909F7B28D2B51914678F /* step5.png in Resources */ = {isa = PBXBuildFile; fileRef = B1763EA929EC73C50D294134 /* step5.png */; }; + D33BBB708DE21370608DABD7 /* arrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 66DE8D273E896D7FF5B86DC9 /* arrow@2x.png */; }; + D522835123F06D863FA47A03 /* MapTypesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F371BFD608DCF4DA1671DB03 /* MapTypesViewController.m */; }; + D9978336CFEF2FB35E1512B7 /* mapstyle-silver.json in Resources */ = {isa = PBXBuildFile; fileRef = B4C89E62C05816B590F45D57 /* mapstyle-silver.json */; }; + E026AFA1FD4D48D34E41812C /* x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DD1DD5592D028FDD5E527FDF /* x29@2x.png */; }; + E03EA95750E7A51C555D13D5 /* PolygonsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2ED9C84A592EB6464725BB9C /* PolygonsViewController.m */; }; + E0C0502D8979F84036A8282C /* popup_santa@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5E2E2B19EF6846F007A7A909 /* popup_santa@2x.png */; }; + E16060A9BF2245DFA511E5B9 /* MarkerEventsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B9450C49DDD49AF9182779EB /* MarkerEventsViewController.m */; }; + E1DA9796D5C0B3265B5F5BC2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBB8C9AD891A00824AE32343 /* LaunchScreen.storyboard */; }; + E333D8C48DCB564E46915AB1 /* aeroplane@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3F716156C215C436D55F5696 /* aeroplane@2x.png */; }; + E4FE8CE15D430EB5589CE54C /* voyager@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C56541EDCEFDB922783385B5 /* voyager@2x.png */; }; + E7202399F72A85099331EF13 /* h1.png in Resources */ = {isa = PBXBuildFile; fileRef = 6B05E27C8BB92E89D2DC78AC /* h1.png */; }; + E7E8214CC970237B5D2B03DC /* spitfire@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B15768E1EE7E314DF1B0A395 /* spitfire@2x.png */; }; + EBF76DDAB230B2E52D0BD7D7 /* step6.png in Resources */ = {isa = PBXBuildFile; fileRef = F64D45825D2647898331A1FF /* step6.png */; }; + ECE3127B3EECC2B55DCCC5A6 /* step3.png in Resources */ = {isa = PBXBuildFile; fileRef = 6533FA4161283F8F6A5F2CC3 /* step3.png */; }; + EE4D614B504845ADC2EA771A /* GradientPolylinesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B4378F4DBE15BB0E32690CE /* GradientPolylinesViewController.m */; }; + F01D751710D9C729085F540B /* boat@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 03E00AE555F58259DD31C383 /* boat@2x.png */; }; + FE8BB1E6E58AE2D5B1226C7B /* GestureControlViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71CE23C2A26FD3C29E6AEEA7 /* GestureControlViewController.m */; }; + FF8065B590AD6493752A8632 /* mapstyle-retro.json in Resources */ = {isa = PBXBuildFile; fileRef = 118A0FE8CA1D110B3C694A8D /* mapstyle-retro.json */; }; + FFE41F3FA7C01734283B9D57 /* boat.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B049CFC81E59949D0184B8B /* boat.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 02A7C56A409A0CB977458251 /* DemoAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoAppDelegate.h; sourceTree = ""; }; + 03E00AE555F58259DD31C383 /* boat@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "boat@2x.png"; sourceTree = ""; }; + 09A36E26A0818D4F0DF21051 /* CameraViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraViewController.m; sourceTree = ""; }; + 0C5E03510539F570D757CAEE /* GestureControlViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GestureControlViewController.h; sourceTree = ""; }; + 0CEA86A42848E5BAA7B523FB /* TileLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TileLayerViewController.m; sourceTree = ""; }; + 115963CCE082409262D179F3 /* Samples.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Samples.m; sourceTree = ""; }; + 118A0FE8CA1D110B3C694A8D /* mapstyle-retro.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = "mapstyle-retro.json"; sourceTree = ""; }; + 1318E0A2BA2A41F2E8759CFE /* IndoorMuseumNavigationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IndoorMuseumNavigationViewController.m; sourceTree = ""; }; + 15E621C7788D69B720052601 /* SnapshotReadyViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SnapshotReadyViewController.m; sourceTree = ""; }; + 15E6F2AA55437CFAC24BCFEE /* MarkerInfoWindowViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerInfoWindowViewController.h; sourceTree = ""; }; + 16C4B08486C9905568685983 /* h1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "h1@2x.png"; sourceTree = ""; }; + 1B4378F4DBE15BB0E32690CE /* GradientPolylinesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GradientPolylinesViewController.m; sourceTree = ""; }; + 21267D205F7EC280F26D97ED /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 281FB700EEAC3323C52CB587 /* AnimatedCurrentLocationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimatedCurrentLocationViewController.h; sourceTree = ""; }; + 2A77C64C5A8B8109E6AFC41F /* GeocoderViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GeocoderViewController.m; sourceTree = ""; }; + 2AAFE928C9FDAA1B5703AE31 /* StructuredGeocoderViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StructuredGeocoderViewController.m; sourceTree = ""; }; + 2B049CFC81E59949D0184B8B /* boat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = boat.png; sourceTree = ""; }; + 2B0884C720D5AE0BDCC1903D /* newark_nj_1922.jpg */ = {isa = PBXFileReference; lastKnownFileType = text; path = newark_nj_1922.jpg; sourceTree = ""; }; + 2BC67BDB51522BC85EBFED8E /* bulgaria.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bulgaria.png; sourceTree = ""; }; + 2D030F81AEB6D3278B8EA303 /* VisibleRegionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VisibleRegionViewController.h; sourceTree = ""; }; + 2EB950AA302B77F16CF18088 /* AnimatedUIViewMarkerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AnimatedUIViewMarkerViewController.m; sourceTree = ""; }; + 2ED9C84A592EB6464725BB9C /* PolygonsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PolygonsViewController.m; sourceTree = ""; }; + 304A2B780A4A9B2D32261355 /* australia.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = australia.png; sourceTree = ""; }; + 33BC04FF4445AA4649D16101 /* TileLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TileLayerViewController.h; sourceTree = ""; }; + 3408578DDDF6D8B183D9B91E /* StyledMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StyledMapViewController.m; sourceTree = ""; }; + 346BE94E21267BA7E00E1B94 /* museum-exhibits.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = "museum-exhibits.json"; sourceTree = ""; }; + 3B0B37F0669CF34418A263F9 /* step7.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step7.png; sourceTree = ""; }; + 3B3D673FA7036FA5D07CB287 /* MapZoomViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapZoomViewController.m; sourceTree = ""; }; + 3E7BBD64FA078FC4259FCBD2 /* MapLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapLayerViewController.m; sourceTree = ""; }; + 3F716156C215C436D55F5696 /* aeroplane@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "aeroplane@2x.png"; sourceTree = ""; }; + 430314741C873551A75A6748 /* glow-marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "glow-marker@2x.png"; sourceTree = ""; }; + 440513A7769094565EA82DD2 /* botswana-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "botswana-large.png"; sourceTree = ""; }; + 4B436F286FDDDE125BA44AEE /* MapTypesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapTypesViewController.h; sourceTree = ""; }; + 4D8E4A10394122CD9C9CD157 /* argentina-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "argentina-large.png"; sourceTree = ""; }; + 4F955F74654756E7F7DBD9C1 /* step2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step2@2x.png"; sourceTree = ""; }; + 51B3E8FEDF5AFB146C099C21 /* MapLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapLayerViewController.h; sourceTree = ""; }; + 5563ED82E4E77A76707DAF12 /* CustomIndoorViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomIndoorViewController.m; sourceTree = ""; }; + 57D0C4A29B66857C926387F0 /* glow-marker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "glow-marker.png"; sourceTree = ""; }; + 5B3B4C44092471CA56ACFD4C /* voyager.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voyager.png; sourceTree = ""; }; + 5C9035433ADBDA49B4FF973F /* StructuredGeocoderViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StructuredGeocoderViewController.h; sourceTree = ""; }; + 5E2E2B19EF6846F007A7A909 /* popup_santa@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "popup_santa@2x.png"; sourceTree = ""; }; + 5F34640A0EABB50A24705F03 /* MyLocationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyLocationViewController.h; sourceTree = ""; }; + 60944672BB9D460BE315ECB8 /* step4@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step4@2x.png"; sourceTree = ""; }; + 611E9F57BD5274878732C0BE /* TrafficMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TrafficMapViewController.m; sourceTree = ""; }; + 6140C36D94B102F62C26B713 /* IndoorMuseumNavigationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IndoorMuseumNavigationViewController.h; sourceTree = ""; }; + 6533FA4161283F8F6A5F2CC3 /* step3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step3.png; sourceTree = ""; }; + 658307DB8F671E9CA0B1A2E6 /* GoogleMapsDemos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GoogleMapsDemos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6614BE28012C7A7508437047 /* arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = arrow.png; sourceTree = ""; }; + 66DE8D273E896D7FF5B86DC9 /* arrow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "arrow@2x.png"; sourceTree = ""; }; + 6B05E27C8BB92E89D2DC78AC /* h1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = h1.png; sourceTree = ""; }; + 6C186E84CB691DD6239C5E57 /* botswana.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = botswana.png; sourceTree = ""; }; + 6C6A156792608F06437E5931 /* step1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step1@2x.png"; sourceTree = ""; }; + 6F4C119D42CF37317FA0DFA9 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 71CE23C2A26FD3C29E6AEEA7 /* GestureControlViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GestureControlViewController.m; sourceTree = ""; }; + 721936633CF79A85B5EF0E92 /* FitBoundsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FitBoundsViewController.m; sourceTree = ""; }; + 726F8CE865076A20EE4C4408 /* PanoramaViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PanoramaViewController.h; sourceTree = ""; }; + 77E4A5AC4E17EB252D617792 /* x29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = x29.png; sourceTree = ""; }; + 7BAA2BF38E0B41197BB6CA14 /* GroundOverlayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GroundOverlayViewController.h; sourceTree = ""; }; + 7DB963F858613E50A64CFAE6 /* FrameRateViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrameRateViewController.m; sourceTree = ""; }; + 8038AEC0AF9CBD2EBB975F54 /* GroundOverlayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GroundOverlayViewController.m; sourceTree = ""; }; + 805803DEB0E29A1395C6ABB6 /* TrafficMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TrafficMapViewController.h; sourceTree = ""; }; + 8348F1BC33E5DCAAB85BBA43 /* DoubleMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DoubleMapViewController.h; sourceTree = ""; }; + 87C50C8E191C32391FCDE143 /* CameraViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraViewController.h; sourceTree = ""; }; + 89DFB3350D98DA617A2ECE85 /* MarkerInfoWindowViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerInfoWindowViewController.m; sourceTree = ""; }; + 8BEC50A442A8A75604D7007D /* FixedPanoramaViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FixedPanoramaViewController.h; sourceTree = ""; }; + 8DA013030BC14C994AEA2428 /* IndoorViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IndoorViewController.m; sourceTree = ""; }; + 90AFC572AAEC55CB23E0EDB4 /* BasicMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BasicMapViewController.h; sourceTree = ""; }; + 91BCD28A8D5451665C5B3FDE /* spitfire.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = spitfire.png; sourceTree = ""; }; + 937B2FC602E80ADC11CF364F /* aeroplane.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = aeroplane.png; sourceTree = ""; }; + 99C6A64731FB5249BA53255D /* step4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step4.png; sourceTree = ""; }; + 9F4CB893BCACB294FBCEC850 /* australia-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "australia-large.png"; sourceTree = ""; }; + A0518A8C9E543FFBA7F8305E /* VisibleRegionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VisibleRegionViewController.m; sourceTree = ""; }; + A1D521E4FF1F8F85E084ED60 /* australia-large@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "australia-large@2x.png"; sourceTree = ""; }; + A3AE3B168663EA43D3F2F259 /* step3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step3@2x.png"; sourceTree = ""; }; + A6B80BDCF78F436799462F76 /* argentina.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = argentina.png; sourceTree = ""; }; + A9ECA1CA359738BB8B17657F /* PanoramaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PanoramaViewController.m; sourceTree = ""; }; + AA7FD859248BB356AA73A407 /* FrameRateViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameRateViewController.h; sourceTree = ""; }; + AAB5CC69AA4A14C0B11D0F2B /* MapZoomViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapZoomViewController.h; sourceTree = ""; }; + AB6D8FD2BBEB798B9D0A33DE /* IndoorViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IndoorViewController.h; sourceTree = ""; }; + AB708C911CD9B99ADBB07D08 /* step5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step5@2x.png"; sourceTree = ""; }; + AD6B1AF49A87CC079E9CA862 /* track.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = track.json; sourceTree = ""; }; + B15768E1EE7E314DF1B0A395 /* spitfire@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "spitfire@2x.png"; sourceTree = ""; }; + B1763EA929EC73C50D294134 /* step5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step5.png; sourceTree = ""; }; + B4C89E62C05816B590F45D57 /* mapstyle-silver.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = "mapstyle-silver.json"; sourceTree = ""; }; + B53058D6D185A08DDEF65436 /* Samples.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Samples.h; sourceTree = ""; }; + B53B4C2097A6AD4A98746495 /* MapsDemoAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapsDemoAssets.xcassets; sourceTree = ""; }; + B7B684AAFC3CA2232721C6D0 /* step8.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step8.png; sourceTree = ""; }; + B9450C49DDD49AF9182779EB /* MarkerEventsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerEventsViewController.m; sourceTree = ""; }; + BB653990DAD6FDE28A9E4158 /* FitBoundsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FitBoundsViewController.h; sourceTree = ""; }; + BB85B60A794D982F001E5D1D /* step2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step2.png; sourceTree = ""; }; + BEDC49669DBDA8BE37BFAB29 /* GeocoderViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeocoderViewController.h; sourceTree = ""; }; + BF0878372A9A7274B74BCBE0 /* MyLocationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyLocationViewController.m; sourceTree = ""; }; + C0E7585025B57F969D8C3CCD /* step6@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step6@2x.png"; sourceTree = ""; }; + C37F2216D5D94A3F8B698AF2 /* AnimatedCurrentLocationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AnimatedCurrentLocationViewController.m; sourceTree = ""; }; + C3E84ED0331DFB2A7082CA36 /* MarkerLayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkerLayerViewController.m; sourceTree = ""; }; + C4C3F5556675D1A9408F7C5F /* MasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MasterViewController.h; sourceTree = ""; }; + C56541EDCEFDB922783385B5 /* voyager@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "voyager@2x.png"; sourceTree = ""; }; + C762E469D796AFF83D3516EF /* FixedPanoramaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FixedPanoramaViewController.m; sourceTree = ""; }; + C7AD415580B4A6B35532456A /* DemoAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoAppDelegate.m; sourceTree = ""; }; + CF8997BED409E528D34BA344 /* PolylinesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolylinesViewController.h; sourceTree = ""; }; + D0E3C8118FBF6DF1D4996693 /* step7@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step7@2x.png"; sourceTree = ""; }; + D30CF0DFCD652D6DF10187D7 /* MarkerLayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerLayerViewController.h; sourceTree = ""; }; + D4CFDA7D44E2CF33EB10072B /* MarkersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MarkersViewController.m; sourceTree = ""; }; + D5E5AF68BBC86BA530755A20 /* step8@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "step8@2x.png"; sourceTree = ""; }; + D66EF243066A58A0D10280D4 /* step1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step1.png; sourceTree = ""; }; + D78B4ACC4D102CC9EC32C8A3 /* SnapshotReadyViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SnapshotReadyViewController.h; sourceTree = ""; }; + DA6BF21C0287E5828A770C09 /* bulgaria-large.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bulgaria-large.png"; sourceTree = ""; }; + DB481265195ED47C9D3588AB /* CustomIndoorViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomIndoorViewController.h; sourceTree = ""; }; + DBB8C9AD891A00824AE32343 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + DC77B503E93E88F6E254A170 /* CustomMarkersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomMarkersViewController.m; sourceTree = ""; }; + DD1DD5592D028FDD5E527FDF /* x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "x29@2x.png"; sourceTree = ""; }; + DD87868F23D05E5CFADEAB4F /* MarkerEventsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkerEventsViewController.h; sourceTree = ""; }; + E40A76286C824B75258FDF5E /* StyledMapViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StyledMapViewController.h; sourceTree = ""; }; + E4F81E0232539CC089AE8893 /* CustomMarkersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomMarkersViewController.h; sourceTree = ""; }; + E85063B5DC5D93C8875E3EBC /* PolygonsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolygonsViewController.h; sourceTree = ""; }; + EE1D504741871952487BB5BE /* MasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MasterViewController.m; sourceTree = ""; }; + EF01EFEF0FC77447C02FE86C /* popup_santa.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = popup_santa.png; sourceTree = ""; }; + F371BFD608DCF4DA1671DB03 /* MapTypesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapTypesViewController.m; sourceTree = ""; }; + F59393018D5AC385BE80BE57 /* MarkersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkersViewController.h; sourceTree = ""; }; + F64D45825D2647898331A1FF /* step6.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = step6.png; sourceTree = ""; }; + F8DFCC9D1D1F70D492149D65 /* DoubleMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DoubleMapViewController.m; sourceTree = ""; }; + FA4145303F340FC3675AABA5 /* PolylinesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PolylinesViewController.m; sourceTree = ""; }; + FAF7CEAC34C5EBC5F05412EC /* GradientPolylinesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GradientPolylinesViewController.h; sourceTree = ""; }; + FC2C54DF6BAAE16F877D8A66 /* AnimatedUIViewMarkerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimatedUIViewMarkerViewController.h; sourceTree = ""; }; + FC5AD93CD72DE8F5B004A621 /* BasicMapViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BasicMapViewController.m; sourceTree = ""; }; + FCA64CCA86165B04BDDE0A93 /* SDKDemoAPIKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoAPIKey.h; sourceTree = ""; }; + FFEED4DDB04F69C84A1924F1 /* mapstyle-night.json */ = {isa = PBXFileReference; lastKnownFileType = text; path = "mapstyle-night.json"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8B9B135C626B0FF8CC12CB4D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9594F950A37D8665796B334D /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D27564BB990E3F72332953D = { + isa = PBXGroup; + children = ( + 3F84FF5646DF0F79669A6A34 /* Source */, + E5703B81550DFC98B8650F07 /* Frameworks */, + 9C667AD7285A7D36180D29BE /* Products */, + ); + sourceTree = ""; + }; + 3F84FF5646DF0F79669A6A34 /* Source */ = { + isa = PBXGroup; + children = ( + 45BAB561C27E46122745337A /* GoogleMapsDemos */, + ); + name = Source; + sourceTree = ""; + }; + 45BAB561C27E46122745337A /* GoogleMapsDemos */ = { + isa = PBXGroup; + children = ( + E9C287B2214772B64F89F90B /* Resources */, + FF5D55EF552627DDF8369CDC /* Samples */, + 02A7C56A409A0CB977458251 /* DemoAppDelegate.h */, + C7AD415580B4A6B35532456A /* DemoAppDelegate.m */, + B53B4C2097A6AD4A98746495 /* MapsDemoAssets.xcassets */, + C4C3F5556675D1A9408F7C5F /* MasterViewController.h */, + EE1D504741871952487BB5BE /* MasterViewController.m */, + FCA64CCA86165B04BDDE0A93 /* SDKDemoAPIKey.h */, + 21267D205F7EC280F26D97ED /* main.m */, + ); + path = GoogleMapsDemos; + sourceTree = ""; + }; + 8AEE26F54DD8496915CC250C /* Museum-Icons */ = { + isa = PBXGroup; + children = ( + 6B05E27C8BB92E89D2DC78AC /* h1.png */, + 16C4B08486C9905568685983 /* h1@2x.png */, + 91BCD28A8D5451665C5B3FDE /* spitfire.png */, + B15768E1EE7E314DF1B0A395 /* spitfire@2x.png */, + 5B3B4C44092471CA56ACFD4C /* voyager.png */, + C56541EDCEFDB922783385B5 /* voyager@2x.png */, + 77E4A5AC4E17EB252D617792 /* x29.png */, + DD1DD5592D028FDD5E527FDF /* x29@2x.png */, + ); + path = "Museum-Icons"; + sourceTree = ""; + }; + 9C667AD7285A7D36180D29BE /* Products */ = { + isa = PBXGroup; + children = ( + 658307DB8F671E9CA0B1A2E6 /* GoogleMapsDemos.app */, + ); + name = Products; + sourceTree = ""; + }; + E5703B81550DFC98B8650F07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6F4C119D42CF37317FA0DFA9 /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + E9C287B2214772B64F89F90B /* Resources */ = { + isa = PBXGroup; + children = ( + 8AEE26F54DD8496915CC250C /* Museum-Icons */, + DBB8C9AD891A00824AE32343 /* LaunchScreen.storyboard */, + 937B2FC602E80ADC11CF364F /* aeroplane.png */, + 3F716156C215C436D55F5696 /* aeroplane@2x.png */, + 4D8E4A10394122CD9C9CD157 /* argentina-large.png */, + A6B80BDCF78F436799462F76 /* argentina.png */, + 6614BE28012C7A7508437047 /* arrow.png */, + 66DE8D273E896D7FF5B86DC9 /* arrow@2x.png */, + 9F4CB893BCACB294FBCEC850 /* australia-large.png */, + A1D521E4FF1F8F85E084ED60 /* australia-large@2x.png */, + 304A2B780A4A9B2D32261355 /* australia.png */, + 2B049CFC81E59949D0184B8B /* boat.png */, + 03E00AE555F58259DD31C383 /* boat@2x.png */, + 440513A7769094565EA82DD2 /* botswana-large.png */, + 6C186E84CB691DD6239C5E57 /* botswana.png */, + DA6BF21C0287E5828A770C09 /* bulgaria-large.png */, + 2BC67BDB51522BC85EBFED8E /* bulgaria.png */, + 57D0C4A29B66857C926387F0 /* glow-marker.png */, + 430314741C873551A75A6748 /* glow-marker@2x.png */, + FFEED4DDB04F69C84A1924F1 /* mapstyle-night.json */, + 118A0FE8CA1D110B3C694A8D /* mapstyle-retro.json */, + B4C89E62C05816B590F45D57 /* mapstyle-silver.json */, + 346BE94E21267BA7E00E1B94 /* museum-exhibits.json */, + 2B0884C720D5AE0BDCC1903D /* newark_nj_1922.jpg */, + EF01EFEF0FC77447C02FE86C /* popup_santa.png */, + 5E2E2B19EF6846F007A7A909 /* popup_santa@2x.png */, + D66EF243066A58A0D10280D4 /* step1.png */, + 6C6A156792608F06437E5931 /* step1@2x.png */, + BB85B60A794D982F001E5D1D /* step2.png */, + 4F955F74654756E7F7DBD9C1 /* step2@2x.png */, + 6533FA4161283F8F6A5F2CC3 /* step3.png */, + A3AE3B168663EA43D3F2F259 /* step3@2x.png */, + 99C6A64731FB5249BA53255D /* step4.png */, + 60944672BB9D460BE315ECB8 /* step4@2x.png */, + B1763EA929EC73C50D294134 /* step5.png */, + AB708C911CD9B99ADBB07D08 /* step5@2x.png */, + F64D45825D2647898331A1FF /* step6.png */, + C0E7585025B57F969D8C3CCD /* step6@2x.png */, + 3B0B37F0669CF34418A263F9 /* step7.png */, + D0E3C8118FBF6DF1D4996693 /* step7@2x.png */, + B7B684AAFC3CA2232721C6D0 /* step8.png */, + D5E5AF68BBC86BA530755A20 /* step8@2x.png */, + AD6B1AF49A87CC079E9CA862 /* track.json */, + ); + path = Resources; + sourceTree = ""; + }; + FF5D55EF552627DDF8369CDC /* Samples */ = { + isa = PBXGroup; + children = ( + 281FB700EEAC3323C52CB587 /* AnimatedCurrentLocationViewController.h */, + C37F2216D5D94A3F8B698AF2 /* AnimatedCurrentLocationViewController.m */, + FC2C54DF6BAAE16F877D8A66 /* AnimatedUIViewMarkerViewController.h */, + 2EB950AA302B77F16CF18088 /* AnimatedUIViewMarkerViewController.m */, + 90AFC572AAEC55CB23E0EDB4 /* BasicMapViewController.h */, + FC5AD93CD72DE8F5B004A621 /* BasicMapViewController.m */, + 87C50C8E191C32391FCDE143 /* CameraViewController.h */, + 09A36E26A0818D4F0DF21051 /* CameraViewController.m */, + DB481265195ED47C9D3588AB /* CustomIndoorViewController.h */, + 5563ED82E4E77A76707DAF12 /* CustomIndoorViewController.m */, + E4F81E0232539CC089AE8893 /* CustomMarkersViewController.h */, + DC77B503E93E88F6E254A170 /* CustomMarkersViewController.m */, + 8348F1BC33E5DCAAB85BBA43 /* DoubleMapViewController.h */, + F8DFCC9D1D1F70D492149D65 /* DoubleMapViewController.m */, + BB653990DAD6FDE28A9E4158 /* FitBoundsViewController.h */, + 721936633CF79A85B5EF0E92 /* FitBoundsViewController.m */, + 8BEC50A442A8A75604D7007D /* FixedPanoramaViewController.h */, + C762E469D796AFF83D3516EF /* FixedPanoramaViewController.m */, + AA7FD859248BB356AA73A407 /* FrameRateViewController.h */, + 7DB963F858613E50A64CFAE6 /* FrameRateViewController.m */, + BEDC49669DBDA8BE37BFAB29 /* GeocoderViewController.h */, + 2A77C64C5A8B8109E6AFC41F /* GeocoderViewController.m */, + 0C5E03510539F570D757CAEE /* GestureControlViewController.h */, + 71CE23C2A26FD3C29E6AEEA7 /* GestureControlViewController.m */, + FAF7CEAC34C5EBC5F05412EC /* GradientPolylinesViewController.h */, + 1B4378F4DBE15BB0E32690CE /* GradientPolylinesViewController.m */, + 7BAA2BF38E0B41197BB6CA14 /* GroundOverlayViewController.h */, + 8038AEC0AF9CBD2EBB975F54 /* GroundOverlayViewController.m */, + 6140C36D94B102F62C26B713 /* IndoorMuseumNavigationViewController.h */, + 1318E0A2BA2A41F2E8759CFE /* IndoorMuseumNavigationViewController.m */, + AB6D8FD2BBEB798B9D0A33DE /* IndoorViewController.h */, + 8DA013030BC14C994AEA2428 /* IndoorViewController.m */, + 51B3E8FEDF5AFB146C099C21 /* MapLayerViewController.h */, + 3E7BBD64FA078FC4259FCBD2 /* MapLayerViewController.m */, + 4B436F286FDDDE125BA44AEE /* MapTypesViewController.h */, + F371BFD608DCF4DA1671DB03 /* MapTypesViewController.m */, + AAB5CC69AA4A14C0B11D0F2B /* MapZoomViewController.h */, + 3B3D673FA7036FA5D07CB287 /* MapZoomViewController.m */, + DD87868F23D05E5CFADEAB4F /* MarkerEventsViewController.h */, + B9450C49DDD49AF9182779EB /* MarkerEventsViewController.m */, + 15E6F2AA55437CFAC24BCFEE /* MarkerInfoWindowViewController.h */, + 89DFB3350D98DA617A2ECE85 /* MarkerInfoWindowViewController.m */, + D30CF0DFCD652D6DF10187D7 /* MarkerLayerViewController.h */, + C3E84ED0331DFB2A7082CA36 /* MarkerLayerViewController.m */, + F59393018D5AC385BE80BE57 /* MarkersViewController.h */, + D4CFDA7D44E2CF33EB10072B /* MarkersViewController.m */, + 5F34640A0EABB50A24705F03 /* MyLocationViewController.h */, + BF0878372A9A7274B74BCBE0 /* MyLocationViewController.m */, + 726F8CE865076A20EE4C4408 /* PanoramaViewController.h */, + A9ECA1CA359738BB8B17657F /* PanoramaViewController.m */, + E85063B5DC5D93C8875E3EBC /* PolygonsViewController.h */, + 2ED9C84A592EB6464725BB9C /* PolygonsViewController.m */, + CF8997BED409E528D34BA344 /* PolylinesViewController.h */, + FA4145303F340FC3675AABA5 /* PolylinesViewController.m */, + B53058D6D185A08DDEF65436 /* Samples.h */, + 115963CCE082409262D179F3 /* Samples.m */, + D78B4ACC4D102CC9EC32C8A3 /* SnapshotReadyViewController.h */, + 15E621C7788D69B720052601 /* SnapshotReadyViewController.m */, + 5C9035433ADBDA49B4FF973F /* StructuredGeocoderViewController.h */, + 2AAFE928C9FDAA1B5703AE31 /* StructuredGeocoderViewController.m */, + E40A76286C824B75258FDF5E /* StyledMapViewController.h */, + 3408578DDDF6D8B183D9B91E /* StyledMapViewController.m */, + 33BC04FF4445AA4649D16101 /* TileLayerViewController.h */, + 0CEA86A42848E5BAA7B523FB /* TileLayerViewController.m */, + 805803DEB0E29A1395C6ABB6 /* TrafficMapViewController.h */, + 611E9F57BD5274878732C0BE /* TrafficMapViewController.m */, + 2D030F81AEB6D3278B8EA303 /* VisibleRegionViewController.h */, + A0518A8C9E543FFBA7F8305E /* VisibleRegionViewController.m */, + ); + path = Samples; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F934453EF090BCCB93E78E83 /* GoogleMapsDemos */ = { + isa = PBXNativeTarget; + buildConfigurationList = 56D55B92E97A2AAB3D261D4A /* Build configuration list for PBXNativeTarget "GoogleMapsDemos" */; + buildPhases = ( + C434996613C0A2FED463783D /* Resources */, + 06DE6F27AD1DD8E5163683B6 /* Sources */, + 8B9B135C626B0FF8CC12CB4D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GoogleMapsDemos; + productName = GoogleMapsDemos; + productReference = 658307DB8F671E9CA0B1A2E6 /* GoogleMapsDemos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E1E34A01E38954F1DD4BCD39 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 0800; + }; + buildConfigurationList = 80C3BB6A64A9375104C7ACB9 /* Build configuration list for PBXProject "GoogleMapsDemos" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 1D27564BB990E3F72332953D; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F934453EF090BCCB93E78E83 /* GoogleMapsDemos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C434996613C0A2FED463783D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 378986C8682C3E9502BF3AB1 /* aeroplane.png in Resources */, + E333D8C48DCB564E46915AB1 /* aeroplane@2x.png in Resources */, + 2A2DEEBBC313603053606408 /* argentina-large.png in Resources */, + 925BF76B710DA288319498D5 /* argentina.png in Resources */, + C2BC30E26D4D2515FDB15848 /* arrow.png in Resources */, + D33BBB708DE21370608DABD7 /* arrow@2x.png in Resources */, + 098A6903B793226E41655E9A /* australia-large.png in Resources */, + D2D3662C0F97C35BB54234A7 /* australia-large@2x.png in Resources */, + 4619B918B8B9F159C1E96881 /* australia.png in Resources */, + FFE41F3FA7C01734283B9D57 /* boat.png in Resources */, + F01D751710D9C729085F540B /* boat@2x.png in Resources */, + 9D84E6619020CE531E5823CD /* botswana-large.png in Resources */, + 3B5BFC9FF8920B3C0094CB2C /* botswana.png in Resources */, + 68679F97011184371A35D7C7 /* bulgaria-large.png in Resources */, + BA2D028BE5ED8042FEB05D8F /* bulgaria.png in Resources */, + 91A98DD42AE05DDD991B8788 /* glow-marker.png in Resources */, + 8EFDA781EBA17E7CD7301272 /* glow-marker@2x.png in Resources */, + E7202399F72A85099331EF13 /* h1.png in Resources */, + 22F37937FCB1678CA41A3B9C /* h1@2x.png in Resources */, + 8F9917C4115CD02B657B331A /* spitfire.png in Resources */, + E7E8214CC970237B5D2B03DC /* spitfire@2x.png in Resources */, + C3BC945D74D0E19BCCA532DE /* voyager.png in Resources */, + E4FE8CE15D430EB5589CE54C /* voyager@2x.png in Resources */, + 8852EA6A02F703F08F9458F3 /* x29.png in Resources */, + E026AFA1FD4D48D34E41812C /* x29@2x.png in Resources */, + 3F1CD8E1AF755A78EB905F52 /* popup_santa.png in Resources */, + E0C0502D8979F84036A8282C /* popup_santa@2x.png in Resources */, + B646C0BAD2A245557B519931 /* step1.png in Resources */, + 1A3DA97F99AF723B7107AF35 /* step1@2x.png in Resources */, + 3A03EFC97500E90F5BA4B154 /* step2.png in Resources */, + A1F2D1C33D98B28D454849A2 /* step2@2x.png in Resources */, + ECE3127B3EECC2B55DCCC5A6 /* step3.png in Resources */, + CEF41B4E3CA0239A4A40AD88 /* step3@2x.png in Resources */, + 853063CF351F34F9F1DBD8CC /* step4.png in Resources */, + 20D8B040CD85902D32D3FF1F /* step4@2x.png in Resources */, + D315909F7B28D2B51914678F /* step5.png in Resources */, + 5B8111397A7EABE0DCC77622 /* step5@2x.png in Resources */, + EBF76DDAB230B2E52D0BD7D7 /* step6.png in Resources */, + 7D2186B159E9848AA6C7386C /* step6@2x.png in Resources */, + 42873FDCAF070F7C97FA6CD8 /* step7.png in Resources */, + 3BBAD9FD0A0306B2F3A0017B /* step7@2x.png in Resources */, + 7AE0FCC85B7BBE162025751C /* step8.png in Resources */, + C0210C5B8B348A0348D92DA9 /* step8@2x.png in Resources */, + 15C05007C9E2A96F24EA9F5E /* newark_nj_1922.jpg in Resources */, + 4AF30BA2FC6298BE86F69993 /* mapstyle-night.json in Resources */, + FF8065B590AD6493752A8632 /* mapstyle-retro.json in Resources */, + D9978336CFEF2FB35E1512B7 /* mapstyle-silver.json in Resources */, + A04BBCC3FF015903AF7CD93F /* museum-exhibits.json in Resources */, + 59060F253A6002024FC080CD /* track.json in Resources */, + E1DA9796D5C0B3265B5F5BC2 /* LaunchScreen.storyboard in Resources */, + 2697C9A8D582C19EB30D5FD6 /* MapsDemoAssets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 06DE6F27AD1DD8E5163683B6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 06A6DB21EBC3BEE5EFC18F63 /* DemoAppDelegate.m in Sources */, + 6CC9DED8E8F3CE56B8E94872 /* main.m in Sources */, + 2C99845EF56A93DE151A312C /* MasterViewController.m in Sources */, + 8950E155E20CB2893D279F3D /* AnimatedCurrentLocationViewController.m in Sources */, + 04A6E32691762C9B82741DD3 /* AnimatedUIViewMarkerViewController.m in Sources */, + 6D1C9B07CAEDF524673ED31E /* BasicMapViewController.m in Sources */, + 64984B84B09FD0DF72308C07 /* CameraViewController.m in Sources */, + A85222E1796CDBCBB8F98B6B /* CustomIndoorViewController.m in Sources */, + 20298941EFC09F43D63DCE5A /* CustomMarkersViewController.m in Sources */, + 5C7670767D52307133D1BCFE /* DoubleMapViewController.m in Sources */, + C72707078B04ECA88BB00B14 /* FitBoundsViewController.m in Sources */, + 2D468948D33349CAA702223C /* FixedPanoramaViewController.m in Sources */, + C24A5F52692CDC51864F522C /* FrameRateViewController.m in Sources */, + 93B36402D287634E247E5631 /* GeocoderViewController.m in Sources */, + FE8BB1E6E58AE2D5B1226C7B /* GestureControlViewController.m in Sources */, + EE4D614B504845ADC2EA771A /* GradientPolylinesViewController.m in Sources */, + 703FCE6348C17E69ED669A78 /* GroundOverlayViewController.m in Sources */, + 51B83EFF06E04A24A563001A /* IndoorMuseumNavigationViewController.m in Sources */, + 0FA2ABEFE1C97FBAC51862A4 /* IndoorViewController.m in Sources */, + C68C326EC8F06BD2844D239A /* MapLayerViewController.m in Sources */, + D522835123F06D863FA47A03 /* MapTypesViewController.m in Sources */, + 5659C71D0D7318D593860C3F /* MapZoomViewController.m in Sources */, + E16060A9BF2245DFA511E5B9 /* MarkerEventsViewController.m in Sources */, + 74F05FB9E466D95B85A15A58 /* MarkerInfoWindowViewController.m in Sources */, + 19FE865B8D874541ADF29D86 /* MarkerLayerViewController.m in Sources */, + C665CD379C7D3EA34F31733E /* MarkersViewController.m in Sources */, + A137149150C011207E2F8EE1 /* MyLocationViewController.m in Sources */, + 59EE98AF87BFE6B4535EC9A6 /* PanoramaViewController.m in Sources */, + E03EA95750E7A51C555D13D5 /* PolygonsViewController.m in Sources */, + 8FE940554B1218140ABEE6BB /* PolylinesViewController.m in Sources */, + 0A6ABA75278F49BBE253AB14 /* Samples.m in Sources */, + A35320D9B69272ACF3B9139F /* SnapshotReadyViewController.m in Sources */, + 4A15D118D86D82C11D071B2C /* StructuredGeocoderViewController.m in Sources */, + 1356F8E5750C793966437D77 /* StyledMapViewController.m in Sources */, + 8491B4753D6795A5CB69AF74 /* TileLayerViewController.m in Sources */, + 0BFB1CE395345832444BB754 /* TrafficMapViewController.m in Sources */, + 0A993DA21C49FA93BD34D9AB /* VisibleRegionViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2723CC697F2C564456B7C001 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = ./GoogleMapsDemos/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LIBRARY_SEARCH_PATHS = ( + ., + "$(SDKROOT)/System/Library/Frameworks", + ); + PRODUCT_NAME = GoogleMapsDemos; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)"; + USE_HEADERMAP = NO; + WRAPPER_PREFIX = ""; + }; + name = Default; + }; + C39588D2DC04B7B9CBE846E5 /* 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 */ + 56D55B92E97A2AAB3D261D4A /* Build configuration list for PBXNativeTarget "GoogleMapsDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2723CC697F2C564456B7C001 /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; + 80C3BB6A64A9375104C7ACB9 /* Build configuration list for PBXProject "GoogleMapsDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C39588D2DC04B7B9CBE846E5 /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = E1E34A01E38954F1DD4BCD39 /* Project object */; +} diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/DemoAppDelegate.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/DemoAppDelegate.h new file mode 100644 index 0000000..66cd1ad --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/DemoAppDelegate.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface DemoAppDelegate : 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/Example/GoogleMapsDemos/DemoAppDelegate.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/DemoAppDelegate.m new file mode 100644 index 0000000..7993d7a --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/DemoAppDelegate.m @@ -0,0 +1,117 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/DemoAppDelegate.h" + +#import +#import "GoogleMapsDemos/MasterViewController.h" +#import "GoogleMapsDemos/SDKDemoAPIKey.h" + +@implementation DemoAppDelegate { + id _services; +} + +@synthesize window = _window; + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSLog(@"Build version: %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.GoogleMapsDemos for more information"; + @throw [NSException exceptionWithName:@"DemoAppDelegate" + 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]]; + MasterViewController *master = [[MasterViewController 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/Example/GoogleMapsDemos/Info.plist b/Pods/GoogleMaps/Example/GoogleMapsDemos/Info.plist new file mode 100644 index 0000000..2bc0fc8 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.example.GoogleMapsDemos + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + Show your location on the map + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Contents.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f3dcd21 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,78 @@ +{ + "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" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Maps-SDK-Demo-App_120.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Maps-SDK-Demo-App_180.png", + "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" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Maps-SDK-Demo-App_76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Maps-SDK-Demo-App_152.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Maps-SDK-Demo-App_167.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_120.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_120.png new file mode 100644 index 0000000..46d9c3f Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_120.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_152.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_152.png new file mode 100644 index 0000000..237bba1 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_152.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_167.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_167.png new file mode 100644 index 0000000..8bee0dd Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_167.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_180.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_180.png new file mode 100644 index 0000000..0299a1d Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_180.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_76.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_76.png new file mode 100644 index 0000000..db89154 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/AppIcon.appiconset/Maps-SDK-Demo-App_76.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/Contents.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/Contents.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..7bb8752 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,57 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "LaunchImage-iPhonePortrait2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "LaunchImage-iPhonePortraitRetina4.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadPortrait1x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadLandscape1x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadPortrait2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadLandscape2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png new file mode 100644 index 0000000..05bd4e5 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png new file mode 100644 index 0000000..72fb5ed Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png new file mode 100644 index 0000000..146fa4b Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png new file mode 100644 index 0000000..2b288a0 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png new file mode 100644 index 0000000..81aa4c3 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png new file mode 100644 index 0000000..c841827 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/MapsDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.h new file mode 100644 index 0000000..1facbea --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.h @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@class DemoAppDelegate; + +@interface MasterViewController : UITableViewController < + UISplitViewControllerDelegate, + UITableViewDataSource, + UITableViewDelegate> + +@property(nonatomic, assign) DemoAppDelegate *appDelegate; + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.m new file mode 100644 index 0000000..c864268 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/MasterViewController.m @@ -0,0 +1,163 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/MasterViewController.h" + +#import +#import "GoogleMapsDemos/DemoAppDelegate.h" +#import "GoogleMapsDemos/Samples/Samples.h" + +@implementation MasterViewController { + 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:UIBarButtonItemStylePlain + 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]; + + if (!_isPhone) { + [self loadDemo:0 atIndex:0]; + } +} + +#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 { + NSArray *demosInSection = [_demos objectAtIndex:section]; + return [demosInSection 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/Example/GoogleMapsDemos/Resources/LaunchScreen.storyboard b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/LaunchScreen.storyboard new file mode 100644 index 0000000..8c740c7 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/LaunchScreen.storyboard @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1.png new file mode 100644 index 0000000..fff8197 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1@2x.png new file mode 100644 index 0000000..ce36c63 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/h1@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire.png new file mode 100644 index 0000000..5c76dc9 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire@2x.png new file mode 100644 index 0000000..a09b75c Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/spitfire@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager.png new file mode 100644 index 0000000..d657f22 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager@2x.png new file mode 100644 index 0000000..b2a668e Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/voyager@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29.png new file mode 100644 index 0000000..0edd3f1 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29@2x.png new file mode 100644 index 0000000..eda3d15 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/Museum-Icons/x29@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane.png new file mode 100644 index 0000000..5114ee4 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane@2x.png new file mode 100644 index 0000000..5c5012c Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/aeroplane@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ar.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ar.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ar.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina-large.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina-large.png new file mode 100644 index 0000000..b75247c Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina-large.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina.png new file mode 100644 index 0000000..9095376 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/argentina.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow.png new file mode 100644 index 0000000..8d8c3f7 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow@2x.png new file mode 100644 index 0000000..4b0ff7c Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/arrow@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large.png new file mode 100644 index 0000000..787aed5 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large@2x.png new file mode 100644 index 0000000..15d4d2a Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia-large@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia.png new file mode 100644 index 0000000..12afbb9 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/australia.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat.png new file mode 100644 index 0000000..67221da Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat@2x.png new file mode 100644 index 0000000..3f316d3 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/boat@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana-large.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana-large.png new file mode 100644 index 0000000..c150491 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana-large.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana.png new file mode 100644 index 0000000..a006d99 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/botswana.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria-large.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria-large.png new file mode 100644 index 0000000..0107da0 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria-large.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria.png new file mode 100644 index 0000000..04cdb29 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/bulgaria.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ca.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ca.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ca.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/cs.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/cs.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/da.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/da.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/da.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/de.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/de.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/de.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/el.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/el.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/el.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en_GB.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en_GB.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/en_GB.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/es.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/es.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/es.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fi.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fi.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fr.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/fr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker.png new file mode 100644 index 0000000..eb0f596 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker@2x.png new file mode 100644 index 0000000..e415c3f Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/glow-marker@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/he.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/he.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/he.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hr.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hu.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/hu.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/id.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/id.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/id.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/it.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/it.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/it.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ja.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ja.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ko.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ko.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ko.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-night.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-night.json new file mode 100644 index 0000000..1cbd616 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-night.json @@ -0,0 +1,191 @@ +[ + { + "featureType": "all", + "elementType": "geometry", + "stylers": [ + { + "color": "#242f3e" + } + ] + }, + { + "featureType": "all", + "elementType": "labels.text.stroke", + "stylers": [ + { + "lightness": -80 + } + ] + }, + { + "featureType": "administrative", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#263c3f" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#6b9a76" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#2b3544" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9ca5b3" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#38414e" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#212a37" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#1f2835" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#f3d19c" + } + ] + }, + { + "featureType": "road.local", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#38414e" + } + ] + }, + { + "featureType": "road.local", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#212a37" + } + ] + }, + { + "featureType": "transit", + "elementType": "geometry", + "stylers": [ + { + "color": "#2f3948" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#17263c" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#515c6d" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.stroke", + "stylers": [ + { + "lightness": -20 + } + ] + } +] diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-retro.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-retro.json new file mode 100644 index 0000000..f6a16e8 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-retro.json @@ -0,0 +1,191 @@ +[ + { + "featureType": "all", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#755f5d" + } + ] + }, + { + "featureType": "administrative", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#d4ccb9" + } + ] + }, + { + "featureType": "administrative.country", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#baafae" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#d4ccb9" + } + ] + }, + { + "featureType": "landscape.man_made", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#ebe3cd" + } + ] + }, + { + "featureType": "landscape.natural", + "elementType": "geometry", + "stylers": [ + { + "color": "#ebe3cd" + } + ] + }, + { + "featureType": "landscape.natural", + "elementType": "geometry.fill", + "stylers": [ + { + "lightness": -10 + } + ] + }, + { + "featureType": "poi", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#d4ccb9" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.icon", + "stylers": [ + { + "hue": "#ff7f00" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#9ba56f" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#f5f1e6" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#dfd8c3" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#fdfcf8" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#e4e3df" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#f2cb77" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#ecb43d" + } + ] + }, + { + "featureType": "road.highway.controlled_access", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#e98d58" + } + ] + }, + { + "featureType": "road.highway.controlled_access", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#d27f4f" + } + ] + }, + { + "featureType": "transit.line", + "elementType": "geometry", + "stylers": [ + { + "color": "#d4ccb9" + } + ] + }, + { + "featureType": "transit.station.airport", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#d4ccb9" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#b9d3c2" + } + ] + } +] diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-silver.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-silver.json new file mode 100644 index 0000000..340e5d6 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/mapstyle-silver.json @@ -0,0 +1,101 @@ +[ + { + "featureType": "all", + "elementType": "geometry", + "stylers": [ + { + "color": "#f5f5f5" + } + ] + }, + { + "featureType": "all", + "elementType": "labels.icon", + "stylers": [ + { + "saturation": -100 + } + ] + }, + { + "featureType": "all", + "elementType": "labels.text", + "stylers": [ + { + "saturation": -100 + } + ] + }, + { + "featureType": "poi", + "elementType": "geometry", + "stylers": [ + { + "color": "#eeeeee" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#e5e5e5" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry", + "stylers": [ + { + "color": "#ffffff" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#dadada" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.icon", + "stylers": [ + { + "lightness": 30 + } + ] + }, + { + "featureType": "transit.line", + "elementType": "geometry", + "stylers": [ + { + "color": "#e5e5e5" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "geometry", + "stylers": [ + { + "color": "#eeeeee" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#c9c9c9" + } + ] + } +] diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ms.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ms.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ms.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/museum-exhibits.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/museum-exhibits.json new file mode 100644 index 0000000..231334b --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/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/Example/GoogleMapsDemos/Resources/nb.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/nb.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/nb.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/newark_nj_1922.jpg b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/newark_nj_1922.jpg new file mode 100644 index 0000000..1f4ae59 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/newark_nj_1922.jpg differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/nl.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/nl.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pl.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pl.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa.png new file mode 100644 index 0000000..f2968ef Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa@2x.png new file mode 100644 index 0000000..3f90828 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/popup_santa@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt_PT.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt_PT.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/pt_PT.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ro.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ro.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ro.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ru.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/ru.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sk.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sk.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sk.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1.png new file mode 100644 index 0000000..1cac697 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1@2x.png new file mode 100644 index 0000000..8d99108 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step1@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2.png new file mode 100644 index 0000000..18ee7f2 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2@2x.png new file mode 100644 index 0000000..5c37b1d Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step2@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3.png new file mode 100644 index 0000000..795b90f Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3@2x.png new file mode 100644 index 0000000..950d754 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step3@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4.png new file mode 100644 index 0000000..3d7416b Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4@2x.png new file mode 100644 index 0000000..7ae50e5 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step4@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5.png new file mode 100644 index 0000000..3a8bd1e Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5@2x.png new file mode 100644 index 0000000..236f3a0 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step5@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6.png new file mode 100644 index 0000000..c6580e6 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6@2x.png new file mode 100644 index 0000000..2bed812 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step6@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7.png new file mode 100644 index 0000000..4e17178 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7@2x.png new file mode 100644 index 0000000..88932e6 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step7@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8.png new file mode 100644 index 0000000..7eefde7 Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8@2x.png b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8@2x.png new file mode 100644 index 0000000..6f49e3d Binary files /dev/null and b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/step8@2x.png differ diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sv.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sv.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/sv.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/th.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/th.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/th.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/tr.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/tr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/track.json b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/track.json new file mode 100644 index 0000000..1d49290 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/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/Example/GoogleMapsDemos/Resources/uk.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/uk.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/uk.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/vi.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/vi.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/vi.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_CN.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_CN.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_CN.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_TW.lproj/InfoPlist.strings b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_TW.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Resources/zh_TW.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/SDKDemoAPIKey.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/SDKDemoAPIKey.h new file mode 100644 index 0000000..33f432a --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/SDKDemoAPIKey.h @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * To use GoogleMapsDemos, please register an API Key for your application and set it here. Your + * API Key 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/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.h new file mode 100644 index 0000000..e274b13 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import +#import + +#import + +@interface AnimatedCurrentLocationViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.m new file mode 100644 index 0000000..280e39b --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.m @@ -0,0 +1,109 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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]; + if ([_manager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { + [_manager requestWhenInUseAuthorization]; + } + _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 = location.coordinate; + + // 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/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.h new file mode 100644 index 0000000..43de2ee --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface AnimatedUIViewMarkerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.m new file mode 100644 index 0000000..5414a96 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.m @@ -0,0 +1,134 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.h" + +#import + +// Returns a random value from 0-1.0f. +static CGFloat randf() { return (((float)arc4random() / 0x100000000) * 1.0f); } + +@interface AnimatedUIViewMarkerViewController () +@end + +@implementation AnimatedUIViewMarkerViewController { + GMSMapView *_mapView; + UIView *_infoView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = + [GMSCameraPosition cameraWithLatitude:-33.8683 longitude:151.2086 zoom:5]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.delegate = self; + + self.view = _mapView; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillEnterForeground) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [_mapView clear]; + [self addDefaultMarker]; +} + +- (void)applicationWillEnterForeground { + [_mapView clear]; + [self addDefaultMarker]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (UIView *)mapView:(GMSMapView *)mapView markerInfoContents:(GMSMarker *)marker { + // Show an info window with dynamic content - a simple background color animation. + _infoView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow"]]; + UIView *infoView = _infoView; + marker.tracksInfoWindowChanges = YES; + UIColor *color = [UIColor colorWithHue:randf() saturation:1.f brightness:1.f alpha:1.0f]; + _infoView.backgroundColor = [UIColor clearColor]; + [UIView animateWithDuration:1.0 + delay:1.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + infoView.backgroundColor = color; + } + completion:^(BOOL finished) { + [UIView animateWithDuration:1.0 + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + infoView.backgroundColor = [UIColor clearColor]; + } + completion:^(BOOL finished2) { + marker.tracksInfoWindowChanges = NO; + }]; + }]; + + return _infoView; +} + +- (void)mapView:(GMSMapView *)mapView didCloseInfoWindowOfMarker:(GMSMarker *)marker { + _infoView = nil; + marker.tracksInfoWindowChanges = NO; +} + +- (void)addDefaultMarker { + // Add a custom 'glow' marker with a pulsing blue shadow on Sydney. + GMSMarker *sydneyMarker = [[GMSMarker alloc] init]; + sydneyMarker.title = @"Sydney!"; + sydneyMarker.iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"glow-marker"]]; + sydneyMarker.position = CLLocationCoordinate2DMake(-33.8683, 151.2086); + sydneyMarker.iconView.contentMode = UIViewContentModeCenter; + CGRect oldBound = sydneyMarker.iconView.bounds; + CGRect bound = oldBound; + bound.size.width *= 2; + bound.size.height *= 2; + sydneyMarker.iconView.bounds = bound; + sydneyMarker.groundAnchor = CGPointMake(0.5, 0.75); + sydneyMarker.infoWindowAnchor = CGPointMake(0.5, 0.25); + UIView *sydneyGlow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"glow-marker"]]; + sydneyGlow.layer.shadowColor = [UIColor blueColor].CGColor; + sydneyGlow.layer.shadowOffset = CGSizeZero; + sydneyGlow.layer.shadowRadius = 8.0; + sydneyGlow.layer.shadowOpacity = 1.0; + sydneyGlow.layer.opacity = 0.0; + [sydneyMarker.iconView addSubview:sydneyGlow]; + sydneyGlow.center = CGPointMake(oldBound.size.width, oldBound.size.height); + sydneyMarker.map = _mapView; + [UIView animateWithDuration:1.0 + delay:0.0 + options:UIViewAnimationOptionCurveEaseInOut | UIViewKeyframeAnimationOptionAutoreverse | + UIViewKeyframeAnimationOptionRepeat + animations:^{ + sydneyGlow.layer.opacity = 1.0; + } + completion:^(BOOL finished) { + // If the animation is ever terminated, no need to keep tracking the view for changes. + sydneyMarker.tracksViewChanges = NO; + }]; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.h new file mode 100644 index 0000000..64dab07 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface BasicMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.m new file mode 100644 index 0000000..c46c77f --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/BasicMapViewController.m @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/BasicMapViewController.h" + +#import + +@interface BasicMapViewController () +@end + +@implementation BasicMapViewController { + UILabel *_statusLabel; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.868 + longitude:151.2086 + zoom:6]; + GMSMapView *view = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + view.delegate = self; + self.view = view; + + // Add status label, initially hidden. + _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 30)]; + _statusLabel.alpha = 0.0f; + _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _statusLabel.backgroundColor = [UIColor blueColor]; + _statusLabel.textColor = [UIColor whiteColor]; + _statusLabel.textAlignment = NSTextAlignmentCenter; + + [view addSubview:_statusLabel]; +} + +- (void)mapViewDidStartTileRendering:(GMSMapView *)mapView { + _statusLabel.alpha = 0.8f; + _statusLabel.text = @"Rendering"; +} + +- (void)mapViewDidFinishTileRendering:(GMSMapView *)mapView { + _statusLabel.alpha = 0.0f; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.h new file mode 100644 index 0000000..598c923 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface CameraViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.m new file mode 100644 index 0000000..0e43a3a --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CameraViewController.m @@ -0,0 +1,76 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.h new file mode 100644 index 0000000..6490a04 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.h @@ -0,0 +1,19 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface CustomIndoorViewController : UIViewController +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.m new file mode 100644 index 0000000..2acc0c0 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomIndoorViewController.m @@ -0,0 +1,154 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.h new file mode 100644 index 0000000..6452081 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface CustomMarkersViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.m new file mode 100644 index 0000000..f0d0bfc --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/CustomMarkersViewController.m @@ -0,0 +1,120 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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 -10 and +10 degrees + + marker.map = _mapView; +} + +- (void)didTapClear { + [_mapView clear]; + [self addDefaultMarkers]; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.h new file mode 100644 index 0000000..e617460 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface DoubleMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.m new file mode 100644 index 0000000..273cb55 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/DoubleMapViewController.m @@ -0,0 +1,80 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/FitBoundsViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FitBoundsViewController.h new file mode 100644 index 0000000..67daa82 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FitBoundsViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface FitBoundsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FitBoundsViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FitBoundsViewController.m new file mode 100644 index 0000000..a408b82 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FitBoundsViewController.m @@ -0,0 +1,95 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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 { + if ([_markers count] == 0) return; + CLLocationCoordinate2D firstPos = ((GMSMarker *)_markers.firstObject).position; + GMSCoordinateBounds *bounds = + [[GMSCoordinateBounds alloc] initWithCoordinate:firstPos coordinate:firstPos]; + for (GMSMarker *marker in _markers) { + 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/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.h new file mode 100644 index 0000000..a9d7db6 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface FixedPanoramaViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.m new file mode 100644 index 0000000..56b9515 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FixedPanoramaViewController.m @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/FrameRateViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FrameRateViewController.h new file mode 100644 index 0000000..a4cd6b9 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FrameRateViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface FrameRateViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FrameRateViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FrameRateViewController.m new file mode 100644 index 0000000..28d2a42 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/FrameRateViewController.m @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/FrameRateViewController.h" + +#import + +@interface FrameRateViewController () + +@end + +@implementation FrameRateViewController { + GMSMapView *_mapView; + UITextView *_statusTextView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = + [GMSCameraPosition cameraWithLatitude:-33.868 longitude:151.2086 zoom:6]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.view = _mapView; + + // Add a display for the current frame rate mode. + _statusTextView = [[UITextView alloc] init]; + _statusTextView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0); + _statusTextView.text = @""; + _statusTextView.textAlignment = NSTextAlignmentCenter; + _statusTextView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8f]; + _statusTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _statusTextView.editable = NO; + [self.view addSubview:_statusTextView]; + [_statusTextView sizeToFit]; + + // Add a button toggling through modes. + self.navigationItem.rightBarButtonItem = + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay + target:self + action:@selector(didTapNext)]; + [self updateStatus]; +} + +- (void)didTapNext { + _mapView.preferredFrameRate = [self nextFrameRate]; + [self updateStatus]; +} + ++ (NSString *)nameForFrameRate:(GMSFrameRate)frameRate { + switch (frameRate) { + case kGMSFrameRatePowerSave: + return @"PowerSave"; + case kGMSFrameRateConservative: + return @"Conservative"; + case kGMSFrameRateMaximum: + return @"Maximum"; + } +} + +- (GMSFrameRate)nextFrameRate { + switch (_mapView.preferredFrameRate) { + case kGMSFrameRatePowerSave: + return kGMSFrameRateConservative; + case kGMSFrameRateConservative: + return kGMSFrameRateMaximum; + case kGMSFrameRateMaximum: + return kGMSFrameRatePowerSave; + } +} + +- (void)updateStatus { + _statusTextView.text = + [NSString stringWithFormat:@"Preferred frame rate: %@", + [self.class nameForFrameRate:_mapView.preferredFrameRate]]; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.h new file mode 100644 index 0000000..5520240 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.h @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import + +@interface GeocoderViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.m new file mode 100644 index 0000000..59a0a70 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GeocoderViewController.m @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/GestureControlViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GestureControlViewController.h new file mode 100644 index 0000000..a460bd3 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GestureControlViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface GestureControlViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GestureControlViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GestureControlViewController.m new file mode 100644 index 0000000..6b44381 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GestureControlViewController.m @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.h new file mode 100644 index 0000000..04122fa --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface GradientPolylinesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.m new file mode 100644 index 0000000..d4846f4 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GradientPolylinesViewController.m @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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++) { + NSDictionary *dict = [_trackData objectAtIndex:i]; + double elevation = [[dict 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/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.h new file mode 100644 index 0000000..42c9dbc --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface GroundOverlayViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.m new file mode 100644 index 0000000..17210ba --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/GroundOverlayViewController.m @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/GroundOverlayViewController.h" + +#import + +@interface GroundOverlayViewController () +@end + +@implementation GroundOverlayViewController + +- (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]; + mapView.delegate = self; + + // 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.tappable = YES; + groundOverlay.position = newark; + groundOverlay.bounds = overlayBounds; + groundOverlay.map = mapView; + + self.view = mapView; +} + +- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { + float opacity = (((float)arc4random()/0x100000000)*0.5f + 0.5f); + ((GMSGroundOverlay *)overlay).opacity = opacity; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.h new file mode 100644 index 0000000..b671dc6 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import + +@interface IndoorMuseumNavigationViewController : UIViewController< + GMSMapViewDelegate, + GMSIndoorDisplayDelegate> + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.m new file mode 100644 index 0000000..36db445 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.m @@ -0,0 +1,130 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/IndoorViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorViewController.h new file mode 100644 index 0000000..20e88f5 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface IndoorViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorViewController.m new file mode 100644 index 0000000..915aa88 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/IndoorViewController.m @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MapLayerViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapLayerViewController.h new file mode 100644 index 0000000..0fd414b --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapLayerViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MapLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapLayerViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapLayerViewController.m new file mode 100644 index 0000000..1e7f5e1 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapLayerViewController.m @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MapTypesViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapTypesViewController.h new file mode 100644 index 0000000..c0ced47 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapTypesViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MapTypesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapTypesViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapTypesViewController.m new file mode 100644 index 0000000..04c5e05 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapTypesViewController.m @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MapZoomViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapZoomViewController.h new file mode 100644 index 0000000..a99939c --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapZoomViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MapZoomViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapZoomViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapZoomViewController.m new file mode 100644 index 0000000..ffa4196 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MapZoomViewController.m @@ -0,0 +1,89 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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; + _zoomRangeView.editable = NO; + [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/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.h new file mode 100644 index 0000000..8edb2a1 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.h @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import + +@interface MarkerEventsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.m new file mode 100644 index 0000000..79f2444 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerEventsViewController.m @@ -0,0 +1,85 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.h new file mode 100644 index 0000000..6e1ea08 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MarkerInfoWindowViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.m new file mode 100644 index 0000000..dd352cb --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerInfoWindowViewController.m @@ -0,0 +1,114 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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; +} + +- (void)mapView:(GMSMapView *)mapView didCloseInfoWindowOfMarker:(GMSMarker *)marker { + NSString *message = + [NSString stringWithFormat:@"Info window for marker %@ closed.", marker.title]; + [self showMessage:message]; +} + +- (void)mapView:(GMSMapView *)mapView didLongPressInfoWindowOfMarker:(GMSMarker *)marker { + NSString *message = + [NSString stringWithFormat:@"Info window for marker %@ long pressed.", marker.title]; + [self showMessage:message]; +} + +#pragma mark Private + +- (void)showMessage:(NSString *)message { + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil + message:message + delegate:nil + cancelButtonTitle:nil + otherButtonTitles:nil, nil]; + [alertView show]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [alertView dismissWithClickedButtonIndex:0 animated:YES]; + }); +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.h new file mode 100644 index 0000000..83a6686 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.h @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import + +@interface MarkerLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.m new file mode 100644 index 0000000..7950063 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkerLayerViewController.m @@ -0,0 +1,152 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MarkersViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkersViewController.h new file mode 100644 index 0000000..403d32d --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkersViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MarkersViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkersViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkersViewController.m new file mode 100644 index 0000000..063ea9c --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MarkersViewController.m @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/MyLocationViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MyLocationViewController.h new file mode 100644 index 0000000..a9e2723 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MyLocationViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface MyLocationViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MyLocationViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MyLocationViewController.m new file mode 100644 index 0000000..e2c051b --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/MyLocationViewController.m @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/PanoramaViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PanoramaViewController.h new file mode 100644 index 0000000..657a111 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PanoramaViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface PanoramaViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PanoramaViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PanoramaViewController.m new file mode 100644 index 0000000..7582714 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PanoramaViewController.m @@ -0,0 +1,88 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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; + UILabel *_statusLabel; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + _view = [GMSPanoramaView panoramaWithFrame:CGRectZero + nearCoordinate:kPanoramaNear]; + _view.backgroundColor = [UIColor grayColor]; + _view.delegate = self; + self.view = _view; + + // Add status label, initially hidden. + _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 30)]; + _statusLabel.alpha = 0.0f; + _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _statusLabel.backgroundColor = [UIColor blueColor]; + _statusLabel.textColor = [UIColor whiteColor]; + _statusLabel.textAlignment = NSTextAlignmentCenter; + + [_view addSubview:_statusLabel]; +} + +#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; + } +} + +- (void)panoramaViewDidStartRendering:(GMSPanoramaView *)panoramaView { + _statusLabel.alpha = 0.8f; + _statusLabel.text = @"Rendering"; +} + +- (void)panoramaViewDidFinishRendering:(GMSPanoramaView *)panoramaView { + _statusLabel.alpha = 0.0f; +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.h new file mode 100644 index 0000000..972a71c --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.h @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import + +@interface PolygonsViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.m new file mode 100644 index 0000000..32c7158 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolygonsViewController.m @@ -0,0 +1,270 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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.holes = @[ [self pathOfNewYorkStateHole] ]; + 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 *)pathOfNewYorkStateHole { + GMSMutablePath *path = [GMSMutablePath path]; + [path addLatitude:43.5000 longitude:-76.3651]; + [path addLatitude:43.5000 longitude:-74.3651]; + [path addLatitude:42.0000 longitude:-74.3651]; + 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/Example/GoogleMapsDemos/Samples/PolylinesViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolylinesViewController.h new file mode 100644 index 0000000..a303558 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolylinesViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface PolylinesViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolylinesViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolylinesViewController.m new file mode 100644 index 0000000..257e759 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/PolylinesViewController.m @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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)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 = 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/Example/GoogleMapsDemos/Samples/Samples.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/Samples.h new file mode 100644 index 0000000..00c4b91 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/Samples.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface Samples : NSObject ++ (NSArray *)loadSections; ++ (NSArray *)loadDemos; ++ (NSDictionary *)newDemo:(Class) class + withTitle:(NSString *)title + andDescription:(NSString *)description; +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/Samples.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/Samples.m new file mode 100644 index 0000000..1278824 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/Samples.m @@ -0,0 +1,192 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/Samples.h" + +// Map Demos +#import "GoogleMapsDemos/Samples/BasicMapViewController.h" +#import "GoogleMapsDemos/Samples/CustomIndoorViewController.h" +#import "GoogleMapsDemos/Samples/DoubleMapViewController.h" +#import "GoogleMapsDemos/Samples/FrameRateViewController.h" +#import "GoogleMapsDemos/Samples/GestureControlViewController.h" +#import "GoogleMapsDemos/Samples/IndoorMuseumNavigationViewController.h" +#import "GoogleMapsDemos/Samples/IndoorViewController.h" +#import "GoogleMapsDemos/Samples/MapTypesViewController.h" +#import "GoogleMapsDemos/Samples/MapZoomViewController.h" +#import "GoogleMapsDemos/Samples/MyLocationViewController.h" +#import "GoogleMapsDemos/Samples/SnapshotReadyViewController.h" +#import "GoogleMapsDemos/Samples/StyledMapViewController.h" +#import "GoogleMapsDemos/Samples/TrafficMapViewController.h" +#import "GoogleMapsDemos/Samples/VisibleRegionViewController.h" + +// Panorama Demos +#import "GoogleMapsDemos/Samples/FixedPanoramaViewController.h" +#import "GoogleMapsDemos/Samples/PanoramaViewController.h" + +// Overlay Demos +#import "GoogleMapsDemos/Samples/AnimatedCurrentLocationViewController.h" +#import "GoogleMapsDemos/Samples/AnimatedUIViewMarkerViewController.h" +#import "GoogleMapsDemos/Samples/CustomMarkersViewController.h" +#import "GoogleMapsDemos/Samples/GradientPolylinesViewController.h" +#import "GoogleMapsDemos/Samples/GroundOverlayViewController.h" +#import "GoogleMapsDemos/Samples/MarkerEventsViewController.h" +#import "GoogleMapsDemos/Samples/MarkerInfoWindowViewController.h" +#import "GoogleMapsDemos/Samples/MarkerLayerViewController.h" +#import "GoogleMapsDemos/Samples/MarkersViewController.h" +#import "GoogleMapsDemos/Samples/PolygonsViewController.h" +#import "GoogleMapsDemos/Samples/PolylinesViewController.h" +#import "GoogleMapsDemos/Samples/TileLayerViewController.h" + +// Camera Demos +#import "GoogleMapsDemos/Samples/CameraViewController.h" +#import "GoogleMapsDemos/Samples/FitBoundsViewController.h" +#import "GoogleMapsDemos/Samples/MapLayerViewController.h" + +// Services +#import "GoogleMapsDemos/Samples/GeocoderViewController.h" +#import "GoogleMapsDemos/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:[StyledMapViewController class] + withTitle:@"Styled Map" + 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:[SnapshotReadyViewController class] + withTitle:@"Snapshot Ready" + 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], + [self newDemo:[FrameRateViewController class] + withTitle:@"Frame Rate" + 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:[AnimatedUIViewMarkerViewController class] + withTitle:@"UIView 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/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.h new file mode 100644 index 0000000..b88e25e --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface SnapshotReadyViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.m new file mode 100644 index 0000000..9dfa981 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/SnapshotReadyViewController.m @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/SnapshotReadyViewController.h" + +#import + +@interface SnapshotReadyViewController () +@end + +@implementation SnapshotReadyViewController { + GMSMapView *_mapView; + UILabel *_statusLabel; + UIBarButtonItem *_waitButton; + BOOL _isAwaitingSnapshot; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + GMSCameraPosition *camera = + [GMSCameraPosition cameraWithLatitude:-33.868 longitude:151.2086 zoom:6]; + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + _mapView.delegate = self; + self.view = _mapView; + + // Add status label, initially hidden. + _statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 30)]; + _statusLabel.alpha = 0.0f; + _statusLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _statusLabel.backgroundColor = [UIColor blueColor]; + _statusLabel.textColor = [UIColor whiteColor]; + _statusLabel.textAlignment = NSTextAlignmentCenter; + + // Add a wait button to signify on the next SnapshotReady event, a screenshot of the map will + // be taken. + _waitButton = [[UIBarButtonItem alloc] initWithTitle:@"Wait for snapshot" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapWait)]; + self.navigationItem.rightBarButtonItems = @[ _waitButton ]; + [_mapView addSubview:_statusLabel]; +} + +#pragma mark GMSMapViewDelegate + +- (void)mapViewSnapshotReady:(GMSMapView *)mapView { + if (_isAwaitingSnapshot) { + _isAwaitingSnapshot = NO; + _waitButton.enabled = YES; + _waitButton.title = @"Wait for snapshot"; + [self takeSnapshot]; + } + + _statusLabel.alpha = 0.8f; + _statusLabel.text = @"Snapshot Ready"; + // Remove status label after 1 second. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + _statusLabel.alpha = 0.0f; + }); +} + +#pragma mark Private + +- (void)didTapWait { + _isAwaitingSnapshot = YES; + _waitButton.enabled = NO; + _waitButton.title = @"Waiting"; +} + +- (void)takeSnapshot { + // Take a snapshot of the map. + UIGraphicsBeginImageContextWithOptions(_mapView.bounds.size, YES, 0); + [_mapView drawViewHierarchyInRect:_mapView.bounds afterScreenUpdates:YES]; + UIImage *mapSnapShot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + // Put snapshot image into an UIImageView and overlay on top of map. + UIImageView *imageView = [[UIImageView alloc] initWithImage:mapSnapShot]; + imageView.layer.borderColor = [UIColor redColor].CGColor; + imageView.layer.borderWidth = 10.0f; + [_mapView addSubview:imageView]; + + // Remove imageView after 1 second. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:1 + animations:^{ + imageView.alpha = 0.0f; + } + completion:^(BOOL finished) { + [imageView removeFromSuperview]; + }]; + }); +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.h new file mode 100644 index 0000000..fc290b4 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface StructuredGeocoderViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.m new file mode 100644 index 0000000..34ec2ed --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StructuredGeocoderViewController.m @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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; + marker.map = _mapView; + mapView.selectedMarker = marker; + } 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/Example/GoogleMapsDemos/Samples/StyledMapViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StyledMapViewController.h new file mode 100644 index 0000000..ab70aea --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StyledMapViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface StyledMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StyledMapViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StyledMapViewController.m new file mode 100644 index 0000000..ab131d0 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/StyledMapViewController.m @@ -0,0 +1,138 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/Samples/StyledMapViewController.h" + +#import + +static NSString *const kNormalType = @"Normal"; +static NSString *const kRetroType = @"Retro"; +static NSString *const kGrayscaleType = @"Grayscale"; +static NSString *const kNightType = @"Night"; +static NSString *const kNoPOIsType = @"No business points of interest, no transit"; + +@interface StyledMapViewController () +@end + +@implementation StyledMapViewController { + UIBarButtonItem *_barButtonItem; + GMSMapView *_mapView; + GMSMapStyle *_retroStyle; + GMSMapStyle *_grayscaleStyle; + GMSMapStyle *_nightStyle; + GMSMapStyle *_noPOIsStyle; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Error handling is skipped here for brevity, however it is recommended that you look at the + // error returned from |styleWithContentsOfFileURL:error:| if it returns nil. This error will + // provide information on why your style was not able to be loaded. + + NSURL *retroURL = [[NSBundle mainBundle] URLForResource:@"mapstyle-retro" + withExtension:@"json"]; + _retroStyle = [GMSMapStyle styleWithContentsOfFileURL:retroURL error:NULL]; + + NSURL *grayscaleURL = [[NSBundle mainBundle] URLForResource:@"mapstyle-silver" + withExtension:@"json"]; + _grayscaleStyle = [GMSMapStyle styleWithContentsOfFileURL:grayscaleURL error:NULL]; + + NSURL *nightURL = [[NSBundle mainBundle] URLForResource:@"mapstyle-night" + withExtension:@"json"]; + _nightStyle = [GMSMapStyle styleWithContentsOfFileURL:nightURL error:NULL]; + + NSString *noPOIsString = @" [\n" + " {\n" + " \"featureType\": \"poi.business\",\n" + " \"elementType\": \"all\",\n" + " \"stylers\": [\n" + " {\n" + " \"visibility\": \"off\"\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " \"featureType\": \"transit\",\n" + " \"elementType\": \"all\",\n" + " \"stylers\": [\n" + " {\n" + " \"visibility\": \"off\"\n" + " }\n" + " ]\n" + " }\n" + " ]"; + _noPOIsStyle = [GMSMapStyle styleWithJSONString:noPOIsString error:NULL]; + + GMSCameraPosition *camera = + [GMSCameraPosition cameraWithLatitude:-33.868 longitude:151.2086 zoom:12]; + + _mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera]; + self.view = _mapView; + + _mapView.mapStyle = _retroStyle; + + UIBarButtonItem *styleButton = [[UIBarButtonItem alloc] initWithTitle:@"Style" + style:UIBarButtonItemStyleBordered + target:self + action:@selector(changeMapStyle:)]; + self.navigationItem.rightBarButtonItem = styleButton; + self.navigationItem.title = kRetroType; +} + +- (void)changeMapStyle:(UIBarButtonItem *)sender { + UIActionSheet *actionSheet = [[UIActionSheet alloc] + initWithTitle:@"Select map style" + delegate:self + cancelButtonTitle:nil + destructiveButtonTitle:nil + otherButtonTitles:kRetroType, kGrayscaleType, kNightType, kNormalType, kNoPOIsType, nil]; + [actionSheet showFromBarButtonItem:sender animated:YES]; +} + +#pragma mark - UIActionSheetDelegate + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + switch (buttonIndex) { + case 0: + _mapView.mapStyle = _retroStyle; + self.navigationItem.title = kRetroType; + break; + case 1: + _mapView.mapStyle = _grayscaleStyle; + self.navigationItem.title = kGrayscaleType; + break; + case 2: + _mapView.mapStyle = _nightStyle; + self.navigationItem.title = kNightType; + break; + case 3: + _mapView.mapStyle = nil; + self.navigationItem.title = kNormalType; + break; + case 4: + _mapView.mapStyle = _noPOIsStyle; + self.navigationItem.title = kNoPOIsType; + break; + default: + break; + } +} + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.h new file mode 100644 index 0000000..4641d6c --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface TileLayerViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.m new file mode 100644 index 0000000..d67dbc8 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TileLayerViewController.m @@ -0,0 +1,80 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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:@"https://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/Example/GoogleMapsDemos/Samples/TrafficMapViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TrafficMapViewController.h new file mode 100644 index 0000000..596aa4e --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TrafficMapViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface TrafficMapViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TrafficMapViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TrafficMapViewController.m new file mode 100644 index 0000000..08d6913 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/TrafficMapViewController.m @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.h b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.h new file mode 100644 index 0000000..79c24cc --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.h @@ -0,0 +1,20 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface VisibleRegionViewController : UIViewController + +@end diff --git a/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.m new file mode 100644 index 0000000..e9d1491 --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/Samples/VisibleRegionViewController.m @@ -0,0 +1,75 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GoogleMapsDemos/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/Example/GoogleMapsDemos/main.m b/Pods/GoogleMaps/Example/GoogleMapsDemos/main.m new file mode 100644 index 0000000..98e341e --- /dev/null +++ b/Pods/GoogleMaps/Example/GoogleMapsDemos/main.m @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import + +#import "GoogleMapsDemos/DemoAppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([DemoAppDelegate class])); + } +} diff --git a/Pods/GoogleMaps/Example/Podfile b/Pods/GoogleMaps/Example/Podfile new file mode 100644 index 0000000..28436dd --- /dev/null +++ b/Pods/GoogleMaps/Example/Podfile @@ -0,0 +1,5 @@ +source 'https://github.com/CocoaPods/Specs.git' + +target 'GoogleMapsDemos' do + pod 'GoogleMaps', '= 2.1.0' +end diff --git a/Pods/GoogleMaps/Example/README.GoogleMapsDemos b/Pods/GoogleMaps/Example/README.GoogleMapsDemos new file mode 100644 index 0000000..392eea5 --- /dev/null +++ b/Pods/GoogleMaps/Example/README.GoogleMapsDemos @@ -0,0 +1,21 @@ +GoogleMapsDemos 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 7.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.GoogleMapsDemos". + + * 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/README.md b/Pods/GoogleMaps/README.md new file mode 100644 index 0000000..e441285 --- /dev/null +++ b/Pods/GoogleMaps/README.md @@ -0,0 +1,88 @@ +# Google Maps SDK for iOS + +This pod contains the Google Maps SDK for iOS, supporting both Objective C and +Swift. + +Use the [Google Maps SDK for iOS] +(https://developers.google.com/maps/documentation/ios-sdk/) to enrich your +app with interactive maps and immersive street view panoramas, and add your +own custom elements such as markers, windows and polylines. + +# Getting Started + +* *Guides*: Read our [Getting Started guides] + (https://developers.google.com/maps/documentation/ios-sdk/intro). +* *Demo Videos*: View [pre-recorded online demos] + (https://developers.google.com/maps/documentation/ios-sdk/#demos). +* *Code samples*: In order to try out our demo app, use + + ``` + $ pod try GoogleMaps + ``` + + and follow the instructions on our [developer pages] + (https://developers.google.com/maps/documentation/ios-sdk/code-samples). + +* *Support*: Find support from various channels and communities. + + * Support pages for [Google Maps SDK for iOS] + (https://developers.google.com/maps/documentation/ios-sdk/support). + * Stack Overflow, using the [google-maps] + (https://stackoverflow.com/questions/tagged/google-maps) tag. + * [Google Maps APIs Premium Plan] + (https://developers.google.com/maps/premium/support) customers have + access to business-level support through Google's [Enterprise Support + Portal](https://google.secure.force.com/) + +* *Report issues*: Use our issue tracker to [file a bug] + (https://code.google.com/p/gmaps-api-issues/issues/entry?template=Maps%20SDK%20for%20iOS%20-%20Bug) + or a [feature request] + (https://code.google.com/p/gmaps-api-issues/issues/entry?template=Maps%20SDK%20for%20iOS%20-%20Feature%20Request) + +# Installation + +To integrate Google Maps SDK for iOS into your Xcode project using CocoaPods, +specify it in your `Podfile`: + +``` +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '7.0' +target 'YOUR_APPLICATION_TARGET_NAME_HERE' do + pod 'GoogleMaps' +end +``` + +Then, run the following command: + +``` +$ pod install +``` + +Before you can start using the API, you have to activate it in the [Google +Developer Console](https://console.developers.google.com/) and integrate the +respective API key in your project. For detailed installation instructions, +visit Google's Getting Started Guides for the [Google Maps SDK for iOS] +(https://developers.google.com/maps/documentation/ios-sdk/start). + +# Migration from version 1 + +If you are using the Google Places API for iOS as part of the Google Maps SDK +for iOS version 1 please check the [migration guide](https://developers.google.com/places/migrate-to-v2) +for more information on upgrading your project. + +# License and Terms of Service + +By using the Google Maps SDK for iOS you accept Google's Terms of Service and +Policies. Pay attention particularly to the following aspects: + +* Depending on your app and use case, you may be required to display + attribution. Read more about [attribution requirements] + (https://developers.google.com/maps/documentation/ios-sdk/intro#attribution_requirements). +* Your API usage is subject to quota limitations. Read more about [usage + limits](https://developers.google.com/maps/pricing-and-plans/). +* The [Terms of Service](https://developers.google.com/maps/terms) are a + comprehensive description of the legal contract that you enter with Google + by using the Google Maps SDK for iOS. You may want to pay special attention + to [section 10] + (https://developers.google.com/maps/terms#10-license-restrictions), as it + talks in detail about what you can do with the API, and what you can't. diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/GoogleMapsBase b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/GoogleMapsBase new file mode 120000 index 0000000..11c8237 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/GoogleMapsBase @@ -0,0 +1 @@ +Versions/Current/GoogleMapsBase \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Headers b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Modules b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/TwitterKit.framework/Resources b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Resources similarity index 100% rename from TwitterKit.framework/Resources rename to Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Resources diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/GoogleMapsBase b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/GoogleMapsBase new file mode 100644 index 0000000..2195316 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/GoogleMapsBase differ diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCompatabilityMacros.h b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCompatabilityMacros.h new file mode 100644 index 0000000..2deea17 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCompatabilityMacros.h @@ -0,0 +1,43 @@ +// +// GMSCompatabilityMacros.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 +// + +#import + +#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 +#define GMS_NULLABLE_INSTANCETYPE instancetype +#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 +#define GMS_NULLABLE_INSTANCETYPE nullable instancetype +#endif + +#if __has_feature(objc_generics) && defined(__IPHONE_9_0) && \ + __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0 +#define GMS_DECLARE_GENERICS 1 +#else +#define GMS_DECLARE_GENERICS 0 +#endif + +#if GMS_DECLARE_GENERICS +#define GMS_NSArrayOf(value) NSArray +#define GMS_NSDictionaryOf(key, value) NSDictionary +#define GMS_NSSetOf(value) NSSet +#else +#define GMS_NSArrayOf(value) NSArray +#define GMS_NSDictionaryOf(key, value) NSDictionary +#define GMS_NSSetOf(value) NSSet +#endif diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCoordinateBounds.h b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCoordinateBounds.h new file mode 100644 index 0000000..4db7511 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCoordinateBounds.h @@ -0,0 +1,82 @@ +// +// 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 + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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; + +/** + * 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 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GoogleMapsBase.h b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GoogleMapsBase.h new file mode 100644 index 0000000..9183bb1 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GoogleMapsBase.h @@ -0,0 +1,2 @@ +#import +#import diff --git a/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Modules/module.modulemap b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..360c546 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,4 @@ +framework module GoogleMapsBase { umbrella header "GoogleMapsBase.h" +header "GMSCompatabilityMacros.h" +header "GMSCoordinateBounds.h" +export * module * { export * } } diff --git a/TwitterKit.framework/Versions/Current b/Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/Current similarity index 100% rename from TwitterKit.framework/Versions/Current rename to Pods/GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/Current diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/GoogleMaps b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/GoogleMaps new file mode 120000 index 0000000..17ed6fb --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/GoogleMaps @@ -0,0 +1 @@ +Versions/Current/GoogleMaps \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Headers b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Modules b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Resources b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps new file mode 100644 index 0000000..824b6c4 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/GoogleMaps differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h new file mode 100644 index 0000000..5d178b0 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.h @@ -0,0 +1,78 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR thoroughfare; + +/** Locality or city. */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR locality; + +/** Subdivision of locality, district or park. */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR subLocality; + +/** Region/State/Administrative area. */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR administrativeArea; + +/** Postal/Zip code. */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR postalCode; + +/** The country name. */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR country; + +/** An array of NSString containing formatted lines of the address. May be nil. */ +@property(nonatomic, copy, readonly) GMS_NSArrayOf(NSString *) *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR)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 *GMS_NULLABLE_PTR)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; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCALayer.h new file mode 100644 index 0000000..c10bc7b --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h new file mode 100644 index 0000000..be088ca --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraPosition.h @@ -0,0 +1,131 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** +* 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 from the nadir (directly facing the Earth). 0 is + * straight down, 90 is parallel to the ground. Note that the maximum angle allowed is dependent + * on the zoom. You can think of it as a series of line segments as a function of zoom, rather + * than a step function. For zoom 16 and above, the maximum angle is 65 degrees. For zoom 10 and + * below, the maximum angle is 30 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 towards which the camera points. + * @param zoom The zoom level near the center of the screen. + * @param bearing Bearing of the camera in degrees clockwise from true north. + * @param viewingAngle The angle, in degrees, of the camera angle from the nadir (directly facing + * the Earth) + */ +- (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; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h new file mode 100644 index 0000000..d1ec13a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCameraUpdate.h @@ -0,0 +1,115 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSCameraPosition; +@class GMSCoordinateBounds; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h new file mode 100644 index 0000000..37ae9cc --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h @@ -0,0 +1,59 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR strokeColor; + +/** + * The interior of the circle is painted with fillColor. + * The default value is nil, resulting in no fill. + */ +@property(nonatomic, strong) UIColor *GMS_NULLABLE_PTR 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds+GoogleMaps.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds+GoogleMaps.h new file mode 100644 index 0000000..01b29ef --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds+GoogleMaps.h @@ -0,0 +1,44 @@ +// +// GMSCoordinateBounds+GoogleMaps.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 +// + + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +#import + +@class GMSPath; + +GMS_ASSUME_NONNULL_BEGIN + +@interface GMSCoordinateBounds (GoogleMaps) + +/** + * 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 |path|. + */ +- (GMSCoordinateBounds *)includingPath:(GMSPath *)path; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSDeprecationMacros.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSDeprecationMacros.h new file mode 100644 index 0000000..837dd16 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSDeprecationMacros.h @@ -0,0 +1,18 @@ +// +// GMSDeprecationMacros.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 +// + +#ifndef IPHONE_MAPS_SDK_MAPS_GMSDEPRECATIONMACROS_H_ +#define IPHONE_MAPS_SDK_MAPS_GMSDEPRECATIONMACROS_H_ + +#ifndef __GMS_AVAILABLE_BUT_DEPRECATED +#define __GMS_AVAILABLE_BUT_DEPRECATED __deprecated +#endif + +#endif diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h new file mode 100644 index 0000000..089b6ee --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeocoder.h @@ -0,0 +1,66 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +GMS_ASSUME_NONNULL_BEGIN + +@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 *GMS_NULLABLE_PTR, + NSError *GMS_NULLABLE_PTR); + +/** + * 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 *GMS_NULLABLE_PTR)firstResult; + +/** Returns an array of all the results (contains GMSAddress), including the first result. */ +- (GMS_NSArrayOf(GMSAddress *) * GMS_NULLABLE_PTR)results; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h new file mode 100644 index 0000000..9ffcf94 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGeometryUtils.h @@ -0,0 +1,236 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +@class GMSPath; +@class GMSStrokeStyle; +@class GMSStyleSpan; + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 considered + * 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 +GMS_NSArrayOf(GMSStyleSpan *) *GMSStyleSpans(GMSPath *path, + GMS_NSArrayOf(GMSStrokeStyle *) *styles, + GMS_NSArrayOf(NSNumber *) *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 +GMS_NSArrayOf(GMSStyleSpan *) *GMSStyleSpansOffset(GMSPath *path, + GMS_NSArrayOf(GMSStrokeStyle *) *styles, + GMS_NSArrayOf(NSNumber *) *lengths, + GMSLengthKind lengthKind, + double lengthOffset); + +/**@}*/ + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h new file mode 100644 index 0000000..efbd6df --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSGroundOverlay.h @@ -0,0 +1,91 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSCoordinateBounds; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR icon; + +/** + * Sets the opacity of the ground overlay, between 0 (completely transparent) + * and 1 (default) inclusive. + */ +@property(nonatomic, assign) float opacity; + +/** + * 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 *GMS_NULLABLE_PTR bounds; + +/** + * Convenience constructor for GMSGroundOverlay for a particular |bounds| and + * |icon|. Will set |position| accordingly. + */ ++ (instancetype)groundOverlayWithBounds:(GMSCoordinateBounds *GMS_NULLABLE_PTR)bounds + icon:(UIImage *GMS_NULLABLE_PTR)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 *GMS_NULLABLE_PTR)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; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h new file mode 100644 index 0000000..fe1b55a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorBuilding.h @@ -0,0 +1,47 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSIndoorLevel; + +/** + * 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) GMS_NSArrayOf(GMSIndoorLevel *) * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h new file mode 100644 index 0000000..870e94a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorDisplay.h @@ -0,0 +1,71 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSIndoorBuilding; +@class GMSIndoorLevel; + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 *GMS_NULLABLE_PTR)building; + +/** + * Raised when the activeLevel has changed. This event is raised for all + * changes, including explicit setting of the property. + */ +- (void)didChangeActiveLevel:(GMSIndoorLevel *GMS_NULLABLE_PTR)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 GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR activeLevel; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h new file mode 100644 index 0000000..1ecd377 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSIndoorLevel.h @@ -0,0 +1,37 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR name; + +/** Localized short display name for the level, e.g. "1". */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR shortName; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h new file mode 100644 index 0000000..3bc878f --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h @@ -0,0 +1,106 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapStyle.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapStyle.h new file mode 100644 index 0000000..8f75ae1 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapStyle.h @@ -0,0 +1,54 @@ +// +// GMSMapStyle.h +// Google Maps SDK for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * GMSMapStyle holds details about a style which can be applied to a map. + * + * With style options you can customize the presentation of the standard Google map styles, changing + * the visual display of features like roads, parks, and other points of interest. As well as + * changing the style of these features, you can also hide features entirely. This means that you + * can emphasize particular components of the map or make the map complement the content of your + * app. + * + * For more information see: https://developers.google.com/maps/documentation/ios-sdk/styling + */ +@interface GMSMapStyle : NSObject + +/** + * Creates a style using a string containing JSON. + * + * Returns nil and populates |error| (if provided) if |style| is invalid. + */ ++ (GMS_NULLABLE_INSTANCETYPE)styleWithJSONString:(NSString *)style + error:(NSError *__autoreleasing GMS_NULLABLE_PTR *)error; + +/** + * Creates a style using a file containing JSON. + * + * Returns nil and populates |error| (if provided) if |style| is invalid, the file cannot be read, + * or the URL is not a file URL. + */ ++ (GMS_NULLABLE_INSTANCETYPE) + styleWithContentsOfFileURL:(NSURL *)fileURL + error:(NSError *__autoreleasing GMS_NULLABLE_PTR *)error; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h new file mode 100644 index 0000000..d8b518d --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView+Animation.h @@ -0,0 +1,67 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h new file mode 100644 index 0000000..e75fba6 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapView.h @@ -0,0 +1,459 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import +#import + +@class GMSCameraPosition; +@class GMSCameraUpdate; +@class GMSCoordinateBounds; +@class GMSIndoorDisplay; +@class GMSMapLayer; +@class GMSMapStyle; +@class GMSMapView; +@class GMSMarker; +@class GMSOverlay; +@class GMSProjection; + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 tapped. + * @param coordinate The location that was tapped. + */ +- (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + * Called after a marker has been tapped. + * + * @param mapView The map view that was tapped. + * @param marker The marker that was tapped. + * @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 a marker's info window has been long pressed. + */ +- (void)mapView:(GMSMapView *)mapView didLongPressInfoWindowOfMarker:(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 tapped. + * @param overlay The overlay that was tapped. + */ +- (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay; + +/** + * Called after a POI has been tapped. + * + * @param mapView The map view that was tapped. + * @param placeID The placeID of the POI that was tapped. + * @param name The name of the POI that was tapped. + * @param location The location of the POI that was tapped. + */ +- (void)mapView:(GMSMapView *)mapView + didTapPOIWithPlaceID:(NSString *)placeID + name:(NSString *)name + location:(CLLocationCoordinate2D)location; + +/** + * 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 *GMS_NULLABLE_PTR)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 display as contents in the info window, or nil to + * use the default content rendering instead + */ + +- (UIView *GMS_NULLABLE_PTR)mapView:(GMSMapView *)mapView markerInfoContents:(GMSMarker *)marker; + +/** + * Called when the marker's info window is closed. + */ +- (void)mapView:(GMSMapView *)mapView didCloseInfoWindowOfMarker:(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; + +/** + * Called when tiles have just been requested or labels have just started rendering. + */ +- (void)mapViewDidStartTileRendering:(GMSMapView *)mapView; + +/** + * Called when all tiles have been loaded (or failed permanently) and labels have been rendered. + */ +- (void)mapViewDidFinishTileRendering:(GMSMapView *)mapView; + +/** + * Called when map is stable (tiles loaded, labels rendered, camera idle) and overlay objects have + * been rendered. + */ +- (void)mapViewSnapshotReady:(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; + +/** + * Rendering frame rates for GMSMapView. + */ +typedef enum { + /** Use the minimum frame rate to conserve battery usage. */ + kGMSFrameRatePowerSave, + + /** + * Use a median frame rate to provide smoother rendering and conserve processing cycles. + */ + kGMSFrameRateConservative, + + /** + * Use the maximum frame rate for a device. For low end devices this will be 30 FPS, + * for high end devices 60 FPS. + */ + kGMSFrameRateMaximum, + +} GMSFrameRate; + +/** + * 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 GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR 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; + +/** + * Controls the style of the map. + * + * A non-nil mapStyle will only apply if mapType is Normal. + */ +@property(nonatomic, strong, nullable) GMSMapStyle *mapStyle; + +/** + * 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 + * around 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; + +/** + * Controls the rendering frame rate. + * Default value is kGMSFrameRateMaximum. + */ +@property(nonatomic, assign) GMSFrameRate preferredFrameRate; + +/** + * 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 invalid this method will return a nil camera. + */ +- (GMSCameraPosition *GMS_NULLABLE_PTR)cameraForBounds:(GMSCoordinateBounds *)bounds + insets:(UIEdgeInsets)insets; + +/** + * Changes the camera according to |update|. + * The camera change is instantaneous (with no animation). + */ +- (void)moveCamera:(GMSCameraUpdate *)update; + +/** + * Check whether the given camera positions would practically cause the camera to be rendered the + * same, taking into account the level of precision and transformations used internally. + */ +- (BOOL)areEqualForRenderingPosition:(GMSCameraPosition *)position + position:(GMSCameraPosition *)otherPosition; + +@end + +/** + * Accessibility identifier for the compass button. + */ +extern NSString *const kGMSAccessibilityCompass; + +/** + * Accessibility identifier for the "my location" button. + */ +extern NSString *const kGMSAccessibilityMyLocation; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h new file mode 100644 index 0000000..ebf67ab --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarker.h @@ -0,0 +1,192 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSMarkerLayer; +@class GMSPanoramaView; +@class UIImage; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR icon; + +/** + * Marker view to render. If left nil, falls back to the |icon| property instead. + * + * Supports animation of all animatable properties of UIView, except |frame| and |center|. Changing + * these properties or their corresponding CALayer version, including |position|, is not supported. + * + * Note that the view behaves as if |clipsToBounds| is set to YES, regardless of its actual + * value. + */ +@property(nonatomic, strong) UIView *GMS_NULLABLE_PTR iconView; + +/** + * Controls whether the icon for this marker should be redrawn every frame. + * + * Note that when this changes from NO to YES, the icon is guaranteed to be redrawn next frame. + * + * Defaults to YES. + * Has no effect if |iconView| is nil. + */ +@property(nonatomic, assign) BOOL tracksViewChanges; + +/** + * Controls whether the info window for this marker should be redrawn every frame. + * + * Note that when this changes from NO to YES, the info window is guaranteed to be redrawn next + * frame. + * + * Defaults to NO. + */ +@property(nonatomic, assign) BOOL tracksInfoWindowChanges; + +/** + * 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 GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR)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; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h new file mode 100644 index 0000000..9f41e2b --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMarkerLayer.h @@ -0,0 +1,52 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMutablePath.h new file mode 100644 index 0000000..4f0100c --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOrientation.h new file mode 100644 index 0000000..c30342d --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/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 static GMSOrientation GMSOrientationMake(CLLocationDirection heading, double pitch) { + GMSOrientation orientation = {heading, pitch}; + return orientation; +} + +#ifdef __cplusplus +} +#endif diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h new file mode 100644 index 0000000..6603ea2 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSOverlay.h @@ -0,0 +1,66 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h new file mode 100644 index 0000000..3f90436 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanorama.h @@ -0,0 +1,40 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSPanoramaLink; + +/** + * 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) GMS_NSArrayOf(GMSPanoramaLink *) * links; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h new file mode 100644 index 0000000..bc33f94 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCamera.h @@ -0,0 +1,86 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h new file mode 100644 index 0000000..7188f9d --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaCameraUpdate.h @@ -0,0 +1,35 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h new file mode 100644 index 0000000..51100a2 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLayer.h @@ -0,0 +1,47 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h new file mode 100644 index 0000000..c48a6e8 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaLink.h @@ -0,0 +1,33 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h new file mode 100644 index 0000000..80fae9e --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaService.h @@ -0,0 +1,62 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSPanorama; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR panorama, + NSError *GMS_NULLABLE_PTR 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h new file mode 100644 index 0000000..9fad276 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPanoramaView.h @@ -0,0 +1,268 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import + +@class GMSMarker; +@class GMSPanorama; +@class GMSPanoramaCamera; +@class GMSPanoramaCameraUpdate; +@class GMSPanoramaView; + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 *GMS_NULLABLE_PTR)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; + +/** + * Called when the panorama tiles for the current view have just been requested + * and are beginning to load. + */ +- (void)panoramaViewDidStartRendering:(GMSPanoramaView *)panoramaView; + +/** + * Called when the panorama tiles have been loaded (or permanently failed to load) + * and rendered on screen. + */ +- (void)panoramaViewDidFinishRendering:(GMSPanoramaView *)panoramaView; + +@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 *GMS_NULLABLE_PTR panorama; + +/** GMSPanoramaView delegate. */ +@property(nonatomic, weak) IBOutlet id GMS_NULLABLE_PTR 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h new file mode 100644 index 0000000..3e77d10 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.h @@ -0,0 +1,111 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + */ ++ (GMS_NULLABLE_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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h new file mode 100644 index 0000000..a0e1c66 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolygon.h @@ -0,0 +1,58 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +GMS_ASSUME_NONNULL_BEGIN + +@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 *GMS_NULLABLE_PTR path; + +/** + * The array of GMSPath instances that describes any holes in this polygon. The coordinates + * composing each path must be valid. + */ +@property(nonatomic, copy) GMS_NSArrayOf(GMSPath *) * GMS_NULLABLE_PTR holes; + +/** 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 *GMS_NULLABLE_PTR strokeColor; + +/** The fill color. Defaults to blueColor. */ +@property(nonatomic, strong) UIColor *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR)path; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h new file mode 100644 index 0000000..8ac0bc5 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPolyline.h @@ -0,0 +1,109 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +@class GMSPath; + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR)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) GMS_NSArrayOf(GMSStyleSpan *) * GMS_NULLABLE_PTR spans; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSProjection.h new file mode 100644 index 0000000..7596dba --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h new file mode 100644 index 0000000..503314d --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSServices.h @@ -0,0 +1,61 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** 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 must be called exactly once + * by your application before any iOS Maps SDK object is initialized. + * + * @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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h new file mode 100644 index 0000000..43fdb0a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSSyncTileLayer.h @@ -0,0 +1,39 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h new file mode 100644 index 0000000..566a82d --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSTileLayer.h @@ -0,0 +1,113 @@ +// +// 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 +// + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class GMSMapView; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR)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 *GMS_NULLABLE_PTR 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSUISettings.h new file mode 100644 index 0000000..40b1f78 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/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 useful 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 programmatically 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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h new file mode 100644 index 0000000..058bba8 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.h @@ -0,0 +1,61 @@ +// +// 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +@class NSURL; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * |GMSTileURLConstructor| is a block taking |x|, |y| and |zoom| + * and returning an NSURL, or nil to indicate no tile for that location. + */ +typedef NSURL *GMS_NULLABLE_PTR (^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 *GMS_NULLABLE_PTR userAgent; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h new file mode 100644 index 0000000..165ac99 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h @@ -0,0 +1,50 @@ +// +// 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 diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..5763714 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,40 @@ +framework module GoogleMaps { umbrella header "GoogleMaps.h" +header "GMSAddress.h" +header "GMSCALayer.h" +header "GMSCameraPosition.h" +header "GMSCameraUpdate.h" +header "GMSCircle.h" +header "GMSCoordinateBounds+GoogleMaps.h" +header "GMSDeprecationMacros.h" +header "GMSGeocoder.h" +header "GMSGeometryUtils.h" +header "GMSGroundOverlay.h" +header "GMSIndoorBuilding.h" +header "GMSIndoorDisplay.h" +header "GMSIndoorLevel.h" +header "GMSMapLayer.h" +header "GMSMapStyle.h" +header "GMSMapView+Animation.h" +header "GMSMapView.h" +header "GMSMarker.h" +header "GMSMarkerLayer.h" +header "GMSMutablePath.h" +header "GMSOrientation.h" +header "GMSOverlay.h" +header "GMSPanorama.h" +header "GMSPanoramaCamera.h" +header "GMSPanoramaCameraUpdate.h" +header "GMSPanoramaLayer.h" +header "GMSPanoramaLink.h" +header "GMSPanoramaService.h" +header "GMSPanoramaView.h" +header "GMSPath.h" +header "GMSPolygon.h" +header "GMSPolyline.h" +header "GMSProjection.h" +header "GMSServices.h" +header "GMSSyncTileLayer.h" +header "GMSTileLayer.h" +header "GMSUISettings.h" +header "GMSURLTileLayer.h" +export * module * { export * } } diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.mom b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.mom differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.omo b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/Storage.omo differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/VersionInfo.plist b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCacheStorage.momd/VersionInfo.plist differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/DroidSansMerged-Regular.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/DroidSansMerged-Regular.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-1x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-1x.png new file mode 100644 index 0000000..b68a9f0 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-1x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-2x.png new file mode 100644 index 0000000..3d077df Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-3x.png new file mode 100644 index 0000000..5180530 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavNightModeSprites-0-3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-1x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-1x.png new file mode 100644 index 0000000..447bcea Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-1x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-2x.png new file mode 100644 index 0000000..a4cd614 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-3x.png new file mode 100644 index 0000000..db6a3e1 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSNavSprites-0-3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png new file mode 100644 index 0000000..9f0d53a Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-1x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png new file mode 100644 index 0000000..0d23cee Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png new file mode 100644 index 0000000..00f2c57 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/GMSSprites-0-3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist new file mode 100644 index 0000000..48cd4cd Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Info.plist differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Black.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Black.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BlackItalic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BlackItalic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Bold.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Bold.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BoldItalic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-BoldItalic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Italic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Italic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Light.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Light.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-LightItalic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-LightItalic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Medium.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Medium.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-MediumItalic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-MediumItalic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Regular.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Regular.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Thin.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-Thin.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-ThinItalic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Roboto-ThinItalic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Italic.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Italic.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Regular.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/RobotoCondensed-Regular.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Siemreap.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Siemreap.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Tharlon-Regular.ttf b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/Tharlon-Regular.ttf differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings new file mode 100644 index 0000000..63f9da1 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ar.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png new file mode 100644 index 0000000..cec89b6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png new file mode 100644 index 0000000..7a3d29d Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png new file mode 100644 index 0000000..74eace5 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_background@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png new file mode 100644 index 0000000..11fee99 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png new file mode 100644 index 0000000..a73d1d6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night.png new file mode 100644 index 0000000..df8c234 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night@2x.png new file mode 100644 index 0000000..dccbf03 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_compass_night@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png new file mode 100644 index 0000000..c09a65f Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png new file mode 100644 index 0000000..379be62 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/button_my_location@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings new file mode 100644 index 0000000..5ba1693 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ca.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings new file mode 100644 index 0000000..2184066 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/cs.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings new file mode 100644 index 0000000..7277ae9 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/da.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png new file mode 100644 index 0000000..7f7c2fe Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/dav_one_way_16_256.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings new file mode 100644 index 0000000..bc87d22 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/de.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings new file mode 100644 index 0000000..7160259 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/el.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings new file mode 100644 index 0000000..4dd8f57 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_AU.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_AU.lproj/GMSCore.strings new file mode 100644 index 0000000..68669f3 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_AU.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings new file mode 100644 index 0000000..68669f3 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_GB.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_IN.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_IN.lproj/GMSCore.strings new file mode 100644 index 0000000..68669f3 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/en_IN.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings new file mode 100644 index 0000000..9180f8f Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_419.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_419.lproj/GMSCore.strings new file mode 100644 index 0000000..584359b Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_419.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_MX.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_MX.lproj/GMSCore.strings new file mode 100644 index 0000000..584359b Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/es_MX.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings new file mode 100644 index 0000000..92f4ff2 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fi.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings new file mode 100644 index 0000000..473a74c Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr_CA.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr_CA.lproj/GMSCore.strings new file mode 100644 index 0000000..6f93103 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/fr_CA.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt.png new file mode 100644 index 0000000..c8eb786 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@2x.png new file mode 100644 index 0000000..2eddacd Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@3x.png new file mode 100644 index 0000000..d971d34 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_56x20_with_2_stroke_color_60x22pt@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt.png new file mode 100644 index 0000000..1a2341e Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@2x.png new file mode 100644 index 0000000..9641b49 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@3x.png new file mode 100644 index 0000000..5a188c9 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/googlelogo_light_56x20_with_2_stroke_color_60x22pt@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings new file mode 100644 index 0000000..d184291 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/he.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hi.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hi.lproj/GMSCore.strings new file mode 100644 index 0000000..97aeec3 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hi.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings new file mode 100644 index 0000000..f90d35f Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings new file mode 100644 index 0000000..a1fbcb2 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/hu.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png new file mode 100644 index 0000000..db933c8 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png new file mode 100644 index 0000000..65fee67 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_compass_needle@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png new file mode 100644 index 0000000..dccdcfd Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png new file mode 100644 index 0000000..ccb840e Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png new file mode 100644 index 0000000..0300f62 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ic_qu_direction_mylocation@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings new file mode 100644 index 0000000..ab3ed0c Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/id.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings new file mode 100644 index 0000000..2180dd0 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/it.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings new file mode 100644 index 0000000..ddaaf93 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ja.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings new file mode 100644 index 0000000..4053b92 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ko.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings new file mode 100644 index 0000000..4420d6f Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ms.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nb.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nb.lproj/GMSCore.strings new file mode 100644 index 0000000..f47fd08 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nb.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings new file mode 100644 index 0000000..fb2610b Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/nl.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings new file mode 100644 index 0000000..ef54f7d Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pl.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png new file mode 100644 index 0000000..4e42166 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture_dim.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture_dim.png new file mode 100644 index 0000000..576dc88 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/polyline_colors_texture_dim.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings new file mode 100644 index 0000000..f75de18 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_BR.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_BR.lproj/GMSCore.strings new file mode 100644 index 0000000..f75de18 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_BR.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings new file mode 100644 index 0000000..5448d50 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/pt_PT.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings new file mode 100644 index 0000000..d26a3e7 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ro.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_1-1.png b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_1-1.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png new file mode 100644 index 0000000..08672e6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_128-32.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png new file mode 100644 index 0000000..ba0b0a5 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_16-4.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_2-1.png b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_2-1.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png new file mode 100644 index 0000000..45a66a4 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_256-64.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_32-8.png b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_32-8.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png new file mode 100644 index 0000000..b2efb5d Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_4-1.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png new file mode 100644 index 0000000..664e9f6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_64-16.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png new file mode 100644 index 0000000..dabc352 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/road_8-2.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings new file mode 100644 index 0000000..0d98dd6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/ru.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings new file mode 100644 index 0000000..bc8a409 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sk.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings new file mode 100644 index 0000000..61b5e68 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/sv.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings new file mode 100644 index 0000000..92d6492 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/th.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings new file mode 100644 index 0000000..54f1ba6 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/tr.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings new file mode 100644 index 0000000..ea455a2 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/uk.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings new file mode 100644 index 0000000..6246d75 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/vi.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings new file mode 100644 index 0000000..ebd3314 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_CN.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_HK.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_HK.lproj/GMSCore.strings new file mode 100644 index 0000000..a67bb9e Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_HK.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings new file mode 100644 index 0000000..d5a0a5a Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/GMSCoreResources.bundle/zh_TW.lproj/GMSCore.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist new file mode 100644 index 0000000..eed82c9 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/Info.plist differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png new file mode 100644 index 0000000..c8e4a41 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png new file mode 100644 index 0000000..3e8fdca Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png new file mode 100644 index 0000000..1d8aee7 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_left@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png new file mode 100644 index 0000000..6189714 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png new file mode 100644 index 0000000..8abc3f7 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png new file mode 100644 index 0000000..7c35f06 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/bubble_right@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png new file mode 100644 index 0000000..ad62a2b Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png new file mode 100644 index 0000000..50fa00b Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@2x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png new file mode 100644 index 0000000..a368b46 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/default_marker@3x.png differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/en.lproj/InfoPlist.strings b/Pods/GoogleMaps/Subspecs/Maps/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/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/en.lproj/InfoPlist.strings differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/oss_licenses_maps.txt.gz b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/oss_licenses_maps.txt.gz new file mode 100644 index 0000000..9c86ee3 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle/oss_licenses_maps.txt.gz differ diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/Current b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/GoogleMapsCore b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/GoogleMapsCore new file mode 120000 index 0000000..2761192 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/GoogleMapsCore @@ -0,0 +1 @@ +Versions/Current/GoogleMapsCore \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Headers b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Modules b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Resources b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/GoogleMapsCore b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/GoogleMapsCore new file mode 100644 index 0000000..21b31b1 Binary files /dev/null and b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/GoogleMapsCore differ diff --git a/TwitterCore.framework/File b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Headers/GoogleMapsCore.h similarity index 100% rename from TwitterCore.framework/File rename to Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Headers/GoogleMapsCore.h diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Modules/module.modulemap b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..ef88a0e --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,2 @@ +framework module GoogleMapsCore { umbrella header "GoogleMapsCore.h" +export * module * { export * } } diff --git a/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/Current b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/Pods/GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_core.a b/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_core.a new file mode 100644 index 0000000..1523f9a Binary files /dev/null and b/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_core.a differ diff --git a/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_full.a b/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_full.a new file mode 100644 index 0000000..e029595 Binary files /dev/null and b/Pods/GoogleNetworkingUtilities/Libraries/libGTMSessionFetcher_full.a differ diff --git a/Pods/GooglePlaces/CHANGELOG b/Pods/GooglePlaces/CHANGELOG new file mode 100644 index 0000000..8892182 --- /dev/null +++ b/Pods/GooglePlaces/CHANGELOG @@ -0,0 +1,37 @@ +Version 2.1.0 - September 2016 +============================== + +Resolved Issues: + - Added workaround for the `userEmail` triggering false positives during + submission to the Apple app store. + - Errors returned from lookUpPlaceID(_:callback:) now have the correct error + domain and codes. + - Small fixes to RTL support in the place picker. + - Minor reliability improvements for the autocomplete widget and place picker. + +Version 2.0.1 - July 2016 +========================= + +Resolved Issues: + - Array properties are now correctly typed when accessed from Swift. + +Version 2.0.0 - July 2016 +========================= + +Improvements: + - This release splits the Places API from the Maps SDK. Previously, if you + wanted to use the Places API you had to include all of GoogleMaps. As a + result of the split, the final size of the Places API binary is 70% smaller + than the previous combined binary. If you are using only the Maps SDK you + will not be affected unless you have pinned your dependency on GoogleMaps + to a version earlier than 2.0. In this case, you should update this + restriction in your Podfile. If you are using the Places API, see the + migration guide online for more details. + +Resolved Issues: + - The Places framework is now available as a download for manual installation. + For details, see the get started guide online. + - The Places API no longer requests permission to access the user's location + on behalf of the app. This means that existing code which calls + GMSPlacesClient.currentPlaceWithCallback(_:) must be updated to ensure that + the location permission has been granted beforehand. diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos.xcodeproj/project.pbxproj b/Pods/GooglePlaces/Example/GooglePlacesDemos.xcodeproj/project.pbxproj new file mode 100644 index 0000000..769905f --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos.xcodeproj/project.pbxproj @@ -0,0 +1,423 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 14412E07BEABD68FE1334FE4 /* AutocompleteWithCustomColors.m in Sources */ = {isa = PBXBuildFile; fileRef = FC03FEE150596E9432F612DF /* AutocompleteWithCustomColors.m */; }; + 15D96A2CC730B0DEF8195766 /* MainSplitViewControllerBehaviorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 818B68D04D9AD33F4EBC54D2 /* MainSplitViewControllerBehaviorManager.m */; }; + 267E31192A188455AB41020F /* AutocompletePushViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BAEA89ED8C397E73E2E9730 /* AutocompletePushViewController.m */; }; + 2A5A993302F4E6E15EA9BCD2 /* PlacesDemoAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85EDEFE42680FF093454D1AB /* PlacesDemoAssets.xcassets */; }; + 2EC566C0AE2182DE0FEBA86B /* DemoAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B81B9326DD5B37C08B8CCAA /* DemoAppDelegate.m */; }; + 33CF1F7D8EA0E1114BDBE9FB /* AutocompleteWithSearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F7419DDC82CA074FF0A508C /* AutocompleteWithSearchViewController.m */; }; + 3D237EDDDDD9B9D7369D8487 /* AutocompleteModalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E1502E6F1A86B1773138153B /* AutocompleteModalViewController.m */; }; + 42128D6A32D50325383D28DD /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97ADBE3D50D731A88C24F3F0 /* UIKit.framework */; }; + 47C405AEA3807E2E1A4B5268 /* AutocompleteWithSearchDisplayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 243C9279E0BF845B23337398 /* AutocompleteWithSearchDisplayController.m */; }; + 5A60C11EA9FF366E66BBFCE8 /* AutocompleteBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E6E2A1285D9A0BEBDCD503 /* AutocompleteBaseViewController.m */; }; + 5B89B9BA327B31EC08E64538 /* BaseDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D4BD9841268F97FD23E770 /* BaseDemoViewController.m */; }; + 66310EE76B375B01262F52A1 /* DemoData.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F2203BCCE0CFC3DC4F38B30 /* DemoData.m */; }; + 6D5DBE8177ED77F3DDFA6C8E /* PagingPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 21153EB819432638638C2C26 /* PagingPhotoView.m */; }; + 91123AFDEB15F27812A8FCFF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1D344C64A66F10D55590797E /* Localizable.strings */; }; + A63EB7AD0A24EABBF2030D93 /* AutocompleteWithTextFieldController.m in Sources */ = {isa = PBXBuildFile; fileRef = ADB9E4A950DB2EA454486E47 /* AutocompleteWithTextFieldController.m */; }; + C54FD416450638D80BD9A3C0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F65D01DD8887E3B1B9731DAE /* LaunchScreen.storyboard */; }; + D064B1789A04566EA32E30E1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0589812EE1F702A7BBA34D /* main.m */; }; + E01ECF8484B4E41781E8B4A1 /* PhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6078563FD3AF8109B2586941 /* PhotosViewController.m */; }; + E56159A1819452F650C1D995 /* DemoListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 36C753B6637575A8CD81C699 /* DemoListViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0D33295E02D9501C1D45D615 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + 13C513E93D85CC7B765F9CDB /* AutocompleteWithCustomColors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteWithCustomColors.h; sourceTree = ""; }; + 16C539680ABA55CA7AFC3002 /* GooglePlacesDemos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GooglePlacesDemos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1A982A54052D846FC64B7AB5 /* pt_BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt_BR; path = pt_BR.lproj/Localizable.strings; sourceTree = ""; }; + 1BAEA89ED8C397E73E2E9730 /* AutocompletePushViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompletePushViewController.m; sourceTree = ""; }; + 1D0589812EE1F702A7BBA34D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 1D767BA5698A891C36ADF830 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; + 1DB6FA3886B89186F55A12E3 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Localizable.strings; sourceTree = ""; }; + 1FE971FB44A73F445279DA7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 21153EB819432638638C2C26 /* PagingPhotoView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PagingPhotoView.m; sourceTree = ""; }; + 216CA36352B45FBC79C98B8F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 243C9279E0BF845B23337398 /* AutocompleteWithSearchDisplayController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteWithSearchDisplayController.m; sourceTree = ""; }; + 273B1A3F7AA7EED9084B5033 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; + 2D52E39A30BD9D0AFC323F7F /* es_419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es_419; path = es_419.lproj/Localizable.strings; sourceTree = ""; }; + 2E4275BAD2E59FA193904FFC /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; + 31A6A624F6D9631C9BFECF9A /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + 36AEC349198E6BC061546705 /* AutocompleteWithSearchDisplayController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteWithSearchDisplayController.h; sourceTree = ""; }; + 36C753B6637575A8CD81C699 /* DemoListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoListViewController.m; sourceTree = ""; }; + 37CA5C28547C65246AC4EAE6 /* zh_HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_HK; path = zh_HK.lproj/Localizable.strings; sourceTree = ""; }; + 3A822A5CF223FF6ADFCA9458 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + 3CB3E42FBF23DD0411D5EFB5 /* PagingPhotoView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PagingPhotoView.h; sourceTree = ""; }; + 4268AFE07F8787F0CA36AA3B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + 42C8FE938EFBD5D6633C190F /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = ""; }; + 4F2203BCCE0CFC3DC4F38B30 /* DemoData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoData.m; sourceTree = ""; }; + 50E198C48B1A11E3B03DBE30 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = ""; }; + 55F92A67B1F0F18431DD759C /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = ""; }; + 6078563FD3AF8109B2586941 /* PhotosViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotosViewController.m; sourceTree = ""; }; + 611B5E7788AB0459C2AA1A54 /* DemoData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoData.h; sourceTree = ""; }; + 651835542B635C74A3D0F711 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; + 67BD5ABA8E74D336AE54C0B0 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + 69D781288A761B789E0F63B6 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; + 6EBC31158FA04B806E2549AF /* PhotosViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhotosViewController.h; sourceTree = ""; }; + 77688EB771E8262C6C224CE8 /* en_IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_IN; path = en_IN.lproj/Localizable.strings; sourceTree = ""; }; + 818B68D04D9AD33F4EBC54D2 /* MainSplitViewControllerBehaviorManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainSplitViewControllerBehaviorManager.m; sourceTree = ""; }; + 84E89BC0ADE5F983320B9730 /* AutocompletePushViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompletePushViewController.h; sourceTree = ""; }; + 85EDEFE42680FF093454D1AB /* PlacesDemoAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = PlacesDemoAssets.xcassets; sourceTree = ""; }; + 88DC6C46CAE8787FB54161AF /* AutocompleteBaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteBaseViewController.h; sourceTree = ""; }; + 8F7419DDC82CA074FF0A508C /* AutocompleteWithSearchViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteWithSearchViewController.m; sourceTree = ""; }; + 91F10567E79D600B5D3A7385 /* DemoListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoListViewController.h; sourceTree = ""; }; + 97ADBE3D50D731A88C24F3F0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 9B81B9326DD5B37C08B8CCAA /* DemoAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoAppDelegate.m; sourceTree = ""; }; + A00F12FEC785E9E287E42582 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + A06B8C341933D23425E2C178 /* AutocompleteModalViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteModalViewController.h; sourceTree = ""; }; + A3D4BD9841268F97FD23E770 /* BaseDemoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseDemoViewController.m; sourceTree = ""; }; + A8162D0DA3B592BBB6E3FA35 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + A8233D7A6FCDAD9F3E3FFEFC /* pt_PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt_PT; path = pt_PT.lproj/Localizable.strings; sourceTree = ""; }; + A997870E831544227236DF00 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + AADF2995CB717FE4D341C944 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; + ADB9E4A950DB2EA454486E47 /* AutocompleteWithTextFieldController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteWithTextFieldController.m; sourceTree = ""; }; + AF5862DFC2F44C360067E8C5 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + B346F058E854F6546CE4A113 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + B36F7F4AFB067A447A95D21C /* fr_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr_CA; path = fr_CA.lproj/Localizable.strings; sourceTree = ""; }; + B4ABFD22EF8C141BB930DA79 /* es_MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es_MX; path = es_MX.lproj/Localizable.strings; sourceTree = ""; }; + B61CFC263869B93126B2C502 /* MainSplitViewControllerBehaviorManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainSplitViewControllerBehaviorManager.h; sourceTree = ""; }; + B89AD97A10365521ED09C03F /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = ""; }; + BB25E63BA7525B4F9C996E94 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_CN; path = zh_CN.lproj/Localizable.strings; sourceTree = ""; }; + C4230A88E51B60A9F4C0CF9B /* BaseDemoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseDemoViewController.h; sourceTree = ""; }; + CBCF32F12AADFF34B1745273 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_TW; path = zh_TW.lproj/Localizable.strings; sourceTree = ""; }; + CE0FA365A96C1D97CE01B652 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; + CF1B1E3D7B5C131B7DF1C10C /* AutocompleteWithSearchViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteWithSearchViewController.h; sourceTree = ""; }; + D0E6E2A1285D9A0BEBDCD503 /* AutocompleteBaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteBaseViewController.m; sourceTree = ""; }; + D1C229B5A414955539EBE107 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + D5401C957612DEF87E02DC2C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + DAB6D5F45E10BD22F8FBB4BA /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + E008571503BEEFC5C2BF5D66 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + E0C040E0617744F5A55C8922 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; + E1502E6F1A86B1773138153B /* AutocompleteModalViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteModalViewController.m; sourceTree = ""; }; + E766ACF338BF4D9D9B3C86BA /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = ""; }; + E8EBD6572BDD22DD2C8C37F0 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = ""; }; + F2689EA2B2A4F7DC9912D5DE /* AutocompleteWithTextFieldController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutocompleteWithTextFieldController.h; sourceTree = ""; }; + F3A846055E79359BAF267E70 /* SDKDemoAPIKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDKDemoAPIKey.h; sourceTree = ""; }; + F7EFEB4946D6A093B9475CDD /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + F8ABF15D04AF351D0A21A5CD /* DemoAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoAppDelegate.h; sourceTree = ""; }; + FA2B20801D4CBBF7439CF693 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; + FB0BACDCF9A220122AFB87B5 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = ""; }; + FC03FEE150596E9432F612DF /* AutocompleteWithCustomColors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutocompleteWithCustomColors.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A91F0EDBE05C0121150AEE46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 42128D6A32D50325383D28DD /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 272C787B5065093A74F813D3 /* Source */ = { + isa = PBXGroup; + children = ( + E437630B6C8A8B3D1C7679DE /* GooglePlacesDemos */, + ); + name = Source; + sourceTree = ""; + }; + 385B9B480DD2892194F01AC1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 97ADBE3D50D731A88C24F3F0 /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5451EE1805656445BDA54845 /* Resources */ = { + isa = PBXGroup; + children = ( + F65D01DD8887E3B1B9731DAE /* LaunchScreen.storyboard */, + 1D344C64A66F10D55590797E /* Localizable.strings */, + 85EDEFE42680FF093454D1AB /* PlacesDemoAssets.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + 581A54906DCDED616577E5AE /* Autocomplete */ = { + isa = PBXGroup; + children = ( + 88DC6C46CAE8787FB54161AF /* AutocompleteBaseViewController.h */, + D0E6E2A1285D9A0BEBDCD503 /* AutocompleteBaseViewController.m */, + A06B8C341933D23425E2C178 /* AutocompleteModalViewController.h */, + E1502E6F1A86B1773138153B /* AutocompleteModalViewController.m */, + 84E89BC0ADE5F983320B9730 /* AutocompletePushViewController.h */, + 1BAEA89ED8C397E73E2E9730 /* AutocompletePushViewController.m */, + 13C513E93D85CC7B765F9CDB /* AutocompleteWithCustomColors.h */, + FC03FEE150596E9432F612DF /* AutocompleteWithCustomColors.m */, + 36AEC349198E6BC061546705 /* AutocompleteWithSearchDisplayController.h */, + 243C9279E0BF845B23337398 /* AutocompleteWithSearchDisplayController.m */, + CF1B1E3D7B5C131B7DF1C10C /* AutocompleteWithSearchViewController.h */, + 8F7419DDC82CA074FF0A508C /* AutocompleteWithSearchViewController.m */, + F2689EA2B2A4F7DC9912D5DE /* AutocompleteWithTextFieldController.h */, + ADB9E4A950DB2EA454486E47 /* AutocompleteWithTextFieldController.m */, + ); + path = Autocomplete; + sourceTree = ""; + }; + 5C46D1B4BA2F599C5C91F42C /* Samples */ = { + isa = PBXGroup; + children = ( + 581A54906DCDED616577E5AE /* Autocomplete */, + 3CB3E42FBF23DD0411D5EFB5 /* PagingPhotoView.h */, + 21153EB819432638638C2C26 /* PagingPhotoView.m */, + 6EBC31158FA04B806E2549AF /* PhotosViewController.h */, + 6078563FD3AF8109B2586941 /* PhotosViewController.m */, + ); + path = Samples; + sourceTree = ""; + }; + 7C4A8D45D6D1B907A8C0521E = { + isa = PBXGroup; + children = ( + 272C787B5065093A74F813D3 /* Source */, + 385B9B480DD2892194F01AC1 /* Frameworks */, + 87ED31F365F5BCA83FBDE8E8 /* Products */, + ); + sourceTree = ""; + }; + 87ED31F365F5BCA83FBDE8E8 /* Products */ = { + isa = PBXGroup; + children = ( + 16C539680ABA55CA7AFC3002 /* GooglePlacesDemos.app */, + ); + name = Products; + sourceTree = ""; + }; + 903D8B003C69F9D605250637 /* Support */ = { + isa = PBXGroup; + children = ( + C4230A88E51B60A9F4C0CF9B /* BaseDemoViewController.h */, + A3D4BD9841268F97FD23E770 /* BaseDemoViewController.m */, + B61CFC263869B93126B2C502 /* MainSplitViewControllerBehaviorManager.h */, + 818B68D04D9AD33F4EBC54D2 /* MainSplitViewControllerBehaviorManager.m */, + ); + path = Support; + sourceTree = ""; + }; + E437630B6C8A8B3D1C7679DE /* GooglePlacesDemos */ = { + isa = PBXGroup; + children = ( + 5451EE1805656445BDA54845 /* Resources */, + 5C46D1B4BA2F599C5C91F42C /* Samples */, + 903D8B003C69F9D605250637 /* Support */, + F8ABF15D04AF351D0A21A5CD /* DemoAppDelegate.h */, + 9B81B9326DD5B37C08B8CCAA /* DemoAppDelegate.m */, + 611B5E7788AB0459C2AA1A54 /* DemoData.h */, + 4F2203BCCE0CFC3DC4F38B30 /* DemoData.m */, + 91F10567E79D600B5D3A7385 /* DemoListViewController.h */, + 36C753B6637575A8CD81C699 /* DemoListViewController.m */, + F3A846055E79359BAF267E70 /* SDKDemoAPIKey.h */, + 1D0589812EE1F702A7BBA34D /* main.m */, + ); + path = GooglePlacesDemos; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 71EABCA8BFCB342F947AF4D2 /* GooglePlacesDemos */ = { + isa = PBXNativeTarget; + buildConfigurationList = E489F9A9DC7E458708AB572E /* Build configuration list for PBXNativeTarget "GooglePlacesDemos" */; + buildPhases = ( + 2A017491F6C5A9DBB4F6E6F6 /* Resources */, + 45EF04B6738E3486BE121AEC /* Sources */, + A91F0EDBE05C0121150AEE46 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GooglePlacesDemos; + productName = GooglePlacesDemos; + productReference = 16C539680ABA55CA7AFC3002 /* GooglePlacesDemos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B0EA45FFFB815F6F5A3E1BDD /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 0800; + }; + buildConfigurationList = 63964CA36C9733EFF27C66CD /* Build configuration list for PBXProject "GooglePlacesDemos" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 7C4A8D45D6D1B907A8C0521E; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 71EABCA8BFCB342F947AF4D2 /* GooglePlacesDemos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2A017491F6C5A9DBB4F6E6F6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C54FD416450638D80BD9A3C0 /* LaunchScreen.storyboard in Resources */, + 91123AFDEB15F27812A8FCFF /* Localizable.strings in Resources */, + 2A5A993302F4E6E15EA9BCD2 /* PlacesDemoAssets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 45EF04B6738E3486BE121AEC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2EC566C0AE2182DE0FEBA86B /* DemoAppDelegate.m in Sources */, + 66310EE76B375B01262F52A1 /* DemoData.m in Sources */, + E56159A1819452F650C1D995 /* DemoListViewController.m in Sources */, + D064B1789A04566EA32E30E1 /* main.m in Sources */, + 5A60C11EA9FF366E66BBFCE8 /* AutocompleteBaseViewController.m in Sources */, + 3D237EDDDDD9B9D7369D8487 /* AutocompleteModalViewController.m in Sources */, + 267E31192A188455AB41020F /* AutocompletePushViewController.m in Sources */, + 14412E07BEABD68FE1334FE4 /* AutocompleteWithCustomColors.m in Sources */, + 47C405AEA3807E2E1A4B5268 /* AutocompleteWithSearchDisplayController.m in Sources */, + 33CF1F7D8EA0E1114BDBE9FB /* AutocompleteWithSearchViewController.m in Sources */, + A63EB7AD0A24EABBF2030D93 /* AutocompleteWithTextFieldController.m in Sources */, + 6D5DBE8177ED77F3DDFA6C8E /* PagingPhotoView.m in Sources */, + E01ECF8484B4E41781E8B4A1 /* PhotosViewController.m in Sources */, + 5B89B9BA327B31EC08E64538 /* BaseDemoViewController.m in Sources */, + 15D96A2CC730B0DEF8195766 /* MainSplitViewControllerBehaviorManager.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 1D344C64A66F10D55590797E /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E0C040E0617744F5A55C8922 /* ar */, + FA2B20801D4CBBF7439CF693 /* ca */, + 31A6A624F6D9631C9BFECF9A /* cs */, + 69D781288A761B789E0F63B6 /* da */, + A8162D0DA3B592BBB6E3FA35 /* de */, + 273B1A3F7AA7EED9084B5033 /* el */, + 1FE971FB44A73F445279DA7F /* en */, + B89AD97A10365521ED09C03F /* en_AU */, + E766ACF338BF4D9D9B3C86BA /* en_GB */, + 77688EB771E8262C6C224CE8 /* en_IN */, + A997870E831544227236DF00 /* es */, + 2D52E39A30BD9D0AFC323F7F /* es_419 */, + B4ABFD22EF8C141BB930DA79 /* es_MX */, + D5401C957612DEF87E02DC2C /* fi */, + D1C229B5A414955539EBE107 /* fr */, + B36F7F4AFB067A447A95D21C /* fr_CA */, + 50E198C48B1A11E3B03DBE30 /* he */, + 42C8FE938EFBD5D6633C190F /* hi */, + FB0BACDCF9A220122AFB87B5 /* hr */, + DAB6D5F45E10BD22F8FBB4BA /* hu */, + 55F92A67B1F0F18431DD759C /* id */, + B346F058E854F6546CE4A113 /* it */, + 4268AFE07F8787F0CA36AA3B /* ja */, + E008571503BEEFC5C2BF5D66 /* ko */, + 1DB6FA3886B89186F55A12E3 /* ms */, + AADF2995CB717FE4D341C944 /* nb */, + 0D33295E02D9501C1D45D615 /* nl */, + AF5862DFC2F44C360067E8C5 /* pl */, + 2E4275BAD2E59FA193904FFC /* pt */, + 1A982A54052D846FC64B7AB5 /* pt_BR */, + A8233D7A6FCDAD9F3E3FFEFC /* pt_PT */, + E8EBD6572BDD22DD2C8C37F0 /* ro */, + 216CA36352B45FBC79C98B8F /* ru */, + CE0FA365A96C1D97CE01B652 /* sk */, + 1D767BA5698A891C36ADF830 /* sv */, + 3A822A5CF223FF6ADFCA9458 /* th */, + F7EFEB4946D6A093B9475CDD /* tr */, + 67BD5ABA8E74D336AE54C0B0 /* uk */, + 651835542B635C74A3D0F711 /* vi */, + BB25E63BA7525B4F9C996E94 /* zh_CN */, + 37CA5C28547C65246AC4EAE6 /* zh_HK */, + CBCF32F12AADFF34B1745273 /* zh_TW */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F65D01DD8887E3B1B9731DAE /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + A00F12FEC785E9E287E42582 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1BA056F14489621B09D65C69 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + INTERMEDIATE_DIR = "$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)"; + SDKROOT = iphoneos; + SHARED_INTERMEDIATE_DIR = "$(SYMROOT)/DerivedSources/$(CONFIGURATION)"; + }; + name = Default; + }; + C88A918BB441623AA489A34D /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = ./GooglePlacesDemos/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LIBRARY_SEARCH_PATHS = ( + ., + "$(SDKROOT)/System/Library/Frameworks", + ); + PRODUCT_NAME = GooglePlacesDemos; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)"; + USE_HEADERMAP = NO; + WRAPPER_PREFIX = ""; + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 63964CA36C9733EFF27C66CD /* Build configuration list for PBXProject "GooglePlacesDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1BA056F14489621B09D65C69 /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; + E489F9A9DC7E458708AB572E /* Build configuration list for PBXNativeTarget "GooglePlacesDemos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C88A918BB441623AA489A34D /* Default */, + ); + defaultConfigurationIsVisible = 1; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = B0EA45FFFB815F6F5A3E1BDD /* Project object */; +} diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.h new file mode 100644 index 0000000..f80c12b --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.h @@ -0,0 +1,22 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +@interface DemoAppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.m new file mode 100644 index 0000000..70457f9 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoAppDelegate.m @@ -0,0 +1,100 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/DemoAppDelegate.h" + +#import +#import + +#import "GooglePlacesDemos/DemoData.h" +#import "GooglePlacesDemos/DemoListViewController.h" +#import "GooglePlacesDemos/SDKDemoAPIKey.h" +#import "GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.h" + +@implementation DemoAppDelegate { + MainSplitViewControllerBehaviorManager *_splitViewManager; +} + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + NSLog(@"Build version: %d", __apple_build_version__); + + // Do a quick check to see if you've provided an API key, in a real app you wouldn't need this but + // for the demo it means we can provide a better error message. + if (![kAPIKey length]) { + // Blow up if APIKeys have not yet been set. + NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier]; + NSString *format = @"Configure APIKeys inside SDKDemoAPIKey.h for your bundle `%@`, see " + @"README.GooglePlacesDemos for more information"; + @throw [NSException exceptionWithName:@"DemoAppDelegate" + reason:[NSString stringWithFormat:format, bundleId] + userInfo:nil]; + } + + // Provide the Places API with your API key. + [GMSPlacesClient provideAPIKey:kAPIKey]; + // Provide the Maps API with your API key. You may not need this in your app, however we do need + // this for the demo app as it uses Maps. + [GMSServices provideAPIKey:kAPIKey]; + + // Log the required open source licenses! Yes, just NSLog-ing them is not enough but is good for + // a demo. + NSLog(@"Google Maps open source licenses:\n%@", [GMSServices openSourceLicenseInfo]); + NSLog(@"Google Places open source licenses:\n%@", [GMSPlacesClient openSourceLicenseInfo]); + + + // Manually create a window. If you are using a storyboard in your own app you can ignore the rest + // of this method. + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + + // Create our view controller with the list of demos. + DemoData *demoData = [[DemoData alloc] init]; + DemoListViewController *masterViewController = + [[DemoListViewController alloc] initWithDemoData:demoData]; + UINavigationController *masterNavigationController = + [[UINavigationController alloc] initWithRootViewController:masterViewController]; + + // If UISplitViewController is not available (only on iOS 7 running on an iPhone) we need to do + // something different with our UI. On iOS 8 and later UISplitViewController is available on iPad + // and iPhone so if you were using a UISplitViewController in your own app this check would not be + // needed. + if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_8_0 && + [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + masterNavigationController.viewControllers = @[ masterViewController ]; + self.window.rootViewController = masterNavigationController; + } else { + // UISplitViewController is available, use that. + + _splitViewManager = [[MainSplitViewControllerBehaviorManager alloc] init]; + + // Setup the split view controller. + UISplitViewController *splitViewController = [[UISplitViewController alloc] init]; + UIViewController *detailViewController = + [demoData.firstDemo createViewControllerForSplitView:splitViewController]; + splitViewController.delegate = _splitViewManager; + splitViewController.viewControllers = @[ masterNavigationController, detailViewController ]; + self.window.rootViewController = splitViewController; + } + + [self.window makeKeyAndVisible]; + + return YES; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.h new file mode 100644 index 0000000..fc49675 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.h @@ -0,0 +1,93 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +/* + * This file contains a set of data objects which represent the list of demos which are provided by + * this sample app. + */ + +/** + * Represents a specific demo sample, stores the title and the name of the view controller which + * contains the demo code. + */ +@interface Demo : NSObject + +/** + * The title of the demo. This is displayed in the list of demos. + */ +@property(nonatomic, readonly) NSString *title; + +/** + * Construct a |Demo| object with the specified view controller which contains the demo code. + * + * @param viewControllerClass The class of the view controller to display when the demo is selected + * from the list. + */ +- (instancetype)initWithViewControllerClass:(Class)viewControllerClass; + +/** + * Construct and return a new UIViewController instance which contains the view to present when the + * demo is selected from the list. + * + * @param splitViewController The |UISplitViewController| in which the demo will be presented. NOTE: + * This may be nil. + */ +- (UIViewController *)createViewControllerForSplitView:(UISplitViewController *)splitViewController; + +@end + +/** + * A group of demos which comprise a section in the list of demos. + */ +@interface DemoSection : NSObject + +/** + * The title of the section. + */ +@property(nonatomic, readonly) NSString *title; + +/** + * The list of demos which are contained in the section. + */ +@property(nonatomic, readonly) NSArray *demos; + +/** + * Initialise a |DemoSection| with the specified title and list of demos. + * + * @param title The title of the section. + * @param demos The demos contained in the section. + */ +- (instancetype)initWithTitle:(NSString *)title demos:(NSArray *)demos; + +@end + +/** + * A class which encapsulates the data required to create and display demos. + */ +@interface DemoData : NSObject + +/** + * A list of sections to display. + */ +@property(nonatomic, readonly) NSArray *sections; + +/** + * The first demo to display when launched in side-by-side mode. + */ +@property(nonatomic, readonly) Demo *firstDemo; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.m new file mode 100644 index 0000000..3e5b11d --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoData.m @@ -0,0 +1,109 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/DemoData.h" + +#import "GooglePlacesDemos/Support/BaseDemoViewController.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.h" +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.h" +#import "GooglePlacesDemos/Samples/PhotosViewController.h" + +@implementation Demo { + Class _viewControllerClass; +} + +- (instancetype)initWithViewControllerClass:(Class)viewControllerClass { + if ((self = [self init])) { + _title = [viewControllerClass demoTitle]; + _viewControllerClass = viewControllerClass; + } + return self; +} + +- (UIViewController *)createViewControllerForSplitView: + (UISplitViewController *)splitViewController { + // Construct the demo view controller. + UIViewController *demoViewController = [[_viewControllerClass alloc] init]; + // Configure its left bar button item to display the displayModeButtonItem provided by the + // splitViewController. + demoViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem; + demoViewController.navigationItem.leftItemsSupplementBackButton = YES; + + // Wrap the demo in a navigation controller. + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:demoViewController]; + + return navigationController; +} + +@end + +@implementation DemoSection + +- (instancetype)initWithTitle:(NSString *)title demos:(NSArray *)demos { + if ((self = [self init])) { + _title = [title copy]; + _demos = [demos copy]; + } + return self; +} + +@end + +@implementation DemoData + +- (instancetype)init { + if ((self = [super init])) { + NSArray *autocompleteDemos = @[ + [[Demo alloc] initWithViewControllerClass:[AutocompleteWithCustomColors class]], + [[Demo alloc] initWithViewControllerClass:[AutocompleteModalViewController class]], + [[Demo alloc] initWithViewControllerClass:[AutocompletePushViewController class]], + [[Demo alloc] initWithViewControllerClass:[AutocompleteWithSearchDisplayController class]], + [[Demo alloc] initWithViewControllerClass:[AutocompleteWithSearchViewController class]], + [[Demo alloc] initWithViewControllerClass:[AutocompleteWithTextFieldController class]], + ]; + + NSArray *otherDemos = @[ + [[Demo alloc] initWithViewControllerClass:[PhotosViewController class]], + ]; + + + _sections = @[ + [[DemoSection alloc] + initWithTitle:NSLocalizedString(@"Demo.Section.Title.Autocomplete", + @"Title of the autocomplete demo section") + demos:autocompleteDemos], + [[DemoSection alloc] + initWithTitle:NSLocalizedString(@"Demo.Section.Title.Programmatic", + @"Title of the 'Programmatic' demo section") + demos:otherDemos], + ]; + } + return self; +} + +- (Demo *)firstDemo { + return _sections[0].demos[0]; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.h new file mode 100644 index 0000000..6c1d776 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +#import "GooglePlacesDemos/DemoData.h" + +/** + * The class which displays the list of demos. + */ +@interface DemoListViewController : UITableViewController + +/** + * Construct a new list controller using the provided demo data. + * + * @param demoData The demo data to display in the list. + */ +- (instancetype)initWithDemoData:(DemoData *)demoData; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.m new file mode 100644 index 0000000..98cad57 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/DemoListViewController.m @@ -0,0 +1,141 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/DemoListViewController.h" + +#import + +// The cell reuse identifier we are going to use. +static NSString *const kCellIdentifier = @"DemoCellIdentifier"; + +@implementation DemoListViewController { + DemoData *_demoData; +} + +- (instancetype)initWithDemoData:(DemoData *)demoData { + if ((self = [self init])) { + _demoData = demoData; + NSString *titleFormat = + NSLocalizedString(@"App.NameAndVersion", + @"The name of the app to display in a navigation bar along with a " + @"placeholder for the SDK version number"); + self.title = [NSString stringWithFormat:titleFormat, [GMSPlacesClient SDKVersion]]; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Register a plain old UITableViewCell as this will be sufficient for our list. + [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier]; +} + +/** + * Private method which is called when a demo is selected. Constructs the demo view controller and + * displays it. + * + * @param demo The demo to show. + */ +- (void)showDemo:(Demo *)demo { + // Ask the demo to give us the view controller which contains the demo. + UIViewController *viewController = + [demo createViewControllerForSplitView:self.splitViewController]; + + // Check to see if we are on iOS 7. + if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_8_0) { + // We are on iOS 8+, just call the real -showDetailViewControllerMethod. UIKit will + // automatically either push onto the navigation stack on a small screen, or present it on the + // left side of the |UISplitViewController| if there is enough space. + [self showDetailViewController:viewController sender:self]; + } else { + [self iOS7showDetailViewController:viewController sender:self]; + } +} + +#pragma mark - UITableViewDataSource/Delegate + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return _demoData.sections.count; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return _demoData.sections[section].demos.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + // Dequeue a table view cell to use. + UITableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath]; + + // Grab the demo object. + Demo *demo = _demoData.sections[indexPath.section].demos[indexPath.row]; + + // Configure the demo title on the cell. + cell.textLabel.text = demo.title; + + return cell; +} + +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + return _demoData.sections[section].title; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + // Get the demo which was selected. + Demo *demo = _demoData.sections[indexPath.section].demos[indexPath.row]; + + [self showDemo:demo]; +} + +#pragma mark - iOS 7 Support + +// +// NOTE! All the following code is probably not required in your own app, it is just to enable iOS 7 +// support of this demo app. +// + +- (void)iOS7showDetailViewController:(UIViewController *)vc sender:(id)sender { + // We are on iOS 7, so we are going to have to do some stuff to mimic the behavior that + // -showDetailViewControllerMethod has on iOS 8+. + + // Check to see if we have a split view controller. + if (self.splitViewController) { + // If we do then update its .viewControllers property, -showDetailViewController:sender: + // didn't exist in iOS 7. + self.splitViewController.viewControllers = @[ self.splitViewController.viewControllers[0], vc ]; + } else { + // If there is not a split view controller then we must be on an iPhone, which means we should + // just use the navigation stack. However, the view controller we are given might be a + // UINavigationController, in which case we should only push the thing(s) *in* the navigation + // controller, rather than the navigation controller itself as UIKit does not allow this. + NSMutableArray *viewControllers = + [self.navigationController.viewControllers mutableCopy]; + if ([vc isKindOfClass:[UINavigationController class]]) { + UINavigationController *navigationController = (UINavigationController *)vc; + [viewControllers addObjectsFromArray:navigationController.viewControllers]; + } else { + [viewControllers addObject:vc]; + } + [self.navigationController setViewControllers:viewControllers animated:YES]; + } +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Info.plist b/Pods/GooglePlaces/Example/GooglePlacesDemos/Info.plist new file mode 100644 index 0000000..5bb4912 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Info.plist @@ -0,0 +1,53 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleIcons~ipad + + CFBundleIdentifier + com.example.GooglePlacesDemos + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSApplicationCategoryType + + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + Show your location on a map + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationLandscapeLeft + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/Base.lproj/LaunchScreen.storyboard b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..1eb368c --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Contents.json b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..50d8477 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,78 @@ +{ + "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" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Places-API-Demo-App_120.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Places-API-Demo-App_180.png", + "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" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Places-API-Demo-App_76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Places-API-Demo-App_152.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Places-API-Demo-App_167.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_120.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_120.png new file mode 100644 index 0000000..79d7dc9 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_120.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_152.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_152.png new file mode 100644 index 0000000..0151e0d Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_152.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_167.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_167.png new file mode 100644 index 0000000..cee6f35 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_167.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_180.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_180.png new file mode 100644 index 0000000..969960e Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_180.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_76.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_76.png new file mode 100644 index 0000000..94600cd Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/AppIcon.appiconset/Places-API-Demo-App_76.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/Contents.json b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/Contents.json b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..7bb8752 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,57 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "LaunchImage-iPhonePortrait2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "LaunchImage-iPhonePortraitRetina4.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadPortrait1x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadLandscape1x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadPortrait2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "filename" : "LaunchImage-iPadLandscape2x.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png new file mode 100644 index 0000000..ec46a4d Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape1x.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png new file mode 100644 index 0000000..acb9205 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadLandscape2x.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png new file mode 100644 index 0000000..a8d3f8d Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait1x.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png new file mode 100644 index 0000000..1512d10 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPadPortrait2x.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png new file mode 100644 index 0000000..81aa4c3 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortrait2x.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png new file mode 100644 index 0000000..c841827 Binary files /dev/null and b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/PlacesDemoAssets.xcassets/LaunchImage.launchimage/LaunchImage-iPhonePortraitRetina4.png differ diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ar.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ar.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ar.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ca.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ca.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ca.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/cs.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/cs.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/cs.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/da.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/da.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/da.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/de.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/de.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/de.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/el.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/el.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/el.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_AU.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_AU.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_AU.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_GB.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_GB.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_GB.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_IN.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_IN.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/en_IN.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_419.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_419.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_419.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_MX.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_MX.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/es_MX.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fi.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fi.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fi.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr_CA.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr_CA.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/fr_CA.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/he.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/he.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/he.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hi.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hi.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hi.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hr.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hr.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hr.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hu.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hu.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/hu.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/id.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/id.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/id.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/it.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/it.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/it.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ja.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ja.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ja.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ko.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ko.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ko.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ms.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ms.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ms.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nb.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nb.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nb.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nl.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nl.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/nl.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pl.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pl.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pl.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_BR.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_BR.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_BR.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_PT.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_PT.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/pt_PT.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ro.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ro.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ro.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ru.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ru.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/ru.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sk.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sk.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sk.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sv.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sv.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/sv.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/th.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/th.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/th.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/tr.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/tr.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/tr.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/uk.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/uk.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/uk.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/vi.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/vi.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/vi.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_CN.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_CN.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_CN.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_HK.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_HK.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_HK.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_TW.lproj/Localizable.strings b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_TW.lproj/Localizable.strings new file mode 100644 index 0000000..be3d849 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Resources/zh_TW.lproj/Localizable.strings @@ -0,0 +1,47 @@ +// NOTE: This is the english localization and has been copied to all languages. The reason for doing +// this is it signals to iOS that the app "supports" all of these languages. This is helpful for a +// demo as it allows you to try out the localization features of the Places API. In a shipping app +// you should never do this, it is for demonstration purposes only. + + +// The name of the app to display in a navigation bar along with a placeholder for the SDK version number. +"App.NameAndVersion"="Places Demos: %1$@"; + +// Title of the full-screen autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.FullScreen"="Full-Screen Autocomplete"; +// Title of the pushed autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Push"="Push Autocomplete"; +// Title of the UISearchController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchController"="UISearchController"; +// Title of the UISearchDisplayController autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UISearchDisplayController"="UISearchDisplayController"; +// Title of the UITextField autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.UITextField"="UITextField"; +// Title of the Styling autocomplete demo for display in a list or nav header +"Demo.Title.Autocomplete.Styling"="Custom Autocomplete Styling"; +// Title of the photos demo for display in a list or nav header +"Demo.Title.Photos"="Photos"; +// Title of the autocomplete demo section +"Demo.Section.Title.Autocomplete"="Autocomplete"; +// Title of the 'Programmatic' demo section +"Demo.Section.Title.Programmatic"="Programmatic APIs"; + +// Button title for 'show autocomplete widget' +"Demo.Content.Autocomplete.ShowWidgetButton"="Show Autocomplete Widget"; +// Prompt to enter text for autocomplete demo +"Demo.Content.Autocomplete.EnterTextPrompt"="Enter Autocomplete Text Here"; +// Format string for 'autocomplete failed with error' message +"Demo.Content.Autocomplete.FailedErrorMessage"="Autocomplete failed with error: %1$@"; +// String for 'autocomplete canceled message' +"Demo.Content.Autocomplete.WasCanceledMessage"="Autocomplete was canceled"; +// Error message to display when a demo is not available on the current version of iOS +"Demo.Content.iOSVersionNotSupported"="UISearchController not supported on this version of iOS"; +// Button title for the 'Yellow and Brown' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown"="Yellow and Brown"; +// Button title for the 'White on Black' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack"="White on Black"; +// Button title for the 'Blue Colors' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.BlueColors"="Blue Colors"; +// Button title for the 'Hot Dog Stand' styled autocomplete widget. +"Demo.Content.Autocomplete.Styling.Colors.HotDogStand"="Hot Dog Stand"; + diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/SDKDemoAPIKey.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/SDKDemoAPIKey.h new file mode 100644 index 0000000..bd8f211 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/SDKDemoAPIKey.h @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * To use GooglePlacesDemos, please register an API Key for your application and set it here. Your + * API Key should be kept private. + * + * See documentation on getting an API Key for your API Project here: + * https://developers.google.com/places/ios-api/start#get-key + */ + +#error Register your API key and insert here, then delete this line. +static NSString *const kAPIKey = @""; diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h new file mode 100644 index 0000000..bbf43eb --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Support/BaseDemoViewController.h" + +#import + +/** + * All other autocomplete demo classes inherit from this class. This class optionally adds a button + * to present the autocomplete widget, and displays the results when these are selected. + */ +@interface AutocompleteBaseViewController : BaseDemoViewController + +/** + * Build a UIButton to display the autocomplete widget and add it to the UI. This should be called + * only if the demo requires such a button, e.g. demos for modal presentation of widgets would use + * this, while a UITextField demo would not. + * + * @param selector The selector to send to self when the button is tapped. + * + * @return The UIButton which was added to the UI. + */ +- (UIButton *)createShowAutocompleteButton:(SEL)selector; + +/** + * Add the result display view below the specified view. This should be called after the rest of the + * UI has been configured. + * + * @param view The view to add the results below, this may be nil to indicate that the result view + * should fill the parent. + */ +- (void)addResultViewBelow:(UIView *)view; + +- (void)autocompleteDidSelectPlace:(GMSPlace *)place; +- (void)autocompleteDidFail:(NSError *)error; +- (void)autocompleteDidCancel; +- (void)showCustomMessageInResultPane:(NSString *)message; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.m new file mode 100644 index 0000000..b16ed80 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.m @@ -0,0 +1,122 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +@implementation AutocompleteBaseViewController { + UITextView *_textView; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Configure a background color. + self.view.backgroundColor = [UIColor whiteColor]; + + // Create a text view. + _textView = [[UITextView alloc] init]; + _textView.editable = NO; + _textView.translatesAutoresizingMaskIntoConstraints = NO; +} + +- (UIButton *)createShowAutocompleteButton:(SEL)selector { + // Create a button to show the autocomplete widget. + UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; + [button setTitle:NSLocalizedString(@"Demo.Content.Autocomplete.ShowWidgetButton", + @"Button title for 'show autocomplete widget'") + forState:UIControlStateNormal]; + [button addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside]; + button.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:button]; + // Position the button from the top of the view. + [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1 + constant:8]]; + // Centre it horizontally. + [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeCenterX + multiplier:1 + constant:0]]; + + return button; +} + +- (void)addResultViewBelow:(UIView *)view { + NSAssert(_textView.superview == nil, @"%s should not be called twice", sel_getName(_cmd)); + [self.view addSubview:_textView]; + // Position it horizontally so it fills the parent. + [self.view + addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-(0)-[_textView]-(0)-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_textView)]]; + // If we have a view place it below that. + if (view) { + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[view]-[_textView]-(0)-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings( + view, _textView)]]; + } else { + // Otherwise make it fill the parent vertically. + [self.view + addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|-(0)-[_textView]-(0)-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_textView)]]; + } +} + +- (void)autocompleteDidSelectPlace:(GMSPlace *)place { + NSMutableAttributedString *text = + [[NSMutableAttributedString alloc] initWithString:[place description]]; + if (place.attributions) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n"]]; + [text appendAttributedString:place.attributions]; + } + _textView.attributedText = text; +} + +- (void)autocompleteDidFail:(NSError *)error { + NSString *formatString = + NSLocalizedString(@"Demo.Content.Autocomplete.FailedErrorMessage", + @"Format string for 'autocomplete failed with error' message"); + _textView.text = [NSString stringWithFormat:formatString, error]; +} + +- (void)autocompleteDidCancel { + _textView.text = NSLocalizedString(@"Demo.Content.Autocomplete.WasCanceledMessage", + @"String for 'autocomplete canceled message'"); +} + +- (void)showCustomMessageInResultPane:(NSString *)message { + _textView.text = message; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.h new file mode 100644 index 0000000..dceb663 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/** + * Demo showing a modally presented Autocomplete view controller. + */ +@interface AutocompleteModalViewController : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.m new file mode 100644 index 0000000..463c919 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.m @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteModalViewController.h" + +#import + +@interface AutocompleteModalViewController () +@end + +@implementation AutocompleteModalViewController + ++ (NSString *)demoTitle { + return NSLocalizedString( + @"Demo.Title.Autocomplete.FullScreen", + @"Title of the full-screen autocomplete demo for display in a list or nav header"); +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Configure the UI. Tell our superclass we want a button and a result view below that. + UIButton *button = + [self createShowAutocompleteButton:@selector(showAutocompleteWidgetButtonTapped)]; + [self addResultViewBelow:button]; +} + +#pragma mark - Actions + +- (IBAction)showAutocompleteWidgetButtonTapped { + // When the button is pressed modally present the autocomplete view controller. + GMSAutocompleteViewController *autocompleteViewController = + [[GMSAutocompleteViewController alloc] init]; + autocompleteViewController.delegate = self; + [self presentViewController:autocompleteViewController animated:YES completion:nil]; +} + +#pragma mark - GMSAutocompleteViewControllerDelegate + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didAutocompleteWithPlace:(GMSPlace *)place { + // Dismiss the view controller and tell our superclass to populate the result view. + [viewController dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidSelectPlace:place]; +} + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didFailAutocompleteWithError:(NSError *)error { + // Dismiss the view controller and notify our superclass of the failure. + [viewController dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidFail:error]; +} + +- (void)wasCancelled:(GMSAutocompleteViewController *)viewController { + // Dismiss the controller and show a message that it was canceled. + [viewController dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidCancel]; +} + +- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; +} + +- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.h new file mode 100644 index 0000000..64d87e1 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/** + * Demo showing a Autocomplete view controller pushed on the navigation stack. + */ +@interface AutocompletePushViewController : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.m new file mode 100644 index 0000000..6532f7c --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.m @@ -0,0 +1,94 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompletePushViewController.h" + +#import + +@interface AutocompletePushViewController () +@property(nonatomic, strong) GMSAutocompleteViewController *autocompleteViewController; +@end + +@implementation AutocompletePushViewController + ++ (NSString *)demoTitle { + return NSLocalizedString( + @"Demo.Title.Autocomplete.Push", + @"Title of the pushed autocomplete demo for display in a list or nav header"); +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Configure the UI. Tell our superclass we want a button and a result view below that. + UIButton *button = + [self createShowAutocompleteButton:@selector(showAutocompleteWidgetButtonTapped)]; + [self addResultViewBelow:button]; +} + +#pragma mark - Getters/Setters + +- (GMSAutocompleteViewController *)autocompleteViewController { + if (_autocompleteViewController == nil) { + _autocompleteViewController = [[GMSAutocompleteViewController alloc] init]; + _autocompleteViewController.delegate = self; + } + return _autocompleteViewController; +} + +#pragma mark - Actions + +- (IBAction)showAutocompleteWidgetButtonTapped { + // When the button is tapped just push the autocomplete view controller onto the stack. + [self.navigationController pushViewController:self.autocompleteViewController animated:YES]; +} + +#pragma mark - GMSAutocompleteViewControllerDelegate + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didAutocompleteWithPlace:(GMSPlace *)place { + // Dismiss the view controller and tell our superclass to populate the result view. + [self.navigationController popToViewController:self animated:YES]; + [self autocompleteDidSelectPlace:place]; +} + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didFailAutocompleteWithError:(NSError *)error { + // Dismiss the view controller and notify our superclass of the failure. + [self.navigationController popToViewController:self animated:YES]; + [self autocompleteDidFail:error]; +} + +- (void)wasCancelled:(GMSAutocompleteViewController *)viewController { + // Dismiss the controller and show a message that it was canceled. + [self.navigationController popToViewController:self animated:YES]; + [self autocompleteDidCancel]; +} + +- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; +} + +- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.h new file mode 100644 index 0000000..4f13d5f --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/* + * SDK Demo showing how to customise colors in the full-screen Autocomplete Widget. + */ +@interface AutocompleteWithCustomColors : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.m new file mode 100644 index 0000000..d621f91 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.m @@ -0,0 +1,298 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithCustomColors.h" + +#import + +/** Height of buttons in this controller's UI */ +static const CGFloat kButtonHeight = 44.0f; + +/** + * Simple subclass of GMSAutocompleteViewController solely for the purpose of localising appearance + * proxy changes to this part of the demo app. + */ +@interface GMSStyledAutocompleteViewController : GMSAutocompleteViewController +@end + +@implementation GMSStyledAutocompleteViewController +@end + +@interface AutocompleteWithCustomColors () +@end + +@implementation AutocompleteWithCustomColors { + UIButton *_brownThemeButton; + UIButton *_blackThemeButton; + UIButton *_blueThemeButton; + UIButton *_hotDogThemeButton; + + UIColor *_backgroundColor; + UIColor *_darkBackgroundColor; + UIColor *_primaryTextColor; + UIColor *_highlightColor; + UIColor *_secondaryColor; + UIColor *_separatorColor; + UIColor *_tintColor; + UIColor *_searchBarTintColor; + UIColor *_selectedTableCellBackgroundColor; +} + ++ (NSString *)demoTitle { + return NSLocalizedString( + @"Demo.Title.Autocomplete.Styling", + @"Title of the Styling autocomplete demo for display in a list or nav header"); +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + NSString *titleYellowAndBrown = + NSLocalizedString(@"Demo.Content.Autocomplete.Styling.Colors.YellowAndBrown", + @"Button title for the 'Yellow and Brown' styled autocomplete widget."); + NSString *titleWhiteOnBlack = + NSLocalizedString(@"Demo.Content.Autocomplete.Styling.Colors.WhiteOnBlack", + @"Button title for the 'WhiteOnBlack' styled autocomplete widget."); + NSString *titleBlueColors = + NSLocalizedString(@"Demo.Content.Autocomplete.Styling.Colors.BlueColors", + @"Button title for the 'BlueColors' styled autocomplete widget."); + NSString *titleHotDogStand = + NSLocalizedString(@"Demo.Content.Autocomplete.Styling.Colors.HotDogStand", + @"Button title for the 'Hot Dog Stand' styled autocomplete widget."); + + CGFloat nextControlY = 70.0f; + _brownThemeButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_brownThemeButton setTitle:titleYellowAndBrown forState:UIControlStateNormal]; + _brownThemeButton.frame = CGRectMake(0, nextControlY, self.view.bounds.size.width, kButtonHeight); + _brownThemeButton.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_brownThemeButton addTarget:self + action:@selector(didTapButton:) + forControlEvents:UIControlEventTouchUpInside]; + nextControlY += kButtonHeight; + + _blackThemeButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_blackThemeButton setTitle:titleWhiteOnBlack forState:UIControlStateNormal]; + _blackThemeButton.frame = CGRectMake(0, nextControlY, self.view.bounds.size.width, kButtonHeight); + _blackThemeButton.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_blackThemeButton addTarget:self + action:@selector(didTapButton:) + forControlEvents:UIControlEventTouchUpInside]; + nextControlY += kButtonHeight; + + _blueThemeButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_blueThemeButton setTitle:titleBlueColors forState:UIControlStateNormal]; + _blueThemeButton.frame = CGRectMake(0, nextControlY, self.view.bounds.size.width, kButtonHeight); + _blueThemeButton.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_blueThemeButton addTarget:self + action:@selector(didTapButton:) + forControlEvents:UIControlEventTouchUpInside]; + nextControlY += kButtonHeight; + + _hotDogThemeButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [_hotDogThemeButton setTitle:titleHotDogStand forState:UIControlStateNormal]; + _hotDogThemeButton.frame = + CGRectMake(0, nextControlY, self.view.bounds.size.width, kButtonHeight); + _hotDogThemeButton.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_hotDogThemeButton addTarget:self + action:@selector(didTapButton:) + forControlEvents:UIControlEventTouchUpInside]; + + [self.view addSubview:_brownThemeButton]; + [self.view addSubview:_blackThemeButton]; + [self.view addSubview:_blueThemeButton]; + [self.view addSubview:_hotDogThemeButton]; + + [self addResultViewBelow:_hotDogThemeButton]; + + self.definesPresentationContext = YES; +} + +- (void)didTapButton:(UIButton *)button { + if (button == _brownThemeButton) { + _backgroundColor = [UIColor colorWithRed:215.0f / 255.0f + green:204.0f / 255.0f + blue:200.0f / 255.0f + alpha:1.0f]; + _selectedTableCellBackgroundColor = [UIColor colorWithRed:236.0f / 255.0f + green:225.0f / 255.0f + blue:220.0f / 255.0f + alpha:1.0f]; + _darkBackgroundColor = + [UIColor colorWithRed:93.0f / 255.0f green:64.0f / 255.0f blue:55.0f / 255.0f alpha:1.0f]; + _primaryTextColor = [UIColor colorWithWhite:0.33f alpha:1.0f]; + + _highlightColor = + [UIColor colorWithRed:255.0f / 255.0f green:235.0f / 255.0f blue:0.0f / 255.0f alpha:1.0f]; + _secondaryColor = [UIColor colorWithWhite:114.0f / 255.0f alpha:1.0f]; + _tintColor = [UIColor colorWithRed:219 / 255.0f green:207 / 255.0f blue:28 / 255.0f alpha:1.0f]; + _searchBarTintColor = [UIColor yellowColor]; + _separatorColor = [UIColor colorWithWhite:182.0f / 255.0f alpha:1.0f]; + } else if (button == _blueThemeButton) { + _backgroundColor = [UIColor colorWithRed:225.0f / 255.0f + green:241.0f / 255.0f + blue:252.0f / 255.0f + alpha:1.0f]; + _selectedTableCellBackgroundColor = [UIColor colorWithRed:213.0f / 255.0f + green:219.0f / 255.0f + blue:230.0f / 255.0f + alpha:1.0f]; + _darkBackgroundColor = [UIColor colorWithRed:187.0f / 255.0f + green:222.0f / 255.0f + blue:248.0f / 255.0f + alpha:1.0f]; + _primaryTextColor = [UIColor colorWithWhite:0.5f alpha:1.0f]; + _highlightColor = + [UIColor colorWithRed:76.0f / 255.0f green:175.0f / 255.0f blue:248.0f / 255.0f alpha:1.0f]; + _secondaryColor = [UIColor colorWithWhite:0.5f alpha:0.65f]; + _tintColor = + [UIColor colorWithRed:0 / 255.0f green:142 / 255.0f blue:248.0f / 255.0f alpha:1.0f]; + _searchBarTintColor = _tintColor; + _separatorColor = [UIColor colorWithWhite:0.5f alpha:0.65f]; + } else if (button == _blackThemeButton) { + _backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.0f]; + _selectedTableCellBackgroundColor = [UIColor colorWithWhite:0.35f alpha:1.0f]; + _darkBackgroundColor = [UIColor colorWithWhite:0.2f alpha:1.0f]; + _primaryTextColor = [UIColor whiteColor]; + _highlightColor = [UIColor colorWithRed:0.75f green:1.0f blue:0.75f alpha:1.0f]; + _secondaryColor = [UIColor colorWithWhite:1.0f alpha:0.5f]; + _tintColor = [UIColor whiteColor]; + _searchBarTintColor = _tintColor; + _separatorColor = [UIColor colorWithRed:0.5f green:0.75f blue:0.5f alpha:0.30f]; + } else if (button == _hotDogThemeButton) { + _backgroundColor = [UIColor yellowColor]; + _selectedTableCellBackgroundColor = [UIColor whiteColor]; + _darkBackgroundColor = [UIColor redColor]; + _primaryTextColor = [UIColor blackColor]; + _highlightColor = [UIColor redColor]; + _secondaryColor = [UIColor colorWithWhite:0.0f alpha:0.6f]; + _tintColor = [UIColor redColor]; + _searchBarTintColor = [UIColor whiteColor]; + _separatorColor = [UIColor redColor]; + } + [self presentAutocompleteController]; +} + +- (void)presentAutocompleteController { + // Use UIAppearance proxies to change the appearance of UI controls in + // GMSAutocompleteViewController. Here we use appearanceWhenContainedIn to localise changes to + // just this part of the Demo app. This will generally not be necessary in a real application as + // you will probably want the same theme to apply to all elements in your app. + UIActivityIndicatorView *appearence = [UIActivityIndicatorView + appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil]; + [appearence setColor:_primaryTextColor]; + + [[UINavigationBar appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil] + setBarTintColor:_darkBackgroundColor]; + [[UINavigationBar appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil] + setTintColor:_searchBarTintColor]; + + // Color of typed text in search bar. + NSDictionary *searchBarTextAttributes = @{ + NSForegroundColorAttributeName : _searchBarTintColor, + NSFontAttributeName : [UIFont systemFontOfSize:[UIFont systemFontSize]] + }; + [[UITextField appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil] + setDefaultTextAttributes:searchBarTextAttributes]; + + // Color of the "Search" placeholder text in search bar. For this example, we'll make it the same + // as the bar tint color but with added transparency. + CGFloat increasedAlpha = CGColorGetAlpha(_searchBarTintColor.CGColor) * 0.75f; + UIColor *placeHolderColor = [_searchBarTintColor colorWithAlphaComponent:increasedAlpha]; + + NSDictionary *placeholderAttributes = @{ + NSForegroundColorAttributeName : placeHolderColor, + NSFontAttributeName : [UIFont systemFontOfSize:[UIFont systemFontSize]] + }; + NSAttributedString *attributedPlaceholder = + [[NSAttributedString alloc] initWithString:@"Search" attributes:placeholderAttributes]; + + [[UITextField appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil] + setAttributedPlaceholder:attributedPlaceholder]; + + // Change the background color of selected table cells. + UIView *selectedBackgroundView = [[UIView alloc] init]; + selectedBackgroundView.backgroundColor = _selectedTableCellBackgroundColor; + id tableCellAppearance = + [UITableViewCell appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil]; + [tableCellAppearance setSelectedBackgroundView:selectedBackgroundView]; + + // Depending on the navigation bar background color, it might also be necessary to customise the + // icons displayed in the search bar to something other than the default. The + // setupSearchBarCustomIcons method contains example code to do this. + + GMSAutocompleteViewController *acController = [[GMSStyledAutocompleteViewController alloc] init]; + acController.delegate = self; + acController.tableCellBackgroundColor = _backgroundColor; + acController.tableCellSeparatorColor = _separatorColor; + acController.primaryTextColor = _primaryTextColor; + acController.primaryTextHighlightColor = _highlightColor; + acController.secondaryTextColor = _secondaryColor; + acController.tintColor = _tintColor; + + [self presentViewController:acController animated:YES completion:nil]; +} + +/* + * This method shows how to replace the "search" and "clear text" icons in the search bar with + * custom icons in the case where the default gray icons don't match a custom background. + */ +- (void)setupSearchBarCustomIcons { + id searchBarAppearanceProxy = + [UISearchBar appearanceWhenContainedIn:[GMSStyledAutocompleteViewController class], nil]; + [searchBarAppearanceProxy setImage:[UIImage imageNamed:@"custom_clear_x_high"] + forSearchBarIcon:UISearchBarIconClear + state:UIControlStateHighlighted]; + [searchBarAppearanceProxy setImage:[UIImage imageNamed:@"custom_clear_x"] + forSearchBarIcon:UISearchBarIconClear + state:UIControlStateNormal]; + [searchBarAppearanceProxy setImage:[UIImage imageNamed:@"custom_search"] + forSearchBarIcon:UISearchBarIconSearch + state:UIControlStateNormal]; +} + +#pragma mark - GMSAutocompleteViewControllerDelegate + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didAutocompleteWithPlace:(GMSPlace *)place { + [self dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidSelectPlace:place]; +} + +- (void)viewController:(GMSAutocompleteViewController *)viewController + didFailAutocompleteWithError:(NSError *)error { + [self dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidFail:error]; +} + +- (void)wasCancelled:(GMSAutocompleteViewController *)viewController { + [self dismissViewControllerAnimated:YES completion:nil]; + [self autocompleteDidCancel]; +} + +- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; +} + +- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.h new file mode 100644 index 0000000..335ed41 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/* + * A demo showing how to use GMSAutocompleteViewController with a UISearchDisplayController. + */ +@interface AutocompleteWithSearchDisplayController : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.m new file mode 100644 index 0000000..01a982d --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.m @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchDisplayController.h" + +#import + +/* The default height of a UISearchBar. */ +static CGFloat kSearchBarHeight = 44.0f; + +@interface AutocompleteWithSearchDisplayController () +@end + +@implementation AutocompleteWithSearchDisplayController { + UISearchDisplayController *_searchDisplayController; + GMSAutocompleteTableDataSource *_tableDataSource; +} + ++ (NSString *)demoTitle { + return NSLocalizedString(@"Demo.Title.Autocomplete.UISearchDisplayController", + @"Title of the UISearchDisplayController autocomplete demo for display " + @"in a list or nav header"); +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Don't overlay the status bar with the search bar when active. + self.edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight; + + // Create and setup the search bar. + UISearchBar *searchBar = [[UISearchBar alloc] + initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, kSearchBarHeight)]; + searchBar.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; + + // Construct the autocomplete table datasource. + _tableDataSource = [[GMSAutocompleteTableDataSource alloc] init]; + _tableDataSource.delegate = self; + + // Configure a UISearchDisplayController. + _searchDisplayController = + [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self]; + _searchDisplayController.searchResultsDataSource = _tableDataSource; + _searchDisplayController.searchResultsDelegate = _tableDataSource; + _searchDisplayController.delegate = self; + _searchDisplayController.displaysSearchBarInNavigationBar = NO; + + // Add the search bar and tell our superclass to add the result panel below it. + [self.view addSubview:searchBar]; + [self addResultViewBelow:searchBar]; +} + +#pragma mark - UISearchDisplayDelegate + +- (BOOL)searchDisplayController:(UISearchDisplayController *)controller + shouldReloadTableForSearchString:(NSString *)searchString { + // Notify the autocomplete table data source of the change in text. + [_tableDataSource sourceTextHasChanged:searchString]; + return NO; +} + +- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller { + [_tableDataSource sourceTextHasChanged:@""]; +} + +#pragma mark - GMSAutocompleteTableDataSourceDelegate + +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didAutocompleteWithPlace:(GMSPlace *)place { + // Tell our superclass to show the results, and dismiss the search controller. + [self autocompleteDidSelectPlace:place]; + [_searchDisplayController setActive:NO animated:YES]; +} + +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didFailAutocompleteWithError:(NSError *)error { + // Tell our superclass to show the error, and dismiss the search controller. + [self autocompleteDidFail:error]; + [_searchDisplayController setActive:NO animated:YES]; +} + +- (void)didRequestAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource { + // Start the network activity indicator and reload the table of results. + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; + [_searchDisplayController.searchResultsTableView reloadData]; +} + +- (void)didUpdateAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource { + // Stop the network activity indicator and reload the table of results. + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; + [_searchDisplayController.searchResultsTableView reloadData]; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.h new file mode 100644 index 0000000..8e7253c --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/** + * Demo showing the use of GMSAutocompleteViewController with a UISearchController. + * NOTE: iOS 8+ required to run. + */ +@interface AutocompleteWithSearchViewController : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.m new file mode 100644 index 0000000..0196df3 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.m @@ -0,0 +1,108 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithSearchViewController.h" + +#import + +@interface AutocompleteWithSearchViewController () +@end + +@implementation AutocompleteWithSearchViewController { + UISearchController *_searchController; + GMSAutocompleteResultsViewController *_acViewController; +} + ++ (NSString *)demoTitle { + return NSLocalizedString( + @"Demo.Title.Autocomplete.UISearchController", + @"Title of the UISearchController autocomplete demo for display in a list or nav header"); +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + _acViewController = [[GMSAutocompleteResultsViewController alloc] init]; + _acViewController.delegate = self; + + _searchController = + [[UISearchController alloc] initWithSearchResultsController:_acViewController]; + _searchController.hidesNavigationBarDuringPresentation = NO; + _searchController.dimsBackgroundDuringPresentation = YES; + + _searchController.searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal; + + [_searchController.searchBar sizeToFit]; + self.navigationItem.titleView = _searchController.searchBar; + self.definesPresentationContext = YES; + + // Work around a UISearchController bug that doesn't reposition the table view correctly when + // rotating to landscape. + self.edgesForExtendedLayout = UIRectEdgeAll; + self.extendedLayoutIncludesOpaqueBars = YES; + + _searchController.searchResultsUpdater = _acViewController; + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + _searchController.modalPresentationStyle = UIModalPresentationPopover; + } else { + _searchController.modalPresentationStyle = UIModalPresentationFullScreen; + } + + if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_8_0) { + NSString *message = NSLocalizedString( + @"Demo.Content.iOSVersionNotSupported", + @"Error message to display when a demo is not available on the current version of iOS"); + [super showCustomMessageInResultPane:message]; + } + + [self addResultViewBelow:nil]; +} + +#pragma mark - GMSAutocompleteResultsViewControllerDelegate + +- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController + didAutocompleteWithPlace:(GMSPlace *)place { + // Display the results and dismiss the search controller. + [_searchController setActive:NO]; + [self autocompleteDidSelectPlace:place]; +} + +- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController + didFailAutocompleteWithError:(NSError *)error { + // Display the error and dismiss the search controller. + [_searchController setActive:NO]; + [self autocompleteDidFail:error]; +} + +// Show and hide the network activity indicator when we start/stop loading results. + +- (void)didRequestAutocompletePredictionsForResultsController: + (GMSAutocompleteResultsViewController *)resultsController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; +} + +- (void)didUpdateAutocompletePredictionsForResultsController: + (GMSAutocompleteResultsViewController *)resultsController { + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.h new file mode 100644 index 0000000..057fca2 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteBaseViewController.h" + +/* + * This demo shows how to manually present a UITableViewController and supply it with autocomplete + * text from an arbitrary source, in this case a UITextField. + */ +@interface AutocompleteWithTextFieldController : AutocompleteBaseViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.m new file mode 100644 index 0000000..7ec503b --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.m @@ -0,0 +1,192 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/Autocomplete/AutocompleteWithTextFieldController.h" + +#import + +@interface AutocompleteWithTextFieldController () +@end + +@implementation AutocompleteWithTextFieldController { + UITextField *_searchField; + UITableViewController *_resultsController; + GMSAutocompleteTableDataSource *_tableDataSource; +} + ++ (NSString *)demoTitle { + return NSLocalizedString( + @"Demo.Title.Autocomplete.UITextField", + @"Title of the UITextField autocomplete demo for display in a list or nav header"); +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + // Configure the text field to our linking. + _searchField = [[UITextField alloc] initWithFrame:CGRectZero]; + _searchField.translatesAutoresizingMaskIntoConstraints = NO; + _searchField.borderStyle = UITextBorderStyleNone; + _searchField.backgroundColor = [UIColor whiteColor]; + _searchField.placeholder = NSLocalizedString(@"Demo.Content.Autocomplete.EnterTextPrompt", + @"Prompt to enter text for autocomplete demo"); + _searchField.autocorrectionType = UITextAutocorrectionTypeNo; + _searchField.keyboardType = UIKeyboardTypeDefault; + _searchField.returnKeyType = UIReturnKeyDone; + _searchField.clearButtonMode = UITextFieldViewModeWhileEditing; + _searchField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; + + [_searchField addTarget:self + action:@selector(textFieldDidChange:) + forControlEvents:UIControlEventEditingChanged]; + _searchField.delegate = self; + + // Setup the results view controller. + _tableDataSource = [[GMSAutocompleteTableDataSource alloc] init]; + _tableDataSource.delegate = self; + _resultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain]; + _resultsController.tableView.delegate = _tableDataSource; + _resultsController.tableView.dataSource = _tableDataSource; + + [self.view addSubview:_searchField]; + // Use auto layout to place the text field, as we need to take the top layout guide into + // consideration. + [self.view + addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-[_searchField]-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_searchField)]]; + [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_searchField + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.topLayoutGuide + attribute:NSLayoutAttributeBottom + multiplier:1 + constant:8]]; + + [self addResultViewBelow:_searchField]; +} + +#pragma mark - GMSAutocompleteTableDataSourceDelegate + +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didAutocompleteWithPlace:(GMSPlace *)place { + [_searchField resignFirstResponder]; + [self autocompleteDidSelectPlace:place]; + _searchField.text = place.name; +} + +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didFailAutocompleteWithError:(NSError *)error { + [_searchField resignFirstResponder]; + [self autocompleteDidFail:error]; + _searchField.text = @""; +} + +- (void)didRequestAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource { + [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; + [_resultsController.tableView reloadData]; +} + +- (void)didUpdateAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource { + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; + [_resultsController.tableView reloadData]; +} + +#pragma mark - UITextFieldDelegate + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + [self addChildViewController:_resultsController]; + + // Add the results controller. + _resultsController.view.translatesAutoresizingMaskIntoConstraints = NO; + _resultsController.view.alpha = 0.0f; + [self.view addSubview:_resultsController.view]; + + // Layout it out below the text field using auto layout. + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[_searchField]-[resultView]-(0)-|" + options:0 + metrics:nil + views:@{ + @"_searchField" : _searchField, + @"resultView" : _resultsController.view + }]]; + [self.view addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-(0)-[resultView]-(0)-|" + options:0 + metrics:nil + views:@{ + @"resultView" : _resultsController.view + }]]; + + // Force a layout pass otherwise the table will animate in weirdly. + [self.view layoutIfNeeded]; + + // Reload the data. + [_resultsController.tableView reloadData]; + + // Animate in the results. + [UIView animateWithDuration:0.5 + animations:^{ + _resultsController.view.alpha = 1.0f; + } + completion:^(BOOL finished) { + [_resultsController didMoveToParentViewController:self]; + }]; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField { + // Dismiss the results. + [_resultsController willMoveToParentViewController:nil]; + [UIView animateWithDuration:0.5 + animations:^{ + _resultsController.view.alpha = 0.0f; + } + completion:^(BOOL finished) { + [_resultsController.view removeFromSuperview]; + [_resultsController removeFromParentViewController]; + }]; +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return NO; +} + +- (BOOL)textFieldShouldClear:(UITextField *)textField { + [textField resignFirstResponder]; + textField.text = @""; + return NO; +} + +#pragma mark - Private Methods + +- (void)textFieldDidChange:(UITextField *)textField { + [_tableDataSource sourceTextHasChanged:textField.text]; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.h new file mode 100644 index 0000000..0a52ba7 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.h @@ -0,0 +1,40 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +/** + * Represents a place photo, along with the attributions which are required to be displayed along + * with it. + */ +@interface AttributedPhoto : NSObject + +@property(nonatomic, strong) UIImage *image; + +@property(nonatomic, strong) NSAttributedString *attributions; + +@end + +/* + * A horizontally-paging scroll view that displays a list of photo images and their attributions. + */ +@interface PagingPhotoView : UIScrollView + +/** + * An array of |AttributedPhoto| objects representing the photos to display. + */ +@property(nonatomic, copy) NSArray *photoList; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.m new file mode 100644 index 0000000..b9c4b27 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PagingPhotoView.m @@ -0,0 +1,165 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/PagingPhotoView.h" + +/** + * Class to store the image and text views that display the image and attributions. + */ +@interface ImageViewAndAttribution : NSObject + +@property(nonatomic, strong) UIImageView *imageView; + +@property(nonatomic, strong) UITextView *attributionView; + +@end + +@implementation ImageViewAndAttribution +@end + +@implementation AttributedPhoto +@end + +@interface PagingPhotoView () +@end + +@implementation PagingPhotoView { + // An array of |ImageViewAndAttribution| objects representing the actual views that are + // being displayed. + NSMutableArray *_photoImageViews; + // Whether we should update the image and attribution view frames on the next |layoutSubviews| + // call. This should be set to YES whenever the frame is updated or the photos change. + BOOL _imageLayoutUpdateNeeded; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + _photoImageViews = [NSMutableArray array]; + self.pagingEnabled = YES; + } + return self; +} + +- (void)setPhotoList:(NSArray *)photoList { + // First, remove all of the existing image and attribution subviews. + for (ImageViewAndAttribution *photoView in _photoImageViews) { + [photoView.imageView removeFromSuperview]; + [photoView.attributionView removeFromSuperview]; + } + [_photoImageViews removeAllObjects]; + + // Add the new images and attributions as subviews. + _photoList = [photoList copy]; + for (AttributedPhoto *photo in photoList) { + UITextView *textView = [[UITextView alloc] initWithFrame:CGRectZero]; + textView.delegate = self; + textView.editable = NO; + textView.attributedText = photo.attributions; + [self addSubview:textView]; + + UIImageView *imageView = [[UIImageView alloc] initWithImage:photo.image]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.clipsToBounds = YES; + [self addSubview:imageView]; + + ImageViewAndAttribution *attributedView = [[ImageViewAndAttribution alloc] init]; + attributedView.imageView = imageView; + attributedView.attributionView = textView; + [_photoImageViews addObject:attributedView]; + } + [self updateContentSize]; + _imageLayoutUpdateNeeded = YES; +} + +- (void)setFrame:(CGRect)frame { + _imageLayoutUpdateNeeded = YES; + + // We want to make sure that we are still scrolled to the same photo when the frame changes. + // Measure the current content offset and scroll to the same fraction along the content after the + // frame change. + CGFloat scrollOffsetFraction = 0; + if (self.contentSize.width != 0) { + scrollOffsetFraction = self.contentOffset.x / self.contentSize.width; + } + [super setFrame:frame]; + [UIView performWithoutAnimation:^{ + [self updateContentSize]; + self.contentOffset = + CGPointMake(scrollOffsetFraction * self.contentSize.width, -self.contentInset.top); + }]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + if (_imageLayoutUpdateNeeded) { + [self layoutImages]; + _imageLayoutUpdateNeeded = NO; + } +} + +#pragma mark - UITextViewDelegate + +- (BOOL)textView:(UITextView *)textView + shouldInteractWithURL:(NSURL *)url + inRange:(NSRange)characterRange { + // Make links clickable. + return YES; +} + +#pragma mark - Helper methods + +/** + * Update the content size of the scroll view based on the number of photos and the view's width. + * This should be called whenever the frame changes or the number of photos has changed. + */ +- (void)updateContentSize { + CGRect insetBounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset); + CGFloat usableScrollViewHeight = insetBounds.size.height; + + self.contentSize = + CGSizeMake(_photoImageViews.count * self.frame.size.width, usableScrollViewHeight); +} + +/** + * Updates the frames of the images and attributions. + */ +- (void)layoutImages { + CGFloat contentWidth = 0; + CGFloat scrollViewWidth = self.bounds.size.width; + CGRect insetBounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset); + CGFloat usableScrollViewHeight = insetBounds.size.height; + + // Lay out the images one after the other horizontally. + for (ImageViewAndAttribution *attributedImageView in _photoImageViews) { + UITextView *attributionView = attributedImageView.attributionView; + UIImageView *imageView = attributedImageView.imageView; + [attributionView sizeToFit]; + CGFloat attributionHeight = attributionView.frame.size.height; + CGFloat imageHeight = usableScrollViewHeight - attributionHeight; + + // Put the attribution view aligned to the same left edge as the photo, in the bottom left + // corner of the screen. + attributionView.frame = + CGRectMake(contentWidth, imageHeight, scrollViewWidth, attributionHeight); + imageView.frame = CGRectMake(contentWidth, 0, scrollViewWidth, imageHeight); + contentWidth += imageView.frame.size.width; + } +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.h new file mode 100644 index 0000000..99c7a40 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.h @@ -0,0 +1,24 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import "GooglePlacesDemos/Support/BaseDemoViewController.h" + +/** + * The photo demo view controller. Presents a place picker and then displays photos in a carrousel + * for the selected place. + */ +@interface PhotosViewController : BaseDemoViewController + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.m new file mode 100644 index 0000000..9e74f01 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Samples/PhotosViewController.m @@ -0,0 +1,131 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Samples/PhotosViewController.h" + +#import +#import + +#import "GooglePlacesDemos/Samples/PagingPhotoView.h" + +@implementation PhotosViewController { + GMSPlacePicker *_placePicker; + GMSPlacesClient *_placesClient; + PagingPhotoView *_photoView; + UIActivityIndicatorView *_indicatorView; + NSMapTable *_imagesByPhoto; +} + ++ (NSString *)demoTitle { + return NSLocalizedString(@"Demo.Title.Photos", + @"Title of the photos demo for display in a list or nav header"); +} + +- (instancetype)init { + if ((self = [super init])) { + GMSPlacePickerConfig *config = [[GMSPlacePickerConfig alloc] initWithViewport:nil]; + _placePicker = [[GMSPlacePicker alloc] initWithConfig:config]; + _placesClient = [GMSPlacesClient sharedClient]; + _imagesByPhoto = [NSMapTable strongToStrongObjectsMapTable]; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + // Configure the photo view where we are going to display the loaded photos. + _photoView = [[PagingPhotoView alloc] initWithFrame:self.view.bounds]; + _photoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self.view addSubview:_photoView]; + + // Setup the loading indicator. + _indicatorView = [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _indicatorView.autoresizingMask = + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; + _indicatorView.center = _photoView.center; + [_indicatorView startAnimating]; + [self.view addSubview:_indicatorView]; + + // Present the place picker. + [_placePicker pickPlaceWithCallback:^(GMSPlace *place, NSError *error) { + if (place) { + [_placesClient lookUpPhotosForPlaceID:place.placeID + callback:^(GMSPlacePhotoMetadataList *photos, + NSError *__nullable photoError) { + if (photos != nil) { + [self displayPhotoList:photos]; + } else { + NSLog(@"Photo metadata lookup failed: %@", photoError); + } + }]; + } else if (error) { + NSLog(@"Place picking failed with error: %@", error); + } else { + NSLog(@"Place picking cancelled."); + } + }]; +} + +#pragma mark - Private methods + +// Displays a list of photos. +- (void)displayPhotoList:(GMSPlacePhotoMetadataList *)photos { + // Create a dispatch group for photo requests. We will enter this group immediately before making + // each photo request, and leave when the request completes. This provides a mechanism for waiting + // for all of the photo requests to complete. + dispatch_group_t photoRequestGroup = dispatch_group_create(); + for (GMSPlacePhotoMetadata *photo in photos.results) { + dispatch_group_enter(photoRequestGroup); + [_placesClient loadPlacePhoto:photo + callback:^(UIImage *photoImage, NSError *error) { + if (photoImage == nil) { + NSLog(@"Photo request failed with error: %@", error); + } else { + [_imagesByPhoto setObject:photoImage forKey:photo]; + } + dispatch_group_leave(photoRequestGroup); + }]; + } + + // The block will be called once all photo requests have completed. + dispatch_group_notify(photoRequestGroup, + dispatch_get_main_queue(), + ^{ + NSMutableArray *attributedPhotos = [NSMutableArray array]; + for (GMSPlacePhotoMetadata *photo in photos.results) { + UIImage *image = [_imagesByPhoto objectForKey:photo]; + if (image == nil) { + continue; + } + AttributedPhoto *attributedPhoto = [[AttributedPhoto alloc] init]; + attributedPhoto.image = image; + attributedPhoto.attributions = photo.attributions; + [attributedPhotos addObject:attributedPhoto]; + } + _photoView.photoList = attributedPhotos; + [_indicatorView stopAnimating]; + }); +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.h new file mode 100644 index 0000000..6e8759f --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.h @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +/** + * Base view controller for all demos in the Places Demo app. Provides some basic functionality + * which is common across demos. + */ +@interface BaseDemoViewController : UIViewController + +/** + * The title of the demo. Displayed in lists and navigation bars. + * + * NOTE: This must be overridden by subclasses. + */ ++ (NSString *)demoTitle; + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.m new file mode 100644 index 0000000..2b03ac9 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/BaseDemoViewController.m @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Support/BaseDemoViewController.h" + +@implementation BaseDemoViewController + ++ (NSString *)demoTitle { + // This should be overridden by subclasses, so should not be called. + return nil; +} + +- (instancetype)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle { + if ((self = [super initWithNibName:name bundle:bundle])) { + self.title = [[self class] demoTitle]; + } + return self; +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.h b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.h new file mode 100644 index 0000000..220b8d1 --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.h @@ -0,0 +1,27 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#import + +/** + * A class which manages the behavior of a |UISplitViewController| to achieve the UX we want for + * this demo app. Specifically it tells the |UISplitViewController| to display the list of demos on + * first launch if there is not enough space to have two panes, instead of just the first demo in + * the list. After first launch if the device transitions from regular to compact it will instead + * show the demo which is currently open. + */ +@interface MainSplitViewControllerBehaviorManager : NSObject + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.m new file mode 100644 index 0000000..fdcf2be --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.m @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "GooglePlacesDemos/Support/MainSplitViewControllerBehaviorManager.h" + +@implementation MainSplitViewControllerBehaviorManager { + BOOL _hasBeenCollapsedBefore; +} + +#pragma mark - UISplitViewControllerDelegate + +- (BOOL)splitViewController:(UISplitViewController *)splitViewController + collapseSecondaryViewController:(UIViewController *)secondaryViewController + ontoPrimaryViewController:(UIViewController *)primaryViewController { + // This override is probably not needed in your own app. This tells the |UISplitViewController| to + // display the list of demos on first launch if there is not enough space to have two panes, + // instead of just the first demo in the list. After first launch if the device transitions from + // regular to compact it will instead show the demo which is currently open. + if (_hasBeenCollapsedBefore) { + return NO; + } else { + _hasBeenCollapsedBefore = YES; + return YES; + } +} + +@end diff --git a/Pods/GooglePlaces/Example/GooglePlacesDemos/main.m b/Pods/GooglePlaces/Example/GooglePlacesDemos/main.m new file mode 100644 index 0000000..8803d3b --- /dev/null +++ b/Pods/GooglePlaces/Example/GooglePlacesDemos/main.m @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Google Inc. All rights reserved. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import + +#import "GooglePlacesDemos/DemoAppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([DemoAppDelegate class])); + } +} diff --git a/Pods/GooglePlaces/Example/Podfile b/Pods/GooglePlaces/Example/Podfile new file mode 100644 index 0000000..7514b74 --- /dev/null +++ b/Pods/GooglePlaces/Example/Podfile @@ -0,0 +1,7 @@ +source 'https://github.com/CocoaPods/Specs.git' + +target 'GooglePlacesDemos' do + pod 'GooglePlaces', '= 2.1.0' + pod 'GooglePlacePicker', '= 2.1.0' + pod 'GoogleMaps', '= 2.1.0' +end diff --git a/Pods/GooglePlaces/Example/README.GooglePlacesDemos b/Pods/GooglePlaces/Example/README.GooglePlacesDemos new file mode 100644 index 0000000..430c6c0 --- /dev/null +++ b/Pods/GooglePlaces/Example/README.GooglePlacesDemos @@ -0,0 +1,18 @@ +GooglePlacesDemos contains a demo application showcasing various features of +the Google Places API for iOS. + +Before starting, please note that these demos are directed towards a technical +audience. You'll also need Xcode 7.3 or later, with the iOS SDK 7.0 or later. + +If you're new to the API, please read the Introduction section of the Google +Places API for iOS documentation- + https://developers.google.com/places/ios-api/ + +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.GooglePlacesDemos". + + * Open the project in Xcode, and update `SDKDemoAPIKey.h` with this key. diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/GooglePlaces b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/GooglePlaces new file mode 120000 index 0000000..69cb11e --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/GooglePlaces @@ -0,0 +1 @@ +Versions/Current/GooglePlaces \ No newline at end of file diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Headers b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Modules b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Resources b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/GooglePlaces b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/GooglePlaces new file mode 100644 index 0000000..d789383 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/GooglePlaces differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAddressComponent.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAddressComponent.h new file mode 100644 index 0000000..d0dc2e9 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAddressComponent.h @@ -0,0 +1,36 @@ +// +// GMSAddressComponent.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * Represents a component of an address, e.g., street number, postcode, city, etc. + */ +@interface GMSAddressComponent : NSObject + +/** + * Type of the address component. For a list of supported types, see + * https://developers.google.com/places/supported_types#table2. This string will be one of the + * constants defined in GMSPlaceTypes.h. + */ +@property(nonatomic, readonly, copy) NSString *type; + +/** Name of the address component, e.g. "Sydney" */ +@property(nonatomic, readonly, copy) NSString *name; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFetcher.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFetcher.h new file mode 100644 index 0000000..8097873 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFetcher.h @@ -0,0 +1,94 @@ +// +// GMSAutocompleteFetcher.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +@class GMSAutocompletePrediction; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * Protocol for objects that can receive callbacks from GMSAutocompleteFetcher + */ +@protocol GMSAutocompleteFetcherDelegate + +@required + +/** + * Called when autocomplete predictions are available. + * @param predictions an array of GMSAutocompletePrediction objects. + */ +- (void)didAutocompleteWithPredictions:(GMS_NSArrayOf(GMSAutocompletePrediction *) *)predictions; + +/** + * Called when an autocomplete request returns an error. + * @param error the error that was received. + */ +- (void)didFailAutocompleteWithError:(NSError *)error; + +@end + +/** + * GMSAutocompleteFetcher is a wrapper around the lower-level autocomplete APIs that encapsulates + * some of the complexity of requesting autocomplete predictions as the user is typing. Calling + * sourceTextHasChanged will generally result in the provided delegate being called with + * autocomplete predictions for the queried text, with the following provisos: + * + * - The fetcher may not necessarily request predictions on every call of sourceTextHasChanged if + * several requests are made within a short amount of time. + * - The delegate will only be called with prediction results if those predictions are for the + * text supplied in the most recent call to sourceTextHasChanged. + */ +@interface GMSAutocompleteFetcher : NSObject + +/** + * Initialise the fetcher + * @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. + */ +- (instancetype)initWithBounds:(GMSCoordinateBounds *GMS_NULLABLE_PTR)bounds + filter:(GMSAutocompleteFilter *GMS_NULLABLE_PTR)filter + NS_DESIGNATED_INITIALIZER; + +/** Delegate to be notified with autocomplete prediction results. */ +@property(nonatomic, weak) id GMS_NULLABLE_PTR delegate; + +/** Bounds used to bias the autocomplete search (can be nil). */ +@property(nonatomic, strong) GMSCoordinateBounds *GMS_NULLABLE_PTR autocompleteBounds; + +/** Filter to apply to autocomplete suggestions (can be nil). */ +@property(nonatomic, strong) GMSAutocompleteFilter *GMS_NULLABLE_PTR autocompleteFilter; + +/** + * Notify the fetcher that the source text to autocomplete has changed. + * + * This method should only be called from the main thread. Calling this method from another thread + * will result in undefined behavior. Calls to |GMSAutocompleteFetcherDelegate| methods will also be + * called on the main thread. + * + * This method is non-blocking. + * @param text The partial text to autocomplete. + */ +- (void)sourceTextHasChanged:(NSString *GMS_NULLABLE_PTR)text; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFilter.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFilter.h new file mode 100644 index 0000000..4425135 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFilter.h @@ -0,0 +1,78 @@ +// +// GMSAutocompleteFilter.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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; + +/** + * The country to restrict results to. This should be a ISO 3166-1 Alpha-2 country code (case + * insensitive). If nil, no country filtering will take place. + */ +@property(nonatomic, copy) NSString *GMS_NULLABLE_PTR country; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h new file mode 100644 index 0000000..bc77959 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h @@ -0,0 +1,39 @@ +// +// GMSAutocompleteMatchFragment.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompletePrediction.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompletePrediction.h new file mode 100644 index 0000000..55fe373 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompletePrediction.h @@ -0,0 +1,88 @@ +// +// GMSAutocompletePrediction.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/* + * 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; + +/** + * The main text of a prediction as a NSAttributedString, usually the name of the place. + * E.g. "Sydney Opera House". + * + * Text ranges that match user input are have a |kGMSAutocompleteMatchAttribute|, + * like |attributedFullText|. + */ +@property(nonatomic, copy, readonly) NSAttributedString *attributedPrimaryText; + +/** + * The secondary text of a prediction as a NSAttributedString, usually the location of the place. + * E.g. "Sydney, New South Wales, Australia". + * + * Text ranges that match user input are have a |kGMSAutocompleteMatchAttribute|, like + * |attributedFullText|. + * + * May be nil. + */ +@property(nonatomic, copy, readonly) NSAttributedString *GMS_NULLABLE_PTR attributedSecondaryText; + +/** + * An optional property representing the place ID of the prediction, suitable for use in a place + * details request. + */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR placeID; + +/** + * The types of this autocomplete result. Types are NSStrings, valid values are any types + * documented at . + */ +@property(nonatomic, copy, readonly) GMS_NSArrayOf(NSString *) *types; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteResultsViewController.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteResultsViewController.h new file mode 100644 index 0000000..cf89e5f --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteResultsViewController.h @@ -0,0 +1,140 @@ +// +// GMSAutocompleteResultsViewController.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import +#import + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSAutocompleteResultsViewController; + +/** + * Protocol used by |GMSAutocompleteResultsViewController|, to communicate the user's interaction + * with the controller to the application. + */ +@protocol GMSAutocompleteResultsViewControllerDelegate + +@required + +/** + * Called when a place has been selected from the available autocomplete predictions. + * @param resultsController The |GMSAutocompleteResultsViewController| that generated the event. + * @param place The |GMSPlace| that was returned. + */ +- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController + didAutocompleteWithPlace:(GMSPlace *)place; + +/** + * Called when a non-retryable error occurred when retrieving autocomplete predictions or place + * details. A non-retryable error is defined as one that is unlikely to be fixed by immediately + * retrying the operation. + *

+ * Only the following values of |GMSPlacesErrorCode| are retryable: + *

    + *
  • kGMSPlacesNetworkError + *
  • kGMSPlacesServerError + *
  • kGMSPlacesInternalError + *
+ * All other error codes are non-retryable. + * @param resultsController The |GMSAutocompleteResultsViewController| that generated the event. + * @param error The |NSError| that was returned. + */ +- (void)resultsController:(GMSAutocompleteResultsViewController *)resultsController + didFailAutocompleteWithError:(NSError *)error; + +@optional + +/** + * Called when the user selects an autocomplete prediction from the list but before requesting + * place details. Returning NO from this method will suppress the place details fetch and + * didAutocompleteWithPlace will not be called. + * @param resultsController The |GMSAutocompleteResultsViewController| that generated the event. + * @param prediction The |GMSAutocompletePrediction| that was selected. + */ +- (BOOL)resultsController:(GMSAutocompleteResultsViewController *)resultsController + didSelectPrediction:(GMSAutocompletePrediction *)prediction; + +/** + * Called once every time new autocomplete predictions are received. + * @param resultsController The |GMSAutocompleteResultsViewController| that generated the event. + */ +- (void)didUpdateAutocompletePredictionsForResultsController: + (GMSAutocompleteResultsViewController *)resultsController; + +/** + * Called once immediately after a request for autocomplete predictions is made. + * @param resultsController The |GMSAutocompleteResultsViewController| that generated the event. + */ +- (void)didRequestAutocompletePredictionsForResultsController: + (GMSAutocompleteResultsViewController *)resultsController; + +@end + + +/** + * GMSAutocompleteResultsViewController provides an interface that displays place autocomplete + * predictions in a table view. The table view will be automatically updated as input text + * changes. + * + * This class is intended to be used as the search results controller of a UISearchController. Pass + * an instance of |GMSAutocompleteResultsViewController| to UISearchController's + * initWithSearchResultsController method, then set the controller as the UISearchController's + * searchResultsUpdater property. + * + * Use the |GMSAutocompleteResultsViewControllerDelegate| delegate protocol to be notified when a + * place is selected from the list. + */ +@interface GMSAutocompleteResultsViewController : UIViewController < + UISearchResultsUpdating> + +/** Delegate to be notified when a place is selected. */ +@property(nonatomic, weak) + id GMS_NULLABLE_PTR delegate; + +/** Bounds used to bias the autocomplete search (can be nil). */ +@property(nonatomic, strong) GMSCoordinateBounds *GMS_NULLABLE_PTR autocompleteBounds; + +/** Filter to apply to autocomplete suggestions (can be nil). */ +@property(nonatomic, strong) GMSAutocompleteFilter *GMS_NULLABLE_PTR autocompleteFilter; + +/** The background color of table cells. */ +@property(nonatomic, strong) IBInspectable UIColor *tableCellBackgroundColor; + +/** The color of the separator line between table cells. */ +@property(nonatomic, strong) IBInspectable UIColor *tableCellSeparatorColor; + +/** The color of result name text in autocomplete results */ +@property(nonatomic, strong) IBInspectable UIColor *primaryTextColor; + +/** The color used to highlight matching text in autocomplete results */ +@property(nonatomic, strong) IBInspectable UIColor *primaryTextHighlightColor; + +/** The color of the second row of text in autocomplete results. */ +@property(nonatomic, strong) IBInspectable UIColor *secondaryTextColor; + +/** The tint color applied to controls in the Autocomplete view. */ +@property(nonatomic, strong) IBInspectable UIColor *GMS_NULLABLE_PTR tintColor; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteTableDataSource.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteTableDataSource.h new file mode 100644 index 0000000..4ddd5fa --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteTableDataSource.h @@ -0,0 +1,164 @@ +// +// GMSAutocompleteTableDataSource.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import +#import + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSAutocompleteTableDataSource; + +/** + * Protocol used by |GMSAutocompleteTableDataSource|, to communicate the user's interaction with the + * data source to the application. + */ +@protocol GMSAutocompleteTableDataSourceDelegate + +@required + +/** + * Called when a place has been selected from the available autocomplete predictions. + * @param tableDataSource The |GMSAutocompleteTableDataSource| that generated the event. + * @param place The |GMSPlace| that was returned. + */ +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didAutocompleteWithPlace:(GMSPlace *)place; + +/** + * Called when a non-retryable error occurred when retrieving autocomplete predictions or place + * details. A non-retryable error is defined as one that is unlikely to be fixed by immediately + * retrying the operation. + *

+ * Only the following values of |GMSPlacesErrorCode| are retryable: + *

    + *
  • kGMSPlacesNetworkError + *
  • kGMSPlacesServerError + *
  • kGMSPlacesInternalError + *
+ * All other error codes are non-retryable. + * @param tableDataSource The |GMSAutocompleteTableDataSource| that generated the event. + * @param error The |NSError| that was returned. + */ +- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didFailAutocompleteWithError:(NSError *)error; + +@optional + +/** + * Called when the user selects an autocomplete prediction from the list but before requesting + * place details. Returning NO from this method will suppress the place details fetch and + * didAutocompleteWithPlace will not be called. + * @param tableDataSource The |GMSAutocompleteTableDataSource| that generated the event. + * @param prediction The |GMSAutocompletePrediction| that was selected. + */ +- (BOOL)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource + didSelectPrediction:(GMSAutocompletePrediction *)prediction; + +/** + * Called once every time new autocomplete predictions are received. + * @param tableDataSource The |GMSAutocompleteTableDataSource| that generated the event. + */ +- (void)didUpdateAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource; + +/** + * Called once immediately after a request for autocomplete predictions is made. + * @param tableDataSource The |GMSAutocompleteTableDataSource| that generated the event. + */ +- (void)didRequestAutocompletePredictionsForTableDataSource: + (GMSAutocompleteTableDataSource *)tableDataSource; + +@end + + +/** + * GMSAutocompleteTableDataSource provides an interface for providing place autocomplete + * predictions to populate a UITableView by implementing the UITableViewDataSource and + * UITableViewDelegate protocols. + * + * GMSAutocompleteTableDataSource is designed to be used as the data source for a + * UISearchDisplayController. + * + * NOTE: Unless iOS 7 compatibility is required, using UISearchController with + * |GMSAutocompleteResultsViewController| instead of UISearchDisplayController is highly + * recommended. + * + * Set an instance of GMSAutocompleteTableDataSource as the searchResultsDataSource and + * searchResultsDelegate properties of UISearchDisplayController. In your implementation of + * shouldReloadTableForSearchString, call sourceTextHasChanged with the current search string. + * + * Use the |GMSAutocompleteTableDataSourceDelegate| delegate protocol to be notified when a place is + * selected from the list. Because autocomplete predictions load asynchronously, it is necessary + * to implement didUpdateAutocompletePredictions and call reloadData on the + * UISearchDisplayController's table view. + * + */ +@interface GMSAutocompleteTableDataSource : NSObject < + UITableViewDataSource, UITableViewDelegate> + +/** Delegate to be notified when a place is selected or picking is cancelled. */ +@property(nonatomic, weak) + IBOutlet id GMS_NULLABLE_PTR delegate; + +/** Bounds used to bias the autocomplete search (can be nil). */ +@property(nonatomic, strong) GMSCoordinateBounds *GMS_NULLABLE_PTR autocompleteBounds; + +/** Filter to apply to autocomplete suggestions (can be nil). */ +@property(nonatomic, strong) GMSAutocompleteFilter *GMS_NULLABLE_PTR autocompleteFilter; + +/** The background color of table cells. */ +@property(nonatomic, strong) UIColor *tableCellBackgroundColor; + +/** The color of the separator line between table cells. */ +@property(nonatomic, strong) UIColor *tableCellSeparatorColor; + +/** The color of result name text in autocomplete results */ +@property(nonatomic, strong) UIColor *primaryTextColor; + +/** The color used to highlight matching text in autocomplete results */ +@property(nonatomic, strong) UIColor *primaryTextHighlightColor; + +/** The color of the second row of text in autocomplete results. */ +@property(nonatomic, strong) UIColor *secondaryTextColor; + +/** The tint color applied to controls in the Autocomplete view. */ +@property(nonatomic, strong) UIColor *GMS_NULLABLE_PTR tintColor; + +/** Designated initializer */ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +/** + * Notify the data source that the source text to autocomplete has changed. + * + * This method should only be called from the main thread. Calling this method from another thread + * will result in undefined behavior. Calls to |GMSAutocompleteTableDataSourceDelegate| methods will + * also be called on the main thread. + * + * This method is non-blocking. + * @param text The partial text to autocomplete. + */ +- (void)sourceTextHasChanged:(NSString *GMS_NULLABLE_PTR)text; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteViewController.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteViewController.h new file mode 100644 index 0000000..3ebd960 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteViewController.h @@ -0,0 +1,140 @@ +// +// GMSAutocompleteViewController.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import +#import + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSAutocompleteViewController; + +/** + * Protocol used by |GMSAutocompleteViewController|, to communicate the user's interaction + * with the controller to the application. + */ +@protocol GMSAutocompleteViewControllerDelegate + +@required + +/** + * Called when a place has been selected from the available autocomplete predictions. + * @param viewController The |GMSAutocompleteViewController| that generated the event. + * @param place The |GMSPlace| that was returned. + */ +- (void)viewController:(GMSAutocompleteViewController *)viewController + didAutocompleteWithPlace:(GMSPlace *)place; + +/** + * Called when a non-retryable error occurred when retrieving autocomplete predictions or place + * details. A non-retryable error is defined as one that is unlikely to be fixed by immediately + * retrying the operation. + *

+ * Only the following values of |GMSPlacesErrorCode| are retryable: + *

    + *
  • kGMSPlacesNetworkError + *
  • kGMSPlacesServerError + *
  • kGMSPlacesInternalError + *
+ * All other error codes are non-retryable. + * @param viewController The |GMSAutocompleteViewController| that generated the event. + * @param error The |NSError| that was returned. + */ +- (void)viewController:(GMSAutocompleteViewController *)viewController + didFailAutocompleteWithError:(NSError *)error; + +/** + * Called when the user taps the Cancel button in a |GMSAutocompleteViewController|. + * @param viewController The |GMSAutocompleteViewController| that generated the event. + */ +- (void)wasCancelled:(GMSAutocompleteViewController *)viewController; + +@optional + +/** + * Called when the user selects an autocomplete prediction from the list but before requesting + * place details. Returning NO from this method will suppress the place details fetch and + * didAutocompleteWithPlace will not be called. + * @param viewController The |GMSAutocompleteViewController| that generated the event. + * @param prediction The |GMSAutocompletePrediction| that was selected. + */ +- (BOOL)viewController:(GMSAutocompleteViewController *)viewController + didSelectPrediction:(GMSAutocompletePrediction *)prediction; + +/** + * Called once every time new autocomplete predictions are received. + * @param viewController The |GMSAutocompleteViewController| that generated the event. + */ +- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController; + +/** + * @param viewController The |GMSAutocompleteViewController| that generated the event. + * Called once immediately after a request for autocomplete predictions is made. + */ +- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController; + +@end + + +/** + * GMSAutocompleteViewController provides an interface that displays a table of autocomplete + * predictions that updates as the user enters text. Place selections made by the user are + * returned to the app via the |GMSAutocompleteViewControllerResultsDelegate| protocol. + * + * To use GMSAutocompleteViewController, set its delegate to an object in your app that + * conforms to the |GMSAutocompleteViewControllerDelegate| protocol and present the controller + * (eg using presentViewController). The |GMSAutocompleteViewControllerDelegate| delegate methods + * can be used to determine when the user has selected a place or has cancelled selection. + */ +@interface GMSAutocompleteViewController : UIViewController + +/** Delegate to be notified when a place is selected or picking is cancelled. */ +@property(nonatomic, weak) + IBOutlet id GMS_NULLABLE_PTR delegate; + +/** Bounds used to bias the autocomplete search (can be nil). */ +@property(nonatomic, strong) GMSCoordinateBounds *GMS_NULLABLE_PTR autocompleteBounds; + +/** Filter to apply to autocomplete suggestions (can be nil). */ +@property(nonatomic, strong) GMSAutocompleteFilter *GMS_NULLABLE_PTR autocompleteFilter; + +/** The background color of table cells. */ +@property(nonatomic, strong) IBInspectable UIColor *tableCellBackgroundColor; + +/** The color of the separator line between table cells. */ +@property(nonatomic, strong) IBInspectable UIColor *tableCellSeparatorColor; + +/** The color of result name text in autocomplete results */ +@property(nonatomic, strong) IBInspectable UIColor *primaryTextColor; + +/** The color used to highlight matching text in autocomplete results */ +@property(nonatomic, strong) IBInspectable UIColor *primaryTextHighlightColor; + +/** The color of the second row of text in autocomplete results. */ +@property(nonatomic, strong) IBInspectable UIColor *secondaryTextColor; + +/** The tint color applied to controls in the Autocomplete view. */ +@property(nonatomic, strong) IBInspectable UIColor *GMS_NULLABLE_PTR tintColor; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlace.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlace.h new file mode 100644 index 0000000..86834a0 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlace.h @@ -0,0 +1,138 @@ +// +// GMSPlace.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSAddressComponent; +@class GMSCoordinateBounds; + + +/** 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, assign) CLLocationCoordinate2D coordinate; + +/** + * Represents the open now status of the place at the time that the place was created. + */ +@property(nonatomic, readonly, assign) 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 *GMS_NULLABLE_PTR phoneNumber; + +/** + * Address of the place as a simple string. + */ +@property(nonatomic, copy, readonly) NSString *GMS_NULLABLE_PTR 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, assign) 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, assign) GMSPlacesPriceLevel priceLevel; + +/** + * The types of this place. Types are NSStrings, valid values are any types documented at + * . + */ +@property(nonatomic, copy, readonly) GMS_NSArrayOf(NSString *) *types; + +/** Website for this place. */ +@property(nonatomic, copy, readonly) NSURL *GMS_NULLABLE_PTR 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 *GMS_NULLABLE_PTR attributions; + +/** + * The recommended viewport for this place. May be nil if the size of the place is not known. + * + * This returns a viewport of a size that is suitable for displaying this place. For example, a + * |GMSPlace| object representing a store may have a relatively small viewport, while a |GMSPlace| + * object representing a country may have a very large viewport. + */ +@property(nonatomic, strong, readonly) GMSCoordinateBounds *GMS_NULLABLE_PTR viewport; + +/** + * An array of |GMSAddressComponent| objects representing the components in the place's address. + * These components are provided for the purpose of extracting structured information about the + * place's address: for example, finding the city that a place is in. + * + * These components should not be used for address formatting. If a formatted address is required, + * use the |formattedAddress| property, which provides a localized formatted address. + */ +@property(nonatomic, copy, readonly) + GMS_NSArrayOf(GMSAddressComponent *) *GMS_NULLABLE_PTR addressComponents; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihood.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihood.h new file mode 100644 index 0000000..79a923a --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihood.h @@ -0,0 +1,45 @@ +// +// GMSPlaceLikelihood.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@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 + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h new file mode 100644 index 0000000..9156bcc --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h @@ -0,0 +1,46 @@ +// +// GMSPlaceLikelihoodList.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +@class GMSPlaceLikelihood; + +/** + * 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 likelihoods, sorted in descending order. */ +@property(nonatomic, copy) GMS_NSArrayOf(GMSPlaceLikelihood *) *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 *GMS_NULLABLE_PTR attributions; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadata.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadata.h new file mode 100644 index 0000000..de05af2 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadata.h @@ -0,0 +1,44 @@ +// +// GMSPlacePhotoMetadata.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * The metadata corresponding to a single photo associated with a place. + */ +@interface GMSPlacePhotoMetadata : NSObject + +/** + * The data provider attribution string for this photo. + * + * 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 GMSPlacePhotoMetadata is shown, as + * described in the Places API Terms of Service. + */ +@property(nonatomic, readonly, copy) NSAttributedString* GMS_NULLABLE_PTR attributions; + +/** + * The maximum pixel size in which this photo is available. + */ +@property(nonatomic, readonly, assign) CGSize maxSize; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadataList.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadataList.h new file mode 100644 index 0000000..e913f6f --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadataList.h @@ -0,0 +1,34 @@ +// +// GMSPlacePhotoMetadataList.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import + +GMS_ASSUME_NONNULL_BEGIN + +/** + * A list of |GMSPlacePhotoMetadata| objects. + */ +@interface GMSPlacePhotoMetadataList : NSObject + +/** + * The array of |GMSPlacePhotoMetadata| objects. + */ +@property(nonatomic, readonly, copy) GMS_NSArrayOf(GMSPlacePhotoMetadata *) * results; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceTypes.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceTypes.h new file mode 100644 index 0000000..acbceee --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceTypes.h @@ -0,0 +1,141 @@ +// +// GMSPlaceTypes.h +// Google Places API for iOS +// +// Copyright 2016 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 kGMSPlaceTypeAdministrativeAreaLevel4; +extern NSString *const kGMSPlaceTypeAdministrativeAreaLevel5; +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 kGMSPlaceTypePostalCodeSuffix; +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 kGMSPlaceTypeStreetNumber; +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/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesClient.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesClient.h new file mode 100644 index 0000000..fc4c87d --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesClient.h @@ -0,0 +1,239 @@ +// +// GMSPlacesClient.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif +#import +#import +#import + + +@class GMSAutocompleteFilter; +@class GMSAutocompletePrediction; +@class GMSPlaceLikelihoodList; +@class GMSPlacePhotoMetadata; +@class GMSPlacePhotoMetadataList; + +GMS_ASSUME_NONNULL_BEGIN + +/** + * @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 occurred, 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 occurred, 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 occurred, if any. + */ +typedef void (^GMSAutocompletePredictionsCallback)(GMS_NSArrayOf(GMSAutocompletePrediction *) * + GMS_NULLABLE_PTR results, + NSError *GMS_NULLABLE_PTR error); + +/** + * @relates GMSPlacesClient + * Callback type for receiving place photos results. If an error occurred, |photos| will be nil and + * |error| will contain information about the error. + * @param photos The result containing |GMSPlacePhotoMetadata| objects. + * @param error The error that occurred, if any. + */ +typedef void (^GMSPlacePhotoMetadataResultCallback)( + GMSPlacePhotoMetadataList *GMS_NULLABLE_PTR photos, NSError *GMS_NULLABLE_PTR error); + +/** + * @relates GMSPlacesClient + * Callback type for receiving |UIImage| objects from a |GMSPlacePhotoMetadata| object. If an error + * occurred, |photo| will be nil and |error| will contain information about the error. + * @param photo The |UIImage| which was loaded. + * @param error The error that occurred, if any. + */ +typedef void (^GMSPlacePhotoImageResultCallback)(UIImage *GMS_NULLABLE_PTR photo, + 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 Places API 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; + +/** + * Provides your API key to the Google Places API 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 *)key; + +/** + * Returns the open source software license information for the Google Places API for iOS. This + * information must be made available within your application. + */ ++ (NSString *)openSourceLicenseInfo; + +/** + * Returns the version for this release of the Google Places API for iOS. + */ ++ (NSString *)SDKVersion; + +/** + * 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; + +/** + * Gets the metadata for up to 10 photos associated with a place. + * + * Photos are sourced from a variety of locations, including business owners and photos contributed + * by Google+ users. In most cases, these photos can be used without attribution, or will have the + * required attribution included as a part of the image. However, you must use the |attributions| + * property in the response to retrieve any additional attributions required, and display those + * attributions in your application wherever you display the image. A maximum of 10 photos is + * returned. + * + * Multiple calls of this method will probably return the same photos each time. However, this is + * not guaranteed because the underlying data may have changed. + * + * This method performs a network lookup. + * + * @param placeID The place ID for which to lookup photos. + * @param callback The callback to invoke with the lookup result. + */ +- (void)lookUpPhotosForPlaceID:(NSString *)placeID + callback:(GMSPlacePhotoMetadataResultCallback)callback; + +/** + * Loads the image for a specific photo at its maximum size. + * + * Image data may be cached. If the requested photo does not exist in the cache then a network + * lookup will be performed. + * + * @param photo The photo for which to load a |UIImage|. + * @param callback The callback to invoke with the loaded |UIImage|. + */ +- (void)loadPlacePhoto:(GMSPlacePhotoMetadata *)photo + callback:(GMSPlacePhotoImageResultCallback)callback; + +/** + * Loads the image for a specific photo, scaled to fit the given maximum dimensions. + * + * The image will be scaled to fit within the given dimensions while maintaining the aspect ratio of + * the original image. This scaling is performed server-side. + * + * If the scale parameter is not 1.0 maxSize will be multiplied by this value and the returned + * UIImage will be set to have the specified scale. This parameter should be set to the screen scale + * if you are loading images for display on screen. + * + * Image data may be cached. If the requested photo does not exist in the cache then a network + * lookup will be performed. + * + * NOTE: After applying the scale factor the dimensions in maxSize will be rounded up to the nearest + * integer before use. If an image is requested which is larger than the maximum size available a + * smaller image may be returned. + * + * @param photo The photo for which to load a |UIImage|. + * @param maxSize The maximum size of the image. + * @param scale The scale to load the image at. + * @param callback The callback to invoke with the loaded |UIImage|. + */ +- (void)loadPlacePhoto:(GMSPlacePhotoMetadata *)photo + constrainedToSize:(CGSize)maxSize + scale:(CGFloat)scale + callback:(GMSPlacePhotoImageResultCallback)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. + * + * NOTE: This method requires that your app has permission to access the devices location. Before + * calling this make sure to request access to the users location using [CLLocationManager + * requestWhenInUseAuthorization] or [CLLocationManager requestAlwaysAuthorization]. If you do call + * this method and your app does not have the correct authorization status, the callback will be + * called with 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/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesErrors.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesErrors.h new file mode 100644 index 0000000..7564418 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesErrors.h @@ -0,0 +1,103 @@ +// +// GMSPlacesErrors.h +// Google Places API for iOS +// +// Copyright 2016 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(modules) +@import GoogleMapsBase; +#else +#import +#endif + +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. + *

+ * If you believe this error represents a bug, please file a report using the instructions on our + * community and support page. + */ + kGMSPlacesServerError = -2, + /** + * An internal error occurred in the Places API library. + *

+ * If you believe this error represents a bug, please file a report using the instructions on our + * community and support page. + */ + 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 usage limits 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. See the usage limits guide for more + * information. + */ + 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. See the usage limits guide for more + * information. + */ + kGMSPlacesDeviceRateLimitExceeded = -8, + /** + * The Places API for iOS 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, + /** + * The Places API could not find the user's location. This may be because the user has not allowed + * the application to access location information. + */ + kGMSPlacesLocationError = -11 +}; + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSUserAddedPlace.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSUserAddedPlace.h new file mode 100644 index 0000000..639bc6f --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSUserAddedPlace.h @@ -0,0 +1,53 @@ +// +// GMSUserAddedPlace.h +// Google Places API for iOS +// +// Copyright 2016 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 + +#if __has_feature(modules) +@import GoogleMapsBase; +#else +#import +#endif + +GMS_ASSUME_NONNULL_BEGIN + +/** + * 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 *GMS_NULLABLE_PTR name; + +/** Address of the place. */ +@property(nonatomic, copy) NSString *GMS_NULLABLE_PTR address; + +/** Location of the place. */ +@property(nonatomic, assign) CLLocationCoordinate2D coordinate; + +/** Phone number of the place. */ +@property(nonatomic, copy) NSString *GMS_NULLABLE_PTR phoneNumber; + +/** List of types of the place as an array of NSStrings, like the GMSPlace.types property. +* Only table 1 types +* are valid. +*/ +@property(nonatomic, copy) GMS_NSArrayOf(NSString *) * GMS_NULLABLE_PTR types; + +/** The website for the place. */ +@property(nonatomic, copy) NSString *GMS_NULLABLE_PTR website; + +@end + +GMS_ASSUME_NONNULL_END diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GooglePlaces.h b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GooglePlaces.h new file mode 100644 index 0000000..e4fb191 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GooglePlaces.h @@ -0,0 +1,17 @@ +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Modules/module.modulemap b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..8a46427 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,19 @@ +framework module GooglePlaces { umbrella header "GooglePlaces.h" +header "GMSAddressComponent.h" +header "GMSAutocompleteFetcher.h" +header "GMSAutocompleteFilter.h" +header "GMSAutocompleteMatchFragment.h" +header "GMSAutocompletePrediction.h" +header "GMSAutocompleteResultsViewController.h" +header "GMSAutocompleteTableDataSource.h" +header "GMSAutocompleteViewController.h" +header "GMSPlace.h" +header "GMSPlaceLikelihood.h" +header "GMSPlaceLikelihoodList.h" +header "GMSPlacePhotoMetadata.h" +header "GMSPlacePhotoMetadataList.h" +header "GMSPlaceTypes.h" +header "GMSPlacesClient.h" +header "GMSPlacesErrors.h" +header "GMSUserAddedPlace.h" +export * module * { export * } } diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/Info.plist b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/Info.plist new file mode 100644 index 0000000..a7332ce Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/Info.plist differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ar.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ar.lproj/GooglePlaces.strings new file mode 100644 index 0000000..bce2694 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ar.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ca.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ca.lproj/GooglePlaces.strings new file mode 100644 index 0000000..c90a9dc Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ca.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/cs.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/cs.lproj/GooglePlaces.strings new file mode 100644 index 0000000..80f6468 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/cs.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/da.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/da.lproj/GooglePlaces.strings new file mode 100644 index 0000000..03d7425 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/da.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/de.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/de.lproj/GooglePlaces.strings new file mode 100644 index 0000000..8b1ed19 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/de.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/el.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/el.lproj/GooglePlaces.strings new file mode 100644 index 0000000..6458ece Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/el.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en.lproj/GooglePlaces.strings new file mode 100644 index 0000000..76f26e4 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_AU.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_AU.lproj/GooglePlaces.strings new file mode 100644 index 0000000..2e7f8c7 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_AU.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_GB.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_GB.lproj/GooglePlaces.strings new file mode 100644 index 0000000..2e7f8c7 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_GB.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_IN.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_IN.lproj/GooglePlaces.strings new file mode 100644 index 0000000..2e7f8c7 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/en_IN.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es.lproj/GooglePlaces.strings new file mode 100644 index 0000000..f83f1da Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_419.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_419.lproj/GooglePlaces.strings new file mode 100644 index 0000000..fe60c57 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_419.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_MX.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_MX.lproj/GooglePlaces.strings new file mode 100644 index 0000000..fe60c57 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/es_MX.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fi.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fi.lproj/GooglePlaces.strings new file mode 100644 index 0000000..36449cf Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fi.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr.lproj/GooglePlaces.strings new file mode 100644 index 0000000..be66a02 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr_CA.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr_CA.lproj/GooglePlaces.strings new file mode 100644 index 0000000..b421ac5 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/fr_CA.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/he.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/he.lproj/GooglePlaces.strings new file mode 100644 index 0000000..ae5d522 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/he.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hi.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hi.lproj/GooglePlaces.strings new file mode 100644 index 0000000..62f4270 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hi.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hr.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hr.lproj/GooglePlaces.strings new file mode 100644 index 0000000..c0b30bf Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hr.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hu.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hu.lproj/GooglePlaces.strings new file mode 100644 index 0000000..62af815 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/hu.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/id.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/id.lproj/GooglePlaces.strings new file mode 100644 index 0000000..dfc646d Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/id.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/it.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/it.lproj/GooglePlaces.strings new file mode 100644 index 0000000..fb40633 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/it.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ja.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ja.lproj/GooglePlaces.strings new file mode 100644 index 0000000..201ee48 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ja.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ko.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ko.lproj/GooglePlaces.strings new file mode 100644 index 0000000..d671954 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ko.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ms.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ms.lproj/GooglePlaces.strings new file mode 100644 index 0000000..4d41e13 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ms.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nb.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nb.lproj/GooglePlaces.strings new file mode 100644 index 0000000..5898919 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nb.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nl.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nl.lproj/GooglePlaces.strings new file mode 100644 index 0000000..54730fe Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/nl.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/oss_licenses_places.txt.gz b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/oss_licenses_places.txt.gz new file mode 100644 index 0000000..38ce7e0 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/oss_licenses_places.txt.gz differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pl.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pl.lproj/GooglePlaces.strings new file mode 100644 index 0000000..a5a8feb Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pl.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark.png new file mode 100644 index 0000000..5d10345 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@2x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@2x.png new file mode 100644 index 0000000..71f1c8e Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@2x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@3x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@3x.png new file mode 100644 index 0000000..6b8b9a6 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-dark@3x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light.png new file mode 100644 index 0000000..7e67463 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@2x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@2x.png new file mode 100644 index 0000000..50c3ace Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@2x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@3x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@3x.png new file mode 100644 index 0000000..bcafb09 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/powered-by-google-light@3x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt.lproj/GooglePlaces.strings new file mode 100644 index 0000000..b69a46c Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_BR.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_BR.lproj/GooglePlaces.strings new file mode 100644 index 0000000..b69a46c Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_BR.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_PT.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_PT.lproj/GooglePlaces.strings new file mode 100644 index 0000000..6fe0e89 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/pt_PT.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ro.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ro.lproj/GooglePlaces.strings new file mode 100644 index 0000000..d710300 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ro.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ru.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ru.lproj/GooglePlaces.strings new file mode 100644 index 0000000..4adff2f Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/ru.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud.png new file mode 100644 index 0000000..1c30882 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@2x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@2x.png new file mode 100644 index 0000000..e199181 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@2x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@3x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@3x.png new file mode 100644 index 0000000..ce3e035 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud@3x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark.png new file mode 100644 index 0000000..a16f5a2 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@2x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@2x.png new file mode 100644 index 0000000..e61cabd Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@2x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@3x.png b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@3x.png new file mode 100644 index 0000000..90d2380 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sad_cloud_dark@3x.png differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sk.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sk.lproj/GooglePlaces.strings new file mode 100644 index 0000000..28494a5 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sk.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sv.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sv.lproj/GooglePlaces.strings new file mode 100644 index 0000000..573dd48 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/sv.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/th.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/th.lproj/GooglePlaces.strings new file mode 100644 index 0000000..51f5c6d Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/th.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/tr.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/tr.lproj/GooglePlaces.strings new file mode 100644 index 0000000..e40fd3a Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/tr.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/uk.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/uk.lproj/GooglePlaces.strings new file mode 100644 index 0000000..b9bfea6 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/uk.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/vi.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/vi.lproj/GooglePlaces.strings new file mode 100644 index 0000000..ef9ad3a Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/vi.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_CN.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_CN.lproj/GooglePlaces.strings new file mode 100644 index 0000000..5e82314 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_CN.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_HK.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_HK.lproj/GooglePlaces.strings new file mode 100644 index 0000000..749cbcd Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_HK.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_TW.lproj/GooglePlaces.strings b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_TW.lproj/GooglePlaces.strings new file mode 100644 index 0000000..1b3d5b9 Binary files /dev/null and b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle/zh_TW.lproj/GooglePlaces.strings differ diff --git a/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/Current b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/Pods/GooglePlaces/Frameworks/GooglePlaces.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Pods/GooglePlaces/README.md b/Pods/GooglePlaces/README.md new file mode 100644 index 0000000..a9986fb --- /dev/null +++ b/Pods/GooglePlaces/README.md @@ -0,0 +1,111 @@ +# Google Places API for iOS + +**NOTE:** This pod is the official pod for the Google Places API for iOS. +Previously this pod was used by another developer, his content has been moved to +[Swift Google Maps API](https://github.com/honghaoz/Swift-Google-Maps-API) on +GitHub. + +This pod contains the Google Places API for iOS, supporting both Objective C and +Swift. + +Use the [Google Places API for iOS] +(https://developers.google.com/places/ios-api/) for exciting features based +on the user's location and Google's Places database. You can enable users to +add a place, autocomplete place names, use a place picker widget, identify +the user's current place or retrieve full details and photos of a place. + +The Google Places API for iOS is distributed as two Pods to allow developers to +have more control over what code is included in their apps. This helps to +create and distribute smaller apps. + +This Pod contains all the Google Places API for iOS functionality which does not +require a map. If you wish to use the [Place Picker] +(https://developers.google.com/places/ios-api/placepicker) in your app then you +should also add the [GooglePlacePicker Pod] +(https://cocoapods.org/pods/GooglePlacePicker). + +# Getting Started + +* *Guides*: Read our [Getting Started guides] + (https://developers.google.com/places/ios-api/start). +* *Demo Videos*: View [pre-recorded online demos] + (https://developers.google.com/places/ios-api/#demos). +* *Code samples*: In order to try out our demo app, run + + ``` + $ pod try GooglePlaces + ``` + + For a demo of the Place Picker component run + + ``` + $ pod try GooglePlacePicker + ``` + + and follow the instructions on our [developer pages] + (https://developers.google.com/places/ios-api/code-samples). + +* *Support*: Find support from various channels and communities. + + * Support pages for [Google Places API for iOS] + (https://developers.google.com/places/support). + * Stack Overflow, using the [google-places-api] + (https://stackoverflow.com/questions/tagged/google-places-api) tag. + +* *Report issues*: Use our issue tracker to [file a bug] + (https://code.google.com/p/gmaps-api-issues/issues/entry?template=Places%20API%20for%20iOS%20-%20Bug) + or a [feature request] + (https://code.google.com/p/gmaps-api-issues/issues/entry?template=Places%20API%20for%20iOS%20-%20Feature%20Request) + +# Installation + +To integrate Google Places API for iOS into your Xcode project using CocoaPods, +specify it in your `Podfile`: + +``` +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '7.0' +target 'YOUR_APPLICATION_TARGET_NAME_HERE' do + pod 'GooglePlaces' +end +``` + +if you are also using the Place Picker add: +``` +pod 'GooglePlacePicker' +``` + +Then, run the following command: + +``` +$ pod install +``` + +Before you can start using the API, you have to activate it in the [Google +Developer Console](https://console.developers.google.com/) and integrate the +respective API key in your project. For detailed installation instructions, +visit Google's Getting Started Guides for the [Google Places API for iOS] +(https://developers.google.com/places/ios-api/start). + +# Migration from version 1 + +If you are using the Google Places API for iOS as part of the Google Maps SDK +for iOS version 1 please check the [migration guide](https://developers.google.com/places/migrate-to-v2) +for more information on upgrading your project. + +# License and Terms of Service + +By using the Google Places API for iOS, you accept Google's Terms of +Service and Policies. Pay attention particularly to the following aspects: + +* Depending on your app and use case, you may be required to display + attribution. Read more about [attribution requirements] + (https://developers.google.com/places/ios-api/attributions). +* Your API usage is subject to quota limitations. Read more about [usage + limits](https://developers.google.com/places/ios-api/usage). +* The [Terms of Service](https://developers.google.com/maps/terms) are a + comprehensive description of the legal contract that you enter with Google + by using the Google Places API for iOS. You may want to pay special + attention to [section 10] + (https://developers.google.com/maps/terms#10-license-restrictions), as it + talks in detail about what you can do with the API, and what you can't. diff --git a/Pods/GoogleSymbolUtilities/Frameworks/frameworks/GoogleSymbolUtilities.framework/GoogleSymbolUtilities b/Pods/GoogleSymbolUtilities/Frameworks/frameworks/GoogleSymbolUtilities.framework/GoogleSymbolUtilities new file mode 100755 index 0000000..408a002 Binary files /dev/null and b/Pods/GoogleSymbolUtilities/Frameworks/frameworks/GoogleSymbolUtilities.framework/GoogleSymbolUtilities differ diff --git a/Pods/GoogleUtilities/Frameworks/frameworks/GoogleUtilities.framework/GoogleUtilities b/Pods/GoogleUtilities/Frameworks/frameworks/GoogleUtilities.framework/GoogleUtilities new file mode 100755 index 0000000..0fd17b6 Binary files /dev/null and b/Pods/GoogleUtilities/Frameworks/frameworks/GoogleUtilities.framework/GoogleUtilities differ diff --git a/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h new file mode 120000 index 0000000..f9dc7db --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.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/AFImageDownloader.h b/Pods/Headers/Private/AFNetworking/AFImageDownloader.h new file mode 120000 index 0000000..ce47c92 --- /dev/null +++ b/Pods/Headers/Private/AFNetworking/AFImageDownloader.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.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/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/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/ChameleonFramework/Chameleon.h b/Pods/Headers/Private/ChameleonFramework/Chameleon.h new file mode 120000 index 0000000..f0cd1b4 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/ChameleonConstants.h b/Pods/Headers/Private/ChameleonFramework/ChameleonConstants.h new file mode 120000 index 0000000..51aa580 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/ChameleonConstants.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/ChameleonEnums.h b/Pods/Headers/Private/ChameleonFramework/ChameleonEnums.h new file mode 120000 index 0000000..8dae61b --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/ChameleonEnums.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonEnums.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/ChameleonMacros.h b/Pods/Headers/Private/ChameleonFramework/ChameleonMacros.h new file mode 120000 index 0000000..f8f6295 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/ChameleonMacros.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonMacros.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/Chameleon_.h b/Pods/Headers/Private/ChameleonFramework/Chameleon_.h new file mode 120000 index 0000000..79c30ab --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/Chameleon_.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/NSArray+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/NSArray+Chameleon.h new file mode 120000 index 0000000..7ff10d4 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/NSArray+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIAppearance+Swift.h b/Pods/Headers/Private/ChameleonFramework/UIAppearance+Swift.h new file mode 120000 index 0000000..63a7d28 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIAppearance+Swift.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIButton+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/UIButton+Chameleon.h new file mode 120000 index 0000000..0fac48a --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIButton+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIColor+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/UIColor+Chameleon.h new file mode 120000 index 0000000..05e4379 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIColor+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIColor+ChameleonPrivate.h b/Pods/Headers/Private/ChameleonFramework/UIColor+ChameleonPrivate.h new file mode 120000 index 0000000..9b926b3 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIColor+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIImage+ChameleonPrivate.h b/Pods/Headers/Private/ChameleonFramework/UIImage+ChameleonPrivate.h new file mode 120000 index 0000000..c93db3d --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIImage+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UILabel+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/UILabel+Chameleon.h new file mode 120000 index 0000000..4350e1e --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UILabel+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UINavigationController+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/UINavigationController+Chameleon.h new file mode 120000 index 0000000..2052867 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UINavigationController+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIView+ChameleonPrivate.h b/Pods/Headers/Private/ChameleonFramework/UIView+ChameleonPrivate.h new file mode 120000 index 0000000..9713ec0 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIView+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Private/ChameleonFramework/UIViewController+Chameleon.h b/Pods/Headers/Private/ChameleonFramework/UIViewController+Chameleon.h new file mode 120000 index 0000000..be31ef4 --- /dev/null +++ b/Pods/Headers/Private/ChameleonFramework/UIViewController+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Private/FCAlertView/FCAlertView.h b/Pods/Headers/Private/FCAlertView/FCAlertView.h new file mode 120000 index 0000000..c2ef20f --- /dev/null +++ b/Pods/Headers/Private/FCAlertView/FCAlertView.h @@ -0,0 +1 @@ +../../../FCAlertView/FCAlertView/Classes/FCAlertView.h \ No newline at end of file diff --git a/Pods/Headers/Private/Firebase/Firebase.h b/Pods/Headers/Private/Firebase/Firebase.h new file mode 120000 index 0000000..6d62033 --- /dev/null +++ b/Pods/Headers/Private/Firebase/Firebase.h @@ -0,0 +1 @@ +../../../Firebase/Core/Sources/Firebase.h \ No newline at end of file diff --git a/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h b/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h new file mode 120000 index 0000000..0aaa73c --- /dev/null +++ b/Pods/Headers/Private/FirebaseInstanceID/FIRInstanceID.h @@ -0,0 +1 @@ +../../../FirebaseInstanceID/Sources/FIRInstanceID.h \ No newline at end of file diff --git a/Pods/Headers/Private/FirebaseMessaging/FIRMessaging.h b/Pods/Headers/Private/FirebaseMessaging/FIRMessaging.h new file mode 120000 index 0000000..54f43c5 --- /dev/null +++ b/Pods/Headers/Private/FirebaseMessaging/FIRMessaging.h @@ -0,0 +1 @@ +../../../FirebaseMessaging/Sources/FIRMessaging.h \ No newline at end of file diff --git a/Pods/Headers/Private/MBProgressHUD/MBProgressHUD.h b/Pods/Headers/Private/MBProgressHUD/MBProgressHUD.h new file mode 120000 index 0000000..19ed4db --- /dev/null +++ b/Pods/Headers/Private/MBProgressHUD/MBProgressHUD.h @@ -0,0 +1 @@ +../../../MBProgressHUD/MBProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMAccessor.h b/Pods/Headers/Private/Realm/RLMAccessor.h new file mode 120000 index 0000000..33b603a --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMAccessor.h @@ -0,0 +1 @@ +../../../Realm/include/RLMAccessor.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMArray.h b/Pods/Headers/Private/Realm/RLMArray.h new file mode 120000 index 0000000..886d1ad --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMArray.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMArray_Private.h b/Pods/Headers/Private/Realm/RLMArray_Private.h new file mode 120000 index 0000000..ac4f7a6 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMArray_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMCollection.h b/Pods/Headers/Private/Realm/RLMCollection.h new file mode 120000 index 0000000..781f825 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMCollection.h @@ -0,0 +1 @@ +../../../Realm/include/RLMCollection.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMConstants.h b/Pods/Headers/Private/Realm/RLMConstants.h new file mode 120000 index 0000000..7f3eabc --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMConstants.h @@ -0,0 +1 @@ +../../../Realm/include/RLMConstants.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMListBase.h b/Pods/Headers/Private/Realm/RLMListBase.h new file mode 120000 index 0000000..43725c7 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMListBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMListBase.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMMigration.h b/Pods/Headers/Private/Realm/RLMMigration.h new file mode 120000 index 0000000..310bdf0 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMMigration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMMigration_Private.h b/Pods/Headers/Private/Realm/RLMMigration_Private.h new file mode 120000 index 0000000..43babfb --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMMigration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObject.h b/Pods/Headers/Private/Realm/RLMObject.h new file mode 120000 index 0000000..79652c6 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObject.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObjectBase.h b/Pods/Headers/Private/Realm/RLMObjectBase.h new file mode 120000 index 0000000..fd12a13 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObjectBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h b/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h new file mode 120000 index 0000000..ff46837 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObjectBase_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase_Dynamic.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObjectSchema.h b/Pods/Headers/Private/Realm/RLMObjectSchema.h new file mode 120000 index 0000000..009a76e --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObjectSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h b/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h new file mode 120000 index 0000000..b580971 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObjectSchema_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObjectStore.h b/Pods/Headers/Private/Realm/RLMObjectStore.h new file mode 120000 index 0000000..e94e3dd --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObjectStore.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectStore.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMObject_Private.h b/Pods/Headers/Private/Realm/RLMObject_Private.h new file mode 120000 index 0000000..d796d64 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMObject_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMOptionalBase.h b/Pods/Headers/Private/Realm/RLMOptionalBase.h new file mode 120000 index 0000000..13a480e --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMOptionalBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMOptionalBase.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMPlatform.h b/Pods/Headers/Private/Realm/RLMPlatform.h new file mode 120000 index 0000000..41aa0f4 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMPlatform.h @@ -0,0 +1 @@ +../../../Realm/include/RLMPlatform.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMProperty.h b/Pods/Headers/Private/Realm/RLMProperty.h new file mode 120000 index 0000000..a772ea7 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMProperty.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMProperty_Private.h b/Pods/Headers/Private/Realm/RLMProperty_Private.h new file mode 120000 index 0000000..bc94be4 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMProperty_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealm.h b/Pods/Headers/Private/Realm/RLMRealm.h new file mode 120000 index 0000000..3df9e03 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealm.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h b/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h new file mode 120000 index 0000000..ab65245 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealmConfiguration+Sync.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration+Sync.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealmConfiguration.h b/Pods/Headers/Private/Realm/RLMRealmConfiguration.h new file mode 120000 index 0000000..26d7c63 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealmConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h b/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h new file mode 120000 index 0000000..de82958 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealmConfiguration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h b/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h new file mode 120000 index 0000000..6a71a01 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealm_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Dynamic.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMRealm_Private.h b/Pods/Headers/Private/Realm/RLMRealm_Private.h new file mode 120000 index 0000000..7c23d77 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMRealm_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMResults.h b/Pods/Headers/Private/Realm/RLMResults.h new file mode 120000 index 0000000..6da1f7f --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMResults.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMResults_Private.h b/Pods/Headers/Private/Realm/RLMResults_Private.h new file mode 120000 index 0000000..177a372 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMResults_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSchema.h b/Pods/Headers/Private/Realm/RLMSchema.h new file mode 120000 index 0000000..a460968 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSchema_Private.h b/Pods/Headers/Private/Realm/RLMSchema_Private.h new file mode 120000 index 0000000..f4cea77 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSchema_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncConfiguration.h b/Pods/Headers/Private/Realm/RLMSyncConfiguration.h new file mode 120000 index 0000000..8762b49 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h b/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h new file mode 120000 index 0000000..ce62fbd --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncConfiguration_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncCredential.h b/Pods/Headers/Private/Realm/RLMSyncCredential.h new file mode 120000 index 0000000..d797aa4 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncCredential.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncCredential.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncManager.h b/Pods/Headers/Private/Realm/RLMSyncManager.h new file mode 120000 index 0000000..27ac52f --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncManager.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncManager.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncSession.h b/Pods/Headers/Private/Realm/RLMSyncSession.h new file mode 120000 index 0000000..9aba909 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncSession.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncSession.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncSession_Private.h b/Pods/Headers/Private/Realm/RLMSyncSession_Private.h new file mode 120000 index 0000000..2470865 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncSession_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncSession_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncUser.h b/Pods/Headers/Private/Realm/RLMSyncUser.h new file mode 120000 index 0000000..841417a --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncUser.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUser.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncUtil.h b/Pods/Headers/Private/Realm/RLMSyncUtil.h new file mode 120000 index 0000000..c78eb1c --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncUtil.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h b/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h new file mode 120000 index 0000000..121d2c2 --- /dev/null +++ b/Pods/Headers/Private/Realm/RLMSyncUtil_Private.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil_Private.h \ No newline at end of file diff --git a/Pods/Headers/Private/Realm/Realm.h b/Pods/Headers/Private/Realm/Realm.h new file mode 120000 index 0000000..eb4a08b --- /dev/null +++ b/Pods/Headers/Private/Realm/Realm.h @@ -0,0 +1 @@ +../../../Realm/include/Realm.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h b/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h new file mode 120000 index 0000000..55a38a2 --- /dev/null +++ b/Pods/Headers/Private/SVProgressHUD/SVIndefiniteAnimatedView.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h b/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h new file mode 120000 index 0000000..581cdc2 --- /dev/null +++ b/Pods/Headers/Private/SVProgressHUD/SVProgressAnimatedView.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h b/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h new file mode 120000 index 0000000..608a8aa --- /dev/null +++ b/Pods/Headers/Private/SVProgressHUD/SVProgressHUD.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h b/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h new file mode 120000 index 0000000..d78beb5 --- /dev/null +++ b/Pods/Headers/Private/SVProgressHUD/SVRadialGradientLayer.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h b/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h new file mode 120000 index 0000000..f9dc7db --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFAutoPurgingImageCache.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h b/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h new file mode 120000 index 0000000..56feb9f --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFHTTPSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFImageDownloader.h b/Pods/Headers/Public/AFNetworking/AFImageDownloader.h new file mode 120000 index 0000000..ce47c92 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFImageDownloader.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFImageDownloader.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h new file mode 120000 index 0000000..67519d9 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h b/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h new file mode 120000 index 0000000..68fc774 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworkReachabilityManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFNetworking.h b/Pods/Headers/Public/AFNetworking/AFNetworking.h new file mode 120000 index 0000000..a5a38da --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h b/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h new file mode 120000 index 0000000..fd1322d --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFSecurityPolicy.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h new file mode 120000 index 0000000..ca8209b --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLRequestSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h b/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h new file mode 120000 index 0000000..e36a765 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLResponseSerialization.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h b/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h new file mode 120000 index 0000000..835101d --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h @@ -0,0 +1 @@ +../../../AFNetworking/AFNetworking/AFURLSessionManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h new file mode 120000 index 0000000..c534ebf --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h new file mode 120000 index 0000000..8f2e221 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h new file mode 120000 index 0000000..74f6649 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIImage+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h new file mode 120000 index 0000000..a95d673 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h new file mode 120000 index 0000000..95017cc --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h new file mode 120000 index 0000000..730b167 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h new file mode 120000 index 0000000..8efd826 --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h b/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h new file mode 120000 index 0000000..c8df6ef --- /dev/null +++ b/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h @@ -0,0 +1 @@ +../../../AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/Chameleon.h b/Pods/Headers/Public/ChameleonFramework/Chameleon.h new file mode 120000 index 0000000..f0cd1b4 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/ChameleonConstants.h b/Pods/Headers/Public/ChameleonFramework/ChameleonConstants.h new file mode 120000 index 0000000..51aa580 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/ChameleonConstants.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonConstants.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/ChameleonEnums.h b/Pods/Headers/Public/ChameleonFramework/ChameleonEnums.h new file mode 120000 index 0000000..8dae61b --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/ChameleonEnums.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonEnums.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/ChameleonMacros.h b/Pods/Headers/Public/ChameleonFramework/ChameleonMacros.h new file mode 120000 index 0000000..f8f6295 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/ChameleonMacros.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/ChameleonMacros.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/Chameleon_.h b/Pods/Headers/Public/ChameleonFramework/Chameleon_.h new file mode 120000 index 0000000..79c30ab --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/Chameleon_.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/Chameleon_.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/NSArray+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/NSArray+Chameleon.h new file mode 120000 index 0000000..7ff10d4 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/NSArray+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/NSArray+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIAppearance+Swift.h b/Pods/Headers/Public/ChameleonFramework/UIAppearance+Swift.h new file mode 120000 index 0000000..63a7d28 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIAppearance+Swift.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIAppearance+Swift.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIButton+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/UIButton+Chameleon.h new file mode 120000 index 0000000..0fac48a --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIButton+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIButton+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIColor+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/UIColor+Chameleon.h new file mode 120000 index 0000000..05e4379 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIColor+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIColor+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIColor+ChameleonPrivate.h b/Pods/Headers/Public/ChameleonFramework/UIColor+ChameleonPrivate.h new file mode 120000 index 0000000..9b926b3 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIColor+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIImage+ChameleonPrivate.h b/Pods/Headers/Public/ChameleonFramework/UIImage+ChameleonPrivate.h new file mode 120000 index 0000000..c93db3d --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIImage+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UILabel+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/UILabel+Chameleon.h new file mode 120000 index 0000000..4350e1e --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UILabel+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UILabel+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UINavigationController+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/UINavigationController+Chameleon.h new file mode 120000 index 0000000..2052867 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UINavigationController+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UINavigationController+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIView+ChameleonPrivate.h b/Pods/Headers/Public/ChameleonFramework/UIView+ChameleonPrivate.h new file mode 120000 index 0000000..9713ec0 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIView+ChameleonPrivate.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIView+ChameleonPrivate.h \ No newline at end of file diff --git a/Pods/Headers/Public/ChameleonFramework/UIViewController+Chameleon.h b/Pods/Headers/Public/ChameleonFramework/UIViewController+Chameleon.h new file mode 120000 index 0000000..be31ef4 --- /dev/null +++ b/Pods/Headers/Public/ChameleonFramework/UIViewController+Chameleon.h @@ -0,0 +1 @@ +../../../ChameleonFramework/Pod/Classes/Objective-C/UIViewController+Chameleon.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/ANSCompatibility.h b/Pods/Headers/Public/Crashlytics/Crashlytics/ANSCompatibility.h new file mode 120000 index 0000000..1acf6df --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/ANSCompatibility.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/Answers.h b/Pods/Headers/Public/Crashlytics/Crashlytics/Answers.h new file mode 120000 index 0000000..c1a493b --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/Answers.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/CLSAttributes.h b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSAttributes.h new file mode 120000 index 0000000..04f0d32 --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSAttributes.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/CLSLogging.h b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSLogging.h new file mode 120000 index 0000000..97a37ef --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSLogging.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/CLSReport.h b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSReport.h new file mode 120000 index 0000000..77fdb84 --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSReport.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/CLSStackFrame.h b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSStackFrame.h new file mode 120000 index 0000000..28f3e18 --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/CLSStackFrame.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h \ No newline at end of file diff --git a/Pods/Headers/Public/Crashlytics/Crashlytics/Crashlytics.h b/Pods/Headers/Public/Crashlytics/Crashlytics/Crashlytics.h new file mode 120000 index 0000000..fae630e --- /dev/null +++ b/Pods/Headers/Public/Crashlytics/Crashlytics/Crashlytics.h @@ -0,0 +1 @@ +../../../../Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h \ No newline at end of file diff --git a/Pods/Headers/Public/FCAlertView/FCAlertView.h b/Pods/Headers/Public/FCAlertView/FCAlertView.h new file mode 120000 index 0000000..c2ef20f --- /dev/null +++ b/Pods/Headers/Public/FCAlertView/FCAlertView.h @@ -0,0 +1 @@ +../../../FCAlertView/FCAlertView/Classes/FCAlertView.h \ No newline at end of file diff --git a/Pods/Headers/Public/Fabric/Fabric/FABAttributes.h b/Pods/Headers/Public/Fabric/Fabric/FABAttributes.h new file mode 120000 index 0000000..a01e803 --- /dev/null +++ b/Pods/Headers/Public/Fabric/Fabric/FABAttributes.h @@ -0,0 +1 @@ +../../../../Fabric/iOS/Fabric.framework/Headers/FABAttributes.h \ No newline at end of file diff --git a/Pods/Headers/Public/Fabric/Fabric/Fabric.h b/Pods/Headers/Public/Fabric/Fabric/Fabric.h new file mode 120000 index 0000000..d4ddd5e --- /dev/null +++ b/Pods/Headers/Public/Fabric/Fabric/Fabric.h @@ -0,0 +1 @@ +../../../../Fabric/iOS/Fabric.framework/Headers/Fabric.h \ No newline at end of file diff --git a/Pods/Headers/Public/Firebase/Firebase.h b/Pods/Headers/Public/Firebase/Firebase.h new file mode 120000 index 0000000..6d62033 --- /dev/null +++ b/Pods/Headers/Public/Firebase/Firebase.h @@ -0,0 +1 @@ +../../../Firebase/Core/Sources/Firebase.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics+AppDelegate.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics+AppDelegate.h new file mode 120000 index 0000000..5ea8208 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics+AppDelegate.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics.h new file mode 120000 index 0000000..92d9b85 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalytics.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalyticsConfiguration.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalyticsConfiguration.h new file mode 120000 index 0000000..1e35aa2 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRAnalyticsConfiguration.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRApp.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRApp.h new file mode 120000 index 0000000..a4ca439 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRApp.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRApp.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRConfiguration.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRConfiguration.h new file mode 120000 index 0000000..4c29bba --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRConfiguration.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIREventNames.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIREventNames.h new file mode 120000 index 0000000..75aea08 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIREventNames.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIROptions.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIROptions.h new file mode 120000 index 0000000..eb7d9f5 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIROptions.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIROptions.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRParameterNames.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRParameterNames.h new file mode 120000 index 0000000..c5f352c --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRParameterNames.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRUserPropertyNames.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRUserPropertyNames.h new file mode 120000 index 0000000..6e26060 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FIRUserPropertyNames.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FirebaseAnalytics.h b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FirebaseAnalytics.h new file mode 120000 index 0000000..0859122 --- /dev/null +++ b/Pods/Headers/Public/FirebaseAnalytics/FirebaseAnalytics/FirebaseAnalytics.h @@ -0,0 +1 @@ +../../../../FirebaseAnalytics/Frameworks/frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRAnalyticsConfiguration.h b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRAnalyticsConfiguration.h new file mode 120000 index 0000000..52778f0 --- /dev/null +++ b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRAnalyticsConfiguration.h @@ -0,0 +1 @@ +../../../../FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRApp.h b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRApp.h new file mode 120000 index 0000000..a0b1657 --- /dev/null +++ b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRApp.h @@ -0,0 +1 @@ +../../../../FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRApp.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRConfiguration.h b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRConfiguration.h new file mode 120000 index 0000000..fd1cfbf --- /dev/null +++ b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIRConfiguration.h @@ -0,0 +1 @@ +../../../../FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIRConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIROptions.h b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIROptions.h new file mode 120000 index 0000000..39759eb --- /dev/null +++ b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FIROptions.h @@ -0,0 +1 @@ +../../../../FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FIROptions.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseCore/FirebaseCore/FirebaseCore.h b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FirebaseCore.h new file mode 120000 index 0000000..8f62016 --- /dev/null +++ b/Pods/Headers/Public/FirebaseCore/FirebaseCore/FirebaseCore.h @@ -0,0 +1 @@ +../../../../FirebaseCore/Frameworks/frameworks/FirebaseCore.framework/Headers/FirebaseCore.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h b/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h new file mode 120000 index 0000000..0aaa73c --- /dev/null +++ b/Pods/Headers/Public/FirebaseInstanceID/FIRInstanceID.h @@ -0,0 +1 @@ +../../../FirebaseInstanceID/Sources/FIRInstanceID.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FIRInstanceID.h b/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FIRInstanceID.h new file mode 120000 index 0000000..61992a3 --- /dev/null +++ b/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FIRInstanceID.h @@ -0,0 +1 @@ +../../../../FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FIRInstanceID.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FirebaseInstanceID.h b/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FirebaseInstanceID.h new file mode 120000 index 0000000..9c6ce35 --- /dev/null +++ b/Pods/Headers/Public/FirebaseInstanceID/FirebaseInstanceID/FirebaseInstanceID.h @@ -0,0 +1 @@ +../../../../FirebaseInstanceID/Frameworks/frameworks/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseMessaging/FIRMessaging.h b/Pods/Headers/Public/FirebaseMessaging/FIRMessaging.h new file mode 120000 index 0000000..54f43c5 --- /dev/null +++ b/Pods/Headers/Public/FirebaseMessaging/FIRMessaging.h @@ -0,0 +1 @@ +../../../FirebaseMessaging/Sources/FIRMessaging.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FIRMessaging.h b/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FIRMessaging.h new file mode 120000 index 0000000..096b7f1 --- /dev/null +++ b/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FIRMessaging.h @@ -0,0 +1 @@ +../../../../FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FIRMessaging.h \ No newline at end of file diff --git a/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FirebaseMessaging.h b/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FirebaseMessaging.h new file mode 120000 index 0000000..fbc9c39 --- /dev/null +++ b/Pods/Headers/Public/FirebaseMessaging/FirebaseMessaging/FirebaseMessaging.h @@ -0,0 +1 @@ +../../../../FirebaseMessaging/Frameworks/frameworks/FirebaseMessaging.framework/Headers/FirebaseMessaging.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..e5e6f0d --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSAddress.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSAddress.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..0d833e2 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCALayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..c25833d --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraPosition.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..19cef17 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCameraUpdate.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..ed0bf8f --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCircle.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCircle.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds+GoogleMaps.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds+GoogleMaps.h new file mode 120000 index 0000000..e2dbc6f --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSCoordinateBounds+GoogleMaps.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSCoordinateBounds+GoogleMaps.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSDeprecationMacros.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSDeprecationMacros.h new file mode 120000 index 0000000..e9bcfb4 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSDeprecationMacros.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSDeprecationMacros.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..262a954 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeocoder.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..f32fa6e --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGeometryUtils.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..aae66e6 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSGroundOverlay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..8a6c57c --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorBuilding.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..22ccfba --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorDisplay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..e1bc766 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSIndoorLevel.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..a2eb0af --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapLayer.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapStyle.h b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapStyle.h new file mode 120000 index 0000000..d3e2ab8 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapStyle.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSMapStyle.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..3706068 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView+Animation.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..9bd2efa --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMapView.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..884b0db --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarker.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..f8b612e --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMarkerLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..8b08ad3 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSMutablePath.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..9ff4860 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOrientation.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..b5857a9 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSOverlay.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..0ead3d5 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanorama.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..6795c1a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCamera.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..fad7176 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaCameraUpdate.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..5d3bbcc --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..c841ae3 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaLink.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..f99813b --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaService.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..063d048 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPanoramaView.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..9864f1b --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPath.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSPath.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..9e7f35a --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolygon.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..83a281c --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSPolyline.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..0960085 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSProjection.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..cd0afa2 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSServices.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..0efad62 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSSyncTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..db57fd1 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..3c79459 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSUISettings.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/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..d561877 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GMSURLTileLayer.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GMSURLTileLayer.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..f3bdb3f --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMaps/GoogleMaps.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Headers/GoogleMaps.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCompatabilityMacros.h b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCompatabilityMacros.h new file mode 120000 index 0000000..025aa4b --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCompatabilityMacros.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCompatabilityMacros.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCoordinateBounds.h b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCoordinateBounds.h new file mode 120000 index 0000000..f674eea --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GMSCoordinateBounds.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GMSCoordinateBounds.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GoogleMapsBase.h b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GoogleMapsBase.h new file mode 120000 index 0000000..585f425 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMapsBase/GoogleMapsBase.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Base/Frameworks/GoogleMapsBase.framework/Versions/A/Headers/GoogleMapsBase.h \ No newline at end of file diff --git a/Pods/Headers/Public/GoogleMaps/GoogleMapsCore/GoogleMapsCore.h b/Pods/Headers/Public/GoogleMaps/GoogleMapsCore/GoogleMapsCore.h new file mode 120000 index 0000000..f9d86e0 --- /dev/null +++ b/Pods/Headers/Public/GoogleMaps/GoogleMapsCore/GoogleMapsCore.h @@ -0,0 +1 @@ +../../../../GoogleMaps/Subspecs/Maps/Frameworks/GoogleMapsCore.framework/Versions/A/Headers/GoogleMapsCore.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAddressComponent.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAddressComponent.h new file mode 120000 index 0000000..3f8321d --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAddressComponent.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAddressComponent.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFetcher.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFetcher.h new file mode 120000 index 0000000..85a3218 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFetcher.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFetcher.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFilter.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFilter.h new file mode 120000 index 0000000..0f0418c --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteFilter.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteFilter.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteMatchFragment.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteMatchFragment.h new file mode 120000 index 0000000..2a14e8d --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteMatchFragment.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteMatchFragment.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompletePrediction.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompletePrediction.h new file mode 120000 index 0000000..4dc2cee --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompletePrediction.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompletePrediction.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteResultsViewController.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteResultsViewController.h new file mode 120000 index 0000000..3a343c5 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteResultsViewController.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteResultsViewController.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteTableDataSource.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteTableDataSource.h new file mode 120000 index 0000000..43bb9b3 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteTableDataSource.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteTableDataSource.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteViewController.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteViewController.h new file mode 120000 index 0000000..638e1db --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSAutocompleteViewController.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSAutocompleteViewController.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlace.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlace.h new file mode 120000 index 0000000..34f08ef --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlace.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlace.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihood.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihood.h new file mode 120000 index 0000000..f788dcb --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihood.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihood.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihoodList.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihoodList.h new file mode 120000 index 0000000..e121e5b --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceLikelihoodList.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceLikelihoodList.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadata.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadata.h new file mode 120000 index 0000000..be94d1a --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadata.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadata.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadataList.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadataList.h new file mode 120000 index 0000000..8a842f3 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacePhotoMetadataList.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacePhotoMetadataList.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceTypes.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceTypes.h new file mode 120000 index 0000000..7ae39eb --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlaceTypes.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlaceTypes.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesClient.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesClient.h new file mode 120000 index 0000000..5371534 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesClient.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesClient.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesErrors.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesErrors.h new file mode 120000 index 0000000..97d0a63 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSPlacesErrors.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSPlacesErrors.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSUserAddedPlace.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSUserAddedPlace.h new file mode 120000 index 0000000..0d512ba --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GMSUserAddedPlace.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GMSUserAddedPlace.h \ No newline at end of file diff --git a/Pods/Headers/Public/GooglePlaces/GooglePlaces/GooglePlaces.h b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GooglePlaces.h new file mode 120000 index 0000000..d645f54 --- /dev/null +++ b/Pods/Headers/Public/GooglePlaces/GooglePlaces/GooglePlaces.h @@ -0,0 +1 @@ +../../../../GooglePlaces/Frameworks/GooglePlaces.framework/Versions/A/Headers/GooglePlaces.h \ No newline at end of file diff --git a/Pods/Headers/Public/MBProgressHUD/MBProgressHUD.h b/Pods/Headers/Public/MBProgressHUD/MBProgressHUD.h new file mode 120000 index 0000000..19ed4db --- /dev/null +++ b/Pods/Headers/Public/MBProgressHUD/MBProgressHUD.h @@ -0,0 +1 @@ +../../../MBProgressHUD/MBProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMArray.h b/Pods/Headers/Public/Realm/RLMArray.h new file mode 120000 index 0000000..886d1ad --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMArray.h @@ -0,0 +1 @@ +../../../Realm/include/RLMArray.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMCollection.h b/Pods/Headers/Public/Realm/RLMCollection.h new file mode 120000 index 0000000..781f825 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMCollection.h @@ -0,0 +1 @@ +../../../Realm/include/RLMCollection.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMConstants.h b/Pods/Headers/Public/Realm/RLMConstants.h new file mode 120000 index 0000000..7f3eabc --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMConstants.h @@ -0,0 +1 @@ +../../../Realm/include/RLMConstants.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMListBase.h b/Pods/Headers/Public/Realm/RLMListBase.h new file mode 120000 index 0000000..43725c7 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMListBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMListBase.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMMigration.h b/Pods/Headers/Public/Realm/RLMMigration.h new file mode 120000 index 0000000..310bdf0 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMMigration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMMigration.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMObject.h b/Pods/Headers/Public/Realm/RLMObject.h new file mode 120000 index 0000000..79652c6 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMObject.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObject.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMObjectBase.h b/Pods/Headers/Public/Realm/RLMObjectBase.h new file mode 120000 index 0000000..fd12a13 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMObjectBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h b/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h new file mode 120000 index 0000000..ff46837 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMObjectBase_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectBase_Dynamic.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMObjectSchema.h b/Pods/Headers/Public/Realm/RLMObjectSchema.h new file mode 120000 index 0000000..009a76e --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMObjectSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMObjectSchema.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMOptionalBase.h b/Pods/Headers/Public/Realm/RLMOptionalBase.h new file mode 120000 index 0000000..13a480e --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMOptionalBase.h @@ -0,0 +1 @@ +../../../Realm/include/RLMOptionalBase.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMPlatform.h b/Pods/Headers/Public/Realm/RLMPlatform.h new file mode 120000 index 0000000..41aa0f4 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMPlatform.h @@ -0,0 +1 @@ +../../../Realm/include/RLMPlatform.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMProperty.h b/Pods/Headers/Public/Realm/RLMProperty.h new file mode 120000 index 0000000..a772ea7 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMProperty.h @@ -0,0 +1 @@ +../../../Realm/include/RLMProperty.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMRealm.h b/Pods/Headers/Public/Realm/RLMRealm.h new file mode 120000 index 0000000..3df9e03 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMRealm.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h b/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h new file mode 120000 index 0000000..ab65245 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMRealmConfiguration+Sync.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration+Sync.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMRealmConfiguration.h b/Pods/Headers/Public/Realm/RLMRealmConfiguration.h new file mode 120000 index 0000000..26d7c63 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMRealmConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealmConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h b/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h new file mode 120000 index 0000000..6a71a01 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMRealm_Dynamic.h @@ -0,0 +1 @@ +../../../Realm/include/RLMRealm_Dynamic.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMResults.h b/Pods/Headers/Public/Realm/RLMResults.h new file mode 120000 index 0000000..6da1f7f --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMResults.h @@ -0,0 +1 @@ +../../../Realm/include/RLMResults.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSchema.h b/Pods/Headers/Public/Realm/RLMSchema.h new file mode 120000 index 0000000..a460968 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSchema.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSchema.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncConfiguration.h b/Pods/Headers/Public/Realm/RLMSyncConfiguration.h new file mode 120000 index 0000000..8762b49 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncConfiguration.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncConfiguration.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncCredential.h b/Pods/Headers/Public/Realm/RLMSyncCredential.h new file mode 120000 index 0000000..d797aa4 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncCredential.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncCredential.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncManager.h b/Pods/Headers/Public/Realm/RLMSyncManager.h new file mode 120000 index 0000000..27ac52f --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncManager.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncManager.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncSession.h b/Pods/Headers/Public/Realm/RLMSyncSession.h new file mode 120000 index 0000000..9aba909 --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncSession.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncSession.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncUser.h b/Pods/Headers/Public/Realm/RLMSyncUser.h new file mode 120000 index 0000000..841417a --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncUser.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUser.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/RLMSyncUtil.h b/Pods/Headers/Public/Realm/RLMSyncUtil.h new file mode 120000 index 0000000..c78eb1c --- /dev/null +++ b/Pods/Headers/Public/Realm/RLMSyncUtil.h @@ -0,0 +1 @@ +../../../Realm/include/RLMSyncUtil.h \ No newline at end of file diff --git a/Pods/Headers/Public/Realm/Realm.h b/Pods/Headers/Public/Realm/Realm.h new file mode 120000 index 0000000..eb4a08b --- /dev/null +++ b/Pods/Headers/Public/Realm/Realm.h @@ -0,0 +1 @@ +../../../Realm/include/Realm.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h b/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h new file mode 120000 index 0000000..55a38a2 --- /dev/null +++ b/Pods/Headers/Public/SVProgressHUD/SVIndefiniteAnimatedView.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h b/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h new file mode 120000 index 0000000..581cdc2 --- /dev/null +++ b/Pods/Headers/Public/SVProgressHUD/SVProgressAnimatedView.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h b/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h new file mode 120000 index 0000000..608a8aa --- /dev/null +++ b/Pods/Headers/Public/SVProgressHUD/SVProgressHUD.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVProgressHUD.h \ No newline at end of file diff --git a/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h b/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h new file mode 120000 index 0000000..d78beb5 --- /dev/null +++ b/Pods/Headers/Public/SVProgressHUD/SVRadialGradientLayer.h @@ -0,0 +1 @@ +../../../SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h \ No newline at end of file diff --git a/Pods/Local Podspecs/RSBarcodes_Swift.podspec.json b/Pods/Local Podspecs/RSBarcodes_Swift.podspec.json new file mode 100644 index 0000000..bcee180 --- /dev/null +++ b/Pods/Local Podspecs/RSBarcodes_Swift.podspec.json @@ -0,0 +1,27 @@ +{ + "name": "RSBarcodes_Swift", + "version": "0.0.9", + "summary": "1D and 2D barcodes reader and generators for iOS 8 with delightful controls. Now Swift.", + "homepage": "https://github.com/yeahdongcn/RSBarcodes_Swift", + "license": { + "type": "MIT", + "file": "LICENSE.md" + }, + "authors": { + "R0CKSTAR": "yeahdongcn@gmail.com" + }, + "platforms": { + "ios": "8.0" + }, + "source": { + "git": "https://github.com/yeahdongcn/RSBarcodes_Swift.git", + "tag": "0.0.9" + }, + "source_files": "Source/*.swift", + "frameworks": [ + "CoreImage", + "AVFoundation", + "QuartzCore" + ], + "requires_arc": true +} diff --git a/Pods/Local Podspecs/SwiftyJSON.podspec.json b/Pods/Local Podspecs/SwiftyJSON.podspec.json new file mode 100644 index 0000000..358633d --- /dev/null +++ b/Pods/Local Podspecs/SwiftyJSON.podspec.json @@ -0,0 +1,23 @@ +{ + "name": "SwiftyJSON", + "version": "2.3.0", + "summary": "SwiftyJSON makes it easy to deal with JSON data in Swift", + "homepage": "https://github.com/SwiftyJSON/SwiftyJSON", + "license": { + "type": "MIT" + }, + "authors": { + "lingoer": "lingoerer@gmail.com", + "tangplin": "tangplin@gmail.com" + }, + "requires_arc": true, + "platforms": { + "osx": "10.9", + "ios": "8.0" + }, + "source": { + "git": "https://github.com/SwiftyJSON/SwiftyJSON.git", + "tag": "2.3.0" + }, + "source_files": "Source/*.swift" +} diff --git a/Pods/MBProgressHUD/LICENSE b/Pods/MBProgressHUD/LICENSE new file mode 100644 index 0000000..1c0d59b --- /dev/null +++ b/Pods/MBProgressHUD/LICENSE @@ -0,0 +1,19 @@ +Copyright © 2009-2016 Matej Bukovinski + +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. \ No newline at end of file diff --git a/Pods/MBProgressHUD/MBProgressHUD.h b/Pods/MBProgressHUD/MBProgressHUD.h new file mode 100644 index 0000000..a10744c --- /dev/null +++ b/Pods/MBProgressHUD/MBProgressHUD.h @@ -0,0 +1,435 @@ +// +// MBProgressHUD.h +// Version 1.0.0 +// Created by Matej Bukovinski on 2.4.09. +// + +// This code is distributed under the terms and conditions of the MIT license. + +// Copyright © 2009-2016 Matej Bukovinski +// +// 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 + +@class MBBackgroundView; +@protocol MBProgressHUDDelegate; + + +extern CGFloat const MBProgressMaxOffset; + +typedef NS_ENUM(NSInteger, MBProgressHUDMode) { + /// UIActivityIndicatorView. + MBProgressHUDModeIndeterminate, + /// A round, pie-chart like, progress view. + MBProgressHUDModeDeterminate, + /// Horizontal progress bar. + MBProgressHUDModeDeterminateHorizontalBar, + /// Ring-shaped progress view. + MBProgressHUDModeAnnularDeterminate, + /// Shows a custom view. + MBProgressHUDModeCustomView, + /// Shows only labels. + MBProgressHUDModeText +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) { + /// Opacity animation + MBProgressHUDAnimationFade, + /// Opacity + scale animation (zoom in when appearing zoom out when disappearing) + MBProgressHUDAnimationZoom, + /// Opacity + scale animation (zoom out style) + MBProgressHUDAnimationZoomOut, + /// Opacity + scale animation (zoom in style) + MBProgressHUDAnimationZoomIn +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDBackgroundStyle) { + /// Solid color background + MBProgressHUDBackgroundStyleSolidColor, + /// UIVisualEffectView or UIToolbar.layer background view + MBProgressHUDBackgroundStyleBlur +}; + +typedef void (^MBProgressHUDCompletionBlock)(); + + +NS_ASSUME_NONNULL_BEGIN + + +/** + * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. + * + * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. + * The MBProgressHUD window spans over the entire space given to it by the initWithFrame: constructor and catches all + * user input on this region, thereby preventing the user operations on components below the view. + * + * @note To still allow touches to pass through the HUD, you can set hud.userInteractionEnabled = NO. + * @attention MBProgressHUD is a UI class and should therefore only be accessed on the main thread. + */ +@interface MBProgressHUD : UIView + +/** + * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that the HUD will be added to + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * @return A reference to the created HUD. + * + * @see hideHUDForView:animated: + * @see animationType + */ ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; + +/// @name Showing and hiding + +/** + * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that is going to be searched for a HUD subview. + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @return YES if a HUD was found and removed, NO otherwise. + * + * @see showHUDAddedTo:animated: + * @see animationType + */ ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; + +/** + * Finds the top-most HUD subview and returns it. + * + * @param view The view that is going to be searched. + * @return A reference to the last HUD subview discovered. + */ ++ (nullable MBProgressHUD *)HUDForView:(UIView *)view; + +/** + * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with + * view.bounds as the parameter. + * + * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as + * the HUD's superview (i.e., the view that the HUD will be added to). + */ +- (instancetype)initWithView:(UIView *)view; + +/** + * Displays the HUD. + * + * @note You need to make sure that the main thread completes its run loop soon after this method call so that + * the user interface can be updated. Call this method when your task is already set up to be executed in a new thread + * (e.g., when using something like NSOperation or making an asynchronous call like NSURLRequest). + * + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * + * @see animationType + */ +- (void)showAnimated:(BOOL)animated; + +/** + * Hides the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated; + +/** + * Hides the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @param delay Delay in seconds until the HUD is hidden. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay; + +/** + * The HUD delegate object. Receives HUD state notifications. + */ +@property (weak, nonatomic) id delegate; + +/** + * Called after the HUD is hiden. + */ +@property (copy, nullable) MBProgressHUDCompletionBlock completionBlock; + +/* + * Grace period is the time (in seconds) that the invoked method may be run without + * showing the HUD. If the task finishes before the grace time runs out, the HUD will + * not be shown at all. + * This may be used to prevent HUD display for very short tasks. + * Defaults to 0 (no grace time). + */ +@property (assign, nonatomic) NSTimeInterval graceTime; + +/** + * The minimum time (in seconds) that the HUD is shown. + * This avoids the problem of the HUD being shown and than instantly hidden. + * Defaults to 0 (no minimum show time). + */ +@property (assign, nonatomic) NSTimeInterval minShowTime; + +/** + * Removes the HUD from its parent view when hidden. + * Defaults to NO. + */ +@property (assign, nonatomic) BOOL removeFromSuperViewOnHide; + +/// @name Appearance + +/** + * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. + */ +@property (assign, nonatomic) MBProgressHUDMode mode; + +/** + * A color that gets forwarded to all labels and supported indicators. Also sets the tintColor + * for custom views on iOS 7+. Set to nil to manage color individually. + * Defaults to semi-translucent black on iOS 7 and later and white on earlier iOS versions. + */ +@property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR; + +/** + * The animation type that should be used when the HUD is shown and hidden. + */ +@property (assign, nonatomic) MBProgressHUDAnimation animationType UI_APPEARANCE_SELECTOR; + +/** + * The bezel offset relative to the center of the view. You can use MBProgressMaxOffset + * and -MBProgressMaxOffset to move the HUD all the way to the screen edge in each direction. + * E.g., CGPointMake(0.f, MBProgressMaxOffset) would position the HUD centered on the bottom edge. + */ +@property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR; + +/** + * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). + * This also represents the minimum bezel distance to the edge of the HUD view. + * Defaults to 20.f + */ +@property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR; + +/** + * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). + */ +@property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR; + +/** + * Force the HUD dimensions to be equal if possible. + */ +@property (assign, nonatomic, getter = isSquare) BOOL square UI_APPEARANCE_SELECTOR; + +/** + * When enabled, the bezel center gets slightly affected by the device accelerometer data. + * Has no effect on iOS < 7.0. Defaults to YES. + */ +@property (assign, nonatomic, getter=areDefaultMotionEffectsEnabled) BOOL defaultMotionEffectsEnabled UI_APPEARANCE_SELECTOR; + +/// @name Progress + +/** + * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. + */ +@property (assign, nonatomic) float progress; + +/// @name ProgressObject + +/** + * The NSProgress object feeding the progress information to the progress indicator. + */ +@property (strong, nonatomic, nullable) NSProgress *progressObject; + +/// @name Views + +/** + * The view containing the labels and indicator (or customView). + */ +@property (strong, nonatomic, readonly) MBBackgroundView *bezelView; + +/** + * View covering the entire HUD area, placed behind bezelView. + */ +@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView; + +/** + * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. + * The view should implement intrinsicContentSize for proper sizing. For best results use approximately 37 by 37 pixels. + */ +@property (strong, nonatomic, nullable) UIView *customView; + +/** + * A label that holds an optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit + * the entire text. + */ +@property (strong, nonatomic, readonly) UILabel *label; + +/** + * A label that holds an optional details message displayed below the labelText message. The details text can span multiple lines. + */ +@property (strong, nonatomic, readonly) UILabel *detailsLabel; + +/** + * A button that is placed below the labels. Visible only if a target / action is added. + */ +@property (strong, nonatomic, readonly) UIButton *button; + +@end + + +@protocol MBProgressHUDDelegate + +@optional + +/** + * Called after the HUD was fully hidden from the screen. + */ +- (void)hudWasHidden:(MBProgressHUD *)hud; + +@end + + +/** + * A progress view for showing definite progress by filling up a circle (pie chart). + */ +@interface MBRoundProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Indicator progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressTintColor; + +/** + * Indicator background (non-progress) color. + * Only applicable on iOS versions older than iOS 7. + * Defaults to translucent white (alpha 0.1). + */ +@property (nonatomic, strong) UIColor *backgroundTintColor; + +/* + * Display mode - NO = round or YES = annular. Defaults to round. + */ +@property (nonatomic, assign, getter = isAnnular) BOOL annular; + +@end + + +/** + * A flat bar progress view. + */ +@interface MBBarProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Bar border line color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *lineColor; + +/** + * Bar background color. + * Defaults to clear [UIColor clearColor]; + */ +@property (nonatomic, strong) UIColor *progressRemainingColor; + +/** + * Bar progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressColor; + +@end + + +@interface MBBackgroundView : UIView + +/** + * The background style. + * Defaults to MBProgressHUDBackgroundStyleBlur on iOS 7 or later and MBProgressHUDBackgroundStyleSolidColor otherwise. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic) MBProgressHUDBackgroundStyle style; + +/** + * The background color or the blur tint color. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic, strong) UIColor *color; + +@end + +@interface MBProgressHUD (Deprecated) + ++ (NSArray *)allHUDsForView:(UIView *)view __attribute__((deprecated("Store references when using more than one HUD per view."))); ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated __attribute__((deprecated("Store references when using more than one HUD per view."))); + +- (id)initWithWindow:(UIWindow *)window __attribute__((deprecated("Use initWithView: instead."))); + +- (void)show:(BOOL)animated __attribute__((deprecated("Use showAnimated: instead."))); +- (void)hide:(BOOL)animated __attribute__((deprecated("Use hideAnimated: instead."))); +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay __attribute__((deprecated("Use hideAnimated:afterDelay: instead."))); + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue + completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +@property (assign) BOOL taskInProgress __attribute__((deprecated("No longer needed."))); + +@property (nonatomic, copy) NSString *labelText __attribute__((deprecated("Use label.text instead."))); +@property (nonatomic, strong) UIFont *labelFont __attribute__((deprecated("Use label.font instead."))); +@property (nonatomic, strong) UIColor *labelColor __attribute__((deprecated("Use label.textColor instead."))); +@property (nonatomic, copy) NSString *detailsLabelText __attribute__((deprecated("Use detailsLabel.text instead."))); +@property (nonatomic, strong) UIFont *detailsLabelFont __attribute__((deprecated("Use detailsLabel.font instead."))); +@property (nonatomic, strong) UIColor *detailsLabelColor __attribute__((deprecated("Use detailsLabel.textColor instead."))); +@property (assign, nonatomic) CGFloat opacity __attribute__((deprecated("Customize bezelView properties instead."))); +@property (strong, nonatomic) UIColor *color __attribute__((deprecated("Customize the bezelView color instead."))); +@property (assign, nonatomic) CGFloat xOffset __attribute__((deprecated("Set offset.x instead."))); +@property (assign, nonatomic) CGFloat yOffset __attribute__((deprecated("Set offset.y instead."))); +@property (assign, nonatomic) CGFloat cornerRadius __attribute__((deprecated("Set bezelView.layer.cornerRadius instead."))); +@property (assign, nonatomic) BOOL dimBackground __attribute__((deprecated("Customize HUD background properties instead."))); +@property (strong, nonatomic) UIColor *activityIndicatorColor __attribute__((deprecated("Use UIAppearance to customize UIActivityIndicatorView. E.g.: [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil].color = [UIColor redColor];"))); +@property (atomic, assign, readonly) CGSize size __attribute__((deprecated("Get the bezelView.frame.size instead."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/MBProgressHUD/MBProgressHUD.m b/Pods/MBProgressHUD/MBProgressHUD.m new file mode 100644 index 0000000..a94f921 --- /dev/null +++ b/Pods/MBProgressHUD/MBProgressHUD.m @@ -0,0 +1,1484 @@ +// +// MBProgressHUD.m +// Version 1.0.0 +// Created by Matej Bukovinski on 2.4.09. +// + +#import "MBProgressHUD.h" +#import + + +#ifndef kCFCoreFoundationVersionNumber_iOS_7_0 + #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 + #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15 +#endif + +#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread."); + +CGFloat const MBProgressMaxOffset = 1000000.f; + +static const CGFloat MBDefaultPadding = 4.f; +static const CGFloat MBDefaultLabelFontSize = 16.f; +static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; + + +@interface MBProgressHUD () { + // Deprecated + UIColor *_activityIndicatorColor; + CGFloat _opacity; +} + +@property (nonatomic, assign) BOOL useAnimation; +@property (nonatomic, assign, getter=hasFinished) BOOL finished; +@property (nonatomic, strong) UIView *indicator; +@property (nonatomic, strong) NSDate *showStarted; +@property (nonatomic, strong) NSArray *paddingConstraints; +@property (nonatomic, strong) NSArray *bezelConstraints; +@property (nonatomic, strong) UIView *topSpacer; +@property (nonatomic, strong) UIView *bottomSpacer; +@property (nonatomic, weak) NSTimer *graceTimer; +@property (nonatomic, weak) NSTimer *minShowTimer; +@property (nonatomic, weak) NSTimer *hideDelayTimer; +@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink; + +// Deprecated +@property (assign) BOOL taskInProgress; + +@end + + +@interface MBProgressHUDRoundedButton : UIButton +@end + + +@implementation MBProgressHUD + +#pragma mark - Class methods + ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [[self alloc] initWithView:view]; + hud.removeFromSuperViewOnHide = YES; + [view addSubview:hud]; + [hud showAnimated:animated]; + return hud; +} + ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [self HUDForView:view]; + if (hud != nil) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + return YES; + } + return NO; +} + ++ (MBProgressHUD *)HUDForView:(UIView *)view { + NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator]; + for (UIView *subview in subviewsEnum) { + if ([subview isKindOfClass:self]) { + return (MBProgressHUD *)subview; + } + } + return nil; +} + +#pragma mark - Lifecycle + +- (void)commonInit { + // Set default values for properties + _animationType = MBProgressHUDAnimationFade; + _mode = MBProgressHUDModeIndeterminate; + _margin = 20.0f; + _opacity = 1.f; + _defaultMotionEffectsEnabled = YES; + + // Default color, depending on the current iOS version + BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f]; + // Transparent background + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + // Make it invisible for now + self.alpha = 0.0f; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.layer.allowsGroupOpacity = NO; + + [self setupViews]; + [self updateIndicators]; + [self registerForNotifications]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if ((self = [super initWithCoder:aDecoder])) { + [self commonInit]; + } + return self; +} + +- (id)initWithView:(UIView *)view { + NSAssert(view, @"View must not be nil."); + return [self initWithFrame:view.bounds]; +} + +- (void)dealloc { + [self unregisterFromNotifications]; +} + +#pragma mark - Show & hide + +- (void)showAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.minShowTimer invalidate]; + self.useAnimation = animated; + self.finished = NO; + // If the grace time is set, postpone the HUD display + if (self.graceTime > 0.0) { + NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.graceTimer = timer; + } + // ... otherwise show the HUD immediately + else { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)hideAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.graceTimer invalidate]; + self.useAnimation = animated; + self.finished = YES; + // If the minShow time is set, calculate how long the HUD was shown, + // and postpone the hiding operation if necessary + if (self.minShowTime > 0.0 && self.showStarted) { + NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted]; + if (interv < self.minShowTime) { + NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.minShowTimer = timer; + return; + } + } + // ... otherwise hide the HUD immediately + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay { + NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.hideDelayTimer = timer; +} + +#pragma mark - Timer callbacks + +- (void)handleGraceTimer:(NSTimer *)theTimer { + // Show the HUD only if the task is still running + if (!self.hasFinished) { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)handleMinShowTimer:(NSTimer *)theTimer { + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)handleHideTimer:(NSTimer *)timer { + [self hideAnimated:[timer.userInfo boolValue]]; +} + +#pragma mark - View Hierrarchy + +- (void)didMoveToSuperview { + [self updateForCurrentOrientationAnimated:NO]; +} + +#pragma mark - Internal show & hide operations + +- (void)showUsingAnimation:(BOOL)animated { + // Cancel any previous animations + [self.bezelView.layer removeAllAnimations]; + [self.backgroundView.layer removeAllAnimations]; + + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + + self.showStarted = [NSDate date]; + self.alpha = 1.f; + + // Needed in case we hide and re-show with the same NSProgress object attached. + [self setNSProgressDisplayLinkEnabled:YES]; + + if (animated) { + [self animateIn:YES withType:self.animationType completion:NULL]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + self.bezelView.alpha = self.opacity; +#pragma clang diagnostic pop + self.backgroundView.alpha = 1.f; + } +} + +- (void)hideUsingAnimation:(BOOL)animated { + if (animated && self.showStarted) { + self.showStarted = nil; + [self animateIn:NO withType:self.animationType completion:^(BOOL finished) { + [self done]; + }]; + } else { + self.showStarted = nil; + self.bezelView.alpha = 0.f; + self.backgroundView.alpha = 1.f; + [self done]; + } +} + +- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion { + // Automatically determine the correct zoom animation type + if (type == MBProgressHUDAnimationZoom) { + type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut; + } + + CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f); + CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f); + + // Set starting state + UIView *bezelView = self.bezelView; + if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = small; + } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = large; + } + + // Perform animations + dispatch_block_t animations = ^{ + if (animatingIn) { + bezelView.transform = CGAffineTransformIdentity; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = large; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = small; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + bezelView.alpha = animatingIn ? self.opacity : 0.f; +#pragma clang diagnostic pop + self.backgroundView.alpha = animatingIn ? 1.f : 0.f; + }; + + // Spring animations are nicer, but only available on iOS 7+ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + [UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; + return; + } +#endif + [UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; +} + +- (void)done { + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + [self setNSProgressDisplayLinkEnabled:NO]; + + if (self.hasFinished) { + self.alpha = 0.0f; + if (self.removeFromSuperViewOnHide) { + [self removeFromSuperview]; + } + } + MBProgressHUDCompletionBlock completionBlock = self.completionBlock; + if (completionBlock) { + completionBlock(); + } + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { + [delegate performSelector:@selector(hudWasHidden:) withObject:self]; + } +} + +#pragma mark - UI + +- (void)setupViews { + UIColor *defaultColor = self.contentColor; + + MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds]; + backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + backgroundView.backgroundColor = [UIColor clearColor]; + backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + backgroundView.alpha = 0.f; + [self addSubview:backgroundView]; + _backgroundView = backgroundView; + + MBBackgroundView *bezelView = [MBBackgroundView new]; + bezelView.translatesAutoresizingMaskIntoConstraints = NO; + bezelView.layer.cornerRadius = 5.f; + bezelView.alpha = 0.f; + [self addSubview:bezelView]; + _bezelView = bezelView; + [self updateBezelMotionEffects]; + + UILabel *label = [UILabel new]; + label.adjustsFontSizeToFitWidth = NO; + label.textAlignment = NSTextAlignmentCenter; + label.textColor = defaultColor; + label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize]; + label.opaque = NO; + label.backgroundColor = [UIColor clearColor]; + _label = label; + + UILabel *detailsLabel = [UILabel new]; + detailsLabel.adjustsFontSizeToFitWidth = NO; + detailsLabel.textAlignment = NSTextAlignmentCenter; + detailsLabel.textColor = defaultColor; + detailsLabel.numberOfLines = 0; + detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + detailsLabel.opaque = NO; + detailsLabel.backgroundColor = [UIColor clearColor]; + _detailsLabel = detailsLabel; + + UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + [button setTitleColor:defaultColor forState:UIControlStateNormal]; + _button = button; + + for (UIView *view in @[label, detailsLabel, button]) { + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + [bezelView addSubview:view]; + } + + UIView *topSpacer = [UIView new]; + topSpacer.translatesAutoresizingMaskIntoConstraints = NO; + topSpacer.hidden = YES; + [bezelView addSubview:topSpacer]; + _topSpacer = topSpacer; + + UIView *bottomSpacer = [UIView new]; + bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO; + bottomSpacer.hidden = YES; + [bezelView addSubview:bottomSpacer]; + _bottomSpacer = bottomSpacer; +} + +- (void)updateIndicators { + UIView *indicator = self.indicator; + BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; + BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]]; + + MBProgressHUDMode mode = self.mode; + if (mode == MBProgressHUDModeIndeterminate) { + if (!isActivityIndicator) { + // Update to indeterminate indicator + [indicator removeFromSuperview]; + indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + [(UIActivityIndicatorView *)indicator startAnimating]; + [self.bezelView addSubview:indicator]; + } + } + else if (mode == MBProgressHUDModeDeterminateHorizontalBar) { + // Update to bar determinate indicator + [indicator removeFromSuperview]; + indicator = [[MBBarProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { + if (!isRoundIndicator) { + // Update to determinante indicator + [indicator removeFromSuperview]; + indicator = [[MBRoundProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + if (mode == MBProgressHUDModeAnnularDeterminate) { + [(MBRoundProgressView *)indicator setAnnular:YES]; + } + } + else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) { + // Update custom view indicator + [indicator removeFromSuperview]; + indicator = self.customView; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeText) { + [indicator removeFromSuperview]; + indicator = nil; + } + indicator.translatesAutoresizingMaskIntoConstraints = NO; + self.indicator = indicator; + + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + + [self updateViewsForColor:self.contentColor]; + [self setNeedsUpdateConstraints]; +} + +- (void)updateViewsForColor:(UIColor *)color { + if (!color) return; + + self.label.textColor = color; + self.detailsLabel.textColor = color; + [self.button setTitleColor:color forState:UIControlStateNormal]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (self.activityIndicatorColor) { + color = self.activityIndicatorColor; + } +#pragma clang diagnostic pop + + // UIAppearance settings are prioritized. If they are preset the set color is ignored. + + UIView *indicator = self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + UIActivityIndicatorView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + // For iOS 9+ + appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + + if (appearance.color == nil) { + ((UIActivityIndicatorView *)indicator).color = color; + } + } else if ([indicator isKindOfClass:[MBRoundProgressView class]]) { + MBRoundProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressTintColor == nil) { + ((MBRoundProgressView *)indicator).progressTintColor = color; + } + if (appearance.backgroundTintColor == nil) { + ((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1]; + } + } else if ([indicator isKindOfClass:[MBBarProgressView class]]) { + MBBarProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressColor == nil) { + ((MBBarProgressView *)indicator).progressColor = color; + } + if (appearance.lineColor == nil) { + ((MBBarProgressView *)indicator).lineColor = color; + } + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if ([indicator respondsToSelector:@selector(setTintColor:)]) { + [indicator setTintColor:color]; + } +#endif + } +} + +- (void)updateBezelMotionEffects { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + MBBackgroundView *bezelView = self.bezelView; + if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return; + + if (self.defaultMotionEffectsEnabled) { + CGFloat effectOffset = 10.f; + UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + effectX.maximumRelativeValue = @(effectOffset); + effectX.minimumRelativeValue = @(-effectOffset); + + UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + effectY.maximumRelativeValue = @(effectOffset); + effectY.minimumRelativeValue = @(-effectOffset); + + UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init]; + group.motionEffects = @[effectX, effectY]; + + [bezelView addMotionEffect:group]; + } else { + NSArray *effects = [bezelView motionEffects]; + for (UIMotionEffect *effect in effects) { + [bezelView removeMotionEffect:effect]; + } + } +#endif +} + +#pragma mark - Layout + +- (void)updateConstraints { + UIView *bezel = self.bezelView; + UIView *topSpacer = self.topSpacer; + UIView *bottomSpacer = self.bottomSpacer; + CGFloat margin = self.margin; + NSMutableArray *bezelConstraints = [NSMutableArray array]; + NSDictionary *metrics = @{@"margin": @(margin)}; + + NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil]; + if (self.indicator) [subviews insertObject:self.indicator atIndex:1]; + + // Remove existing constraints + [self removeConstraints:self.constraints]; + [topSpacer removeConstraints:topSpacer.constraints]; + [bottomSpacer removeConstraints:bottomSpacer.constraints]; + if (self.bezelConstraints) { + [bezel removeConstraints:self.bezelConstraints]; + self.bezelConstraints = nil; + } + + // Center bezel in container (self), applying the offset if set + CGPoint offset = self.offset; + NSMutableArray *centeringConstraints = [NSMutableArray array]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]]; + [self applyPriority:998.f toConstraints:centeringConstraints]; + [self addConstraints:centeringConstraints]; + + // Ensure minimum side margin is kept + NSMutableArray *sideConstraints = [NSMutableArray array]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [self applyPriority:999.f toConstraints:sideConstraints]; + [self addConstraints:sideConstraints]; + + // Minimum bezel size, if set + CGSize minimumSize = self.minSize; + if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) { + NSMutableArray *minSizeConstraints = [NSMutableArray array]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]]; + [self applyPriority:997.f toConstraints:minSizeConstraints]; + [bezelConstraints addObjectsFromArray:minSizeConstraints]; + } + + // Square aspect ratio, if set + if (self.square) { + NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0]; + square.priority = 997.f; + [bezelConstraints addObject:square]; + } + + // Top and bottom spacing + [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + // Top and bottom spaces should be equal + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]]; + + // Layout subviews in bezel + NSMutableArray *paddingConstraints = [NSMutableArray new]; + [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + // Center in bezel + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]]; + // Ensure the minimum edge margin is kept + [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]]; + // Element spacing + if (idx == 0) { + // First, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]]; + } else if (idx == subviews.count - 1) { + // Last, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]]; + } + if (idx > 0) { + // Has previous + NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]; + [bezelConstraints addObject:padding]; + [paddingConstraints addObject:padding]; + } + }]; + + [bezel addConstraints:bezelConstraints]; + self.bezelConstraints = bezelConstraints; + + self.paddingConstraints = [paddingConstraints copy]; + [self updatePaddingConstraints]; + + [super updateConstraints]; +} + +- (void)layoutSubviews { + // There is no need to update constraints if they are going to + // be recreated in [super layoutSubviews] due to needsUpdateConstraints being set. + // This also avoids an issue on iOS 8, where updatePaddingConstraints + // would trigger a zombie object access. + if (!self.needsUpdateConstraints) { + [self updatePaddingConstraints]; + } + [super layoutSubviews]; +} + +- (void)updatePaddingConstraints { + // Set padding dynamically, depending on whether the view is visible or not + __block BOOL hasVisibleAncestors = NO; + [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) { + UIView *firstView = (UIView *)padding.firstItem; + UIView *secondView = (UIView *)padding.secondItem; + BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero); + BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero); + // Set if both views are visible or if there's a visible view on top that doesn't have padding + // added relative to the current view yet + padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f; + hasVisibleAncestors |= secondVisible; + }]; +} + +- (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints { + for (NSLayoutConstraint *constraint in constraints) { + constraint.priority = priority; + } +} + +#pragma mark - Properties + +- (void)setMode:(MBProgressHUDMode)mode { + if (mode != _mode) { + _mode = mode; + [self updateIndicators]; + } +} + +- (void)setCustomView:(UIView *)customView { + if (customView != _customView) { + _customView = customView; + if (self.mode == MBProgressHUDModeCustomView) { + [self updateIndicators]; + } + } +} + +- (void)setOffset:(CGPoint)offset { + if (!CGPointEqualToPoint(offset, _offset)) { + _offset = offset; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMargin:(CGFloat)margin { + if (margin != _margin) { + _margin = margin; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMinSize:(CGSize)minSize { + if (!CGSizeEqualToSize(minSize, _minSize)) { + _minSize = minSize; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setSquare:(BOOL)square { + if (square != _square) { + _square = square; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink { + if (progressObjectDisplayLink != _progressObjectDisplayLink) { + [_progressObjectDisplayLink invalidate]; + + _progressObjectDisplayLink = progressObjectDisplayLink; + + [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + } +} + +- (void)setProgressObject:(NSProgress *)progressObject { + if (progressObject != _progressObject) { + _progressObject = progressObject; + [self setNSProgressDisplayLinkEnabled:YES]; + } +} + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + UIView *indicator = self.indicator; + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + } +} + +- (void)setContentColor:(UIColor *)contentColor { + if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) { + _contentColor = contentColor; + [self updateViewsForColor:contentColor]; + } +} + +- (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled { + if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) { + _defaultMotionEffectsEnabled = defaultMotionEffectsEnabled; + [self updateBezelMotionEffects]; + } +} + +#pragma mark - NSProgress + +- (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled { + // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread, + // so we're refreshing the progress only every frame draw + if (enabled && self.progressObject) { + // Only create if not already active. + if (!self.progressObjectDisplayLink) { + self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)]; + } + } else { + self.progressObjectDisplayLink = nil; + } +} + +- (void)updateProgressFromProgressObject { + self.progress = self.progressObject.fractionCompleted; +} + +#pragma mark - Notifications + +- (void)registerForNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver:self selector:@selector(statusBarOrientationDidChange:) + name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +- (void)unregisterFromNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +#if !TARGET_OS_TV +- (void)statusBarOrientationDidChange:(NSNotification *)notification { + UIView *superview = self.superview; + if (!superview) { + return; + } else { + [self updateForCurrentOrientationAnimated:YES]; + } +} +#endif + +- (void)updateForCurrentOrientationAnimated:(BOOL)animated { + // Stay in sync with the superview in any case + if (self.superview) { + self.bounds = self.superview.bounds; + } + + // Not needed on iOS 8+, compile out when the deployment target allows, + // to avoid sharedApplication problems on extension targets +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000 + // Only needed pre iOS 8 when added to a window + BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0; + if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return; + + // Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check. + // This just ensures we don't get a warning about extension-unsafe API. + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return; + + UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; + UIInterfaceOrientation orientation = application.statusBarOrientation; + CGFloat radians = 0; + + if (UIInterfaceOrientationIsLandscape(orientation)) { + radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2; + // Window coordinates differ! + self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); + } else { + radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f; + } + + if (animated) { + [UIView animateWithDuration:0.3 animations:^{ + self.transform = CGAffineTransformMakeRotation(radians); + }]; + } else { + self.transform = CGAffineTransformMakeRotation(radians); + } +#endif +} + +@end + + +@implementation MBRoundProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + _progress = 0.f; + _annular = NO; + _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f]; + _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + return CGSizeMake(37.f, 37.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressTintColor:(UIColor *)progressTintColor { + NSAssert(progressTintColor, @"The color should not be nil."); + if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) { + _progressTintColor = progressTintColor; + [self setNeedsDisplay]; + } +} + +- (void)setBackgroundTintColor:(UIColor *)backgroundTintColor { + NSAssert(backgroundTintColor, @"The color should not be nil."); + if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) { + _backgroundTintColor = backgroundTintColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + + if (_annular) { + // Draw background + CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f; + UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; + processBackgroundPath.lineWidth = lineWidth; + processBackgroundPath.lineCapStyle = kCGLineCapButt; + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + CGFloat radius = (self.bounds.size.width - lineWidth)/2; + CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees + CGFloat endAngle = (2 * (float)M_PI) + startAngle; + [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_backgroundTintColor set]; + [processBackgroundPath stroke]; + // Draw progress + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare; + processPath.lineWidth = lineWidth; + endAngle = (self.progress * 2 * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_progressTintColor set]; + [processPath stroke]; + } else { + // Draw background + CGFloat lineWidth = 2.f; + CGRect allRect = self.bounds; + CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f); + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + [_progressTintColor setStroke]; + [_backgroundTintColor setFill]; + CGContextSetLineWidth(context, lineWidth); + if (isPreiOS7) { + CGContextFillEllipseInRect(context, circleRect); + } + CGContextStrokeEllipseInRect(context, circleRect); + // 90 degrees + CGFloat startAngle = - ((float)M_PI / 2.f); + // Draw progress + if (isPreiOS7) { + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - lineWidth; + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [_progressTintColor setFill]; + CGContextMoveToPoint(context, center.x, center.y); + CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); + CGContextClosePath(context); + CGContextFillPath(context); + } else { + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = kCGLineCapButt; + processPath.lineWidth = lineWidth * 2.f; + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f); + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + // Ensure that we don't get color overlaping when _progressTintColor alpha < 1.f. + CGContextSetBlendMode(context, kCGBlendModeCopy); + [_progressTintColor set]; + [processPath stroke]; + } + } +} + +@end + + +@implementation MBBarProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _progress = 0.f; + _lineColor = [UIColor whiteColor]; + _progressColor = [UIColor whiteColor]; + _progressRemainingColor = [UIColor clearColor]; + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + return CGSizeMake(120.f, isPreiOS7 ? 20.f : 10.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressColor:(UIColor *)progressColor { + NSAssert(progressColor, @"The color should not be nil."); + if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) { + _progressColor = progressColor; + [self setNeedsDisplay]; + } +} + +- (void)setProgressRemainingColor:(UIColor *)progressRemainingColor { + NSAssert(progressRemainingColor, @"The color should not be nil."); + if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) { + _progressRemainingColor = progressRemainingColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetLineWidth(context, 2); + CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]); + CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]); + + // Draw background + CGFloat radius = (rect.size.height / 2) - 2; + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextFillPath(context); + + // Draw border + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextStrokePath(context); + + CGContextSetFillColorWithColor(context, [_progressColor CGColor]); + radius = radius - 2; + CGFloat amount = self.progress * rect.size.width; + + // Progress in the middle area + if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, amount, 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, amount, rect.size.height - 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextFillPath(context); + } + + // Progress in the right arc + else if (amount > radius + 4) { + CGFloat x = amount - (rect.size.width - radius - 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4); + CGFloat angle = -acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4); + angle = acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextFillPath(context); + } + + // Progress is in the left arc + else if (amount < radius + 4 && amount > 0) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextFillPath(context); + } +} + +@end + + +@interface MBBackgroundView () + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV +@property UIVisualEffectView *effectView; +#endif +#if !TARGET_OS_TV +@property UIToolbar *toolbar; +#endif + +@end + + +@implementation MBBackgroundView + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + _style = MBProgressHUDBackgroundStyleBlur; + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + _color = [UIColor colorWithWhite:0.8f alpha:0.6f]; + } else { + _color = [UIColor colorWithWhite:0.95f alpha:0.6f]; + } + } else { + _style = MBProgressHUDBackgroundStyleSolidColor; + _color = [[UIColor blackColor] colorWithAlphaComponent:0.8]; + } + + self.clipsToBounds = YES; + + [self updateForBackgroundStyle]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + // Smallest size possible. Content pushes against this. + return CGSizeZero; +} + +#pragma mark - Appearance + +- (void)setStyle:(MBProgressHUDBackgroundStyle)style { + if (style == MBProgressHUDBackgroundStyleBlur && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) { + style = MBProgressHUDBackgroundStyleSolidColor; + } + if (_style != style) { + _style = style; + [self updateForBackgroundStyle]; + } +} + +- (void)setColor:(UIColor *)color { + NSAssert(color, @"The color should not be nil."); + if (color != _color && ![color isEqual:_color]) { + _color = color; + [self updateViewsForColor:color]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Views + +- (void)updateForBackgroundStyle { + MBProgressHUDBackgroundStyle style = self.style; + if (style == MBProgressHUDBackgroundStyleBlur) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; + UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect]; + [self addSubview:effectView]; + effectView.frame = self.bounds; + effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + self.backgroundColor = self.color; + self.layer.allowsGroupOpacity = NO; + self.effectView = effectView; + } else { +#endif +#if !TARGET_OS_TV + UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectInset(self.bounds, -100.f, -100.f)]; + toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + toolbar.barTintColor = self.color; + toolbar.translucent = YES; + [self addSubview:toolbar]; + self.toolbar = toolbar; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + [self.effectView removeFromSuperview]; + self.effectView = nil; + } else { +#endif +#if !TARGET_OS_TV + [self.toolbar removeFromSuperview]; + self.toolbar = nil; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + self.backgroundColor = self.color; + } +} + +- (void)updateViewsForColor:(UIColor *)color { + if (self.style == MBProgressHUDBackgroundStyleBlur) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + self.backgroundColor = self.color; + } else { +#if !TARGET_OS_TV + self.toolbar.barTintColor = color; +#endif + } + } else { + self.backgroundColor = self.color; + } +} + +@end + + +@implementation MBProgressHUD (Deprecated) + +#pragma mark - Class + ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated { + NSArray *huds = [MBProgressHUD allHUDsForView:view]; + for (MBProgressHUD *hud in huds) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + } + return [huds count]; +} + ++ (NSArray *)allHUDsForView:(UIView *)view { + NSMutableArray *huds = [NSMutableArray array]; + NSArray *subviews = view.subviews; + for (UIView *aView in subviews) { + if ([aView isKindOfClass:self]) { + [huds addObject:aView]; + } + } + return [NSArray arrayWithArray:huds]; +} + +#pragma mark - Lifecycle + +- (id)initWithWindow:(UIWindow *)window { + return [self initWithView:window]; +} + +#pragma mark - Show & hide + +- (void)show:(BOOL)animated { + [self showAnimated:animated]; +} + +- (void)hide:(BOOL)animated { + [self hideAnimated:animated]; +} + +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { + [self hideAnimated:animated afterDelay:delay]; +} + +#pragma mark - Threading + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { + [self showAnimated:animated whileExecutingBlock:^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + // Start executing the requested task + [target performSelector:method withObject:object]; +#pragma clang diagnostic pop + }]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue { + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(nullable MBProgressHUDCompletionBlock)completion { + self.taskInProgress = YES; + self.completionBlock = completion; + dispatch_async(queue, ^(void) { + block(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self cleanUp]; + }); + }); + [self showAnimated:animated]; +} + +- (void)cleanUp { + self.taskInProgress = NO; + [self hideAnimated:self.useAnimation]; +} + +#pragma mark - Labels + +- (NSString *)labelText { + return self.label.text; +} + +- (void)setLabelText:(NSString *)labelText { + MBMainThreadAssert(); + self.label.text = labelText; +} + +- (UIFont *)labelFont { + return self.label.font; +} + +- (void)setLabelFont:(UIFont *)labelFont { + MBMainThreadAssert(); + self.label.font = labelFont; +} + +- (UIColor *)labelColor { + return self.label.textColor; +} + +- (void)setLabelColor:(UIColor *)labelColor { + MBMainThreadAssert(); + self.label.textColor = labelColor; +} + +- (NSString *)detailsLabelText { + return self.detailsLabel.text; +} + +- (void)setDetailsLabelText:(NSString *)detailsLabelText { + MBMainThreadAssert(); + self.detailsLabel.text = detailsLabelText; +} + +- (UIFont *)detailsLabelFont { + return self.detailsLabel.font; +} + +- (void)setDetailsLabelFont:(UIFont *)detailsLabelFont { + MBMainThreadAssert(); + self.detailsLabel.font = detailsLabelFont; +} + +- (UIColor *)detailsLabelColor { + return self.detailsLabel.textColor; +} + +- (void)setDetailsLabelColor:(UIColor *)detailsLabelColor { + MBMainThreadAssert(); + self.detailsLabel.textColor = detailsLabelColor; +} + +- (CGFloat)opacity { + return _opacity; +} + +- (void)setOpacity:(CGFloat)opacity { + MBMainThreadAssert(); + _opacity = opacity; +} + +- (UIColor *)color { + return self.bezelView.color; +} + +- (void)setColor:(UIColor *)color { + MBMainThreadAssert(); + self.bezelView.color = color; +} + +- (CGFloat)yOffset { + return self.offset.y; +} + +- (void)setYOffset:(CGFloat)yOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(self.offset.x, yOffset); +} + +- (CGFloat)xOffset { + return self.offset.x; +} + +- (void)setXOffset:(CGFloat)xOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(xOffset, self.offset.y); +} + +- (CGFloat)cornerRadius { + return self.bezelView.layer.cornerRadius; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + MBMainThreadAssert(); + self.bezelView.layer.cornerRadius = cornerRadius; +} + +- (BOOL)dimBackground { + MBBackgroundView *backgroundView = self.backgroundView; + UIColor *dimmedColor = [UIColor colorWithWhite:0.f alpha:.2f]; + return backgroundView.style == MBProgressHUDBackgroundStyleSolidColor && [backgroundView.color isEqual:dimmedColor]; +} + +- (void)setDimBackground:(BOOL)dimBackground { + MBMainThreadAssert(); + self.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + self.backgroundView.color = dimBackground ? [UIColor colorWithWhite:0.f alpha:.2f] : [UIColor clearColor]; +} + +- (CGSize)size { + return self.bezelView.frame.size; +} + +- (UIColor *)activityIndicatorColor { + return _activityIndicatorColor; +} + +- (void)setActivityIndicatorColor:(UIColor *)activityIndicatorColor { + if (activityIndicatorColor != _activityIndicatorColor) { + _activityIndicatorColor = activityIndicatorColor; + UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + [indicator setColor:activityIndicatorColor]; + } + } +} + +@end + +@implementation MBProgressHUDRoundedButton + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + CALayer *layer = self.layer; + layer.borderWidth = 1.f; + } + return self; +} + +#pragma mark - Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + // Fully rounded corners + CGFloat height = CGRectGetHeight(self.bounds); + self.layer.cornerRadius = ceil(height / 2.f); +} + +- (CGSize)intrinsicContentSize { + // Only show if we have associated control events + if (self.allControlEvents == 0) return CGSizeZero; + CGSize size = [super intrinsicContentSize]; + // Add some side padding + size.width += 20.f; + return size; +} + +#pragma mark - Color + +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + [super setTitleColor:color forState:state]; + // Update related colors + [self setHighlighted:self.highlighted]; + self.layer.borderColor = color.CGColor; +} + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + UIColor *baseColor = [self titleColorForState:UIControlStateSelected]; + self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor]; +} + +@end diff --git a/Pods/MBProgressHUD/README.mdown b/Pods/MBProgressHUD/README.mdown new file mode 100644 index 0000000..1c8d105 --- /dev/null +++ b/Pods/MBProgressHUD/README.mdown @@ -0,0 +1,126 @@ +# MBProgressHUD + +[![Build Status](https://travis-ci.org/matej/MBProgressHUD.svg?branch=master)](https://travis-ci.org/matej/MBProgressHUD) [![codecov.io](https://codecov.io/github/matej/MBProgressHUD/coverage.svg?branch=master)](https://codecov.io/github/matej/MBProgressHUD?branch=master) + [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/MBProgressHUD.svg?style=flat)](https://cocoapods.org/pods/MBProgressHUD) [![License: MIT](https://img.shields.io/cocoapods/l/MBProgressHUD.svg?style=flat)](http://opensource.org/licenses/MIT) + +`MBProgressHUD` is an iOS drop-in class that displays a translucent HUD with an indicator and/or labels while work is being done in a background thread. The HUD is meant as a replacement for the undocumented, private `UIKit` `UIProgressHUD` with some additional features. + +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/1-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/1.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/2-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/2.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/3-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/3.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/4-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/4.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/5-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/5.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/6-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/6.png) +[![](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/7-thumb.png)](http://dl.dropbox.com/u/378729/MBProgressHUD/v1/7.png) + +**NOTE:** The class has recently undegone a major rewrite. The old version is available in the [legacy](https://github.com/jdg/MBProgressHUD/tree/legacy) branch, should you need it. + +## Requirements + +`MBProgressHUD` works on iOS 6+ and requires ARC to build. It depends on the following Apple frameworks, which should already be included with most Xcode templates: + +* Foundation.framework +* UIKit.framework +* CoreGraphics.framework + +You will need the latest developer tools in order to build `MBProgressHUD`. Old Xcode versions might work, but compatibility will not be explicitly maintained. + +## Adding MBProgressHUD to your project + +### CocoaPods + +[CocoaPods](http://cocoapods.org) is the recommended way to add MBProgressHUD to your project. + +1. Add a pod entry for MBProgressHUD to your Podfile `pod 'MBProgressHUD', '~> 1.0.0'` +2. Install the pod(s) by running `pod install`. +3. Include MBProgressHUD wherever you need it with `#import "MBProgressHUD.h"`. + +### Carthage + +1. Add MBProgressHUD to your Cartfile. e.g., `github "jdg/MBProgressHUD" ~> 1.0.0` +2. Run `carthage update` +3. Follow the rest of the [standard Carthage installation instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) to add MBProgressHUD to your project. + +### Source files + +Alternatively you can directly add the `MBProgressHUD.h` and `MBProgressHUD.m` source files to your project. + +1. Download the [latest code version](https://github.com/matej/MBProgressHUD/archive/master.zip) or add the repository as a git submodule to your git-tracked project. +2. Open your project in Xcode, then drag and drop `MBProgressHUD.h` and `MBProgressHUD.m` onto your project (use the "Product Navigator view"). Make sure to select Copy items when asked if you extracted the code archive outside of your project. +3. Include MBProgressHUD wherever you need it with `#import "MBProgressHUD.h"`. + +### Static library + +You can also add MBProgressHUD as a static library to your project or workspace. + +1. Download the [latest code version](https://github.com/matej/MBProgressHUD/downloads) or add the repository as a git submodule to your git-tracked project. +2. Open your project in Xcode, then drag and drop `MBProgressHUD.xcodeproj` onto your project or workspace (use the "Product Navigator view"). +3. Select your target and go to the Build phases tab. In the Link Binary With Libraries section select the add button. On the sheet find and add `libMBProgressHUD.a`. You might also need to add `MBProgressHUD` to the Target Dependencies list. +4. Include MBProgressHUD wherever you need it with `#import `. + +## Usage + +The main guideline you need to follow when dealing with MBProgressHUD while running long-running tasks is keeping the main thread work-free, so the UI can be updated promptly. The recommended way of using MBProgressHUD is therefore to set it up on the main thread and then spinning the task, that you want to perform, off onto a new thread. + +```objective-c +[MBProgressHUD showHUDAddedTo:self.view animated:YES]; +dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + // Do something... + dispatch_async(dispatch_get_main_queue(), ^{ + [MBProgressHUD hideHUDForView:self.view animated:YES]; + }); +}); +``` + +You can add the HUD on any view or window. It is however a good idea to avoid adding the HUD to certain `UIKit` views with complex view hierarchies - like `UITableView` or `UICollectionView`. Those can mutate their subviews in unexpected ways and thereby break HUD display. + +If you need to configure the HUD you can do this by using the MBProgressHUD reference that showHUDAddedTo:animated: returns. + +```objective-c +MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; +hud.mode = MBProgressHUDModeAnnularDeterminate; +hud.labelText = @"Loading"; +[self doSomethingInBackgroundWithProgressCallback:^(float progress) { + hud.progress = progress; +} completionCallback:^{ + [hud hide:YES]; +}]; +``` + +You can also use a `NSProgress` object and MBProgressHUD will update itself when there is progress reported through that object. + +```objective-c +MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; +hud.mode = MBProgressHUDModeAnnularDeterminate; +hud.labelText = @"Loading"; +NSProgress *progress = [self doSomethingInBackgroundCompletion:^{ + [hud hide:YES]; +}]; +hud.progressObject = progress; +``` + +UI updates should always be done on the main thread. Some MBProgressHUD setters are however considered "thread safe" and can be called from background threads. Those also include `setMode:`, `setCustomView:`, `setLabelText:`, `setLabelFont:`, `setDetailsLabelText:`, `setDetailsLabelFont:` and `setProgress:`. + +If you need to run your long-running task in the main thread, you should perform it with a slight delay, so UIKit will have enough time to update the UI (i.e., draw the HUD) before you block the main thread with your task. + +```objective-c +[MBProgressHUD showHUDAddedTo:self.view animated:YES]; +dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC); +dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + // Do something... + [MBProgressHUD hideHUDForView:self.view animated:YES]; +}); +``` + +You should be aware that any HUD updates issued inside the above block won't be displayed until the block completes. + +For more examples, including how to use MBProgressHUD with asynchronous operations such as NSURLConnection, take a look at the bundled demo project. Extensive API documentation is provided in the header file (MBProgressHUD.h). + + +## License + +This code is distributed under the terms and conditions of the [MIT license](LICENSE). + +## Change-log + +A brief summary of each MBProgressHUD release can be found in the [CHANGELOG](CHANGELOG.mdown). diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..b02ddeb --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,103 @@ +PODS: + - AFNetworking (3.1.0): + - AFNetworking/NSURLSession (= 3.1.0) + - AFNetworking/Reachability (= 3.1.0) + - AFNetworking/Security (= 3.1.0) + - AFNetworking/Serialization (= 3.1.0) + - AFNetworking/UIKit (= 3.1.0) + - AFNetworking/NSURLSession (3.1.0): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (3.1.0) + - AFNetworking/Security (3.1.0) + - AFNetworking/Serialization (3.1.0) + - AFNetworking/UIKit (3.1.0): + - AFNetworking/NSURLSession + - ChameleonFramework (2.1.0): + - ChameleonFramework/Default (= 2.1.0) + - ChameleonFramework/Default (2.1.0) + - Crashlytics (3.8.3): + - Fabric (~> 1.6.3) + - Fabric (1.6.10) + - FCAlertView (1.1.2) + - Firebase/Core (3.7.1): + - FirebaseAnalytics (= 3.4.4) + - FirebaseCore (= 3.4.3) + - Firebase/Messaging (3.7.1): + - Firebase/Core + - FirebaseMessaging (= 1.2.0) + - FirebaseAnalytics (3.4.4): + - FirebaseCore (~> 3.4) + - FirebaseInstanceID (~> 1.0) + - GoogleInterchangeUtilities (~> 1.2) + - GoogleSymbolUtilities (~> 1.1) + - FirebaseCore (3.4.3): + - GoogleInterchangeUtilities (~> 1.2) + - GoogleUtilities (~> 1.2) + - FirebaseInstanceID (1.0.8) + - FirebaseMessaging (1.2.0): + - FirebaseAnalytics (~> 3.3) + - FirebaseInstanceID (~> 1.0) + - GoogleInterchangeUtilities (~> 1.2) + - GoogleIPhoneUtilities (~> 1.2) + - GoogleSymbolUtilities (~> 1.1) + - GoogleInterchangeUtilities (1.2.2): + - GoogleSymbolUtilities (~> 1.1) + - GoogleIPhoneUtilities (1.2.1): + - GoogleSymbolUtilities (~> 1.0) + - GoogleUtilities (~> 1.0) + - GoogleMaps (2.1.0): + - GoogleMaps/Maps (= 2.1.0) + - GoogleMaps/Base (2.1.0) + - GoogleMaps/Maps (2.1.0): + - GoogleMaps/Base (= 2.1.0) + - GooglePlaces (2.1.0): + - GoogleMaps/Base (= 2.1.0) + - GoogleSymbolUtilities (1.1.2) + - GoogleUtilities (1.3.2): + - GoogleSymbolUtilities (~> 1.1) + - MBProgressHUD (1.0.0) + - Realm (2.0.2): + - Realm/Headers (= 2.0.2) + - Realm/Headers (2.0.2) + - SVProgressHUD (2.0.3) + +DEPENDENCIES: + - AFNetworking + - ChameleonFramework + - Crashlytics + - Fabric + - FCAlertView + - Firebase/Core + - Firebase/Messaging + - GoogleMaps + - GooglePlaces + - MBProgressHUD (~> 1.0.0) + - Realm + - SVProgressHUD + +SPEC CHECKSUMS: + AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 + ChameleonFramework: d21a3cc247abfe5e37609a283a8238b03575cf64 + Crashlytics: 2b6dbe138a42395577cfa73dfa1aa7248cadf39e + Fabric: c73f371ee543e3f0b80608f2674750e4910d1669 + FCAlertView: 8c777212f654a8970b9b192904bf8b84d32582a2 + Firebase: be473484f0d515e72ffd04acf22f981773c23e58 + FirebaseAnalytics: 0533a00b681e08fd0b2cd0444b7ddf0ef9fd7a1a + FirebaseCore: 5a885548bbc5f0c410b04f8e9ac9d73ff1221907 + FirebaseInstanceID: ba1e640935235e5fac39dfa816fe7660e72e1a8a + FirebaseMessaging: 789d23fd796594dfb55dcf36cd325541df887c22 + GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7 + GoogleIPhoneUtilities: 63f25e93a3ddcb66884d182aab3a660d98f1479b + GoogleMaps: 06589b9a38097bce0cd6e90f0fd9b5e4b4a9344c + GooglePlaces: 16e96266483b8010e9f275399403647978523c86 + GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96 + GoogleUtilities: 8bbc733218aad26306f9d4a253823986110e3358 + MBProgressHUD: 4890f671c94e8a0f3cf959aa731e9de2f036d71a + Realm: 0f5a194af0b7e6158b83db41b4ee1a00be7ddf1a + SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4 + +PODFILE CHECKSUM: 5e97ee0504e715c4312054d413cb726e8ffdd610 + +COCOAPODS: 1.0.1 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b36e3d3 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,8652 @@ + + + + + archiveVersion + 1 + classes + + objectVersion + 46 + objects + + 00258BC662C715449F67F14364FECB3E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFURLResponseSerialization.h + path + AFNetworking/AFURLResponseSerialization.h + sourceTree + <group> + + 00C1F6DE985E076876867E764E368A44 + + fileRef + ECF5D7E2435E6A2A499D21CDED43E48B + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 00F6517A3117C3CB6C67F1F4DC4FF050 + + fileRef + 25769ECCDF5839D0BC00293976293256 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 010F7F46F32971E403D9BB705DF1A2DF + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.script.sh + path + Pods-HackTX-resources.sh + sourceTree + <group> + + 011CFCDFC70FDCB2D6716099D07BD63F + + includeInIndex + 1 + isa + PBXFileReference + name + placeholder.cpp + path + Realm/ObjectStore/src/placeholder.cpp + sourceTree + <group> + + 014E6E2EFAF9AE5C27A801DC4238C596 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Chameleon.h + path + Pod/Classes/Objective-C/Chameleon.h + sourceTree + <group> + + 0161BE82E13E6D5AAF977DC72F6CEB1F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + SVIndefiniteAnimatedView.h + path + SVProgressHUD/SVIndefiniteAnimatedView.h + sourceTree + <group> + + 027F9A4F74D993477C300CF7206D001A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + Pods-HackTX.release.xcconfig + sourceTree + <group> + + 034014829C1E4434983CF3A690C12017 + + buildSettings + + ALWAYS_SEARCH_USER_PATHS + NO + CLANG_ANALYZER_NONNULL + YES + 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 + ENABLE_TESTABILITY + YES + GCC_C_LANGUAGE_STANDARD + gnu99 + GCC_DYNAMIC_NO_PIC + NO + GCC_OPTIMIZATION_LEVEL + 0 + GCC_PREPROCESSOR_DEFINITIONS + + POD_CONFIGURATION_DEBUG=1 + 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 + 9.0 + ONLY_ACTIVE_ARCH + YES + STRIP_INSTALLED_PRODUCT + NO + SYMROOT + ${SRCROOT}/../build + + isa + XCBuildConfiguration + name + Debug + + 0358B918CA8C9D3C214FD4D41CD69A90 + + fileRef + 102145A1D07EC1C00D362924FBEFDB9C + isa + PBXBuildFile + + 035C9810D15E865D9DD4AA3F4BA4815A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIProgressView+AFNetworking.m + path + UIKit+AFNetworking/UIProgressView+AFNetworking.m + sourceTree + <group> + + 038144FA412425B054D4187A9A71F7D7 + + buildActionMask + 2147483647 + files + + D88CFACD04FD813E0AF3F15CE6B4F9E5 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 0406E22A3C59B67D140C99533B020681 + + children + + 437649DCAB8362EA9F39BFF7585173D1 + 1D05359385AC4E0EF8F41B8C9D372CFA + 5B01CAAE2DFD9DA0CD5D0015943D8241 + + isa + PBXGroup + name + Resources + sourceTree + <group> + + 045FFEDD819A734271DD5DE53C877884 + + fileRef + 7BA9A8B5B4FE3448D5072369AB0A4513 + isa + PBXBuildFile + + 05325FCDF1D30E28F878330892F633A6 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GooglePlaces.framework + path + Frameworks/GooglePlaces.framework + sourceTree + <group> + + 05A3C262D41B8AC17B19E29F4A28B6D0 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncUser.h + path + include/RLMSyncUser.h + sourceTree + <group> + + 05BDDDC3D01F0F38519554C0B0909224 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMCollection.h + path + include/RLMCollection.h + sourceTree + <group> + + 065A73EDC4EEEE691C0F9DB7CA1B24DA + + includeInIndex + 1 + isa + PBXFileReference + name + keychain_helper.cpp + path + Realm/ObjectStore/src/impl/apple/keychain_helper.cpp + sourceTree + <group> + + 072B99A31C094968B4C10AB4D4F91597 + + buildConfigurations + + 8A1619CC62CA728D640D0FD28A8ED4CF + F79E89170ABB5EE3B83077217913A100 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 0742E7DA28B5B91F1894B84E00A82DA0 + + fileRef + 9A02CCAB450B7182D47B38130839FC8D + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 074AC82485DF3FFCA9B411EF41AFEB86 + + children + + FFB8EBAD39FDD86E41FDB1FCBE47588B + 1BE8AF5CBCEF2BBBD8C1632D6C45900E + 00258BC662C715449F67F14364FECB3E + 78A271BEC0CD09221D7DE76766E9B57E + + isa + PBXGroup + name + Serialization + sourceTree + <group> + + 0824B19691E8B71596EEC2ECAFB82958 + + fileRef + FBACF5338B8839F2165A8F8D9692A409 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 083CE88C685B6FB12BB9403313F4D7B8 + + children + + 0161BE82E13E6D5AAF977DC72F6CEB1F + C0AD35AB11FCA31439B676F42DC5ACEF + 88E9AAA2DBA4606C365E0704DF6EDBA9 + B185E0E344FCEC1879D37A6120E1E27E + F0D398496991B317983A8AC724EC7557 + E4ED1D5C4FBA746EDDA4C1567D5C3FDA + CF546E09CAF655ADECECF54D7C35B54A + C533894FE6C533A78CDDAC0CD9BCE7AC + 7BCC7C7D951690976EF961E7F5897E6D + E6408A7F9E6A0DCE86F5CC0973E35A4A + + isa + PBXGroup + name + SVProgressHUD + path + SVProgressHUD + sourceTree + <group> + + 08411B06AF2BF1C07C1182399277DC53 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIColor+Chameleon.m + path + Pod/Classes/Objective-C/UIColor+Chameleon.m + sourceTree + <group> + + 087FE3AAD0A942370684A7E8B7A4E0B2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFNetworkActivityIndicatorManager.h + path + UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h + sourceTree + <group> + + 08908753F71F3D7AE5CEA21127714812 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealmConfiguration_Private.h + path + include/RLMRealmConfiguration_Private.h + sourceTree + <group> + + 08CAEB9B2FBB56A8AF331DA7F3D8C914 + + fileRef + BC663C0365433045138CCCF3AD375052 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 0951ECB79A8DDBA8DC3077FA1CC84E6B + + fileRef + 7BA9A8B5B4FE3448D5072369AB0A4513 + isa + PBXBuildFile + + 0A08FC71C4E3B8E0F8734BCDA7CCA7CB + + fileRef + 8CE550EF3AE9B5A5DFBBBEB3E29E2714 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 0B18135E78B5A298729D582C519AB9C5 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIColor+ChameleonPrivate.m + path + Pod/Classes/Objective-C/UIColor+ChameleonPrivate.m + sourceTree + <group> + + 0B2A088D101EF26A175C58E16E6BB598 + + buildConfigurations + + 63506B10240955B048AB7F5491BEAE73 + 88666ACFDC557A55DBABE61B1B9D2E13 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 0CB4C862467FADB7A8C53306171E7298 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMRealm.mm + path + Realm/RLMRealm.mm + sourceTree + <group> + + 0DBE46FCD42F22067E22F0089C56D2C5 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMAnalytics.mm + path + Realm/RLMAnalytics.mm + sourceTree + <group> + + 0E09CC2965F557896A90F64ECD3EDAEA + + includeInIndex + 1 + isa + PBXFileReference + name + shared_realm.cpp + path + Realm/ObjectStore/src/shared_realm.cpp + sourceTree + <group> + + 0EACCB430C3B9BF28156F65E0C92B21F + + includeInIndex + 1 + isa + PBXFileReference + name + results.cpp + path + Realm/ObjectStore/src/results.cpp + sourceTree + <group> + + 0F541975B7520524A5663D7897CA9F73 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObjectBase_Dynamic.h + path + include/RLMObjectBase_Dynamic.h + sourceTree + <group> + + 0F96A7BCA6B30402998C93ED4F13265B + + fileRef + 8C54BF8B76C4DF3BB91E60307619C937 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 102145A1D07EC1C00D362924FBEFDB9C + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + Security.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Security.framework + sourceTree + DEVELOPER_DIR + + 1044C10B39D9E6B2A903B1EA7404428A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + ChameleonFramework-prefix.pch + sourceTree + <group> + + 105F7B9ABD5D2015417761041A0E45BC + + fileRef + 6995B31EC70CAE4FD7F5C6A5B6C5AEFF + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 11067A4D9D6BD6C9EE8A9D91B49E080B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMResults_Private.h + path + include/RLMResults_Private.h + sourceTree + <group> + + 1223F3E793F48DC17BACD0460FF220C3 + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 88BAE91CDCCA9CF51C40D714205654B1 + remoteInfo + Realm + + 140E5422EBD93C76D81C0BA17D93C498 + + fileRef + 804223344A88B50FDB0DF42F73A21AF4 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 14CD3DFB4A25BAEFB205730FA6750025 + + fileRef + 994D70A405793381AB62BC84EF435DC1 + isa + PBXBuildFile + + 14D3A25841EBBC76C52BAB9F9B66D77D + + fileRef + 947091807389FB48F558546821AABD90 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 14FFC9E4E040B8207EBDFB75B902F70E + + fileRef + 6CA19CE0274852A653112CFF9BE0A2B9 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 153266426FFA7487431CB10C9EE847F3 + + fileRef + 8D7928C4C08267E5679B90EF0DD80B4F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 15460ABD372C937E2A07A2FCDF98C473 + + fileRef + 17BB45079B7CF2C603144AC736394ADC + isa + PBXBuildFile + + 15B615406E392CED397CDD82804A2E59 + + children + + 4C09EE79BA9E155E1258328EF858CE96 + 1C33971EE872AA7CD5201A6D434AB562 + 37131000E30A8B8521F802D2173367DA + + isa + PBXGroup + name + MBProgressHUD + path + MBProgressHUD + sourceTree + <group> + + 16BEF02D02EF88A3478FE3F5A087D918 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + Pods-HackTX.debug.xcconfig + sourceTree + <group> + + 16D27792D8019EC21D669E8B1D990F48 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFSecurityPolicy.h + path + AFNetworking/AFSecurityPolicy.h + sourceTree + <group> + + 178145A465317476B1CF0B202CB9577F + + fileRef + 61E5B623A1D71121720C1A116F5C3141 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 17912D9A89FC36E82029409E9B74A61A + + fileRef + E3F23CCD04275E3397F00A5A1070AFD5 + isa + PBXBuildFile + + 17BB45079B7CF2C603144AC736394ADC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIActivityIndicatorView+AFNetworking.m + path + UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m + sourceTree + <group> + + 184EBAA144634FA2AD11BF652E26BCC3 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + AFNetworking-dummy.m + sourceTree + <group> + + 191F6FA9B378C1E53C7EAEA960DA6ABF + + includeInIndex + 1 + isa + PBXFileReference + name + RLMRealmConfiguration+Sync.mm + path + Realm/RLMRealmConfiguration+Sync.mm + sourceTree + <group> + + 19E6319F6ECED7539AABBD9A211F7AEC + + fileRef + 3FC0F5D9E16D642230561240EF2D1109 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 1A36559A7109BAF920FC3A4C3EEFD22B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UILabel+Chameleon.h + path + Pod/Classes/Objective-C/UILabel+Chameleon.h + sourceTree + <group> + + 1AFA89153325A09AD73AD0F15FE4EC4E + + baseConfigurationReference + 92FD64138758A2E0C16F696FF621DF05 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 6.0 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + 1B0CCE0203B6AC4F936ACC9698B599DC + + fileRef + 351D131C8169BF6999BAF2BAFB46945C + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 1BE8AF5CBCEF2BBBD8C1632D6C45900E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFURLRequestSerialization.m + path + AFNetworking/AFURLRequestSerialization.m + sourceTree + <group> + + 1C289876A4849D6798FC7F154214C5F8 + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + 1C33971EE872AA7CD5201A6D434AB562 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + MBProgressHUD.m + sourceTree + <group> + + 1C806B313FB8E36CA0CFCB623300547B + + includeInIndex + 1 + isa + PBXFileReference + name + RLMArray.mm + path + Realm/RLMArray.mm + sourceTree + <group> + + 1C9B78D6EC022316C546A6A6C5284253 + + fileRef + DBB77D712908602F32D23BEDA4015A8A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 1D05359385AC4E0EF8F41B8C9D372CFA + + includeInIndex + 1 + isa + PBXFileReference + name + checkmark-round.png + path + FCAlertView/Assets/checkmark-round.png + sourceTree + <group> + + 1D5E6799D6D5672545632E32F1EBABA6 + + fileRef + 32E43E4B54195FBA3C31EA5B0F23ACA2 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 1DC3459C2B4CACFEF4F0F2D5AEF0F946 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIView+ChameleonPrivate.m + path + Pod/Classes/Objective-C/UIView+ChameleonPrivate.m + sourceTree + <group> + + 1E34EA32972EA797CD38C418D3A1E5DC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFHTTPSessionManager.m + path + AFNetworking/AFHTTPSessionManager.m + sourceTree + <group> + + 1F97048AF18C3EFEDE731BDEECCD58A8 + + buildConfigurations + + 9413B669A6203AD71A048A8F824ECDD9 + 1AFA89153325A09AD73AD0F15FE4EC4E + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 21F622C970707D17108D57C7D688FF41 + + fileRef + EB366EC1A299ED6F83AABBDAD0FE82D7 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 222CC7D29A8BBDAFE395569CEAC0F3C4 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Fabric.h + path + iOS/Fabric.framework/Headers/Fabric.h + sourceTree + <group> + + 23415204AA48A02959E4E5245CF96037 + + fileRef + 1E34EA32972EA797CD38C418D3A1E5DC + isa + PBXBuildFile + + 2359387620C5E815BF0594BC40311734 + + fileRef + FFB8EBAD39FDD86E41FDB1FCBE47588B + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 24F110ED104C63946574F9105F506C4B + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + 25397BCA03C9A9724DEC769A3AD29804 + + fileRef + 8B45039A4F25B5C3EABDB24E8DFD3B5C + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 256219B86AC2E8239C596FC8FBEBC006 + + fileRef + 0DBE46FCD42F22067E22F0089C56D2C5 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 25769ECCDF5839D0BC00293976293256 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UINavigationController+Chameleon.h + path + Pod/Classes/Objective-C/UINavigationController+Chameleon.h + sourceTree + <group> + + 27A1162A8F92B3CC09AF528186217954 + + fileRef + 9E0EC843F35EB1B2AB716FA565C77A4A + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 27B1429A8801D3B979071A0E8BED1E93 + + baseConfigurationReference + C90AC47E07D866E5E95EB9BA2685F0D3 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 6.1 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + 27E4C74E1C5429BB7A128BA853E95739 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Chameleon_.h + path + Pod/Classes/Objective-C/Chameleon_.h + sourceTree + <group> + + 2806C6BCB72FA01F185D320218CA8ED1 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + Crashlytics.framework + path + iOS/Crashlytics.framework + sourceTree + <group> + + 290EF9D226138E5E3493B9B065967F09 + + fileRef + 85ADCE8D1021B46ED64138DCDA2141D8 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 29BFCB88C3683AFDDB7B7C9DE2ADD970 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + Realm.xcconfig + sourceTree + <group> + + 2AD25FBA8315FF6BB9BE5F822BC0A454 + + children + + 2806C6BCB72FA01F185D320218CA8ED1 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + 2B342A1143B39C7E396A50C6540E2639 + + fileRef + 734519BB99CD5EE94C689204BCBCCE67 + isa + PBXBuildFile + + 2C48558D0E7B7895FD07A7E15710DF8F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealmConfiguration+Sync.h + path + include/RLMRealmConfiguration+Sync.h + sourceTree + <group> + + 2CD4DB07B9E80ADF426DB21DBA98B820 + + buildConfigurationList + 6871AC2A5706BDE3F335824C698A7ACC + buildPhases + + A5A43A8E1106405D9EF264D1675A14C8 + 66624E85C2E9DEA5465F0C6808C38A20 + 76548CE38D1B86DEF6119E22838A458D + + buildRules + + dependencies + + 7117E2B7C30E19A32E50774C3B5320C0 + + isa + PBXNativeTarget + name + FCAlertView + productName + FCAlertView + productReference + 50520C893806752506E6EEE0BB1D9CDD + productType + com.apple.product-type.library.static + + 2CF07A9F9E9EA8B4E8072233188F45CB + + fileRef + 66C24AE670BCF23415ADA6380605623C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 2CF986C67C86BDDA5CDEABD0B68255BB + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libRealm.a + path + libRealm.a + sourceTree + BUILT_PRODUCTS_DIR + + 2D08469CD6468539EDE97D3B7BE6C682 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.script.sh + path + Pods-HackTX-frameworks.sh + sourceTree + <group> + + 2D1E479AE71EA42AF8B1B54F8561425F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncConfiguration_Private.h + path + include/RLMSyncConfiguration_Private.h + sourceTree + <group> + + 2D5CE155885A48B9BE8C331644EC1846 + + fileRef + D359689F94F914474D0840B700E5EE78 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 2D8E8EC45A3A1A1D94AE762CB5028504 + + buildConfigurations + + 034014829C1E4434983CF3A690C12017 + C7E474A03CAE21F1CB6046E7344C59BD + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 2DCD2674A99D81420352E80305151CB0 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMAuthResponseModel.m + path + Realm/RLMAuthResponseModel.m + sourceTree + <group> + + 2E260044B199CD0C301E8BC607C4FC98 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + SystemConfiguration.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/SystemConfiguration.framework + sourceTree + DEVELOPER_DIR + + 2E9E7D3960E7F084A26C54E970C02576 + + fileRef + 05A3C262D41B8AC17B19E29F4A28B6D0 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 2EA6DA768DD82D9DE2D6C469C45A0D4E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + FCAlertView.xcconfig + sourceTree + <group> + + 30DA0B928C8F28E3C3F279C3833458A0 + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + 311E5200832E4AE174A193851A2C6317 + + fileRef + BE0BD3371CFB1C2AA4B8699ED0313680 + isa + PBXBuildFile + + 3158C8D36B8A725577D8F5921EBF6CEE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncSession_Private.h + path + include/RLMSyncSession_Private.h + sourceTree + <group> + + 316E37986CE418F73BDB608B14E1F91E + + fileRef + 867F250B35D3382459596A4DDE74E3A7 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 326A543AD4E39A2DAECA1CD385E3AEA2 + + fileRef + 61E8A4C450D39FB880F93A5D1973A98A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 32B20C18CB66855BC61F31B587F37C13 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + Chameleon_.m + path + Pod/Classes/Objective-C/Chameleon_.m + sourceTree + <group> + + 32E43E4B54195FBA3C31EA5B0F23ACA2 + + includeInIndex + 1 + isa + PBXFileReference + name + collection_notifier.cpp + path + Realm/ObjectStore/src/impl/collection_notifier.cpp + sourceTree + <group> + + 33B60982AD9077D530647478A16047F8 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIButton+Chameleon.m + path + Pod/Classes/Objective-C/UIButton+Chameleon.m + sourceTree + <group> + + 34FFD5E28B5BB412F742D69062922B75 + + fileRef + D8BF6253E9383EC9A3FDD10F864E3827 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 3503EB4712F3DDA078EB9D7046A6902C + + children + + CF4068D76050799901D9EB116C71EF7A + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + 3511AE7ADBB71D05565854F0E70B7D08 + + fileRef + EEB0DD446622993688024F7FB9EC2642 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 351D131C8169BF6999BAF2BAFB46945C + + includeInIndex + 1 + isa + PBXFileReference + name + RLMAccessor.mm + path + Realm/RLMAccessor.mm + sourceTree + <group> + + 35BCAC68AD8EF6034462DEB2D161FABE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + ChameleonConstants.m + path + Pod/Classes/Objective-C/ChameleonConstants.m + sourceTree + <group> + + 36AD26C500DF00391B7BD28BCC86BF8E + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncUser.mm + path + Realm/RLMSyncUser.mm + sourceTree + <group> + + 37131000E30A8B8521F802D2173367DA + + children + + 92FD64138758A2E0C16F696FF621DF05 + D39979812C79D312028DD50E50AADCF2 + 6648F76B2306A63F1BE88DA2B3034D76 + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/MBProgressHUD + sourceTree + <group> + + 373E080E0E6EB98F84EFE4174DC42A67 + + fileRef + 557FE2D1541F2FC8B76B4A9329BBCA43 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 3784A70A6DAF0758CD36D8F3123B325E + + baseConfigurationReference + 2EA6DA768DD82D9DE2D6C469C45A0D4E + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/FCAlertView/FCAlertView-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + 3790779162209722E4C27CEA512E6333 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMNetworkClient.m + path + Realm/RLMNetworkClient.m + sourceTree + <group> + + 3820E0B45A63FA3D9B0B85EE86B7B716 + + children + + F4F28856BDD6FFE26530DD037EAC5909 + 1E34EA32972EA797CD38C418D3A1E5DC + EEB0DD446622993688024F7FB9EC2642 + 6D10393660D7BDAACEDFC835FCE220AF + + isa + PBXGroup + name + NSURLSession + sourceTree + <group> + + 38AD5AEEBFDC3D85F86077880D7EF3D4 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMMigration.h + path + include/RLMMigration.h + sourceTree + <group> + + 38EB37219A3511491E564F140CD39D1B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + FCAlertView-prefix.pch + sourceTree + <group> + + 38EEA9ED6B922946C54AD6BAC93B73AD + + fileRef + 035C9810D15E865D9DD4AA3F4BA4815A + isa + PBXBuildFile + + 3939531EB270B4145C7ADFBAB2E5695F + + fileRef + 087FE3AAD0A942370684A7E8B7A4E0B2 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 395B651E19F2CB1ABBED1C1DDA017437 + + fileRef + 49C0BE8AD21D72D90C0F16C7E267749E + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 3962E111BE7E4B0BF02371AEE6860C5E + + fileRef + 1DC3459C2B4CACFEF4F0F2D5AEF0F946 + isa + PBXBuildFile + + 39A03A7A9B5C6846676C594FCBE2FE4C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Realm.h + path + include/Realm.h + sourceTree + <group> + + 39E3FF933C65B8FB898BE9B6C8D9A944 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMArray_Private.h + path + include/RLMArray_Private.h + sourceTree + <group> + + 39EF664B48BC3CF73B1B4653DE0876DC + + includeInIndex + 1 + isa + PBXFileReference + name + RLMObjectSchema.mm + path + Realm/RLMObjectSchema.mm + sourceTree + <group> + + 3AA3AC330E15B7D4B9A73621944E842F + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + 3BE4AA25F40003242509B18CF80CBBCA + + includeInIndex + 1 + isa + PBXFileReference + name + RLMRealmConfiguration.mm + path + Realm/RLMRealmConfiguration.mm + sourceTree + <group> + + 3C51C0C1DF1AF101E0EF749B8E660489 + + baseConfigurationReference + EE0AFC26190EB72C2BBEEF935B6C2BDB + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/AFNetworking/AFNetworking-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + 3C5FE6AD10D6553D74C92BFD2A5FCDA9 + + fileRef + 953A9C6043FDCBCF322213F1D0889ED2 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 3C7BA6DB4DB1A45741E6731CD4D5F76B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIWebView+AFNetworking.h + path + UIKit+AFNetworking/UIWebView+AFNetworking.h + sourceTree + <group> + + 3D09230B9E8D147EC08BD0F04179D917 + + fileRef + 3BE4AA25F40003242509B18CF80CBBCA + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 3D90767FB726F3CCEA9C6A49DC9E1D39 + + children + + EBC57534427A1FB53B4F4D8D37DF31B8 + 6D89929EFDD6970606420BBA879D5B59 + 0406E22A3C59B67D140C99533B020681 + AF2387BA466815A90CE6D5D906BBEC36 + + isa + PBXGroup + name + FCAlertView + path + FCAlertView + sourceTree + <group> + + 3F0DAEF7AD12BD925424AC392AC64164 + + fileRef + 41A123D94977528A2E8F926767228F0A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 3FABBF3740F613ACE6361AD63E4A536C + + children + + F46170D78366C37B71DA6A70EEA5DB9B + + isa + PBXGroup + name + GoogleUtilities + path + GoogleUtilities + sourceTree + <group> + + 3FC0F5D9E16D642230561240EF2D1109 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFAutoPurgingImageCache.h + path + UIKit+AFNetworking/AFAutoPurgingImageCache.h + sourceTree + <group> + + 3FD09E807BF9E65D2704629D08261714 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleMaps.framework + path + Subspecs/Maps/Frameworks/GoogleMaps.framework + sourceTree + <group> + + 401EF38FAF61AF2A5469D255B3E69C96 + + baseConfigurationReference + 16BEF02D02EF88A3478FE3F5A087D918 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + IPHONEOS_DEPLOYMENT_TARGET + 9.0 + MACH_O_TYPE + staticlib + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PODS_ROOT + $(SRCROOT) + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + PRODUCT_NAME + $(TARGET_NAME) + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + 41A123D94977528A2E8F926767228F0A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIColor+Chameleon.h + path + Pod/Classes/Objective-C/UIColor+Chameleon.h + sourceTree + <group> + + 41DCFBD06C337F7D3B29C2FED717DF2C + + children + + 6CA1689F1CDBDEA272F8EBC907A2FD26 + + isa + PBXGroup + name + Resources + sourceTree + <group> + + 426B448E3256264D133826A2F7CA832D + + fileRef + 014E6E2EFAF9AE5C27A801DC4238C596 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 42E9D96256BEF604FB00F82786327214 + + buildActionMask + 2147483647 + files + + 82B018E48786CF782B4C57A7F381E165 + F9BA9CFF505BABA1B99F2C10AEC14525 + 1D5E6799D6D5672545632E32F1EBABA6 + 08CAEB9B2FBB56A8AF331DA7F3D8C914 + C83E72228632A2D20FDDA2285618BA8C + EACECC9B850C1F0AB5D8974BB2DB74A1 + 4F0CB0D61BC27C4D3DF914D66C1E498F + 52BA851966D3E0A3E5732A30E256DED5 + 0742E7DA28B5B91F1894B84E00A82DA0 + A64307190CDF73F94F37E5AED8818AB9 + 7C4594688F1FE758C65FC9E17F0C1896 + 9F9A093C4CA3A8B17C2E6364B9DE116B + 6E248D56307912339AF1634089F01F85 + DA21507E6FC6972DD62B1ED396649676 + 58624887575B407047AC6ACDAD4C7ECB + 6B703FF7F5B93F7F03052499C45D19E4 + 987B7715A87FCD7528B6420B905D91F6 + 1B0CCE0203B6AC4F936ACC9698B599DC + 256219B86AC2E8239C596FC8FBEBC006 + 6F6EBBAA9D1E56C19E535727BA673AE5 + 4A5EF8D35AFE942505344B114BF4EC3E + 7BD79011FF0D769F04937DDF39646CF4 + 67D4DAB912C502E4519EAE929825E813 + 5721F267AA86242A074420EC69DE18F4 + 69F251D96CE5B8D6B5306F34473BEBFA + 548A9020E1F1CD9A0B8111C448300254 + 3C5FE6AD10D6553D74C92BFD2A5FCDA9 + 4AAE4F7080A256695A3042414D0EF362 + 7203FA61087536D4BD868051D9F743FB + 25397BCA03C9A9724DEC769A3AD29804 + EADA601641D6E5E2E1512F8311537D36 + 6BA9F3C8F3F107609A19C6F71C1F4680 + 4A75C9E545607626A916B522E931D4F0 + 0824B19691E8B71596EEC2ECAFB82958 + FAFE2172A5670E1ECCCFB8308644B69E + 879B21E6F92BB0E8E49EC1A584F62913 + DBB20CE52958E83D2431A6A97EF3FDEF + F1CFC9A519BBF893CD52CD4EF853854B + E644E5FAABD1DFAD3D68B1264E93BB84 + 3D09230B9E8D147EC08BD0F04179D917 + 4E5939C9A4DC8A6CE41F3A2226014D59 + 395B651E19F2CB1ABBED1C1DDA017437 + DA1D7BB9AAB4E8F5C62C53C58E3127FA + 34FFD5E28B5BB412F742D69062922B75 + D7647F50F6A950A883FEA0EF48FC15B1 + 4C4D0D8983D9B48AD72EC53373BB6DF6 + 90719794FF3D105842F640AB3C1D5294 + 46166981FF65D108EC330376105F5F5B + 00C1F6DE985E076876867E764E368A44 + CB6EE5099C791E5D5AF801135E902224 + 619716D40D645E289B09C5254C922C28 + FC47A20F268327D7F11A6ABAFADD63BC + F6687C03FFEC03180FC4B10D784B39E9 + 373E080E0E6EB98F84EFE4174DC42A67 + 27A1162A8F92B3CC09AF528186217954 + 21F622C970707D17108D57C7D688FF41 + B69DF53219174426BC1381A513BA42BD + 316E37986CE418F73BDB608B14E1F91E + 290EF9D226138E5E3493B9B065967F09 + 85C5B9766AC054202435C0CFE4D74D69 + 754BCB57902496938D1A524B94C6D9CC + 178145A465317476B1CF0B202CB9577F + CD2D30F21A2FE51073A6CE6634A2FF49 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 43098B12DF5546CB576986E68DBCAFCA + + fileRef + DA78E3FC2D59335D411082AC8D5A6706 + isa + PBXBuildFile + + 437649DCAB8362EA9F39BFF7585173D1 + + includeInIndex + 1 + isa + PBXFileReference + name + alert-round.png + path + FCAlertView/Assets/alert-round.png + sourceTree + <group> + + 437BD5FA1E5BCE1A6BC2472BFF62F5F5 + + fileRef + 6D89929EFDD6970606420BBA879D5B59 + isa + PBXBuildFile + + 45423C0A2E9714AC0469B85E32642A91 + + fileRef + 1D05359385AC4E0EF8F41B8C9D372CFA + isa + PBXBuildFile + + 45F475F2324F52333E51E3655ADA2C2D + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIButton+AFNetworking.h + path + UIKit+AFNetworking/UIButton+AFNetworking.h + sourceTree + <group> + + 46166981FF65D108EC330376105F5F5B + + fileRef + A193AE29C853F25CC2874A49CEBEAA32 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 478571A70D37AAD6B77C9ED742188D8D + + children + + 16D27792D8019EC21D669E8B1D990F48 + BE0BD3371CFB1C2AA4B8699ED0313680 + + isa + PBXGroup + name + Security + sourceTree + <group> + + 47FD7C7C8021EA2926E5F73AF21D01E3 + + baseConfigurationReference + A4F8F4E2EE06290D520B91E6F9983308 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/ChameleonFramework/ChameleonFramework-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 8.0 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + 49778E0D5CB78A39F8A34843895B4551 + + fileRef + 0F541975B7520524A5663D7897CA9F73 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 49C0BE8AD21D72D90C0F16C7E267749E + + includeInIndex + 1 + isa + PBXFileReference + name + RLMResults.mm + path + Realm/RLMResults.mm + sourceTree + <group> + + 4A1768D3B09DA5A7C451D36DAF859DC9 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMObjectStore.mm + path + Realm/RLMObjectStore.mm + sourceTree + <group> + + 4A5EF8D35AFE942505344B114BF4EC3E + + fileRef + A51B35F2429E5C3C1B20D56F2C69C03E + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4A75C9E545607626A916B522E931D4F0 + + fileRef + 7E1C7C62E8DF627D883635C6300AAECD + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4AAE4F7080A256695A3042414D0EF362 + + fileRef + 3790779162209722E4C27CEA512E6333 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4AC795793934FFD62D512BB1C6163D90 + + buildConfigurations + + 47FD7C7C8021EA2926E5F73AF21D01E3 + 96B1A5AEF4A356F1C4FBF4EF8A891DA4 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 4B31FCA99B3ED3EDA9C48C5ED567D092 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFNetworkActivityIndicatorManager.m + path + UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m + sourceTree + <group> + + 4B335881B2F732F366DF01E4EAC3CDBC + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libPods-HackTX.a + path + libPods-HackTX.a + sourceTree + BUILT_PRODUCTS_DIR + + 4B6619D006E09AE372922F58C2ADC303 + + fileRef + 32B20C18CB66855BC61F31B587F37C13 + isa + PBXBuildFile + + 4BA19F7DD8F9FDA648137B2CE89E7588 + + fileRef + 4B31FCA99B3ED3EDA9C48C5ED567D092 + isa + PBXBuildFile + + 4BFFEBA374AD3562751D0ECDE5DB1D98 + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libMBProgressHUD.a + path + libMBProgressHUD.a + sourceTree + BUILT_PRODUCTS_DIR + + 4C09EE79BA9E155E1258328EF858CE96 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + MBProgressHUD.h + sourceTree + <group> + + 4C4D0D8983D9B48AD72EC53373BB6DF6 + + fileRef + FAAFDCB4C82F19E2ACEFE5A793D7E9C4 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4E2B74ABD7E0224ECA5C6F19CAE14494 + + fileRef + 9C6ECDBC1353D9E1CC1EC4BBAA90051C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 4E5939C9A4DC8A6CE41F3A2226014D59 + + fileRef + C46CC57DF2F5AD39E32A2FCC73CB31E8 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4F0CB0D61BC27C4D3DF914D66C1E498F + + fileRef + F1B00DB7ED33EA59D09C38EF18EC525B + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 4F6BC6201273EF0BE7AACBB376D83667 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncConfiguration.h + path + include/RLMSyncConfiguration.h + sourceTree + <group> + + 4FE51245E672DBC295105C287ABD6246 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + NSArray+Chameleon.m + path + Pod/Classes/Objective-C/NSArray+Chameleon.m + sourceTree + <group> + + 50520C893806752506E6EEE0BB1D9CDD + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libFCAlertView.a + path + libFCAlertView.a + sourceTree + BUILT_PRODUCTS_DIR + + 505DBB05922BE6AA7F5672ED4B778541 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + SVProgressHUD-prefix.pch + sourceTree + <group> + + 508C962B3E08E8DA04956BF349A3CDFD + + buildActionMask + 2147483647 + files + + 045FFEDD819A734271DD5DE53C877884 + 30DA0B928C8F28E3C3F279C3833458A0 + 17912D9A89FC36E82029409E9B74A61A + 0358B918CA8C9D3C214FD4D41CD69A90 + 650FFDAF60A6C37D824BC6364B09A3E5 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 50EC8674627852A08E2B9BAB723FD0FB + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + D058B89EB3B3DF45457A2DCD3971AFFE + remoteInfo + MBProgressHUD + + 52B45D2E71686C92C21DFCECF047FAF3 + + includeInIndex + 1 + isa + PBXFileReference + name + thread_confined.cpp + path + Realm/ObjectStore/src/thread_confined.cpp + sourceTree + <group> + + 52BA851966D3E0A3E5732A30E256DED5 + + fileRef + 065A73EDC4EEEE691C0F9DB7CA1B24DA + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 52F2231AD634FAFD6258A6D2F66E3AA3 + + buildConfigurations + + 401EF38FAF61AF2A5469D255B3E69C96 + C64CAB5899DE23FC097A71FFDC77C046 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 548A9020E1F1CD9A0B8111C448300254 + + fileRef + EE13135D5B5DB16CB42CC2233807E30F + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 5564E637BECE16B305EAAA9C125DC18E + + fileRef + 796FEFA7DCF574265FF334D12D310CB0 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 557FE2D1541F2FC8B76B4A9329BBCA43 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMUpdateChecker.mm + path + Realm/RLMUpdateChecker.mm + sourceTree + <group> + + 56FBBC4D57B7B8421252CD3B3801ABA3 + + fileRef + 7C04E4994284BDCD9F6D9DA37CCB6A41 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 5721F267AA86242A074420EC69DE18F4 + + fileRef + CBE7AA212D63A12240B3FD34EF96FA67 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 57F4A587EE1811563109E2B8BB66038F + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleUtilities.framework + path + Frameworks/frameworks/GoogleUtilities.framework + sourceTree + <group> + + 58624887575B407047AC6ACDAD4C7ECB + + fileRef + F9666AF738588ADE7ED75A15E570A507 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 5997ECDFF8D96054815FFC5E06C17A49 + + fileRef + 7F2E88B654006CB5584AF018CB61A5A0 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 599A2F75AE6DAAD4CB43E3B5C29D090A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIImage+ChameleonPrivate.m + path + Pod/Classes/Objective-C/UIImage+ChameleonPrivate.m + sourceTree + <group> + + 5A705339812A117B1EEFD207502BBA6C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIView+ChameleonPrivate.h + path + Pod/Classes/Objective-C/UIView+ChameleonPrivate.h + sourceTree + <group> + + 5AA8986BDBA8C82524FE5D3CF0466152 + + fileRef + 39E3FF933C65B8FB898BE9B6C8D9A944 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 5AE2E46A16828914FBE9055186274C3B + + fileRef + CD262FFC80FE111F484D56576BC568A3 + isa + PBXBuildFile + + 5B01CAAE2DFD9DA0CD5D0015943D8241 + + includeInIndex + 1 + isa + PBXFileReference + name + close-round.png + path + FCAlertView/Assets/close-round.png + sourceTree + <group> + + 5BD53FD7FDA677CCC069FA6CFEEEF4D5 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIImageView+AFNetworking.h + path + UIKit+AFNetworking/UIImageView+AFNetworking.h + sourceTree + <group> + + 5C7F8728518E04EDE9A8B0FF67806CDD + + fileRef + F0D398496991B317983A8AC724EC7557 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 5CE8EB6E28FD8EBB1D2C5C5A36AA1ABC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + CLSReport.h + path + iOS/Crashlytics.framework/Headers/CLSReport.h + sourceTree + <group> + + 5D52DB6DF2E6C24CECB0015A9177A2E1 + + fileRef + 8777D04A5A8FD9479335E300CBF2942E + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 5DFDE9F29CD4716D399CD9B7B035E4F7 + + children + + D1398EEA3895D7F2CE9772514E61AB66 + DA78E3FC2D59335D411082AC8D5A6706 + + isa + PBXGroup + name + Reachability + sourceTree + <group> + + 5F1F502201CD9082F0C1D187A9353330 + + buildActionMask + 2147483647 + files + + 737F2365811F1334BFB8BB7C396DB1FE + 153266426FFA7487431CB10C9EE847F3 + 8582B41E568BD84BD348B8A6EFBFD441 + 5AA8986BDBA8C82524FE5D3CF0466152 + CD6AC686A742439DE9C35DBDB50BD06F + 91EB709E9E7BD9E5E5693B2CDCDA7E64 + C0716975BE5BCCDB5F8BCFCFBD10B130 + 7855254B5B8C6837D32AFD11871CB9E7 + B51E57F75AF251750B8061A3C85659C3 + 6EB42C75720EBA3E38A274517CF375EC + FCBC8DAE4AABB8879E0986A0A380D990 + C38C528392EF09CA2EC413F1AA092B2A + 49778E0D5CB78A39F8A34843895B4551 + 56FBBC4D57B7B8421252CD3B3801ABA3 + 140E5422EBD93C76D81C0BA17D93C498 + 84607FFF31F25D8D9A9BA85B091113EC + DF1789C3AD7A3289FAD95E628E81DB08 + 5564E637BECE16B305EAAA9C125DC18E + 105F7B9ABD5D2015417761041A0E45BC + F2303B2ADB41B4306D0ED6F958AD3222 + AFDFA06F78F08FB69FAEBA0EDF12768F + F9E78382EF3D8EE8204AEECBAE3C62CD + 0F96A7BCA6B30402998C93ED4F13265B + 7D18136ACEF50DEE2CB0AB8F385C79B0 + 63F3240623F9E15C6C60F4BAEF65C6CB + 8C0921741DBA3D8B8C3345CC91BDB147 + 14D3A25841EBBC76C52BAB9F9B66D77D + FE23A49D003C17D1BAE347E504B7639C + E6CE6E65CFF47B304E8C6E4A1A22D9DD + D38F36DD7F4CACB524C72CDF18733979 + 72DB3C2D11949A9746010D58F69AFAB7 + BF4D54A2FC2D4B7BA65DEAF186482724 + 5997ECDFF8D96054815FFC5E06C17A49 + 2CF07A9F9E9EA8B4E8072233188F45CB + 14FFC9E4E040B8207EBDFB75B902F70E + 95422E64C4CF4B23D38184DF07169E88 + 2E9E7D3960E7F084A26C54E970C02576 + A66E0AF0A1001B7BDAB6F2B6C0C0580C + DE4C71A8EB929C7A589183CAFE0DA7D1 + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 5F73C97D9DB69FF261AEF2522DF455B4 + + children + + 8CE550EF3AE9B5A5DFBBBEB3E29E2714 + 3820E0B45A63FA3D9B0B85EE86B7B716 + 5DFDE9F29CD4716D399CD9B7B035E4F7 + 478571A70D37AAD6B77C9ED742188D8D + 074AC82485DF3FFCA9B411EF41AFEB86 + D4DDD66982DD17BA2E1CC973A09FCD1B + 9B202518E5A7FFCD675009BC4061B685 + + isa + PBXGroup + name + AFNetworking + path + AFNetworking + sourceTree + <group> + + 612E2E089A1E64C8682518A2A239191A + + buildConfigurationList + 52F2231AD634FAFD6258A6D2F66E3AA3 + buildPhases + + DB5E354F3C5B7E719BAC7AEA3823E9F0 + 038144FA412425B054D4187A9A71F7D7 + + buildRules + + dependencies + + 9A70AF712C6307C05CB3E5C366AED3EC + BAC2AC9718E818D794FDA9FCBE5F10BC + 63AB41D8257129EC7AEA19CB175FA683 + 8FA7E5B5EC5A1BD20D04D343482CC112 + B2C13AF00B0B0467938ADD1BC71D0EFE + AB6D0FD1EBA2B07E8DD2C96BFBABF792 + + isa + PBXNativeTarget + name + Pods-HackTX + productName + Pods-HackTX + productReference + 4B335881B2F732F366DF01E4EAC3CDBC + productType + com.apple.product-type.library.static + + 619716D40D645E289B09C5254C922C28 + + fileRef + 36AD26C500DF00391B7BD28BCC86BF8E + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 61E5B623A1D71121720C1A116F5C3141 + + includeInIndex + 1 + isa + PBXFileReference + name + transact_log_handler.cpp + path + Realm/ObjectStore/src/impl/transact_log_handler.cpp + sourceTree + <group> + + 61E8A4C450D39FB880F93A5D1973A98A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFImageDownloader.h + path + UIKit+AFNetworking/AFImageDownloader.h + sourceTree + <group> + + 63506B10240955B048AB7F5491BEAE73 + + baseConfigurationReference + 2EA6DA768DD82D9DE2D6C469C45A0D4E + buildSettings + + CONFIGURATION_BUILD_DIR + $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FCAlertView + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + INFOPLIST_FILE + Target Support Files/FCAlertView/ResourceBundle-FCAlertView-Info.plist + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + PRODUCT_NAME + FCAlertView + SDKROOT + iphoneos + SKIP_INSTALL + YES + TARGETED_DEVICE_FAMILY + 1,2 + WRAPPER_EXTENSION + bundle + + isa + XCBuildConfiguration + name + Debug + + 63A824AF9EB12937A1397B9510DF6675 + + children + + 3503EB4712F3DDA078EB9D7046A6902C + + isa + PBXGroup + name + GoogleSymbolUtilities + path + GoogleSymbolUtilities + sourceTree + <group> + + 63AB41D8257129EC7AEA19CB175FA683 + + isa + PBXTargetDependency + name + FCAlertView + target + 2CD4DB07B9E80ADF426DB21DBA98B820 + targetProxy + B3E45F0B3B5343EF5EEA12DF719C4BA3 + + 63CEF6A400009AC67D2DB3C2F22C5B1A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UILabel+Chameleon.m + path + Pod/Classes/Objective-C/UILabel+Chameleon.m + sourceTree + <group> + + 63F3240623F9E15C6C60F4BAEF65C6CB + + fileRef + CFD23C7C2579B7F3ED29DEBB22950FBC + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 648E7F45D6E4DCE98EED9D4E2F003C1E + + fileRef + D39979812C79D312028DD50E50AADCF2 + isa + PBXBuildFile + + 650FFDAF60A6C37D824BC6364B09A3E5 + + fileRef + 2E260044B199CD0C301E8BC607C4FC98 + isa + PBXBuildFile + + 6648F76B2306A63F1BE88DA2B3034D76 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + MBProgressHUD-prefix.pch + sourceTree + <group> + + 66624E85C2E9DEA5465F0C6808C38A20 + + buildActionMask + 2147483647 + files + + F96AF2236B76293064F519937A337FE0 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 66C24AE670BCF23415ADA6380605623C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncManager.h + path + include/RLMSyncManager.h + sourceTree + <group> + + 67D4DAB912C502E4519EAE929825E813 + + fileRef + BA6E0A642D0E14DF3E434F706131EBBC + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 67D892FCC7F5F11B302B43E622DA0C08 + + children + + 97116464E340CFFCA287431A7F1D44E1 + C0F313278EAF28D5271FFB2571FDE025 + + isa + PBXGroup + name + FirebaseInstanceID + path + FirebaseInstanceID + sourceTree + <group> + + 68447C66DED8D4C5F1BF848329534A24 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIImage+AFNetworking.h + path + UIKit+AFNetworking/UIImage+AFNetworking.h + sourceTree + <group> + + 684743B9B41167B1D3D9B77FD3BC623F + + children + + 8673F9BF9415E820ED287ACA96F8DC60 + + isa + PBXGroup + name + Core + sourceTree + <group> + + 6871AC2A5706BDE3F335824C698A7ACC + + buildConfigurations + + FE8A58E62C67F600AD1469E4F13F9BB9 + 3784A70A6DAF0758CD36D8F3123B325E + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 695A3FA15E903AE48EB0B3BE336F2467 + + fileRef + 4C09EE79BA9E155E1258328EF858CE96 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 6995B31EC70CAE4FD7F5C6A5B6C5AEFF + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMProperty.h + path + include/RLMProperty.h + sourceTree + <group> + + 69F251D96CE5B8D6B5306F34473BEBFA + + fileRef + A9B60F879FDCB9BB52A3022C50A88AA8 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 6A5F8959A9251BC3BDDAC8535F73AC0A + + children + + BBC7594433D7A40A0B7BB6280D4A7572 + D531C0FF953D03C25A1D8FC4288934E6 + + isa + PBXGroup + name + ChameleonFramework + path + ChameleonFramework + sourceTree + <group> + + 6B703FF7F5B93F7F03052499C45D19E4 + + fileRef + 0EACCB430C3B9BF28156F65E0C92B21F + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 6BA9F3C8F3F107609A19C6F71C1F4680 + + fileRef + 4A1768D3B09DA5A7C451D36DAF859DC9 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 6CA1689F1CDBDEA272F8EBC907A2FD26 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + wrapper.plug-in + name + GoogleMaps.bundle + path + Subspecs/Maps/Frameworks/GoogleMaps.framework/Versions/A/Resources/GoogleMaps.bundle + sourceTree + <group> + + 6CA19CE0274852A653112CFF9BE0A2B9 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncSession.h + path + include/RLMSyncSession.h + sourceTree + <group> + + 6D10393660D7BDAACEDFC835FCE220AF + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFURLSessionManager.m + path + AFNetworking/AFURLSessionManager.m + sourceTree + <group> + + 6D89929EFDD6970606420BBA879D5B59 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + FCAlertView.m + path + FCAlertView/Classes/FCAlertView.m + sourceTree + <group> + + 6E248D56307912339AF1634089F01F85 + + fileRef + 011CFCDFC70FDCB2D6716099D07BD63F + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 6EB42C75720EBA3E38A274517CF375EC + + fileRef + 8627565453E2E791A41FF0731917446F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 6EC97BDB3FC508526F9E388095A7DDCC + + fileRef + 5BD53FD7FDA677CCC069FA6CFEEEF4D5 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 6F2A0CB1182DCF166FB48C69BA9AA7D3 + + fileRef + C60A89879434FAC011EF7C7236966097 + isa + PBXBuildFile + + 6F6EBBAA9D1E56C19E535727BA673AE5 + + fileRef + 1C806B313FB8E36CA0CFCB623300547B + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 6FEA7D1C33EE2715829503B1D42E7D7B + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 7F74193519009A1348B026EB930394E1 + remoteInfo + SVProgressHUD + + 70E6B5E63D98ED0D1713EEACB0393EBC + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + Foundation.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/Foundation.framework + sourceTree + DEVELOPER_DIR + + 7117E2B7C30E19A32E50774C3B5320C0 + + isa + PBXTargetDependency + name + FCAlertView-FCAlertView + target + B1AAA1228D99746F4EB488754929B532 + targetProxy + F1ED7882BC7CE7E19BD11A40A7BC7D37 + + 71AF53765B7E79703FC89C9A75E5E9EC + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libChameleonFramework.a + path + libChameleonFramework.a + sourceTree + BUILT_PRODUCTS_DIR + + 71EA2F58426D22F28922C095F78A6FB1 + + fileRef + B185E0E344FCEC1879D37A6120E1E27E + isa + PBXBuildFile + + 7203FA61087536D4BD868051D9F743FB + + fileRef + 8B016B376DC8805B6052E9D206F2F288 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 727BFE93677461E5E7E802B77A946AB9 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ANSCompatibility.h + path + iOS/Crashlytics.framework/Headers/ANSCompatibility.h + sourceTree + <group> + + 72DB3C2D11949A9746010D58F69AFAB7 + + fileRef + 4F6BC6201273EF0BE7AACBB376D83667 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 72F5F0696BD3A9F6E7444261414BFA7B + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncConfiguration.mm + path + Realm/RLMSyncConfiguration.mm + sourceTree + <group> + + 734519BB99CD5EE94C689204BCBCCE67 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + QuartzCore.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/QuartzCore.framework + sourceTree + DEVELOPER_DIR + + 7379277A8D21CD877377F886C8638686 + + children + + B336DA43C9E6E0A8D12F53E72784747A + + isa + PBXGroup + name + GoogleInterchangeUtilities + path + GoogleInterchangeUtilities + sourceTree + <group> + + 737F2365811F1334BFB8BB7C396DB1FE + + fileRef + 39A03A7A9B5C6846676C594FCBE2FE4C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 748DB5EF4CAA4A79E761A9BBD933DFD9 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + CLSStackFrame.h + path + iOS/Crashlytics.framework/Headers/CLSStackFrame.h + sourceTree + <group> + + 754BCB57902496938D1A524B94C6D9CC + + fileRef + 52B45D2E71686C92C21DFCECF047FAF3 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 76548CE38D1B86DEF6119E22838A458D + + buildActionMask + 2147483647 + files + + EC890D269DC463EF00D56865C63233DB + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 766006A1FE00CF3BC5B57612BA353D53 + + fileRef + 734519BB99CD5EE94C689204BCBCCE67 + isa + PBXBuildFile + + 77CF2A7107BC61A2E90025871305961D + + buildConfigurations + + D9C97EB576CE94D4E00FD675005D2868 + 3C51C0C1DF1AF101E0EF749B8E660489 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + 7855254B5B8C6837D32AFD11871CB9E7 + + fileRef + 38AD5AEEBFDC3D85F86077880D7EF3D4 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 78940A6980F359C4304C695EF6E67C5D + + fileRef + 926E1758EE25AF968AC0C498C4AB1ED7 + isa + PBXBuildFile + + 78A271BEC0CD09221D7DE76766E9B57E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFURLResponseSerialization.m + path + AFNetworking/AFURLResponseSerialization.m + sourceTree + <group> + + 796FEFA7DCF574265FF334D12D310CB0 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMPlatform.h + path + include/RLMPlatform.h + sourceTree + <group> + + 7A8F9A1227006BF4BD9E953FD3D91B8D + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + AFNetworking-prefix.pch + sourceTree + <group> + + 7BA9A8B5B4FE3448D5072369AB0A4513 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + CoreGraphics.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CoreGraphics.framework + sourceTree + DEVELOPER_DIR + + 7BCC7C7D951690976EF961E7F5897E6D + + children + + B04FF59E19F454EE7E657EBD8F07127E + + isa + PBXGroup + name + Resources + sourceTree + <group> + + 7BD79011FF0D769F04937DDF39646CF4 + + fileRef + 2DCD2674A99D81420352E80305151CB0 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 7BE65A584E79E775585E6A59B5EE4C9A + + includeInIndex + 1 + isa + PBXFileReference + name + collection_notifications.cpp + path + Realm/ObjectStore/src/collection_notifications.cpp + sourceTree + <group> + + 7C04E4994284BDCD9F6D9DA37CCB6A41 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObjectSchema.h + path + include/RLMObjectSchema.h + sourceTree + <group> + + 7C4594688F1FE758C65FC9E17F0C1896 + + fileRef + D44C8FE1691936E436C1A6F62216040A + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 7D18136ACEF50DEE2CB0AB8F385C79B0 + + fileRef + 2C48558D0E7B7895FD07A7E15710DF8F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 7D6A8F7B7081EA6FBFA6F234E8D30274 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMQueryUtil.mm + path + Realm/RLMQueryUtil.mm + sourceTree + <group> + + 7DB346D0F39D3F0E887471402A8071AB + + children + + 93A4A3777CF96A4AAC1D13BA6DCCEA73 + F4CDA5FA9197A41E0081E84F932906EB + F889A73189014B940118D652E6E4292D + FF72855D2EFF1C13779CAA586D517808 + EB3236CBB7D49A36AD633A6125B230BF + + isa + PBXGroup + sourceTree + <group> + + 7DDBA27D6FB4FC7E2D3E5766C4989AC2 + + children + + E9B6E1747CD6696B75D4D3BA413BB243 + 916BD6B0CC6D0D63FC022B1D13B0F4C8 + + isa + PBXGroup + name + FirebaseMessaging + path + FirebaseMessaging + sourceTree + <group> + + 7E1C7C62E8DF627D883635C6300AAECD + + includeInIndex + 1 + isa + PBXFileReference + name + RLMObservation.mm + path + Realm/RLMObservation.mm + sourceTree + <group> + + 7EBC8DFA155FE6FD1E4E3B7A7D9AE37E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ChameleonConstants.h + path + Pod/Classes/Objective-C/ChameleonConstants.h + sourceTree + <group> + + 7F0578A0258E02384F6229C678E60D31 + + fileRef + B3CE96EF95188B8601CC7EFEDF3A5CFE + isa + PBXBuildFile + + 7F18A631A68678DE5B11D4785CE6E57B + + fileRef + 27E4C74E1C5429BB7A128BA853E95739 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 7F2E88B654006CB5584AF018CB61A5A0 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncCredential.h + path + include/RLMSyncCredential.h + sourceTree + <group> + + 7F74193519009A1348B026EB930394E1 + + buildConfigurationList + C176EBED813E3C9E0A25021198EA027A + buildPhases + + ADE50ECE073EDFB3DC851B8278EFAF2B + BF05A2DCDAF94A4A145F6D05C198AC13 + B5DCAE8DC0F856CEEFD5EB3C02146A3D + + buildRules + + dependencies + + isa + PBXNativeTarget + name + SVProgressHUD + productName + SVProgressHUD + productReference + C0E25938902ED70F39B165929D2F4523 + productType + com.apple.product-type.library.static + + 7F994993061A02071EB66DAA0C072526 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + FirebaseMessaging.framework + path + Frameworks/frameworks/FirebaseMessaging.framework + sourceTree + <group> + + 804223344A88B50FDB0DF42F73A21AF4 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObjectSchema_Private.h + path + include/RLMObjectSchema_Private.h + sourceTree + <group> + + 805FFE9AE0AF6914315E57A448B415A8 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIKit+AFNetworking.h + path + UIKit+AFNetworking/UIKit+AFNetworking.h + sourceTree + <group> + + 82051A21B9C2BD50762EA94356A0313A + + fileRef + 437649DCAB8362EA9F39BFF7585173D1 + isa + PBXBuildFile + + 82B018E48786CF782B4C57A7F381E165 + + fileRef + A50851DD5462A8FDACE18ED550B31634 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 830050886B18F54E17F117FD8EFC717B + + fileRef + B68044ED48DB323D0625E5A3355205FA + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 83C96D6E58D0F45750D20CA819D4D6C8 + + fileRef + 88E9AAA2DBA4606C365E0704DF6EDBA9 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 84607FFF31F25D8D9A9BA85B091113EC + + fileRef + F2BD8D5314F70A913234B9380377C1C0 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 84CA470D6195ECEABCAB05D43D68A04A + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libAFNetworking.a + path + libAFNetworking.a + sourceTree + BUILT_PRODUCTS_DIR + + 850E7446484F5FAD8CCEEC47A8CAF44D + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Answers.h + path + iOS/Crashlytics.framework/Headers/Answers.h + sourceTree + <group> + + 8570C10E07F0A974AF2D7D5BA700BA34 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UINavigationController+Chameleon.m + path + Pod/Classes/Objective-C/UINavigationController+Chameleon.m + sourceTree + <group> + + 8582B41E568BD84BD348B8A6EFBFD441 + + fileRef + B3714275CE72F0D2BF9D54B030313F52 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 85ADCE8D1021B46ED64138DCDA2141D8 + + includeInIndex + 1 + isa + PBXFileReference + name + sync_metadata.cpp + path + Realm/ObjectStore/src/sync_metadata.cpp + sourceTree + <group> + + 85C4DC2062D48A793AAD46D90A7D9B2D + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMListBase.h + path + include/RLMListBase.h + sourceTree + <group> + + 85C5B9766AC054202435C0CFE4D74D69 + + fileRef + A459C1DF2BD9F0F1FEFA494DB23500BC + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 85FAB6F1D2FE0598BF2E61D7BB084B8D + + fileRef + 805FFE9AE0AF6914315E57A448B415A8 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 8627565453E2E791A41FF0731917446F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObject.h + path + include/RLMObject.h + sourceTree + <group> + + 8673F9BF9415E820ED287ACA96F8DC60 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Firebase.h + path + Core/Sources/Firebase.h + sourceTree + <group> + + 867F250B35D3382459596A4DDE74E3A7 + + includeInIndex + 1 + isa + PBXFileReference + name + sync_manager.cpp + path + Realm/ObjectStore/src/sync_manager.cpp + sourceTree + <group> + + 86ACF8802CF6CA9FDEB142E2E7095769 + + children + + EAFC13078BF389FBA7DCF392460D1CEF + + isa + PBXGroup + name + Resources + sourceTree + <group> + + 86D4CFCDED4A2F71887A5C1DBD6960E2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObjectBase.h + path + include/RLMObjectBase.h + sourceTree + <group> + + 871966C404217CC92646BCA5AC8BDE55 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + NSArray+Chameleon.h + path + Pod/Classes/Objective-C/NSArray+Chameleon.h + sourceTree + <group> + + 8777D04A5A8FD9479335E300CBF2942E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIProgressView+AFNetworking.h + path + UIKit+AFNetworking/UIProgressView+AFNetworking.h + sourceTree + <group> + + 879B21E6F92BB0E8E49EC1A584F62913 + + fileRef + AA5D3E9C6B0333E0091B2324BE13C0D8 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 88666ACFDC557A55DBABE61B1B9D2E13 + + baseConfigurationReference + 2EA6DA768DD82D9DE2D6C469C45A0D4E + buildSettings + + CONFIGURATION_BUILD_DIR + $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FCAlertView + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + INFOPLIST_FILE + Target Support Files/FCAlertView/ResourceBundle-FCAlertView-Info.plist + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + PRODUCT_NAME + FCAlertView + SDKROOT + iphoneos + SKIP_INSTALL + YES + TARGETED_DEVICE_FAMILY + 1,2 + WRAPPER_EXTENSION + bundle + + isa + XCBuildConfiguration + name + Release + + 88BAE91CDCCA9CF51C40D714205654B1 + + buildConfigurationList + 072B99A31C094968B4C10AB4D4F91597 + buildPhases + + 42E9D96256BEF604FB00F82786327214 + DDF699E3DA6F4773715E4589F39E2878 + 5F1F502201CD9082F0C1D187A9353330 + + buildRules + + dependencies + + isa + PBXNativeTarget + name + Realm + productName + Realm + productReference + 2CF986C67C86BDDA5CDEABD0B68255BB + productType + com.apple.product-type.library.static + + 88E9AAA2DBA4606C365E0704DF6EDBA9 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + SVProgressAnimatedView.h + path + SVProgressHUD/SVProgressAnimatedView.h + sourceTree + <group> + + 8A1619CC62CA728D640D0FD28A8ED4CF + + baseConfigurationReference + 29BFCB88C3683AFDDB7B7C9DE2ADD970 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/Realm/Realm-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + 8A1C66775955F74D18F385F69FFF1D9E + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleMapsCore.framework + path + Subspecs/Maps/Frameworks/GoogleMapsCore.framework + sourceTree + <group> + + 8A8AC093F26AD2083C22C6D19571F806 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMProperty_Private.h + path + include/RLMProperty_Private.h + sourceTree + <group> + + 8B016B376DC8805B6052E9D206F2F288 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMObject.mm + path + Realm/RLMObject.mm + sourceTree + <group> + + 8B45039A4F25B5C3EABDB24E8DFD3B5C + + includeInIndex + 1 + isa + PBXFileReference + name + RLMObjectBase.mm + path + Realm/RLMObjectBase.mm + sourceTree + <group> + + 8C0921741DBA3D8B8C3345CC91BDB147 + + fileRef + 08908753F71F3D7AE5CEA21127714812 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 8C54BF8B76C4DF3BB91E60307619C937 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealm_Private.h + path + include/RLMRealm_Private.h + sourceTree + <group> + + 8CBF016384A60E0A9EEC595419E4C381 + + fileRef + C533894FE6C533A78CDDAC0CD9BCE7AC + isa + PBXBuildFile + + 8CE550EF3AE9B5A5DFBBBEB3E29E2714 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFNetworking.h + path + AFNetworking/AFNetworking.h + sourceTree + <group> + + 8D7928C4C08267E5679B90EF0DD80B4F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMAccessor.h + path + include/RLMAccessor.h + sourceTree + <group> + + 8DD0C4DDA8CC388707934D517D67B341 + + fileRef + D314231141BFA130C6E4C5FD59BB26D2 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 8E82178CC9BD8BE3DBF2C10E33807AEB + + fileRef + 1BE8AF5CBCEF2BBBD8C1632D6C45900E + isa + PBXBuildFile + + 8FA7E5B5EC5A1BD20D04D343482CC112 + + isa + PBXTargetDependency + name + MBProgressHUD + target + D058B89EB3B3DF45457A2DCD3971AFFE + targetProxy + 50EC8674627852A08E2B9BAB723FD0FB + + 90719794FF3D105842F640AB3C1D5294 + + fileRef + D64E1DBFD2DE6067D5BA14B303547F42 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 916BD6B0CC6D0D63FC022B1D13B0F4C8 + + children + + 7F994993061A02071EB66DAA0C072526 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + 91876889012246EB423DACBF7C54FC9D + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleMapsBase.framework + path + Subspecs/Base/Frameworks/GoogleMapsBase.framework + sourceTree + <group> + + 91EB709E9E7BD9E5E5693B2CDCDA7E64 + + fileRef + A68D036369AA8C9D4B5A9135434D9CD2 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + 926E1758EE25AF968AC0C498C4AB1ED7 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIButton+AFNetworking.m + path + UIKit+AFNetworking/UIButton+AFNetworking.m + sourceTree + <group> + + 928353533005A4198EBDA5B700D37B64 + + buildConfigurationList + 77CF2A7107BC61A2E90025871305961D + buildPhases + + DE0BAD758C6F291CE7E10DA077987C62 + 508C962B3E08E8DA04956BF349A3CDFD + B981229135DBE9773A86BDB116C368D1 + + buildRules + + dependencies + + isa + PBXNativeTarget + name + AFNetworking + productName + AFNetworking + productReference + 84CA470D6195ECEABCAB05D43D68A04A + productType + com.apple.product-type.library.static + + 92FD64138758A2E0C16F696FF621DF05 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + MBProgressHUD.xcconfig + sourceTree + <group> + + 935A7E52B6D8B7E37EAEA138EDC52155 + + buildConfigurationList + 4AC795793934FFD62D512BB1C6163D90 + buildPhases + + 9A2E1C04BBA4F18812DDE7C415A4809A + 977C0F4383D7B900F019F3D515C59032 + E2FE9EB963A455DF3657CD3A206000AC + + buildRules + + dependencies + + isa + PBXNativeTarget + name + ChameleonFramework + productName + ChameleonFramework + productReference + 71AF53765B7E79703FC89C9A75E5E9EC + productType + com.apple.product-type.library.static + + 939AE0655EFEEC3A4166C98D8AA6A977 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.plist.xml + path + Pods-HackTX-acknowledgements.plist + sourceTree + <group> + + 93A4A3777CF96A4AAC1D13BA6DCCEA73 + + explicitFileType + text.script.ruby + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text + name + Podfile + path + ../Podfile + sourceTree + SOURCE_ROOT + xcLanguageSpecificationIdentifier + xcode.lang.ruby + + 9413B669A6203AD71A048A8F824ECDD9 + + baseConfigurationReference + 92FD64138758A2E0C16F696FF621DF05 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 6.0 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + 947091807389FB48F558546821AABD90 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMResults.h + path + include/RLMResults.h + sourceTree + <group> + + 95238997A21C5E4D2AA2666CCAFA6AA0 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + FirebaseCore.framework + path + Frameworks/frameworks/FirebaseCore.framework + sourceTree + <group> + + 953A9C6043FDCBCF322213F1D0889ED2 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMMigration.mm + path + Realm/RLMMigration.mm + sourceTree + <group> + + 95422E64C4CF4B23D38184DF07169E88 + + fileRef + 3158C8D36B8A725577D8F5921EBF6CEE + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + 96B1A5AEF4A356F1C4FBF4EF8A891DA4 + + baseConfigurationReference + A4F8F4E2EE06290D520B91E6F9983308 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/ChameleonFramework/ChameleonFramework-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 8.0 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + 97116464E340CFFCA287431A7F1D44E1 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + FIRInstanceID.h + path + Sources/FIRInstanceID.h + sourceTree + <group> + + 977C0F4383D7B900F019F3D515C59032 + + buildActionMask + 2147483647 + files + + C7124868DB463E4014F23A5AF526F1CF + 3AA3AC330E15B7D4B9A73621944E842F + A31C730A02A476A6AA750B16AA08F9FF + 6F2A0CB1182DCF166FB48C69BA9AA7D3 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 987B7715A87FCD7528B6420B905D91F6 + + fileRef + CE5CD8EA0075C50357A00CEF9541B0FB + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + 988FA15379A4032C312C2049FB88101A + + buildActionMask + 2147483647 + files + + 0951ECB79A8DDBA8DC3077FA1CC84E6B + 1C289876A4849D6798FC7F154214C5F8 + 2B342A1143B39C7E396A50C6540E2639 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 994D70A405793381AB62BC84EF435DC1 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFAutoPurgingImageCache.m + path + UIKit+AFNetworking/AFAutoPurgingImageCache.m + sourceTree + <group> + + 9A02CCAB450B7182D47B38130839FC8D + + includeInIndex + 1 + isa + PBXFileReference + name + list.cpp + path + Realm/ObjectStore/src/list.cpp + sourceTree + <group> + + 9A2E1C04BBA4F18812DDE7C415A4809A + + buildActionMask + 2147483647 + files + + 4B6619D006E09AE372922F58C2ADC303 + A11273A68549A3AA66A4D92764E9A6EA + 5AE2E46A16828914FBE9055186274C3B + BE8800C6A3E4432857AB26ECC78F7ADD + ACA60FF340E8E8002773BE03D1BEAA9F + D385FECF73FAF6EE66F456ED9C7297FA + C0AF386B38730A787EEBE1C1A6DCB395 + D19658D62F2D49EEE1B525F178700423 + D03FEEB55244FFE7A8363981DA629DB8 + E8E8DD19527758AD1F1DC2C310AF0EB9 + A8113B8A15CF74628C874F7F4CF48A25 + 3962E111BE7E4B0BF02371AEE6860C5E + F3973E81DDBB552E2C0A54C3D2B7728C + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + 9A70AF712C6307C05CB3E5C366AED3EC + + isa + PBXTargetDependency + name + AFNetworking + target + 928353533005A4198EBDA5B700D37B64 + targetProxy + DC7AEAE36EA758DE208B02873D3196BA + + 9B202518E5A7FFCD675009BC4061B685 + + children + + 3FC0F5D9E16D642230561240EF2D1109 + 994D70A405793381AB62BC84EF435DC1 + 61E8A4C450D39FB880F93A5D1973A98A + E804A440D15ADFB7854AA09D5EEA1C98 + 087FE3AAD0A942370684A7E8B7A4E0B2 + 4B31FCA99B3ED3EDA9C48C5ED567D092 + B68044ED48DB323D0625E5A3355205FA + 17BB45079B7CF2C603144AC736394ADC + 45F475F2324F52333E51E3655ADA2C2D + 926E1758EE25AF968AC0C498C4AB1ED7 + 68447C66DED8D4C5F1BF848329534A24 + 5BD53FD7FDA677CCC069FA6CFEEEF4D5 + B56E045FCE749DF4D24BF73A7226C6D1 + 805FFE9AE0AF6914315E57A448B415A8 + 8777D04A5A8FD9479335E300CBF2942E + 035C9810D15E865D9DD4AA3F4BA4815A + D5C6133C1AB8CBED5BCDB047B05895A1 + B3CE96EF95188B8601CC7EFEDF3A5CFE + 3C7BA6DB4DB1A45741E6731CD4D5F76B + A3588285C8696740ABB10DF3B2A40BB7 + + isa + PBXGroup + name + UIKit + sourceTree + <group> + + 9C6ECDBC1353D9E1CC1EC4BBAA90051C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIAppearance+Swift.h + path + Pod/Classes/Objective-C/UIAppearance+Swift.h + sourceTree + <group> + + 9CC1A1907EAD823E1AFB28B53111C0D6 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIViewController+Chameleon.h + path + Pod/Classes/Objective-C/UIViewController+Chameleon.h + sourceTree + <group> + + 9DF01569AC243B34630B71F289BED4BD + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSchema_Private.h + path + include/RLMSchema_Private.h + sourceTree + <group> + + 9E0EC843F35EB1B2AB716FA565C77A4A + + includeInIndex + 1 + isa + PBXFileReference + name + RLMUtil.mm + path + Realm/RLMUtil.mm + sourceTree + <group> + + 9EBB1B8C91C20E3F2BA8BC6759CD8F22 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text + path + Pods-HackTX-acknowledgements.markdown + sourceTree + <group> + + 9EE5CF6D4540D2AB571D0BCC65E53B98 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + archive.ar + name + librealm-ios.a + path + core/librealm-ios.a + sourceTree + <group> + + 9F5A74588B7324AE561646BB139561DD + + children + + F461669585DB9A08C8B099FFFF820E8A + 86ACF8802CF6CA9FDEB142E2E7095769 + + isa + PBXGroup + name + GooglePlaces + path + GooglePlaces + sourceTree + <group> + + 9F9A093C4CA3A8B17C2E6364B9DE116B + + fileRef + C92196E90DE9CCC8B0565DD0DB7CCD06 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + A0C03B3C39CF14DC0788A9A78CD59477 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + Pods-HackTX-dummy.m + sourceTree + <group> + + A11273A68549A3AA66A4D92764E9A6EA + + fileRef + 35BCAC68AD8EF6034462DEB2D161FABE + isa + PBXBuildFile + + A14A742F58974524AF26EC9CBE172ACE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIImage+ChameleonPrivate.h + path + Pod/Classes/Objective-C/UIImage+ChameleonPrivate.h + sourceTree + <group> + + A193AE29C853F25CC2874A49CEBEAA32 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncManager.mm + path + Realm/RLMSyncManager.mm + sourceTree + <group> + + A2DD24E6C612863769EE1FC8670DE205 + + fileRef + E4ED1D5C4FBA746EDDA4C1567D5C3FDA + isa + PBXBuildFile + + A31C730A02A476A6AA750B16AA08F9FF + + fileRef + 734519BB99CD5EE94C689204BCBCCE67 + isa + PBXBuildFile + + A3588285C8696740ABB10DF3B2A40BB7 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIWebView+AFNetworking.m + path + UIKit+AFNetworking/UIWebView+AFNetworking.m + sourceTree + <group> + + A3D9BE7FA653120B3F1E0672D8310291 + + fileRef + 5B01CAAE2DFD9DA0CD5D0015943D8241 + isa + PBXBuildFile + + A44F6AC6F72C43DD65623BA935480764 + + children + + A50851DD5462A8FDACE18ED550B31634 + 7BE65A584E79E775585E6A59B5EE4C9A + 32E43E4B54195FBA3C31EA5B0F23ACA2 + BC663C0365433045138CCCF3AD375052 + E71073AE06D6684A3FEF8645E30E0E6F + DB2451C242A2733E4722B6162000ACE9 + F1B00DB7ED33EA59D09C38EF18EC525B + 065A73EDC4EEEE691C0F9DB7CA1B24DA + 9A02CCAB450B7182D47B38130839FC8D + CBECE5FC61750CF1957644D6438B6B83 + D44C8FE1691936E436C1A6F62216040A + C92196E90DE9CCC8B0565DD0DB7CCD06 + 011CFCDFC70FDCB2D6716099D07BD63F + F9666AF738588ADE7ED75A15E570A507 + 0EACCB430C3B9BF28156F65E0C92B21F + CE5CD8EA0075C50357A00CEF9541B0FB + 8D7928C4C08267E5679B90EF0DD80B4F + 351D131C8169BF6999BAF2BAFB46945C + 0DBE46FCD42F22067E22F0089C56D2C5 + 1C806B313FB8E36CA0CFCB623300547B + 39E3FF933C65B8FB898BE9B6C8D9A944 + A51B35F2429E5C3C1B20D56F2C69C03E + 2DCD2674A99D81420352E80305151CB0 + BA6E0A642D0E14DF3E434F706131EBBC + CBE7AA212D63A12240B3FD34EF96FA67 + A9B60F879FDCB9BB52A3022C50A88AA8 + 85C4DC2062D48A793AAD46D90A7D9B2D + EE13135D5B5DB16CB42CC2233807E30F + 953A9C6043FDCBCF322213F1D0889ED2 + CDD96CF370C5A218F897669AD576EE6A + 3790779162209722E4C27CEA512E6333 + 8B016B376DC8805B6052E9D206F2F288 + AFDE422C697173D4B3DBE181BAD34F8C + 8B45039A4F25B5C3EABDB24E8DFD3B5C + 39EF664B48BC3CF73B1B4653DE0876DC + 804223344A88B50FDB0DF42F73A21AF4 + F2BD8D5314F70A913234B9380377C1C0 + 4A1768D3B09DA5A7C451D36DAF859DC9 + 7E1C7C62E8DF627D883635C6300AAECD + B3F7872E09E611FC9194BFB4D039D72D + FBACF5338B8839F2165A8F8D9692A409 + F1852AD0A401252DD67AA07533473B75 + AA5D3E9C6B0333E0091B2324BE13C0D8 + 8A8AC093F26AD2083C22C6D19571F806 + 7D6A8F7B7081EA6FBFA6F234E8D30274 + 0CB4C862467FADB7A8C53306171E7298 + 8C54BF8B76C4DF3BB91E60307619C937 + 3BE4AA25F40003242509B18CF80CBBCA + 191F6FA9B378C1E53C7EAEA960DA6ABF + 08908753F71F3D7AE5CEA21127714812 + C46CC57DF2F5AD39E32A2FCC73CB31E8 + 49C0BE8AD21D72D90C0F16C7E267749E + 11067A4D9D6BD6C9EE8A9D91B49E080B + C283F5CB8870D4117B7E269E66AB370D + 9DF01569AC243B34630B71F289BED4BD + D8BF6253E9383EC9A3FDD10F864E3827 + 72F5F0696BD3A9F6E7444261414BFA7B + 2D1E479AE71EA42AF8B1B54F8561425F + FAAFDCB4C82F19E2ACEFE5A793D7E9C4 + D64E1DBFD2DE6067D5BA14B303547F42 + A193AE29C853F25CC2874A49CEBEAA32 + ECF5D7E2435E6A2A499D21CDED43E48B + 3158C8D36B8A725577D8F5921EBF6CEE + F5D7D1A059B997C8706C16F267EF780D + 36AD26C500DF00391B7BD28BCC86BF8E + E8A66C5313A01E6572BB619766190BCE + F8D8F0F9BB5E27B5581369EC0F239C18 + F85D374DDA0229889F4D0A64BE5E1E01 + 557FE2D1541F2FC8B76B4A9329BBCA43 + 9E0EC843F35EB1B2AB716FA565C77A4A + EB366EC1A299ED6F83AABBDAD0FE82D7 + 0E09CC2965F557896A90F64ECD3EDAEA + 867F250B35D3382459596A4DDE74E3A7 + 85ADCE8D1021B46ED64138DCDA2141D8 + A459C1DF2BD9F0F1FEFA494DB23500BC + 52B45D2E71686C92C21DFCECF047FAF3 + 61E5B623A1D71121720C1A116F5C3141 + AE916CA50FDF560E6BD436FC6C0CDA9D + E5D496BF7B537D437C900FB58B9227CB + C84AAFA5A9C12E793755C7711FFDFA25 + F9F57A1A1A93F72696845843A5D18EF8 + + isa + PBXGroup + name + Realm + path + Realm + sourceTree + <group> + + A459C1DF2BD9F0F1FEFA494DB23500BC + + includeInIndex + 1 + isa + PBXFileReference + name + sync_session.cpp + path + Realm/ObjectStore/src/sync_session.cpp + sourceTree + <group> + + A4F8F4E2EE06290D520B91E6F9983308 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + ChameleonFramework.xcconfig + sourceTree + <group> + + A50851DD5462A8FDACE18ED550B31634 + + includeInIndex + 1 + isa + PBXFileReference + name + collection_change_builder.cpp + path + Realm/ObjectStore/src/impl/collection_change_builder.cpp + sourceTree + <group> + + A51B35F2429E5C3C1B20D56F2C69C03E + + includeInIndex + 1 + isa + PBXFileReference + name + RLMArrayLinkView.mm + path + Realm/RLMArrayLinkView.mm + sourceTree + <group> + + A5507A643AB4E2BCB8960E87D64F1ED9 + + baseConfigurationReference + C90AC47E07D866E5E95EB9BA2685F0D3 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/SVProgressHUD/SVProgressHUD-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 6.1 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + A5A43A8E1106405D9EF264D1675A14C8 + + buildActionMask + 2147483647 + files + + AE6E789B70B0D1D0F90043074F1C3CA7 + 437BD5FA1E5BCE1A6BC2472BFF62F5F5 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + A64307190CDF73F94F37E5AED8818AB9 + + fileRef + CBECE5FC61750CF1957644D6438B6B83 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + A64667C43DAF51587FB3F331B6C10345 + + children + + D8DD803EA30FD7A687BC244DE8347CD6 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + A66E0AF0A1001B7BDAB6F2B6C0C0580C + + fileRef + E231692E3A239E0E192B41688B7F29C1 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + A68D036369AA8C9D4B5A9135434D9CD2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMConstants.h + path + include/RLMConstants.h + sourceTree + <group> + + A6C44D61D3FBE31DD63BC51E25391DC0 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + Fabric.framework + path + iOS/Fabric.framework + sourceTree + <group> + + A8113B8A15CF74628C874F7F4CF48A25 + + fileRef + 8570C10E07F0A974AF2D7D5BA700BA34 + isa + PBXBuildFile + + A8FC6A53F6C1CB4DEFD972664E1AF3B1 + + fileRef + 184EBAA144634FA2AD11BF652E26BCC3 + isa + PBXBuildFile + + A9B60F879FDCB9BB52A3022C50A88AA8 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMConstants.m + path + Realm/RLMConstants.m + sourceTree + <group> + + AA07A0823D0B13472FDA9DE8091977FB + + children + + 9EBB1B8C91C20E3F2BA8BC6759CD8F22 + 939AE0655EFEEC3A4166C98D8AA6A977 + A0C03B3C39CF14DC0788A9A78CD59477 + 2D08469CD6468539EDE97D3B7BE6C682 + 010F7F46F32971E403D9BB705DF1A2DF + 16BEF02D02EF88A3478FE3F5A087D918 + 027F9A4F74D993477C300CF7206D001A + + isa + PBXGroup + name + Pods-HackTX + path + Target Support Files/Pods-HackTX + sourceTree + <group> + + AA5D3E9C6B0333E0091B2324BE13C0D8 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMProperty.mm + path + Realm/RLMProperty.mm + sourceTree + <group> + + AB6D0FD1EBA2B07E8DD2C96BFBABF792 + + isa + PBXTargetDependency + name + SVProgressHUD + target + 7F74193519009A1348B026EB930394E1 + targetProxy + 6FEA7D1C33EE2715829503B1D42E7D7B + + AC3B593D299462E5A7B8A24E46BF9F5A + + buildActionMask + 2147483647 + files + + 82051A21B9C2BD50762EA94356A0313A + 45423C0A2E9714AC0469B85E32642A91 + A3D9BE7FA653120B3F1E0672D8310291 + + isa + PBXResourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + ACA60FF340E8E8002773BE03D1BEAA9F + + fileRef + ECEFB4F98DC9F30F63000BFA48729FD4 + isa + PBXBuildFile + + ACE9C1A6E8D5121C6C5F907C7A9CFCC9 + + fileRef + D5C6133C1AB8CBED5BCDB047B05895A1 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + AD359A96DD4AA2C962E50A5CD5524F54 + + fileRef + A3588285C8696740ABB10DF3B2A40BB7 + isa + PBXBuildFile + + ADE50ECE073EDFB3DC851B8278EFAF2B + + buildActionMask + 2147483647 + files + + BB7095C2F300B1195FBFF00B340D31AC + 71EA2F58426D22F28922C095F78A6FB1 + FB5AB49A46A53C43D6C101F7D63E99C9 + A2DD24E6C612863769EE1FC8670DE205 + 8CBF016384A60E0A9EEC595419E4C381 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + AE181F14D67AEED968808B2E24F5766C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ChameleonMacros.h + path + Pod/Classes/Objective-C/ChameleonMacros.h + sourceTree + <group> + + AE6E789B70B0D1D0F90043074F1C3CA7 + + fileRef + FA6A8DFBD40048CB7CDA1A189A2CE83B + isa + PBXBuildFile + + AE916CA50FDF560E6BD436FC6C0CDA9D + + includeInIndex + 1 + isa + PBXFileReference + name + weak_realm_notifier.cpp + path + Realm/ObjectStore/src/impl/weak_realm_notifier.cpp + sourceTree + <group> + + AF2387BA466815A90CE6D5D906BBEC36 + + children + + 2EA6DA768DD82D9DE2D6C469C45A0D4E + FA6A8DFBD40048CB7CDA1A189A2CE83B + 38EB37219A3511491E564F140CD39D1B + CA0906CA56E847C2E3930E9CAB41FC17 + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/FCAlertView + sourceTree + <group> + + AFDE422C697173D4B3DBE181BAD34F8C + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObject_Private.h + path + include/RLMObject_Private.h + sourceTree + <group> + + AFDFA06F78F08FB69FAEBA0EDF12768F + + fileRef + F72F963661C6F309185D5EB3AFC18AC7 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + B04FF59E19F454EE7E657EBD8F07127E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + wrapper.plug-in + name + SVProgressHUD.bundle + path + SVProgressHUD/SVProgressHUD.bundle + sourceTree + <group> + + B0BE398350F83A7B0102C3BBAFC51428 + + fileRef + B56E045FCE749DF4D24BF73A7226C6D1 + isa + PBXBuildFile + + B185E0E344FCEC1879D37A6120E1E27E + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + SVProgressAnimatedView.m + path + SVProgressHUD/SVProgressAnimatedView.m + sourceTree + <group> + + B1AAA1228D99746F4EB488754929B532 + + buildConfigurationList + 0B2A088D101EF26A175C58E16E6BB598 + buildPhases + + BC778C234FADC69D419EF0A6687E71F6 + B46D077CF704C93273110E1D23FCA74E + AC3B593D299462E5A7B8A24E46BF9F5A + + buildRules + + dependencies + + isa + PBXNativeTarget + name + FCAlertView-FCAlertView + productName + FCAlertView-FCAlertView + productReference + FBA4C1076819E2C466EF6040D7E684A5 + productType + com.apple.product-type.bundle + + B2C13AF00B0B0467938ADD1BC71D0EFE + + isa + PBXTargetDependency + name + Realm + target + 88BAE91CDCCA9CF51C40D714205654B1 + targetProxy + 1223F3E793F48DC17BACD0460FF220C3 + + B336DA43C9E6E0A8D12F53E72784747A + + children + + E4CC874AF123072B0356D6819A359249 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + B3714275CE72F0D2BF9D54B030313F52 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMArray.h + path + include/RLMArray.h + sourceTree + <group> + + B3CE96EF95188B8601CC7EFEDF3A5CFE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIRefreshControl+AFNetworking.m + path + UIKit+AFNetworking/UIRefreshControl+AFNetworking.m + sourceTree + <group> + + B3E45F0B3B5343EF5EEA12DF719C4BA3 + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 2CD4DB07B9E80ADF426DB21DBA98B820 + remoteInfo + FCAlertView + + B3F7872E09E611FC9194BFB4D039D72D + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMOptionalBase.h + path + include/RLMOptionalBase.h + sourceTree + <group> + + B46D077CF704C93273110E1D23FCA74E + + buildActionMask + 2147483647 + files + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + B4F08A61BA865BE60E326DE72C68807F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + CLSAttributes.h + path + iOS/Crashlytics.framework/Headers/CLSAttributes.h + sourceTree + <group> + + B51AA1F7701DCDBCC9B88A15320F9C2C + + fileRef + 00258BC662C715449F67F14364FECB3E + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + B51E57F75AF251750B8061A3C85659C3 + + fileRef + CDD96CF370C5A218F897669AD576EE6A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + B56E045FCE749DF4D24BF73A7226C6D1 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIImageView+AFNetworking.m + path + UIKit+AFNetworking/UIImageView+AFNetworking.m + sourceTree + <group> + + B5CF67F7870BAB70A76E57479B706B42 + + fileRef + 3C7BA6DB4DB1A45741E6731CD4D5F76B + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + B5DCAE8DC0F856CEEFD5EB3C02146A3D + + buildActionMask + 2147483647 + files + + E6FE780D70A83497890D071F8235958A + 83C96D6E58D0F45750D20CA819D4D6C8 + 5C7F8728518E04EDE9A8B0FF67806CDD + FD3F0830BB7C6B96AC46E043FC4A2BE5 + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + B64093F8FF30305F8B2C8B1F5AA06B21 + + children + + F1B4ABB28C8F5CF79D99C86F183C54E8 + DFA9186D05062EC062DA72D8FBB4E6DB + + isa + PBXGroup + name + GoogleMaps + path + GoogleMaps + sourceTree + <group> + + B68044ED48DB323D0625E5A3355205FA + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIActivityIndicatorView+AFNetworking.h + path + UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h + sourceTree + <group> + + B69DF53219174426BC1381A513BA42BD + + fileRef + 0E09CC2965F557896A90F64ECD3EDAEA + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + B742335BAF88A0D21E1274E789280361 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleIPhoneUtilities.framework + path + Frameworks/GoogleIPhoneUtilities.framework + sourceTree + <group> + + B981229135DBE9773A86BDB116C368D1 + + buildActionMask + 2147483647 + files + + 19E6319F6ECED7539AABBD9A211F7AEC + F3774B1449FCE5B3CA3E1B336E11D284 + 326A543AD4E39A2DAECA1CD385E3AEA2 + 3939531EB270B4145C7ADFBAB2E5695F + 0A08FC71C4E3B8E0F8734BCDA7CCA7CB + ED69D9B5A342FE9BD3F170D5CDDEC948 + DFDAC8245626953C4BCE5A2DA6BE2A5B + 2359387620C5E815BF0594BC40311734 + B51AA1F7701DCDBCC9B88A15320F9C2C + 3511AE7ADBB71D05565854F0E70B7D08 + 830050886B18F54E17F117FD8EFC717B + E212C2AA95E8F6EFB69A5F1455F291F8 + C1B65C3D849FEE4FB5298A8135A765DA + 6EC97BDB3FC508526F9E388095A7DDCC + 85FAB6F1D2FE0598BF2E61D7BB084B8D + 5D52DB6DF2E6C24CECB0015A9177A2E1 + ACE9C1A6E8D5121C6C5F907C7A9CFCC9 + B5CF67F7870BAB70A76E57479B706B42 + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + BA6E0A642D0E14DF3E434F706131EBBC + + includeInIndex + 1 + isa + PBXFileReference + name + RLMClassInfo.mm + path + Realm/RLMClassInfo.mm + sourceTree + <group> + + BAC2AC9718E818D794FDA9FCBE5F10BC + + isa + PBXTargetDependency + name + ChameleonFramework + target + 935A7E52B6D8B7E37EAEA138EDC52155 + targetProxy + BCBB5A320E8F14EDF256B0F4A7CBD429 + + BB18238FB9BDC832D3C5C487D4E1B9A5 + + children + + EDCF74D2C2C3493BC2D957847FEA8CFE + 222CC7D29A8BBDAFE395569CEAC0F3C4 + DCAA88918C9033FC501C4AE2CB4A8EA4 + + isa + PBXGroup + name + Fabric + path + Fabric + sourceTree + <group> + + BB7095C2F300B1195FBFF00B340D31AC + + fileRef + C0AD35AB11FCA31439B676F42DC5ACEF + isa + PBXBuildFile + + BBC7594433D7A40A0B7BB6280D4A7572 + + children + + 014E6E2EFAF9AE5C27A801DC4238C596 + 27E4C74E1C5429BB7A128BA853E95739 + 32B20C18CB66855BC61F31B587F37C13 + 7EBC8DFA155FE6FD1E4E3B7A7D9AE37E + 35BCAC68AD8EF6034462DEB2D161FABE + DBB77D712908602F32D23BEDA4015A8A + AE181F14D67AEED968808B2E24F5766C + 871966C404217CC92646BCA5AC8BDE55 + 4FE51245E672DBC295105C287ABD6246 + 9C6ECDBC1353D9E1CC1EC4BBAA90051C + ECEFB4F98DC9F30F63000BFA48729FD4 + D359689F94F914474D0840B700E5EE78 + 33B60982AD9077D530647478A16047F8 + 41A123D94977528A2E8F926767228F0A + 08411B06AF2BF1C07C1182399277DC53 + D314231141BFA130C6E4C5FD59BB26D2 + 0B18135E78B5A298729D582C519AB9C5 + A14A742F58974524AF26EC9CBE172ACE + 599A2F75AE6DAAD4CB43E3B5C29D090A + 1A36559A7109BAF920FC3A4C3EEFD22B + 63CEF6A400009AC67D2DB3C2F22C5B1A + 25769ECCDF5839D0BC00293976293256 + 8570C10E07F0A974AF2D7D5BA700BA34 + 5A705339812A117B1EEFD207502BBA6C + 1DC3459C2B4CACFEF4F0F2D5AEF0F946 + 9CC1A1907EAD823E1AFB28B53111C0D6 + D7F765A54817B9EB8D55FA2B35D34ABE + + isa + PBXGroup + name + Default + sourceTree + <group> + + BC663C0365433045138CCCF3AD375052 + + includeInIndex + 1 + isa + PBXFileReference + name + external_commit_helper.cpp + path + Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp + sourceTree + <group> + + BC778C234FADC69D419EF0A6687E71F6 + + buildActionMask + 2147483647 + files + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + BCBB5A320E8F14EDF256B0F4A7CBD429 + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 935A7E52B6D8B7E37EAEA138EDC52155 + remoteInfo + ChameleonFramework + + BD14B249D67F8DDF85ADCD0501CF7750 + + fileRef + A14A742F58974524AF26EC9CBE172ACE + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + BD9E32CBC2005732732E7A564466520A + + children + + 3FD09E807BF9E65D2704629D08261714 + 8A1C66775955F74D18F385F69FFF1D9E + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + BE0BD3371CFB1C2AA4B8699ED0313680 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFSecurityPolicy.m + path + AFNetworking/AFSecurityPolicy.m + sourceTree + <group> + + BE3F2030D5A2EF5404E107599DD2FEB5 + + fileRef + 871966C404217CC92646BCA5AC8BDE55 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + BE8800C6A3E4432857AB26ECC78F7ADD + + fileRef + 4FE51245E672DBC295105C287ABD6246 + isa + PBXBuildFile + + BF05A2DCDAF94A4A145F6D05C198AC13 + + buildActionMask + 2147483647 + files + + D82F3937F0721FAF320800BB2EBEC238 + 766006A1FE00CF3BC5B57612BA353D53 + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + BF4D54A2FC2D4B7BA65DEAF186482724 + + fileRef + 2D1E479AE71EA42AF8B1B54F8561425F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + C0716975BE5BCCDB5F8BCFCFBD10B130 + + fileRef + 85C4DC2062D48A793AAD46D90A7D9B2D + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + C0AD35AB11FCA31439B676F42DC5ACEF + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + SVIndefiniteAnimatedView.m + path + SVProgressHUD/SVIndefiniteAnimatedView.m + sourceTree + <group> + + C0AF386B38730A787EEBE1C1A6DCB395 + + fileRef + 08411B06AF2BF1C07C1182399277DC53 + isa + PBXBuildFile + + C0B82262F65054AC1448D26D1193E81A + + children + + 95238997A21C5E4D2AA2666CCAFA6AA0 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + C0E25938902ED70F39B165929D2F4523 + + explicitFileType + archive.ar + includeInIndex + 0 + isa + PBXFileReference + name + libSVProgressHUD.a + path + libSVProgressHUD.a + sourceTree + BUILT_PRODUCTS_DIR + + C0F313278EAF28D5271FFB2571FDE025 + + children + + FD53F15C8D7AE0993DD8B39395A7CE57 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + C15993E7EEEA6B1E3132E993A21B7348 + + children + + C0B82262F65054AC1448D26D1193E81A + + isa + PBXGroup + name + FirebaseCore + path + FirebaseCore + sourceTree + <group> + + C176EBED813E3C9E0A25021198EA027A + + buildConfigurations + + 27B1429A8801D3B979071A0E8BED1E93 + A5507A643AB4E2BCB8960E87D64F1ED9 + + defaultConfigurationIsVisible + 0 + defaultConfigurationName + Release + isa + XCConfigurationList + + C1B65C3D849FEE4FB5298A8135A765DA + + fileRef + 68447C66DED8D4C5F1BF848329534A24 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + C2201E34603B014192F0967473518496 + + fileRef + 78A271BEC0CD09221D7DE76766E9B57E + isa + PBXBuildFile + + C283F5CB8870D4117B7E269E66AB370D + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSchema.mm + path + Realm/RLMSchema.mm + sourceTree + <group> + + C38C528392EF09CA2EC413F1AA092B2A + + fileRef + 86D4CFCDED4A2F71887A5C1DBD6960E2 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + C3F12A8CB0F64924AA85651837E2DE87 + + children + + B742335BAF88A0D21E1274E789280361 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + C46CC57DF2F5AD39E32A2FCC73CB31E8 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMRealmUtil.mm + path + Realm/RLMRealmUtil.mm + sourceTree + <group> + + C533894FE6C533A78CDDAC0CD9BCE7AC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + SVRadialGradientLayer.m + path + SVProgressHUD/SVRadialGradientLayer.m + sourceTree + <group> + + C60A89879434FAC011EF7C7236966097 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + UIKit.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/UIKit.framework + sourceTree + DEVELOPER_DIR + + C64CAB5899DE23FC097A71FFDC77C046 + + baseConfigurationReference + 027F9A4F74D993477C300CF7206D001A + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + IPHONEOS_DEPLOYMENT_TARGET + 9.0 + MACH_O_TYPE + staticlib + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PODS_ROOT + $(SRCROOT) + PRODUCT_BUNDLE_IDENTIFIER + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + PRODUCT_NAME + $(TARGET_NAME) + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + C7124868DB463E4014F23A5AF526F1CF + + fileRef + 7BA9A8B5B4FE3448D5072369AB0A4513 + isa + PBXBuildFile + + C7E474A03CAE21F1CB6046E7344C59BD + + buildSettings + + ALWAYS_SEARCH_USER_PATHS + NO + CLANG_ANALYZER_NONNULL + YES + 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 + + POD_CONFIGURATION_RELEASE=1 + $(inherited) + + 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 + 9.0 + STRIP_INSTALLED_PRODUCT + NO + SYMROOT + ${SRCROOT}/../build + VALIDATE_PRODUCT + YES + + isa + XCBuildConfiguration + name + Release + + C83E72228632A2D20FDDA2285618BA8C + + fileRef + E71073AE06D6684A3FEF8645E30E0E6F + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + C84AAFA5A9C12E793755C7711FFDFA25 + + children + + 39A03A7A9B5C6846676C594FCBE2FE4C + B3714275CE72F0D2BF9D54B030313F52 + 05BDDDC3D01F0F38519554C0B0909224 + A68D036369AA8C9D4B5A9135434D9CD2 + 38AD5AEEBFDC3D85F86077880D7EF3D4 + 8627565453E2E791A41FF0731917446F + 86D4CFCDED4A2F71887A5C1DBD6960E2 + 0F541975B7520524A5663D7897CA9F73 + 7C04E4994284BDCD9F6D9DA37CCB6A41 + 796FEFA7DCF574265FF334D12D310CB0 + 6995B31EC70CAE4FD7F5C6A5B6C5AEFF + F72F963661C6F309185D5EB3AFC18AC7 + E0A1B97768782BC607C5436E4C6CFC4F + CFD23C7C2579B7F3ED29DEBB22950FBC + 2C48558D0E7B7895FD07A7E15710DF8F + 947091807389FB48F558546821AABD90 + FECA88F028BDBF82F44BFD3EFD53C49A + 4F6BC6201273EF0BE7AACBB376D83667 + 7F2E88B654006CB5584AF018CB61A5A0 + 66C24AE670BCF23415ADA6380605623C + 6CA19CE0274852A653112CFF9BE0A2B9 + 05A3C262D41B8AC17B19E29F4A28B6D0 + E231692E3A239E0E192B41688B7F29C1 + + isa + PBXGroup + name + Headers + sourceTree + <group> + + C90AC47E07D866E5E95EB9BA2685F0D3 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + SVProgressHUD.xcconfig + sourceTree + <group> + + C92196E90DE9CCC8B0565DD0DB7CCD06 + + includeInIndex + 1 + isa + PBXFileReference + name + object_store.cpp + path + Realm/ObjectStore/src/object_store.cpp + sourceTree + <group> + + C94541FBC1FB2F67AC9117613B53C909 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + CLSLogging.h + path + iOS/Crashlytics.framework/Headers/CLSLogging.h + sourceTree + <group> + + CA0906CA56E847C2E3930E9CAB41FC17 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.plist.xml + path + ResourceBundle-FCAlertView-Info.plist + sourceTree + <group> + + CA79B3B03B658549C05D9C401C394890 + + children + + 91876889012246EB423DACBF7C54FC9D + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + CB6EE5099C791E5D5AF801135E902224 + + fileRef + F5D7D1A059B997C8706C16F267EF780D + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + CBE7AA212D63A12240B3FD34EF96FA67 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMCollection.mm + path + Realm/RLMCollection.mm + sourceTree + <group> + + CBECE5FC61750CF1957644D6438B6B83 + + includeInIndex + 1 + isa + PBXFileReference + name + list_notifier.cpp + path + Realm/ObjectStore/src/impl/list_notifier.cpp + sourceTree + <group> + + CCA9C08C9CBDE367B7BB38B6C0547B94 + + fileRef + 1C33971EE872AA7CD5201A6D434AB562 + isa + PBXBuildFile + + CCFDD184741E8101623CEBA1BAA51E03 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + Realm-dummy.m + sourceTree + <group> + + CD262FFC80FE111F484D56576BC568A3 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + ChameleonFramework-dummy.m + sourceTree + <group> + + CD2D30F21A2FE51073A6CE6634A2FF49 + + fileRef + AE916CA50FDF560E6BD436FC6C0CDA9D + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + CD6AC686A742439DE9C35DBDB50BD06F + + fileRef + 05BDDDC3D01F0F38519554C0B0909224 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + CDD96CF370C5A218F897669AD576EE6A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMMigration_Private.h + path + include/RLMMigration_Private.h + sourceTree + <group> + + CE5CD8EA0075C50357A00CEF9541B0FB + + includeInIndex + 1 + isa + PBXFileReference + name + results_notifier.cpp + path + Realm/ObjectStore/src/impl/results_notifier.cpp + sourceTree + <group> + + CF4068D76050799901D9EB116C71EF7A + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleSymbolUtilities.framework + path + Frameworks/frameworks/GoogleSymbolUtilities.framework + sourceTree + <group> + + CF546E09CAF655ADECECF54D7C35B54A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + SVRadialGradientLayer.h + path + SVProgressHUD/SVRadialGradientLayer.h + sourceTree + <group> + + CFD23C7C2579B7F3ED29DEBB22950FBC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealmConfiguration.h + path + include/RLMRealmConfiguration.h + sourceTree + <group> + + D00DE5F552176E2974F9C1876069E088 + + children + + 684743B9B41167B1D3D9B77FD3BC623F + + isa + PBXGroup + name + Firebase + path + Firebase + sourceTree + <group> + + D03FEEB55244FFE7A8363981DA629DB8 + + fileRef + 599A2F75AE6DAAD4CB43E3B5C29D090A + isa + PBXBuildFile + + D058B89EB3B3DF45457A2DCD3971AFFE + + buildConfigurationList + 1F97048AF18C3EFEDE731BDEECCD58A8 + buildPhases + + E9001FCA1D7F025E918399909B4942DA + 988FA15379A4032C312C2049FB88101A + D70C2B60AD15BF12091026A33774F9F7 + + buildRules + + dependencies + + isa + PBXNativeTarget + name + MBProgressHUD + productName + MBProgressHUD + productReference + 4BFFEBA374AD3562751D0ECDE5DB1D98 + productType + com.apple.product-type.library.static + + D1398EEA3895D7F2CE9772514E61AB66 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFNetworkReachabilityManager.h + path + AFNetworking/AFNetworkReachabilityManager.h + sourceTree + <group> + + D19658D62F2D49EEE1B525F178700423 + + fileRef + 0B18135E78B5A298729D582C519AB9C5 + isa + PBXBuildFile + + D19F5D7B316BFDBDDE17066EEE4CF5C8 + + fileRef + AE181F14D67AEED968808B2E24F5766C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + D1E1EE5F5925B1AFEAE5CEEBE2584246 + + children + + 727BFE93677461E5E7E802B77A946AB9 + 850E7446484F5FAD8CCEEC47A8CAF44D + B4F08A61BA865BE60E326DE72C68807F + C94541FBC1FB2F67AC9117613B53C909 + 5CE8EB6E28FD8EBB1D2C5C5A36AA1ABC + 748DB5EF4CAA4A79E761A9BBD933DFD9 + D259E2C3B843D48345FCBC0CC38C6FF3 + 2AD25FBA8315FF6BB9BE5F822BC0A454 + + isa + PBXGroup + name + Crashlytics + path + Crashlytics + sourceTree + <group> + + D259E2C3B843D48345FCBC0CC38C6FF3 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + Crashlytics.h + path + iOS/Crashlytics.framework/Headers/Crashlytics.h + sourceTree + <group> + + D314231141BFA130C6E4C5FD59BB26D2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIColor+ChameleonPrivate.h + path + Pod/Classes/Objective-C/UIColor+ChameleonPrivate.h + sourceTree + <group> + + D31FEE51AF4BE2A6CC059F80B983CD3B + + fileRef + E804A440D15ADFB7854AA09D5EEA1C98 + isa + PBXBuildFile + + D359689F94F914474D0840B700E5EE78 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIButton+Chameleon.h + path + Pod/Classes/Objective-C/UIButton+Chameleon.h + sourceTree + <group> + + D385FECF73FAF6EE66F456ED9C7297FA + + fileRef + 33B60982AD9077D530647478A16047F8 + isa + PBXBuildFile + + D38F36DD7F4CACB524C72CDF18733979 + + fileRef + 9DF01569AC243B34630B71F289BED4BD + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + D39979812C79D312028DD50E50AADCF2 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + MBProgressHUD-dummy.m + sourceTree + <group> + + D41D8CD98F00B204E9800998ECF8427E + + attributes + + LastSwiftUpdateCheck + 0730 + LastUpgradeCheck + 0700 + + buildConfigurationList + 2D8E8EC45A3A1A1D94AE762CB5028504 + compatibilityVersion + Xcode 3.2 + developmentRegion + English + hasScannedForEncodings + 0 + isa + PBXProject + knownRegions + + en + + mainGroup + 7DB346D0F39D3F0E887471402A8071AB + productRefGroup + FF72855D2EFF1C13779CAA586D517808 + projectDirPath + + projectReferences + + projectRoot + + targets + + 928353533005A4198EBDA5B700D37B64 + 935A7E52B6D8B7E37EAEA138EDC52155 + 2CD4DB07B9E80ADF426DB21DBA98B820 + B1AAA1228D99746F4EB488754929B532 + D058B89EB3B3DF45457A2DCD3971AFFE + 612E2E089A1E64C8682518A2A239191A + 88BAE91CDCCA9CF51C40D714205654B1 + 7F74193519009A1348B026EB930394E1 + + + D44C8FE1691936E436C1A6F62216040A + + includeInIndex + 1 + isa + PBXFileReference + name + object_schema.cpp + path + Realm/ObjectStore/src/object_schema.cpp + sourceTree + <group> + + D4DDD66982DD17BA2E1CC973A09FCD1B + + children + + EE0AFC26190EB72C2BBEEF935B6C2BDB + 184EBAA144634FA2AD11BF652E26BCC3 + 7A8F9A1227006BF4BD9E953FD3D91B8D + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/AFNetworking + sourceTree + <group> + + D531C0FF953D03C25A1D8FC4288934E6 + + children + + A4F8F4E2EE06290D520B91E6F9983308 + CD262FFC80FE111F484D56576BC568A3 + 1044C10B39D9E6B2A903B1EA7404428A + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/ChameleonFramework + sourceTree + <group> + + D5C6133C1AB8CBED5BCDB047B05895A1 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + UIRefreshControl+AFNetworking.h + path + UIKit+AFNetworking/UIRefreshControl+AFNetworking.h + sourceTree + <group> + + D64E1DBFD2DE6067D5BA14B303547F42 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncFileManager.mm + path + Realm/RLMSyncFileManager.mm + sourceTree + <group> + + D70C2B60AD15BF12091026A33774F9F7 + + buildActionMask + 2147483647 + files + + 695A3FA15E903AE48EB0B3BE336F2467 + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + D7647F50F6A950A883FEA0EF48FC15B1 + + fileRef + 72F5F0696BD3A9F6E7444261414BFA7B + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + D7673B94D90B98099ECFC36133BFBBA6 + + fileRef + 7EBC8DFA155FE6FD1E4E3B7A7D9AE37E + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + D7F765A54817B9EB8D55FA2B35D34ABE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIViewController+Chameleon.m + path + Pod/Classes/Objective-C/UIViewController+Chameleon.m + sourceTree + <group> + + D82F3937F0721FAF320800BB2EBEC238 + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + D88CFACD04FD813E0AF3F15CE6B4F9E5 + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + D8BF6253E9383EC9A3FDD10F864E3827 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMSwiftSupport.m + path + Realm/RLMSwiftSupport.m + sourceTree + <group> + + D8DD803EA30FD7A687BC244DE8347CD6 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + FirebaseAnalytics.framework + path + Frameworks/frameworks/FirebaseAnalytics.framework + sourceTree + <group> + + D9C97EB576CE94D4E00FD675005D2868 + + baseConfigurationReference + EE0AFC26190EB72C2BBEEF935B6C2BDB + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/AFNetworking/AFNetworking-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + DA1D7BB9AAB4E8F5C62C53C58E3127FA + + fileRef + C283F5CB8870D4117B7E269E66AB370D + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + DA21507E6FC6972DD62B1ED396649676 + + fileRef + CCFDD184741E8101623CEBA1BAA51E03 + isa + PBXBuildFile + + DA4A36F339A4009A263C486A69C4151B + + children + + C3F12A8CB0F64924AA85651837E2DE87 + + isa + PBXGroup + name + GoogleIPhoneUtilities + path + GoogleIPhoneUtilities + sourceTree + <group> + + DA78E3FC2D59335D411082AC8D5A6706 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFNetworkReachabilityManager.m + path + AFNetworking/AFNetworkReachabilityManager.m + sourceTree + <group> + + DB2451C242A2733E4722B6162000ACE9 + + includeInIndex + 1 + isa + PBXFileReference + name + handover.cpp + path + Realm/ObjectStore/src/impl/handover.cpp + sourceTree + <group> + + DB5E354F3C5B7E719BAC7AEA3823E9F0 + + buildActionMask + 2147483647 + files + + DF05D0925DFEAEEA07DD0F9581B392AE + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + DBB20CE52958E83D2431A6A97EF3FDEF + + fileRef + 7D6A8F7B7081EA6FBFA6F234E8D30274 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + DBB77D712908602F32D23BEDA4015A8A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + ChameleonEnums.h + path + Pod/Classes/Objective-C/ChameleonEnums.h + sourceTree + <group> + + DC72A1326D317C5DC244CCAB3CC3BFA3 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + path + Realm-prefix.pch + sourceTree + <group> + + DC7AEAE36EA758DE208B02873D3196BA + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + 928353533005A4198EBDA5B700D37B64 + remoteInfo + AFNetworking + + DCAA88918C9033FC501C4AE2CB4A8EA4 + + children + + A6C44D61D3FBE31DD63BC51E25391DC0 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + DDF699E3DA6F4773715E4589F39E2878 + + buildActionMask + 2147483647 + files + + 24F110ED104C63946574F9105F506C4B + + isa + PBXFrameworksBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + DE0BAD758C6F291CE7E10DA077987C62 + + buildActionMask + 2147483647 + files + + 14CD3DFB4A25BAEFB205730FA6750025 + 23415204AA48A02959E4E5245CF96037 + D31FEE51AF4BE2A6CC059F80B983CD3B + 4BA19F7DD8F9FDA648137B2CE89E7588 + A8FC6A53F6C1CB4DEFD972664E1AF3B1 + 43098B12DF5546CB576986E68DBCAFCA + 311E5200832E4AE174A193851A2C6317 + 8E82178CC9BD8BE3DBF2C10E33807AEB + C2201E34603B014192F0967473518496 + FCDDD3BC67EA205C87238A2A87D5C36C + 15460ABD372C937E2A07A2FCDF98C473 + 78940A6980F359C4304C695EF6E67C5D + B0BE398350F83A7B0102C3BBAFC51428 + 38EEA9ED6B922946C54AD6BAC93B73AD + 7F0578A0258E02384F6229C678E60D31 + AD359A96DD4AA2C962E50A5CD5524F54 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + DE4C71A8EB929C7A589183CAFE0DA7D1 + + fileRef + F8D8F0F9BB5E27B5581369EC0F239C18 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + DF05D0925DFEAEEA07DD0F9581B392AE + + fileRef + A0C03B3C39CF14DC0788A9A78CD59477 + isa + PBXBuildFile + + DF1789C3AD7A3289FAD95E628E81DB08 + + fileRef + B3F7872E09E611FC9194BFB4D039D72D + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + DFA9186D05062EC062DA72D8FBB4E6DB + + children + + BD9E32CBC2005732732E7A564466520A + 41DCFBD06C337F7D3B29C2FED717DF2C + + isa + PBXGroup + name + Maps + sourceTree + <group> + + DFDAC8245626953C4BCE5A2DA6BE2A5B + + fileRef + 16D27792D8019EC21D669E8B1D990F48 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + E0A1B97768782BC607C5436E4C6CFC4F + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealm_Dynamic.h + path + include/RLMRealm_Dynamic.h + sourceTree + <group> + + E212C2AA95E8F6EFB69A5F1455F291F8 + + fileRef + 45F475F2324F52333E51E3655ADA2C2D + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + E231692E3A239E0E192B41688B7F29C1 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncUtil.h + path + include/RLMSyncUtil.h + sourceTree + <group> + + E2FE9EB963A455DF3657CD3A206000AC + + buildActionMask + 2147483647 + files + + 426B448E3256264D133826A2F7CA832D + 7F18A631A68678DE5B11D4785CE6E57B + D7673B94D90B98099ECFC36133BFBBA6 + 1C9B78D6EC022316C546A6A6C5284253 + D19F5D7B316BFDBDDE17066EEE4CF5C8 + BE3F2030D5A2EF5404E107599DD2FEB5 + 4E2B74ABD7E0224ECA5C6F19CAE14494 + 2D5CE155885A48B9BE8C331644EC1846 + 3F0DAEF7AD12BD925424AC392AC64164 + 8DD0C4DDA8CC388707934D517D67B341 + BD14B249D67F8DDF85ADCD0501CF7750 + F6C4321A0A8B0003BEC037A307112B38 + 00F6517A3117C3CB6C67F1F4DC4FF050 + F8C5EBF5A81C10B08CBC7433094085BC + F432595774C6621E191AC5192DA6A88B + + isa + PBXHeadersBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E3F23CCD04275E3397F00A5A1070AFD5 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + MobileCoreServices.framework + path + Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/MobileCoreServices.framework + sourceTree + DEVELOPER_DIR + + E4CC874AF123072B0356D6819A359249 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + GoogleInterchangeUtilities.framework + path + Frameworks/frameworks/GoogleInterchangeUtilities.framework + sourceTree + <group> + + E4ED1D5C4FBA746EDDA4C1567D5C3FDA + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + SVProgressHUD.m + path + SVProgressHUD/SVProgressHUD.m + sourceTree + <group> + + E5D496BF7B537D437C900FB58B9227CB + + children + + 9EE5CF6D4540D2AB571D0BCC65E53B98 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + E63036DBA7ED3E28F7C892D74F2D4161 + + children + + 7BA9A8B5B4FE3448D5072369AB0A4513 + 70E6B5E63D98ED0D1713EEACB0393EBC + E3F23CCD04275E3397F00A5A1070AFD5 + 734519BB99CD5EE94C689204BCBCCE67 + 102145A1D07EC1C00D362924FBEFDB9C + 2E260044B199CD0C301E8BC607C4FC98 + C60A89879434FAC011EF7C7236966097 + + isa + PBXGroup + name + iOS + sourceTree + <group> + + E6408A7F9E6A0DCE86F5CC0973E35A4A + + children + + C90AC47E07D866E5E95EB9BA2685F0D3 + F8B37EFB1181FAA4FCAEB192DBAD82EC + 505DBB05922BE6AA7F5672ED4B778541 + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/SVProgressHUD + sourceTree + <group> + + E644E5FAABD1DFAD3D68B1264E93BB84 + + fileRef + 191F6FA9B378C1E53C7EAEA960DA6ABF + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + E6CE6E65CFF47B304E8C6E4A1A22D9DD + + fileRef + FECA88F028BDBF82F44BFD3EFD53C49A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + E6FE780D70A83497890D071F8235958A + + fileRef + 0161BE82E13E6D5AAF977DC72F6CEB1F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + E71073AE06D6684A3FEF8645E30E0E6F + + includeInIndex + 1 + isa + PBXFileReference + name + format.cpp + path + Realm/ObjectStore/src/util/format.cpp + sourceTree + <group> + + E804A440D15ADFB7854AA09D5EEA1C98 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + AFImageDownloader.m + path + UIKit+AFNetworking/AFImageDownloader.m + sourceTree + <group> + + E8A66C5313A01E6572BB619766190BCE + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncUtil.mm + path + Realm/RLMSyncUtil.mm + sourceTree + <group> + + E8E8DD19527758AD1F1DC2C310AF0EB9 + + fileRef + 63CEF6A400009AC67D2DB3C2F22C5B1A + isa + PBXBuildFile + + E9001FCA1D7F025E918399909B4942DA + + buildActionMask + 2147483647 + files + + 648E7F45D6E4DCE98EED9D4E2F003C1E + CCA9C08C9CBDE367B7BB38B6C0547B94 + + isa + PBXSourcesBuildPhase + runOnlyForDeploymentPostprocessing + 0 + + E9B6E1747CD6696B75D4D3BA413BB243 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + FIRMessaging.h + path + Sources/FIRMessaging.h + sourceTree + <group> + + EACECC9B850C1F0AB5D8974BB2DB74A1 + + fileRef + DB2451C242A2733E4722B6162000ACE9 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + EADA601641D6E5E2E1512F8311537D36 + + fileRef + 39EF664B48BC3CF73B1B4653DE0876DC + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + EAFC13078BF389FBA7DCF392460D1CEF + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + wrapper.plug-in + name + GooglePlaces.bundle + path + Frameworks/GooglePlaces.framework/Versions/A/Resources/GooglePlaces.bundle + sourceTree + <group> + + EB3236CBB7D49A36AD633A6125B230BF + + children + + AA07A0823D0B13472FDA9DE8091977FB + + isa + PBXGroup + name + Targets Support Files + sourceTree + <group> + + EB366EC1A299ED6F83AABBDAD0FE82D7 + + includeInIndex + 1 + isa + PBXFileReference + name + schema.cpp + path + Realm/ObjectStore/src/schema.cpp + sourceTree + <group> + + EBC57534427A1FB53B4F4D8D37DF31B8 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + FCAlertView.h + path + FCAlertView/Classes/FCAlertView.h + sourceTree + <group> + + EC890D269DC463EF00D56865C63233DB + + fileRef + EBC57534427A1FB53B4F4D8D37DF31B8 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + ECEFB4F98DC9F30F63000BFA48729FD4 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + UIAppearance+Swift.m + path + Pod/Classes/Objective-C/UIAppearance+Swift.m + sourceTree + <group> + + ECF5D7E2435E6A2A499D21CDED43E48B + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncSession.mm + path + Realm/RLMSyncSession.mm + sourceTree + <group> + + ED69D9B5A342FE9BD3F170D5CDDEC948 + + fileRef + D1398EEA3895D7F2CE9772514E61AB66 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + EDCF74D2C2C3493BC2D957847FEA8CFE + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + FABAttributes.h + path + iOS/Fabric.framework/Headers/FABAttributes.h + sourceTree + <group> + + EE0AFC26190EB72C2BBEEF935B6C2BDB + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + text.xcconfig + path + AFNetworking.xcconfig + sourceTree + <group> + + EE13135D5B5DB16CB42CC2233807E30F + + includeInIndex + 1 + isa + PBXFileReference + name + RLMListBase.mm + path + Realm/RLMListBase.mm + sourceTree + <group> + + EE55D7D4409C2486D9EB00F2FC69C32D + + children + + A64667C43DAF51587FB3F331B6C10345 + + isa + PBXGroup + name + FirebaseAnalytics + path + FirebaseAnalytics + sourceTree + <group> + + EEB0DD446622993688024F7FB9EC2642 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFURLSessionManager.h + path + AFNetworking/AFURLSessionManager.h + sourceTree + <group> + + F0D398496991B317983A8AC724EC7557 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + SVProgressHUD.h + path + SVProgressHUD/SVProgressHUD.h + sourceTree + <group> + + F1852AD0A401252DD67AA07533473B75 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMPredicateUtil.mm + path + Realm/RLMPredicateUtil.mm + sourceTree + <group> + + F1B00DB7ED33EA59D09C38EF18EC525B + + includeInIndex + 1 + isa + PBXFileReference + name + index_set.cpp + path + Realm/ObjectStore/src/index_set.cpp + sourceTree + <group> + + F1B4ABB28C8F5CF79D99C86F183C54E8 + + children + + CA79B3B03B658549C05D9C401C394890 + + isa + PBXGroup + name + Base + sourceTree + <group> + + F1CFC9A519BBF893CD52CD4EF853854B + + fileRef + 0CB4C862467FADB7A8C53306171E7298 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + F1ED7882BC7CE7E19BD11A40A7BC7D37 + + containerPortal + D41D8CD98F00B204E9800998ECF8427E + isa + PBXContainerItemProxy + proxyType + 1 + remoteGlobalIDString + B1AAA1228D99746F4EB488754929B532 + remoteInfo + FCAlertView-FCAlertView + + F2303B2ADB41B4306D0ED6F958AD3222 + + fileRef + 8A8AC093F26AD2083C22C6D19571F806 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + F2BD8D5314F70A913234B9380377C1C0 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMObjectStore.h + path + include/RLMObjectStore.h + sourceTree + <group> + + F3774B1449FCE5B3CA3E1B336E11D284 + + fileRef + F4F28856BDD6FFE26530DD037EAC5909 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + F3973E81DDBB552E2C0A54C3D2B7728C + + fileRef + D7F765A54817B9EB8D55FA2B35D34ABE + isa + PBXBuildFile + + F432595774C6621E191AC5192DA6A88B + + fileRef + 9CC1A1907EAD823E1AFB28B53111C0D6 + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + F461669585DB9A08C8B099FFFF820E8A + + children + + 05325FCDF1D30E28F878330892F633A6 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + F46170D78366C37B71DA6A70EEA5DB9B + + children + + 57F4A587EE1811563109E2B8BB66038F + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + F4CDA5FA9197A41E0081E84F932906EB + + children + + E63036DBA7ED3E28F7C892D74F2D4161 + + isa + PBXGroup + name + Frameworks + sourceTree + <group> + + F4F28856BDD6FFE26530DD037EAC5909 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFHTTPSessionManager.h + path + AFNetworking/AFHTTPSessionManager.h + sourceTree + <group> + + F5D7D1A059B997C8706C16F267EF780D + + includeInIndex + 1 + isa + PBXFileReference + name + RLMSyncSessionHandle.mm + path + Realm/RLMSyncSessionHandle.mm + sourceTree + <group> + + F6687C03FFEC03180FC4B10D784B39E9 + + fileRef + F85D374DDA0229889F4D0A64BE5E1E01 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + F6C4321A0A8B0003BEC037A307112B38 + + fileRef + 1A36559A7109BAF920FC3A4C3EEFD22B + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + F72F963661C6F309185D5EB3AFC18AC7 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMRealm.h + path + include/RLMRealm.h + sourceTree + <group> + + F79E89170ABB5EE3B83077217913A100 + + baseConfigurationReference + 29BFCB88C3683AFDDB7B7C9DE2ADD970 + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf-with-dsym + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/Realm/Realm-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + NO + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Release + + F85D374DDA0229889F4D0A64BE5E1E01 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMTokenModels.m + path + Realm/RLMTokenModels.m + sourceTree + <group> + + F889A73189014B940118D652E6E4292D + + children + + 5F73C97D9DB69FF261AEF2522DF455B4 + 6A5F8959A9251BC3BDDAC8535F73AC0A + D1E1EE5F5925B1AFEAE5CEEBE2584246 + BB18238FB9BDC832D3C5C487D4E1B9A5 + 3D90767FB726F3CCEA9C6A49DC9E1D39 + D00DE5F552176E2974F9C1876069E088 + EE55D7D4409C2486D9EB00F2FC69C32D + C15993E7EEEA6B1E3132E993A21B7348 + 67D892FCC7F5F11B302B43E622DA0C08 + 7DDBA27D6FB4FC7E2D3E5766C4989AC2 + 7379277A8D21CD877377F886C8638686 + DA4A36F339A4009A263C486A69C4151B + B64093F8FF30305F8B2C8B1F5AA06B21 + 9F5A74588B7324AE561646BB139561DD + 63A824AF9EB12937A1397B9510DF6675 + 3FABBF3740F613ACE6361AD63E4A536C + 15B615406E392CED397CDD82804A2E59 + A44F6AC6F72C43DD65623BA935480764 + 083CE88C685B6FB12BB9403313F4D7B8 + + isa + PBXGroup + name + Pods + sourceTree + <group> + + F8B37EFB1181FAA4FCAEB192DBAD82EC + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + SVProgressHUD-dummy.m + sourceTree + <group> + + F8C5EBF5A81C10B08CBC7433094085BC + + fileRef + 5A705339812A117B1EEFD207502BBA6C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + F8D8F0F9BB5E27B5581369EC0F239C18 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSyncUtil_Private.h + path + include/RLMSyncUtil_Private.h + sourceTree + <group> + + F9666AF738588ADE7ED75A15E570A507 + + includeInIndex + 1 + isa + PBXFileReference + name + realm_coordinator.cpp + path + Realm/ObjectStore/src/impl/realm_coordinator.cpp + sourceTree + <group> + + F96AF2236B76293064F519937A337FE0 + + fileRef + 70E6B5E63D98ED0D1713EEACB0393EBC + isa + PBXBuildFile + + F9BA9CFF505BABA1B99F2C10AEC14525 + + fileRef + 7BE65A584E79E775585E6A59B5EE4C9A + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + F9E78382EF3D8EE8204AEECBAE3C62CD + + fileRef + E0A1B97768782BC607C5436E4C6CFC4F + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + F9F57A1A1A93F72696845843A5D18EF8 + + children + + 29BFCB88C3683AFDDB7B7C9DE2ADD970 + CCFDD184741E8101623CEBA1BAA51E03 + DC72A1326D317C5DC244CCAB3CC3BFA3 + + isa + PBXGroup + name + Support Files + path + ../Target Support Files/Realm + sourceTree + <group> + + FA6A8DFBD40048CB7CDA1A189A2CE83B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + path + FCAlertView-dummy.m + sourceTree + <group> + + FAAFDCB4C82F19E2ACEFE5A793D7E9C4 + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.objc + name + RLMSyncCredential.m + path + Realm/RLMSyncCredential.m + sourceTree + <group> + + FAFE2172A5670E1ECCCFB8308644B69E + + fileRef + F1852AD0A401252DD67AA07533473B75 + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + FB5AB49A46A53C43D6C101F7D63E99C9 + + fileRef + F8B37EFB1181FAA4FCAEB192DBAD82EC + isa + PBXBuildFile + + FBA4C1076819E2C466EF6040D7E684A5 + + explicitFileType + wrapper.cfbundle + includeInIndex + 0 + isa + PBXFileReference + name + FCAlertView.bundle + path + FCAlertView.bundle + sourceTree + BUILT_PRODUCTS_DIR + + FBACF5338B8839F2165A8F8D9692A409 + + includeInIndex + 1 + isa + PBXFileReference + name + RLMOptionalBase.mm + path + Realm/RLMOptionalBase.mm + sourceTree + <group> + + FC47A20F268327D7F11A6ABAFADD63BC + + fileRef + E8A66C5313A01E6572BB619766190BCE + isa + PBXBuildFile + settings + + COMPILER_FLAGS + -DREALM_HAVE_CONFIG -DREALM_COCOA_VERSION='@"2.0.2"' -D__ASSERTMACROS__ -DREALM_ENABLE_SYNC + + + FCBC8DAE4AABB8879E0986A0A380D990 + + fileRef + AFDE422C697173D4B3DBE181BAD34F8C + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + FCDDD3BC67EA205C87238A2A87D5C36C + + fileRef + 6D10393660D7BDAACEDFC835FCE220AF + isa + PBXBuildFile + + FD3F0830BB7C6B96AC46E043FC4A2BE5 + + fileRef + CF546E09CAF655ADECECF54D7C35B54A + isa + PBXBuildFile + settings + + ATTRIBUTES + + Public + + + + FD53F15C8D7AE0993DD8B39395A7CE57 + + isa + PBXFileReference + lastKnownFileType + wrapper.framework + name + FirebaseInstanceID.framework + path + Frameworks/frameworks/FirebaseInstanceID.framework + sourceTree + <group> + + FE23A49D003C17D1BAE347E504B7639C + + fileRef + 11067A4D9D6BD6C9EE8A9D91B49E080B + isa + PBXBuildFile + settings + + ATTRIBUTES + + Private + + + + FE8A58E62C67F600AD1469E4F13F9BB9 + + baseConfigurationReference + 2EA6DA768DD82D9DE2D6C469C45A0D4E + buildSettings + + CODE_SIGN_IDENTITY[sdk=iphoneos*] + iPhone Developer + DEBUG_INFORMATION_FORMAT + dwarf + ENABLE_STRICT_OBJC_MSGSEND + YES + GCC_NO_COMMON_BLOCKS + YES + GCC_PREFIX_HEADER + Target Support Files/FCAlertView/FCAlertView-prefix.pch + IPHONEOS_DEPLOYMENT_TARGET + 7.0 + MTL_ENABLE_DEBUG_INFO + YES + OTHER_LDFLAGS + + OTHER_LIBTOOLFLAGS + + PRIVATE_HEADERS_FOLDER_PATH + + PRODUCT_NAME + $(TARGET_NAME) + PUBLIC_HEADERS_FOLDER_PATH + + SDKROOT + iphoneos + SKIP_INSTALL + YES + + isa + XCBuildConfiguration + name + Debug + + FECA88F028BDBF82F44BFD3EFD53C49A + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + RLMSchema.h + path + include/RLMSchema.h + sourceTree + <group> + + FF72855D2EFF1C13779CAA586D517808 + + children + + FBA4C1076819E2C466EF6040D7E684A5 + 84CA470D6195ECEABCAB05D43D68A04A + 71AF53765B7E79703FC89C9A75E5E9EC + 50520C893806752506E6EEE0BB1D9CDD + 4BFFEBA374AD3562751D0ECDE5DB1D98 + 4B335881B2F732F366DF01E4EAC3CDBC + 2CF986C67C86BDDA5CDEABD0B68255BB + C0E25938902ED70F39B165929D2F4523 + + isa + PBXGroup + name + Products + sourceTree + <group> + + FFB8EBAD39FDD86E41FDB1FCBE47588B + + includeInIndex + 1 + isa + PBXFileReference + lastKnownFileType + sourcecode.c.h + name + AFURLRequestSerialization.h + path + AFNetworking/AFURLRequestSerialization.h + sourceTree + <group> + + + rootObject + D41D8CD98F00B204E9800998ECF8427E + + diff --git a/Pods/RSBarcodes_Swift/LICENSE.md b/Pods/RSBarcodes_Swift/LICENSE.md new file mode 100644 index 0000000..b8583b7 --- /dev/null +++ b/Pods/RSBarcodes_Swift/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2012-2014 P.D.Q. + +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/RSBarcodes_Swift/README.md b/Pods/RSBarcodes_Swift/README.md new file mode 100644 index 0000000..c67d648 --- /dev/null +++ b/Pods/RSBarcodes_Swift/README.md @@ -0,0 +1,155 @@ +

+ +

+ +RSBarcodes, now Swift. +========== +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Total views](https://sourcegraph.com/api/repos/github.com/yeahdongcn/RSBarcodes_Swift/counters/views.png)](https://sourcegraph.com/github.com/yeahdongcn/RSBarcodes_Swift) +[![Views in the last 24 hours](https://sourcegraph.com/api/repos/github.com/yeahdongcn/RSBarcodes_Swift/counters/views-24h.png)](https://sourcegraph.com/github.com/yeahdongcn/RSBarcodes_Swift) + +RSBarcodes allows you to read 1D and 2D barcodes using the metadata scanning capabilities introduced with iOS 7 and generate the same set of barcode images for displaying and sharing. Now implemented in Swift. + +* Objective-C version. [RSBarcodes](https://github.com/yeahdongcn/RSBarcodes) + +##TODO + +###Generators +- [x] Code39 +- [x] Code39Mod43 +- [x] ExtendedCode39 +- [x] Code93 +- [x] Code128 +- [x] UPCE +- [x] EAN FAMILIY (EAN8 EAN13 ISBN13 ISSN13) +- [x] ITF14 +- [x] Interleaved2of5 +- [ ] DataMatrix +- [x] PDF417 +- [x] QR +- [x] Aztec +- [x] Views + +###Reader +- [x] Views +- [x] ReaderController + +##Installation + +###[CocoaPods](http://cocoapods.org) + +Simply add the following lines to your `Podfile`: +```ruby +# required by Cocoapods 0.36.0.rc.1 for Swift Pods +use_frameworks! + +pod 'RSBarcodes_Swift', '~> 0.0.8' +``` + +*(CocoaPods v0.36 or later required. See [this blog post](http://blog.cocoapods.org/Pod-Authors-Guide-to-CocoaPods-Frameworks/) for details.)* + +###[Carthage](http://github.com/Carthage/Carthage) + +Simply add the following line to your `Cartfile`: + +```ruby +github "yeahdongcn/RSBarcodes_Swift" >= 0.0.8 +``` + +###Manual + +1. Add RSBarcodes_Swift as a [submodule](http://git-scm.com/docs/git-submodule) by opening the Terminal, `cd`-ing into your top-level project directory, and entering the command `git submodule add https://github.com/yeahdongcn/RSBarcodes_Swift.git` +2. Open the `RSBarcodes_Swift` folder, and drag `RSBarcodes.xcodeproj` into the file navigator of your app project. +3. In Xcode, navigate to the target configuration window by clicking on the blue project icon, and select the application target under the "Targets" heading in the sidebar. +4. Ensure that the deployment target of RSBarcodes.framework matches that of the application target. +5. In the tab bar at the top of that window, open the "Build Phases" panel. +6. Expand the "Target Dependencies" group, and add `RSBarcodes.framework`. +7. Click on the `+` button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `RSBarcodes.framework`. + +##Usage + +[HOW TO USE GENERATOR](https://github.com/yeahdongcn/RSBarcodes_Swift/tree/CB) and +[HOW TO USE READER](https://github.com/yeahdongcn/RSBarcodes_Swift/tree/Vicky) + +###Generators + +The simplest way to use the generators is: + + RSUnifiedCodeGenerator.shared.generateCode("2166529V", machineReadableCodeObjectType: AVMetadataObjectTypeCode39Code) + +It will generate an UIImage instance if the `2166529V` is a valid code39 string. For AVMetadataObjectTypeCode128Code, you can change `useBuiltInCode128Generator` to `false` to use my implementation (AutoTable for code128). + +P.S. There are 4 table for encoding a string to code128, `TableA`, `TableB`, `TableC` and `TableAuto`, the `TableAuto` is always the best choice, but if one has certain requirement, try this: + + RSCode128Generator(codeTable: .A).generateCode("123456", machineReadableCodeObjectType: AVMetadataObjectTypeCode128Code) + +These calling simples can be found in the test project. + +###Reader + +The following are steps to get the barcode reader working: + +1. `File` -> `New` -> `File` +2. Under `iOS` click `source` and make sure `Cocoa Touch Class` is selected and hit `Next`. +3. Call the name of the class whatever you want but I will refer to it as `ScanViewController` from now on. +4. Make it a subclass of `RSCodeReaderViewController` and ensure the language is `Swift` and hit `Next` and then `Create` +5. Open your storyboard and drag a `UIViewController` onto it. +6. Show the identity inspect and under custom class select `ScanViewController` +7. The focus mark layer and corners layer are already there working for you. There are two handlers: one for the single tap on the screen along with the focus mark and one detected objects handler, which all detected will come to you. Now in the `ScanViewController.swift` file add the following code into the `viewDidLoad()` or some place more suitable for you: + + override func viewDidLoad() { + super.viewDidLoad() + + self.focusMarkLayer.strokeColor = UIColor.redColor().CGColor + + self.cornersLayer.strokeColor = UIColor.yellowColor().CGColor + + self.tapHandler = { point in + println(point) + } + + self.barcodesHandler = { barcodes in + for barcode in barcodes { + println("Barcode found: type=" + barcode.type + " value=" + barcode.stringValue) + } + } + } + +If you want to ignore some code types, simply add following lines + + let types = NSMutableArray(array: self.output.availableMetadataObjectTypes) + types.removeObject(AVMetadataObjectTypeQRCode) + self.output.metadataObjectTypes = NSArray(array: types) + +###Helper + +Use `RSAbstractCodeGenerator.resizeImage(<#source: UIImage#>, scale: <#CGFloat#>)` to scale the generated image. + +##Miscellaneous + +[The Swift Programming Language 中文版](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/) + +[Online version](http://numbbbbb.github.io/the-swift-programming-language-in-chinese/) generated using [GitBook](https://www.gitbook.io/) + +##License + + The MIT License (MIT) + + Copyright (c) 2012-2014 P.D.Q. + + 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/RSBarcodes_Swift/Source/RSCode128Generator.swift b/Pods/RSBarcodes_Swift/Source/RSCode128Generator.swift new file mode 100644 index 0000000..b4b6190 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCode128Generator.swift @@ -0,0 +1,393 @@ +// +// RSCode128Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/11/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +public enum RSCode128GeneratorCodeTable: Int { + case auto = 0 + case a, b, c +} + +// http://www.barcodeisland.com/code128.phtml +// http://courses.cs.washington.edu/courses/cse370/01au/minirproject/BarcodeBattlers/barcodes.html +open class RSCode128Generator: RSAbstractCodeGenerator, RSCheckDigitGenerator { + class RSCode128GeneratorAutoCodeTable { + var startCodeTable = RSCode128GeneratorCodeTable.auto + var sequence:Array = [] + } + + var codeTable: RSCode128GeneratorCodeTable + var codeTableSize: Int + var autoCodeTable: RSCode128GeneratorAutoCodeTable + + public init(codeTable:RSCode128GeneratorCodeTable) { + self.codeTable = codeTable + self.codeTableSize = CODE128_CHARACTER_ENCODINGS.count + self.autoCodeTable = RSCode128GeneratorAutoCodeTable() + } + + public convenience override init() { + self.init(codeTable: .auto) + } + + func startCodeTableValue(_ startCodeTable: RSCode128GeneratorCodeTable) -> Int { + switch self.autoCodeTable.startCodeTable { + case .a: + return self.codeTableSize - 4 + case .b: + return self.codeTableSize - 3 + case .c: + return self.codeTableSize - 2 + default: + switch startCodeTable { + case .a: + return self.codeTableSize - 4 + case .b: + return self.codeTableSize - 3 + case .c: + return self.codeTableSize - 2 + default: + return 0 + } + } + } + + func middleCodeTableValue(_ codeTable:RSCode128GeneratorCodeTable) -> Int { + switch codeTable { + case .a: + return self.codeTableSize - 6 + case .b: + return self.codeTableSize - 7 + case .c: + return self.codeTableSize - 8 + default: + return 0 + } + } + + func calculateContinousDigits(_ contents:String, defaultCodeTable:RSCode128GeneratorCodeTable, range:Range) { + var isFinished = false + if range.upperBound == contents.length() { + isFinished = true + } + + let length = range.upperBound - range.lowerBound + if (range.lowerBound == 0 && length >= 4) + || (range.lowerBound > 0 && length >= 6) { + var isOrphanDigitUsed = false + // Use START C when continous digits are found from range.location == 0 + if range.lowerBound == 0 { + self.autoCodeTable.startCodeTable = .c + } else { + if length % 2 == 1 { + let digitValue = CODE128_ALPHABET_STRING.location(contents[range.lowerBound]) + self.autoCodeTable.sequence.append(digitValue) + isOrphanDigitUsed = true + } + self.autoCodeTable.sequence.append(self.middleCodeTableValue(.c)) + } + + // Insert all xx combinations + for i in 0.. = (0 ..< 0) + if DIGITS_STRING.location(character) == NSNotFound { + // Non digit found + if continousDigitsStartIndex != NSNotFound { + continousDigitsRange = (continousDigitsStartIndex ..< i) + } else { + let characterValue = CODE128_ALPHABET_STRING.location(character) + self.autoCodeTable.sequence.append(characterValue) + } + } else { + // Digit found + if continousDigitsStartIndex == NSNotFound { + continousDigitsStartIndex = i + } else if i == contents.length() - 1 { + continousDigitsRange = (continousDigitsStartIndex ..< i + 1) + } + } + + if continousDigitsRange.upperBound - continousDigitsRange.lowerBound != 0 { + self.calculateContinousDigits(contents, defaultCodeTable: defaultCodeTable, range: continousDigitsRange) + continousDigitsStartIndex = NSNotFound + } + } + + if self.autoCodeTable.startCodeTable == .auto { + self.autoCodeTable.startCodeTable = defaultCodeTable + } + } + } + + func encodeCharacterString(_ characterString:String) -> String { + return CODE128_CHARACTER_ENCODINGS[CODE128_ALPHABET_STRING.location(characterString)] + } + + override open func initiator() -> String { + switch self.codeTable { + case .auto: + return CODE128_CHARACTER_ENCODINGS[self.startCodeTableValue(self.autoCodeTable.startCodeTable)] + default: + return CODE128_CHARACTER_ENCODINGS[self.startCodeTableValue(self.codeTable)] + } + } + + override open func terminator() -> String { + return CODE128_CHARACTER_ENCODINGS[self.codeTableSize - 1] + "11" + } + + override open func isValid(_ contents: String) -> Bool { + if contents.length() > 0 { + for i in 0.. String { + var barcode = "" + switch self.codeTable { + case .auto: + for i in 0.. String { + var sum = 0 + switch self.codeTable { + case .auto: + sum += self.startCodeTableValue(self.autoCodeTable.startCodeTable) + for i in 0..?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~" + + let CODE128_CHARACTER_ENCODINGS = [ + "11011001100", + "11001101100", + "11001100110", + "10010011000", + "10010001100", + "10001001100", + "10011001000", + "10011000100", + "10001100100", + "11001001000", + "11001000100", + "11000100100", + "10110011100", + "10011011100", + "10011001110", + "10111001100", + "10011101100", + "10011100110", + "11001110010", + "11001011100", + "11001001110", + "11011100100", + "11001110100", + "11101101110", + "11101001100", + "11100101100", + "11100100110", + "11101100100", + "11100110100", + "11100110010", + "11011011000", + "11011000110", + "11000110110", + "10100011000", + "10001011000", + "10001000110", + "10110001000", + "10001101000", + "10001100010", + "11010001000", + "11000101000", + "11000100010", + "10110111000", + "10110001110", + "10001101110", + "10111011000", + "10111000110", + "10001110110", + "11101110110", + "11010001110", + "11000101110", + "11011101000", + "11011100010", + "11011101110", + "11101011000", + "11101000110", + "11100010110", + "11101101000", + "11101100010", + "11100011010", + "11101111010", + "11001000010", + "11110001010", + "10100110000", // 64 + // Visible character encoding for code table A ended. + "10100001100", + "10010110000", + "10010000110", + "10000101100", + "10000100110", + "10110010000", + "10110000100", + "10011010000", + "10011000010", + "10000110100", + "10000110010", + "11000010010", + "11001010000", + "11110111010", + "11000010100", + "10001111010", + "10100111100", + "10010111100", + "10010011110", + "10111100100", + "10011110100", + "10011110010", + "11110100100", + "11110010100", + "11110010010", + "11011011110", + "11011110110", + "11110110110", + "10101111000", + "10100011110", + "10001011110", + // Visible character encoding for code table B ended. + "10111101000", + "10111100010", + "11110101000", + "11110100010", + "10111011110", // to C from A, B (size - 8) + "10111101110", // to B from A, C (size - 7) + "11101011110", // to A from B, C (size - 6) + "11110101110", + "11010000100", // START A (size - 4) + "11010010000", // START B (size - 3) + "11010011100", // START C (size - 2) + "11000111010" // STOP (size - 1) + ] +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCode39Generator.swift b/Pods/RSBarcodes_Swift/Source/RSCode39Generator.swift new file mode 100644 index 0000000..4fdbf27 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCode39Generator.swift @@ -0,0 +1,99 @@ +// +// RSCode39Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/10/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import Foundation + +let CODE39_ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*" + +// http://www.barcodesymbols.com/code39.htm +// http://www.barcodeisland.com/code39.phtml +open class RSCode39Generator: RSAbstractCodeGenerator { + let CODE39_CHARACTER_ENCODINGS = [ + "1010011011010", + "1101001010110", + "1011001010110", + "1101100101010", + "1010011010110", + "1101001101010", + "1011001101010", + "1010010110110", + "1101001011010", + "1011001011010", + "1101010010110", + "1011010010110", + "1101101001010", + "1010110010110", + "1101011001010", + "1011011001010", + "1010100110110", + "1101010011010", + "1011010011010", + "1010110011010", + "1101010100110", + "1011010100110", + "1101101010010", + "1010110100110", + "1101011010010", + "1011011010010", + "1010101100110", + "1101010110010", + "1011010110010", + "1010110110010", + "1100101010110", + "1001101010110", + "1100110101010", + "1001011010110", + "1100101101010", + "1001101101010", + "1001010110110", + "1100101011010", + "1001101011010", + "1001001001010", + "1001001010010", + "1001010010010", + "1010010010010", + "1001011011010" + ] + + func encodeCharacterString(_ characterString:String) -> String { + let location = CODE39_ALPHABET_STRING.location(characterString) + return CODE39_CHARACTER_ENCODINGS[location] + } + + // MAKR: RSAbstractCodeGenerator + + override open func isValid(_ contents: String) -> Bool { + let length = contents.length() + if length > 0 && contents == contents.uppercased() { + for character in contents.characters { + let location = CODE39_ALPHABET_STRING.location(String(character)) + if location == NSNotFound { + return false + } + } + return true + } + return false + } + + override open func initiator() -> String { + return self.encodeCharacterString("*") + } + + override open func terminator() -> String { + return self.encodeCharacterString("*") + } + + override open func barcode(_ contents: String) -> String { + var barcode = "" + for character in contents.characters { + barcode += self.encodeCharacterString(String(character)) + } + return barcode + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCode39Mod43Generator.swift b/Pods/RSBarcodes_Swift/Source/RSCode39Mod43Generator.swift new file mode 100644 index 0000000..c351570 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCode39Mod43Generator.swift @@ -0,0 +1,45 @@ +// +// RSCode39Mod43Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/10/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +// http://www.barcodesymbols.com/code39.htm +// http://www.barcodeisland.com/code39.phtml +open class RSCode39Mod43Generator: RSCode39Generator, RSCheckDigitGenerator { + + // MARK: RSAbstractCodeGenerator + + override open func barcode(_ contents: String) -> String { + return super.barcode(contents + self.checkDigit(contents.uppercased())) + } + + // MARK: RSCheckDigitGenerator + + open func checkDigit(_ contents: String) -> String { + /** + Step 1: From the table below, find the values of each character. + C O D E 3 9 <--Message characters + 12 24 13 14 38 3 9 <--Character values + + Step 2: Sum the character values. + 12 + 24 + 13 + 14 + 38 + 3 + 9 = 113 + + Step 3: Divide the result by 43. + 113 / 43 = 11 with remainder of 27. + + Step 4: From the table, find the character with this value. + 27 = R = Check Character + */ + var sum = 0 + for character in contents.characters { + sum += CODE39_ALPHABET_STRING.location(String(character)) + } + // 43 = CODE39_ALPHABET_STRING's length - 1 -- excludes asterisk + return CODE39_ALPHABET_STRING[sum % (CODE39_ALPHABET_STRING.length() - 1)] + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCode93Generator.swift b/Pods/RSBarcodes_Swift/Source/RSCode93Generator.swift new file mode 100644 index 0000000..57eb5d5 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCode93Generator.swift @@ -0,0 +1,136 @@ +// +// RSCode93Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/11/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +// http://www.barcodeisland.com/code93.phtml +open class RSCode93Generator: RSAbstractCodeGenerator, RSCheckDigitGenerator { + let CODE93_ALPHABET_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*" + + let CODE93_PLACEHOLDER_STRING = "abcd"; + + let CODE93_CHARACTER_ENCODINGS = [ + "100010100", + "101001000", + "101000100", + "101000010", + "100101000", + "100100100", + "100100010", + "101010000", + "100010010", + "100001010", + "110101000", + "110100100", + "110100010", + "110010100", + "110010010", + "110001010", + "101101000", + "101100100", + "101100010", + "100110100", + "100011010", + "101011000", + "101001100", + "101000110", + "100101100", + "100010110", + "110110100", + "110110010", + "110101100", + "110100110", + "110010110", + "110011010", + "101101100", + "101100110", + "100110110", + "100111010", + "100101110", + "111010100", + "111010010", + "111001010", + "101101110", + "101110110", + "110101110", + "100100110", + "111011010", + "111010110", + "100110010", + "101011110" + ] + + + func encodeCharacterString(_ characterString:String) -> String { + return CODE93_CHARACTER_ENCODINGS[CODE93_ALPHABET_STRING.location(characterString)] + } + + override open func isValid(_ contents: String) -> Bool { + if contents.length() > 0 && contents == contents.uppercased() { + for i in 0.. String { + return self.encodeCharacterString("*") + } + + override open func terminator() -> String { + // With the termination bar: 1 + return self.encodeCharacterString("*") + "1" + } + + override open func barcode(_ contents: String) -> String { + var barcode = "" + for character in contents.characters { + barcode += self.encodeCharacterString(String(character)) + } + + let checkDigits = self.checkDigit(contents) + for character in checkDigits.characters { + barcode += self.encodeCharacterString(String(character)) + } + return barcode + } + + // MARK: RSCheckDigitGenerator + + open func checkDigit(_ contents: String) -> String { + // Weighted sum += value * weight + + // The first character + var sum = 0 + for i in 0.. UIImage? + + /** Generate code image using the given machine readable code object type and contents. */ + func generateCode(_ contents:String, machineReadableCodeObjectType:String) -> UIImage? +} + +// Check digit are not required for all code generators. +// UPC-E is using check digit to valid the contents to be encoded. +// Code39Mod43, Code93 and Code128 is using check digit to encode barcode. +public protocol RSCheckDigitGenerator { + func checkDigit(_ contents:String) -> String +} + +// Abstract code generator, provides default functions for validations and generations. +open class RSAbstractCodeGenerator : RSCodeGenerator { + + open var fillColor: UIColor = UIColor.white + open var strokeColor: UIColor = UIColor.black + + // Check whether the given contents are valid. + open func isValid(_ contents:String) -> Bool { + let length = contents.length() + if length > 0 { + for i in 0.. String { + return "" + } + + // Barcode terminator, subclass should return its own value. + open func terminator() -> String { + return "" + } + + // Barcode content, subclass should return its own value. + open func barcode(_ contents:String) -> String { + return "" + } + + // Composer for combining barcode initiator, contents, terminator together. + func completeBarcode(_ barcode:String) -> String { + return self.initiator() + barcode + self.terminator() + } + + // Drawer for completed barcode. + func drawCompleteBarcode(_ completeBarcode:String) -> UIImage? { + let length:Int = completeBarcode.length() + if length <= 0 { + return nil + } + + // Values taken from CIImage generated AVMetadataObjectTypePDF417Code type image + // Top spacing = 1.5 + // Bottom spacing = 2 + // Left & right spacing = 2 + // Height = 28 + let width = length + 4 + let size = CGSize(width: CGFloat(width), height: 28) + UIGraphicsBeginImageContextWithOptions(size, false, 0) + let context = UIGraphicsGetCurrentContext() + + context?.setShouldAntialias(false) + + self.fillColor.setFill() + self.strokeColor.setStroke() + + context?.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) + context?.setLineWidth(1) + + for i in 0.. UIImage? { + return self.generateCode(machineReadableCodeObject.stringValue, machineReadableCodeObjectType: machineReadableCodeObject.type) + } + + open func generateCode(_ contents:String, machineReadableCodeObjectType:String) -> UIImage? { + if self.isValid(contents) { + return self.drawCompleteBarcode(self.completeBarcode(self.barcode(contents))) + } + return nil + } + + // Class funcs + + // Get CIFilter name by machine readable code object type + open class func filterName(_ machineReadableCodeObjectType:String) -> String! { + if machineReadableCodeObjectType == AVMetadataObjectTypeQRCode { + return "CIQRCodeGenerator" + } else if machineReadableCodeObjectType == AVMetadataObjectTypePDF417Code { + return "CIPDF417BarcodeGenerator" + } else if machineReadableCodeObjectType == AVMetadataObjectTypeAztecCode { + return "CIAztecCodeGenerator" + } else if machineReadableCodeObjectType == AVMetadataObjectTypeCode128Code { + return "CICode128BarcodeGenerator" + } else { + return "" + } + } + + // Generate CI related code image + open class func generateCode(_ contents:String, filterName:String) -> UIImage? { + if filterName.length() > 0 { + let filter = CIFilter(name: filterName) + if let filter = filter { + filter.setDefaults() + let inputMessage = contents.data(using: String.Encoding.utf8, allowLossyConversion: false) + filter.setValue(inputMessage, forKey: "inputMessage") + + let outputImage = filter.outputImage + let context = CIContext(options: nil) + if let outputImage = outputImage { + let cgImage = context.createCGImage(outputImage, from: outputImage.extent) + return UIImage(CGImage: cgImage, scale: 1, orientation: UIImageOrientation.Up) + } + } + } + return nil + } + + // Resize image + open class func resizeImage(_ source:UIImage, scale:CGFloat) -> UIImage { + let width = source.size.width * scale + let height = source.size.height * scale + + UIGraphicsBeginImageContext(CGSize(width: width, height: height)) + let context = UIGraphicsGetCurrentContext() + context!.interpolationQuality = CGInterpolationQuality.none + source.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) + let target = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return target! + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCodeLayer.swift b/Pods/RSBarcodes_Swift/Source/RSCodeLayer.swift new file mode 100644 index 0000000..88db8f4 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCodeLayer.swift @@ -0,0 +1,24 @@ +// +// RSCodeLayer.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/13/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit +import QuartzCore + +open class RSCodeLayer: CALayer { + var code: UIImage? + + override open func draw(in ctx: CGContext) { + if let code = self.code { + ctx.saveGState() + + ctx.draw(code.cgImage!, in: self.bounds) + + ctx.restoreGState() + } + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCodeReaderViewController.swift b/Pods/RSBarcodes_Swift/Source/RSCodeReaderViewController.swift new file mode 100644 index 0000000..ae2e461 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCodeReaderViewController.swift @@ -0,0 +1,311 @@ +// +// RSCodeReaderViewController.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/12/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit +import AVFoundation + +open class RSCodeReaderViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { + + open lazy var device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) + open lazy var output = AVCaptureMetadataOutput() + open lazy var session = AVCaptureSession() + var videoPreviewLayer: AVCaptureVideoPreviewLayer? + + open lazy var focusMarkLayer = RSFocusMarkLayer() + open lazy var cornersLayer = RSCornersLayer() + + open var tapHandler: ((CGPoint) -> Void)? + open var barcodesHandler: ((Array) -> Void)? + + var ticker: Timer? + + open var isCrazyMode = false + var isCrazyModeStarted = false + var lensPosition: Float = 0 + + // MARK: Public methods + + open func hasFlash() -> Bool { + if let device = self.device { + return device.hasFlash + } + return false + } + + open func hasTorch() -> Bool { + if let device = self.device { + return device.hasTorch + } + return false + } + + open func toggleTorch() -> Bool { + if self.hasTorch() { + self.session.beginConfiguration() + do { + try self.device?.lockForConfiguration() + } catch _ { + } + + if self.device?.torchMode == .off { + self.device?.torchMode = .on + } else if self.device?.torchMode == .on { + self.device?.torchMode = .off + } + + self.device?.unlockForConfiguration() + self.session.commitConfiguration() + + return self.device!.torchMode == .on + } + return false + } + + // MARK: Private methods + + class func interfaceOrientationToVideoOrientation(_ orientation : UIInterfaceOrientation) -> AVCaptureVideoOrientation { + switch (orientation) { + case .unknown: + fallthrough + case .portrait: + return AVCaptureVideoOrientation.portrait + case .portraitUpsideDown: + return AVCaptureVideoOrientation.portraitUpsideDown + case .landscapeLeft: + return AVCaptureVideoOrientation.landscapeLeft + case .landscapeRight: + return AVCaptureVideoOrientation.landscapeRight + } + } + + func autoUpdateLensPosition() { + self.lensPosition += 0.01 + if self.lensPosition > 1 { + self.lensPosition = 0 + } + do { + try device?.lockForConfiguration() + self.device?.setFocusModeLockedWithLensPosition(self.lensPosition, completionHandler: nil) + device?.unlockForConfiguration() + } catch _ { + } + if session.isRunning { + let when = DispatchTime.now() + Double(Int64(10 * Double(USEC_PER_SEC))) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: when, execute: { + self.autoUpdateLensPosition() + }) + } + } + + func onTick() { + if let ticker = self.ticker { + ticker.invalidate() + } + self.cornersLayer.cornersArray = [] + } + + func onTap(_ gesture: UITapGestureRecognizer) { + let tapPoint = gesture.location(in: self.view) + let focusPoint = CGPoint( + x: tapPoint.x / self.view.bounds.size.width, + y: tapPoint.y / self.view.bounds.size.height) + + if let device = self.device { + do { + try device.lockForConfiguration() + if device.isFocusPointOfInterestSupported { + device.focusPointOfInterest = focusPoint + } else { + print("Focus point of interest not supported.") + } + if self.isCrazyMode { + if device.isFocusModeSupported(.locked) { + device.focusMode = .locked + } else { + print("Locked focus not supported.") + } + if !self.isCrazyModeStarted { + self.isCrazyModeStarted = true + DispatchQueue.main.async(execute: { () -> Void in + self.autoUpdateLensPosition() + }) + } + } else { + if device.isFocusModeSupported(.continuousAutoFocus) { + device.focusMode = .continuousAutoFocus + } else if device.isFocusModeSupported(.autoFocus) { + device.focusMode = .autoFocus + } else { + print("Auto focus not supported.") + } + } + if device.isAutoFocusRangeRestrictionSupported { + device.autoFocusRangeRestriction = .none + } else { + print("Auto focus range restriction not supported.") + } + device.unlockForConfiguration() + self.focusMarkLayer.point = tapPoint + } catch _ { + } + } + + if let tapHandler = self.tapHandler { + tapHandler(tapPoint) + } + } + + func onApplicationWillEnterForeground() { + self.session.startRunning() + } + + func onApplicationDidEnterBackground() { + self.session.stopRunning() + } + + // MARK: Deinitialization + + deinit { + print("RSCodeReaderViewController deinit") + } + + // MARK: View lifecycle + + override open func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if let videoPreviewLayer = self.videoPreviewLayer { + let videoOrientation = RSCodeReaderViewController.interfaceOrientationToVideoOrientation(UIApplication.shared.statusBarOrientation) + if videoPreviewLayer.connection.isVideoOrientationSupported + && videoPreviewLayer.connection.videoOrientation != videoOrientation { + videoPreviewLayer.connection.videoOrientation = videoOrientation + } + } + } + + override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + let frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) + if let videoPreviewLayer = self.videoPreviewLayer { + videoPreviewLayer.frame = frame + } + self.focusMarkLayer.frame = frame + self.cornersLayer.frame = frame + } + + override open func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = UIColor.clear + + var error : NSError? + let input: AVCaptureDeviceInput! + do { + input = try AVCaptureDeviceInput(device: self.device) + } catch let error1 as NSError { + error = error1 + input = nil + } + if let error = error { + print(error.description) + return + } + + if let device = self.device { + do { + try device.lockForConfiguration() + if (self.device?.isFocusModeSupported(.continuousAutoFocus))! { + self.device?.focusMode = .continuousAutoFocus + } + if (self.device?.isAutoFocusRangeRestrictionSupported)! { + self.device?.autoFocusRangeRestriction = .near + } + self.device?.unlockForConfiguration() + } catch _ { + } + } + + if self.session.canAddInput(input) { + self.session.addInput(input) + } + + self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session) + if let videoPreviewLayer = self.videoPreviewLayer { + videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill + videoPreviewLayer.frame = self.view.bounds + self.view.layer.addSublayer(videoPreviewLayer) + } + + let queue = DispatchQueue(label: "com.pdq.rsbarcodes.metadata", attributes: DispatchQueue.Attributes.concurrent) + self.output.setMetadataObjectsDelegate(self, queue: queue) + if self.session.canAddOutput(self.output) { + self.session.addOutput(self.output) + self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes + } + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(RSCodeReaderViewController.onTap(_:))) + self.view.addGestureRecognizer(tapGestureRecognizer) + + self.focusMarkLayer.frame = self.view.bounds + self.view.layer.addSublayer(self.focusMarkLayer) + + self.cornersLayer.frame = self.view.bounds + self.view.layer.addSublayer(self.cornersLayer) + } + + override open func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + NotificationCenter.default.addObserver(self, selector: #selector(RSCodeReaderViewController.onApplicationWillEnterForeground), name:NSNotification.Name.UIApplicationWillEnterForeground, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(RSCodeReaderViewController.onApplicationDidEnterBackground), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil) + + self.session.startRunning() + } + + override open func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil) + + self.session.stopRunning() + } + + // MARK: AVCaptureMetadataOutputObjectsDelegate + + open func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { + var barcodeObjects : Array = [] + var cornersArray : Array<[AnyObject]> = [] + for metadataObject : AnyObject in metadataObjects { + if let videoPreviewLayer = self.videoPreviewLayer { + let transformedMetadataObject = videoPreviewLayer.transformedMetadataObject(for: metadataObject as! AVMetadataObject) + if transformedMetadataObject.isKind(of: AVMetadataMachineReadableCodeObject.self) { + let barcodeObject = transformedMetadataObject as! AVMetadataMachineReadableCodeObject + barcodeObjects.append(barcodeObject) + cornersArray.append(barcodeObject.corners) + } + } + } + + self.cornersLayer.cornersArray = cornersArray + + if barcodeObjects.count > 0 { + if let barcodesHandler = self.barcodesHandler { + barcodesHandler(barcodeObjects) + } + } + + DispatchQueue.main.async(execute: { () -> Void in + if let ticker = self.ticker { + ticker.invalidate() + } + self.ticker = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(RSCodeReaderViewController.onTick), userInfo: nil, repeats: true) + }) + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSCornersLayer.swift b/Pods/RSBarcodes_Swift/Source/RSCornersLayer.swift new file mode 100644 index 0000000..94814f6 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSCornersLayer.swift @@ -0,0 +1,59 @@ +// +// RSCornersLayer.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/13/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit +import QuartzCore + +open class RSCornersLayer: CALayer { + open var strokeColor = UIColor.green.cgColor + open var strokeWidth: CGFloat = 2 + open var drawingCornersArray: Array> = [] + open var cornersArray: Array<[AnyObject]> = [] { + willSet { + DispatchQueue.main.async(execute: { + self.setNeedsDisplay() + }) + } + } + + override open func draw(in ctx: CGContext) { + objc_sync_enter(self) + + ctx.saveGState() + + ctx.setShouldAntialias(true) + ctx.setAllowsAntialiasing(true) + ctx.setFillColor(UIColor.clear.cgColor) + ctx.setStrokeColor(self.strokeColor) + ctx.setLineWidth(self.strokeWidth) + + for corners in self.cornersArray { + for i in 0...corners.count { + var idx = i + if i == corners.count { + idx = 0 + } + let dict = corners[idx] as! NSDictionary + + let x = CGFloat((dict.object(forKey: "X") as! NSNumber).floatValue) + let y = CGFloat((dict.object(forKey: "Y") as! NSNumber).floatValue) + if i == 0 { + ctx.move(to: CGPoint(x: x, y: y)) + } else { + ctx.addLine(to: CGPoint(x: x, y: y)) + } + } + } + + ctx.drawPath(using: CGPathDrawingMode.fillStroke) + + ctx.restoreGState() + + objc_sync_exit(self) + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSEANGenerator.swift b/Pods/RSBarcodes_Swift/Source/RSEANGenerator.swift new file mode 100644 index 0000000..0531938 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSEANGenerator.swift @@ -0,0 +1,127 @@ +// +// RSEANGenerator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/11/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +public let RSBarcodesTypeISBN13Code = "com.pdq.rsbarcodes.isbn13" +public let RSBarcodesTypeISSN13Code = "com.pdq.rsbarcodes.issn13" + +// http://blog.sina.com.cn/s/blog_4015406e0100bsqk.html +open class RSEANGenerator: RSAbstractCodeGenerator { + var length = 0 + // 'O' for odd and 'E' for even + let lefthandParities = [ + "OOOOOO", + "OOEOEE", + "OOEEOE", + "OOEEEO", + "OEOOEE", + "OEEOOE", + "OEEEOO", + "OEOEOE", + "OEOEEO", + "OEEOEO" + ] + // 'R' for right-hand + let parityEncodingTable = [ + ["O" : "0001101", "E" : "0100111", "R" : "1110010"], + ["O" : "0011001", "E" : "0110011", "R" : "1100110"], + ["O" : "0010011", "E" : "0011011", "R" : "1101100"], + ["O" : "0111101", "E" : "0100001", "R" : "1000010"], + ["O" : "0100011", "E" : "0011101", "R" : "1011100"], + ["O" : "0110001", "E" : "0111001", "R" : "1001110"], + ["O" : "0101111", "E" : "0000101", "R" : "1010000"], + ["O" : "0111011", "E" : "0010001", "R" : "1000100"], + ["O" : "0110111", "E" : "0001001", "R" : "1001000"], + ["O" : "0001011", "E" : "0010111", "R" : "1110100"] + ] + + init(length:Int) { + self.length = length + } + + override open func isValid(_ contents: String) -> Bool { + if super.isValid(contents) && self.length == contents.length() { + var sum_odd = 0 + var sum_even = 0 + + for i in 0..<(self.length - 1) { + let digit = Int(contents[i])! + if i % 2 == (self.length == 13 ? 0 : 1) { + sum_even += digit + } else { + sum_odd += digit + } + } + let checkDigit = (10 - (sum_even + sum_odd * 3) % 10) % 10 + return Int(contents[contents.length() - 1]) == checkDigit + } + return false + } + + override open func initiator() -> String { + return "101" + } + + override open func terminator() -> String { + return "101" + } + + func centerGuardPattern() -> String { + return "01010" + } + + override open func barcode(_ contents: String) -> String { + var lefthandParity = "OOOO" + var newContents = contents + if self.length == 13 { + lefthandParity = self.lefthandParities[Int(contents[0])!] + newContents = contents.substring(1, length: contents.length() - 1) + } + + var barcode = "" + for i in 0.. Bool { + // http://www.appsbarcode.com/ISBN.php + return super.isValid(contents) && contents.substring(0, length: 3) == "978" + } +} + +class RSISSN13Generator: RSEAN13Generator { + override func isValid(_ contents: String) -> Bool { + // http://www.appsbarcode.com/ISSN.php + return super.isValid(contents) && contents.substring(0, length: 3) == "977" + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSExtendedCode39Generator.swift b/Pods/RSBarcodes_Swift/Source/RSExtendedCode39Generator.swift new file mode 100644 index 0000000..f17c17a --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSExtendedCode39Generator.swift @@ -0,0 +1,144 @@ +// +// RSExtendedCode39Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/11/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +public let RSBarcodesTypeExtendedCode39Code = "com.pdq.rsbarcodes.code39.ext" + +// http://www.barcodesymbols.com/code39.htm +// http://www.barcodeisland.com/code39.phtml +open class RSExtendedCode39Generator: RSCode39Generator { + func encodeContents(_ contents: String) -> String { + var encodedContents = "" + for character in contents.characters { + let characterString = String(character) + switch characterString { + case "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z": + encodedContents += "+" + characterString.uppercased() + case "!": + encodedContents += "/A" + case "\"": + encodedContents += "/B" + case "#": + encodedContents += "/C" + case "$": + encodedContents += "/D" + case "%": + encodedContents += "/E" + case "&": + encodedContents += "/F" + case "'": + encodedContents += "/G" + case "(": + encodedContents += "/H" + case ")": + encodedContents += "/I" + case "*": + encodedContents += "/J" + case "+": + encodedContents += "/K" + case ",": + encodedContents += "/L" + // - -> /M better to use - + // . -> /N better to use . + case "/": + encodedContents += "/O" + // 0 -> /P better to use 0 + // 1 -> /Q better to use 1 + // 2 -> /R better to use 2 + // 3 -> /S better to use 3 + // 4 -> /T better to use 4 + // 5 -> /U better to use 5 + // 6 -> /V better to use 6 + // 7 -> /W better to use 7 + // 8 -> /X better to use 8 + // 9 -> /Y better to use 9 + case ":": + encodedContents += "/Z" + // ESC -> %A + // FS -> %B + // GS -> %C + // RS -> %D + // US -> %E + case ";": + encodedContents += "%F" + case "<": + encodedContents += "%G" + case "=": + encodedContents += "%H" + case ">": + encodedContents += "%I" + case "?": + encodedContents += "%J" + case "[": + encodedContents += "%K" + case "\\": + encodedContents += "%L" + case "]": + encodedContents += "%M" + case "^": + encodedContents += "%N" + case "_": + encodedContents += "%O" + case "{": + encodedContents += "%P" + case "|": + encodedContents += "%Q" + case "}": + encodedContents += "%R" + case "~": + encodedContents += "%S" + // DEL -> %T + // NUL -> %U + case "@": + encodedContents += "%V" + case "`": + encodedContents += "%W" + // SOH -> $A + // STX -> $B + // ETX -> $C + // EOT -> $D + // ENQ -> $E + // ACK -> $F + // BEL -> $G + // BS -> $H + case "\t": + encodedContents += "$I" + // LF -> $J + // VT -> $K + // FF -> $L + case "\n": + encodedContents += "$M" + // SO -> $N + // SI -> $O + // DLE -> $P + // DC1 -> $Q + // DC2 -> $R + // DC3 -> $S + // DC4 -> $T + // NAK -> $U + // SYN -> $V + // ETB -> $W + // CAN -> $X + // EM -> $Y + // SUB -> $Z + default: + encodedContents += characterString + } + } + return encodedContents + } + + override open func isValid(_ contents: String) -> Bool { + return contents.length() > 0 + } + + override open func barcode(_ contents: String) -> String { + return super.barcode(self.encodeContents(contents)) + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSFocusMarkLayer.swift b/Pods/RSBarcodes_Swift/Source/RSFocusMarkLayer.swift new file mode 100644 index 0000000..32f064d --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSFocusMarkLayer.swift @@ -0,0 +1,84 @@ +// +// RSFocusMarkLayer.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/13/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit +import QuartzCore + +open class RSFocusMarkLayer: CALayer { + // Use camera.app's focus mark size as default + open var size = CGSize(width: 76, height: 76) + // Use camera.app's focus mark sight as default + open var sight: CGFloat = 6 + // Use camera.app's focus mark color as default + open var strokeColor = UIColor(rgba: "#ffcc00").cgColor + open var strokeWidth: CGFloat = 1 + open var delay: CFTimeInterval = 1 + open var canDraw = false + + deinit { + print("RSFocusMarkLayer deinit") + } + + open var point : CGPoint = CGPoint(x: 0, y: 0) { + didSet { + DispatchQueue.main.async(execute: { + self.canDraw = true + self.setNeedsDisplay() + }) + + let when = DispatchTime.now() + Double(Int64(self.delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) + DispatchQueue.main.asyncAfter(deadline: when, execute: { + self.canDraw = false + self.setNeedsDisplay() + }) + } + } + + override open func draw(in ctx: CGContext) { + if !self.canDraw { + return + } + + ctx.saveGState() + + ctx.setShouldAntialias(true) + ctx.setAllowsAntialiasing(true) + ctx.setFillColor(UIColor.clear.cgColor) + ctx.setStrokeColor(self.strokeColor) + ctx.setLineWidth(self.strokeWidth) + + // Rect + ctx.stroke(CGRect(x: self.point.x - self.size.width / 2.0, y: self.point.y - self.size.height / 2.0, width: self.size.width, height: self.size.height)) + + // Focus + for i in 0..<4 { + var endPoint: CGPoint + switch i { + case 0: + ctx.move(to: CGPoint(x: self.point.x, y: self.point.y - self.size.height / 2.0)) + endPoint = CGPoint(x: self.point.x, y: self.point.y - self.size.height / 2.0 + self.sight) + case 1: + ctx.move(to: CGPoint(x: self.point.x, y: self.point.y + self.size.height / 2.0)) + endPoint = CGPoint(x: self.point.x, y: self.point.y + self.size.height / 2.0 - self.sight) + case 2: + ctx.move(to: CGPoint(x: self.point.x - self.size.width / 2.0, y: self.point.y)) + endPoint = CGPoint(x: self.point.x - self.size.width / 2.0 + self.sight, y: self.point.y) + case 3: + ctx.move(to: CGPoint(x: self.point.x + self.size.width / 2.0, y: self.point.y)) + endPoint = CGPoint(x: self.point.x + self.size.width / 2.0 - self.sight, y: self.point.y) + default: + endPoint = CGPoint(x: 0, y: 0) + } + ctx.addLine(to: CGPoint(x: endPoint.x, y: endPoint.y)) + } + + ctx.drawPath(using: CGPathDrawingMode.fillStroke) + + ctx.restoreGState() + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSITF14Generator.swift b/Pods/RSBarcodes_Swift/Source/RSITF14Generator.swift new file mode 100644 index 0000000..5d229b2 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSITF14Generator.swift @@ -0,0 +1,17 @@ +// +// RSITF14Generator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/13/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +// http://www.gs1au.org/assets/documents/info/user_manuals/barcode_technical_details/ITF_14_Barcode_Structure.pdf +// http://www.barcodeisland.com/int2of5.phtml +open class RSITF14Generator: RSITFGenerator { + override open func isValid(_ contents: String) -> Bool { + return super.isValid(contents) && contents.length() == 14 + } +} diff --git a/Pods/RSBarcodes_Swift/Source/RSITFGenerator.swift b/Pods/RSBarcodes_Swift/Source/RSITFGenerator.swift new file mode 100644 index 0000000..2e19160 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/RSITFGenerator.swift @@ -0,0 +1,65 @@ +// +// RSITFGenerator.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/11/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +// http://www.barcodeisland.com/int2of5.phtml +open class RSITFGenerator: RSAbstractCodeGenerator { + let ITF_CHARACTER_ENCODINGS = [ + "00110", + "10001", + "01001", + "11000", + "00101", + "10100", + "01100", + "00011", + "10010", + "01010", + ] + + override open func isValid(_ contents: String) -> Bool { + return super.isValid(contents) && contents.length() % 2 == 0 + } + + override open func initiator() -> String { + return "1010" + } + + override open func terminator() -> String { + return "1101" + } + + override open func barcode(_ contents: String) -> String { + var barcode = "" + for i in 0.. String { + let code = contents.substring(1, length: contents.length() - 2) + let lastDigit = Int(code[code.length() - 1])! + var insertDigits = "0000" + var upc_a = "" + switch lastDigit { + case 0...2: + upc_a += code.substring(0, length: 2) + String(lastDigit) + insertDigits + code.substring(2, length: 3) + case 3:lastDigit + insertDigits = "00000" + upc_a += code.substring(0, length: 3) + insertDigits + code.substring(3, length: 2) + case 4:lastDigit + insertDigits = "00000" + upc_a += code.substring(0, length: 4) + insertDigits + code.substring(4, length: 1) + default: + upc_a += code.substring(0, length: 5) + insertDigits + String(lastDigit) + } + return "00" + upc_a + } + + override open func isValid(_ contents: String) -> Bool { + return super.isValid(contents) + && contents.length() == 8 + && Int(contents[0])! == 0 + && contents[contents.length() - 1] == self.checkDigit(contents) + } + + override open func initiator() -> String { + return "101" + } + + override open func terminator() -> String { + return "010101" + } + + override open func barcode(_ contents: String) -> String { + let checkValue = Int(contents[contents.length() - 1])! + let sequence = UPCE_SEQUENCES[checkValue] + var barcode = "" + for i in 1.. String { + /* + UPC-A check digit is calculated using standard Mod10 method. Here outlines the steps to calculate UPC-A check digit: + + From the right to left, start with odd position, assign the odd/even position to each digit. + Sum all digits in odd position and multiply the result by 3. + Sum all digits in even position. + Sum the results of step 3 and step 4. + divide the result of step 4 by 10. The check digit is the number which adds the remainder to 10. + */ + let upc_a = self.convert2UPC_A(contents) + var sum_odd = 0 + var sum_even = 0 + for i in 0.. UIImage? { + var codeGenerator: RSCodeGenerator? + switch machineReadableCodeObjectType { + case AVMetadataObjectTypeQRCode, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeAztecCode: + return RSAbstractCodeGenerator.generateCode(contents, filterName: RSAbstractCodeGenerator.filterName(machineReadableCodeObjectType)) + case AVMetadataObjectTypeCode39Code: + codeGenerator = RSCode39Generator() + case AVMetadataObjectTypeCode39Mod43Code: + codeGenerator = RSCode39Mod43Generator() + case AVMetadataObjectTypeEAN8Code: + codeGenerator = RSEAN8Generator() + case AVMetadataObjectTypeEAN13Code: + codeGenerator = RSEAN13Generator() + case AVMetadataObjectTypeInterleaved2of5Code: + codeGenerator = RSITFGenerator() + case AVMetadataObjectTypeITF14Code: + codeGenerator = RSITF14Generator() + case AVMetadataObjectTypeUPCECode: + codeGenerator = RSUPCEGenerator() + case AVMetadataObjectTypeCode93Code: + codeGenerator = RSCode93Generator() + // iOS 8 included, but my implementation's performance is better :) + case AVMetadataObjectTypeCode128Code: + if self.isBuiltInCode128GeneratorSelected { + return RSAbstractCodeGenerator.generateCode(contents, filterName: RSAbstractCodeGenerator.filterName(machineReadableCodeObjectType)) + } else { + codeGenerator = RSCode128Generator() + } + case RSBarcodesTypeISBN13Code: + codeGenerator = RSISBN13Generator() + case RSBarcodesTypeISSN13Code: + codeGenerator = RSISSN13Generator() + case RSBarcodesTypeExtendedCode39Code: + codeGenerator = RSExtendedCode39Generator() + default: + print("No code generator selected.") + } + + if codeGenerator != nil { + codeGenerator!.fillColor = self.fillColor + codeGenerator!.strokeColor = self.strokeColor + return codeGenerator!.generateCode(contents, machineReadableCodeObjectType: machineReadableCodeObjectType) + } else { + return nil + } + } + + open func generateCode(_ machineReadableCodeObject: AVMetadataMachineReadableCodeObject) -> UIImage? { + return self.generateCode(machineReadableCodeObject.stringValue, machineReadableCodeObjectType: machineReadableCodeObject.type) + } +} + +let UnifiedCodeGeneratorSharedInstance = RSUnifiedCodeGenerator() diff --git a/Pods/RSBarcodes_Swift/Source/StringExtension.swift b/Pods/RSBarcodes_Swift/Source/StringExtension.swift new file mode 100644 index 0000000..6636311 --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/StringExtension.swift @@ -0,0 +1,42 @@ +// +// Ext.swift +// RSBarcodesSample +// +// Created by R0CKSTAR on 6/10/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +extension String { + func length() -> Int { + return self.characters.count + } + + func trim() -> String { + return self.trimmingCharacters(in: .whitespacesAndNewlines) + } + + func substring(_ location:Int, length:Int) -> String! { + return (self as NSString).substring(with: NSMakeRange(location, length)) + } + + subscript(index: Int) -> String! { + get { + return self.substring(index, length: 1) + } + } + + func location(_ other: String) -> Int { + return (self as NSString).range(of: other).location + } + + func contains(_ other: String) -> Bool { + return (self as NSString).contains(other) + } + + // http://stackoverflow.com/questions/6644004/how-to-check-if-nsstring-is-contains-a-numeric-value + func isNumeric() -> Bool { + return (self as NSString).rangeOfCharacter(from: CharacterSet.decimalDigits.inverted).location == NSNotFound + } +} diff --git a/Pods/RSBarcodes_Swift/Source/UIColorExtension.swift b/Pods/RSBarcodes_Swift/Source/UIColorExtension.swift new file mode 100644 index 0000000..257abda --- /dev/null +++ b/Pods/RSBarcodes_Swift/Source/UIColorExtension.swift @@ -0,0 +1,54 @@ +// +// UIColorExtension.swift +// UIColor-Hex-Swift +// +// Created by R0CKSTAR on 6/13/14. +// Copyright (c) 2014 P.D.Q. All rights reserved. +// + +import UIKit + +extension UIColor { + public convenience init(rgba: String) { + var red: CGFloat = 0.0 + var green: CGFloat = 0.0 + var blue: CGFloat = 0.0 + var alpha: CGFloat = 1.0 + + if rgba.hasPrefix("#") { + let index = rgba.characters.index(rgba.startIndex, offsetBy: 1) + let hex = rgba.substring(from: index) + let scanner = Scanner(string: hex) + var hexValue: CUnsignedLongLong = 0 + if scanner.scanHexInt64(&hexValue) { + switch (hex.characters.count) { + case 3: + red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 + green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 + blue = CGFloat(hexValue & 0x00F) / 15.0 + case 4: + red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 + green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 + blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 + alpha = CGFloat(hexValue & 0x000F) / 15.0 + case 6: + red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 + green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 + blue = CGFloat(hexValue & 0x0000FF) / 255.0 + case 8: + red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 + green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 + blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 + alpha = CGFloat(hexValue & 0x000000FF) / 255.0 + default: + print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8") + } + } else { + print("Scan hex error") + } + } else { + print("Invalid RGB string, missing '#' as prefix") + } + self.init(red:red, green:green, blue:blue, alpha:alpha) + } +} diff --git a/Pods/Realm/LICENSE b/Pods/Realm/LICENSE new file mode 100644 index 0000000..8a84f2b --- /dev/null +++ b/Pods/Realm/LICENSE @@ -0,0 +1,244 @@ +TABLE OF CONTENTS + +1. Apache License version 2.0 +2. Realm Components +3. Export Compliance + +------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +REALM COMPONENTS + +This software contains components with separate copyright and license terms. +Your use of these components is subject to the terms and conditions of the +following licenses. + +For the Realm Core component + + Realm Core Binary License + + Copyright (c) 2011-2016 Realm Inc All rights reserved + + Redistribution and use in binary form, with or without modification, is + permitted provided that the following conditions are met: + + 1. You agree not to attempt to decompile, disassemble, reverse engineer or + otherwise discover the source code from which the binary code was derived. + You may, however, access and obtain a separate license for most of the + source code from which this Software was created, at + http://realm.io/pricing/. + + 2. 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. + + 3. Neither the name of the copyright holder 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. + +EXPORT COMPLIANCE + +You understand that the Software may contain cryptographic functions that may be +subject to export restrictions, and you represent and warrant that you are not +(i) located in a jurisdiction that is subject to United States economic +sanctions (“Prohibited Jurisdiction”), including Cuba, Iran, North Korea, +Sudan, Syria or the Crimea region, (ii) a person listed on any U.S. government +blacklist (to include the List of Specially Designated Nationals and Blocked +Persons or the Consolidated Sanctions List administered by the U.S. Department +of the Treasury’s Office of Foreign Assets Control, or the Denied Persons List +or Entity List administered by the U.S. Department of Commerce) +(“Sanctioned Person”), or (iii) controlled or 50% or more owned by a Sanctioned +Person. + +You agree to comply with all export, re-export and import restrictions and +regulations of the U.S. Department of Commerce or other agency or authority of +the United States or other applicable countries. You also agree not to transfer, +or authorize the transfer of, directly or indirectly, of the Software to any +Prohibited Jurisdiction, or otherwise in violation of any such restrictions or +regulations. diff --git a/Pods/Realm/README.md b/Pods/Realm/README.md new file mode 100644 index 0000000..25fbd78 --- /dev/null +++ b/Pods/Realm/README.md @@ -0,0 +1,73 @@ +![Realm](https://github.com/realm/realm-cocoa/raw/master/logo.png) + +Realm is a mobile database that runs directly inside phones, tablets or wearables. +This repository holds the source code for the iOS, macOS, tvOS & watchOS versions of Realm Swift & Realm Objective-C. + +## Features + +* **Mobile-first:** Realm is the first database built from the ground up to run directly inside phones, tablets and wearables. +* **Simple:** Data is directly [exposed as objects](https://realm.io/docs/objc/latest/#models) and [queryable by code](https://realm.io/docs/objc/latest/#queries), removing the need for ORM's riddled with performance & maintenance issues. Most of our users pick it up intuitively, getting simple apps up & running in minutes. +* **Modern:** Realm supports relationships, generics, vectorization and even Swift. +* **Fast:** Realm is faster than even raw SQLite on common operations, while maintaining an extremely rich feature set. + +## Getting Started + +Please see the detailed instructions in our docs to add [Realm Objective-C](https://realm.io/docs/objc/latest/#installation) _or_ [Realm Swift](https://realm.io/docs/swift/latest/#installation) to your Xcode project. + +## Documentation + +### Realm Objective-C + +The documentation can be found at [realm.io/docs/objc/latest](https://realm.io/docs/objc/latest). +The API reference is located at [realm.io/docs/objc/latest/api](https://realm.io/docs/objc/latest/api). + +### Realm Swift + +The documentation can be found at [realm.io/docs/swift/latest](https://realm.io/docs/swift/latest). +The API reference is located at [realm.io/docs/swift/latest/api](https://realm.io/docs/swift/latest/api). + +## Getting Help + +- **Need help with your code?**: Look for previous questions on the [#realm tag](https://stackoverflow.com/questions/tagged/realm?sort=newest) — or [ask a new question](https://stackoverflow.com/questions/ask?tags=realm). We actively monitor & answer questions on SO! +- **Have a bug to report?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). If possible, include the version of Realm, a full log, the Realm file, and a project that shows the issue. +- **Have a feature request?** [Open an issue](https://github.com/realm/realm-cocoa/issues/new). Tell us what the feature should do, and why you want the feature. +- Sign up for our [**Community Newsletter**](http://eepurl.com/VEKCn) to get regular tips, learn about other use-cases and get alerted of blogposts and tutorials about Realm. + +## Building Realm + +In case you don't want to use the precompiled version, you can build Realm yourself from source. + +Prerequisites: + +* Building Realm requires Xcode 7.3.1 or Xcode 8.0. +* Building Realm documentation requires [jazzy](https://github.com/realm/jazzy) + +Once you have all the necessary prerequisites, building Realm.framework just takes a single command: `sh build.sh build`. You'll need an internet connection the first time you build Realm to download the core binary. + +Run `sh build.sh help` to see all the actions you can perform (build ios/osx, generate docs, test, etc.). + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for more details! + +This project adheres to the [Contributor Covenant Code of Conduct](https://realm.io/conduct). +By participating, you are expected to uphold this code. Please report +unacceptable behavior to [info@realm.io](mailto:info@realm.io). + +## License + +Realm Objective-C & Realm Swift are published under the Apache 2.0 license. +Realm Core is also published under the Apache 2.0 license and is available +[here](https://github.com/realm/realm-core). + +**This product is not being made available to any person located in Cuba, Iran, +North Korea, Sudan, Syria or the Crimea region, or to any other person that is +not eligible to receive the product under U.S. law.** + +## Feedback + +**_If you use Realm and are happy with it, all we ask is that you please consider sending out a tweet mentioning [@realm](https://twitter.com/realm) or email [help@realm.io](mailto:help@realm.io) to share your thoughts!_** + +**_And if you don't like it, please let us know what you would like improved, so we can fix it!_** + +![analytics](https://ga-beacon.appspot.com/UA-50247013-2/realm-cocoa/README?pixel) diff --git a/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp b/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp new file mode 100644 index 0000000..12fca83 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/collection_notifications.cpp @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "collection_notifications.hpp" + +#include "impl/collection_notifier.hpp" + +using namespace realm; +using namespace realm::_impl; + +NotificationToken::NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token) +: m_notifier(std::move(notifier)), m_token(token) +{ +} + +NotificationToken::~NotificationToken() +{ + // m_notifier itself (and not just the pointed-to thing) needs to be accessed + // atomically to ensure that there are no data races when the token is + // destroyed after being modified on a different thread. + // This is needed despite the token not being thread-safe in general as + // users find it very surprising for obj-c objects to care about what + // thread they are deallocated on. + if (auto notifier = m_notifier.exchange({})) { + notifier->remove_callback(m_token); + } +} + +NotificationToken::NotificationToken(NotificationToken&&) = default; + +NotificationToken& NotificationToken::operator=(realm::NotificationToken&& rgt) +{ + if (this != &rgt) { + if (auto notifier = m_notifier.exchange({})) { + notifier->remove_callback(m_token); + } + m_notifier = std::move(rgt.m_notifier); + m_token = rgt.m_token; + } + return *this; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp new file mode 100644 index 0000000..5195a3e --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/apple/external_commit_helper.cpp @@ -0,0 +1,226 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/external_commit_helper.hpp" + +#include "impl/realm_coordinator.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace realm; +using namespace realm::_impl; + +namespace { +// Write a byte to a pipe to notify anyone waiting for data on the pipe +void notify_fd(int fd, int read_fd) +{ + while (true) { + char c = 0; + ssize_t ret = write(fd, &c, 1); + if (ret == 1) { + break; + } + + // If the pipe's buffer is full, we need to read some of the old data in + // it to make space. We don't just read in the code waiting for + // notifications so that we can notify multiple waiters with a single + // write. + assert(ret == -1 && errno == EAGAIN); + char buff[1024]; + read(read_fd, buff, sizeof buff); + } +} +} // anonymous namespace + +void ExternalCommitHelper::FdHolder::close() +{ + if (m_fd != -1) { + ::close(m_fd); + } + m_fd = -1; +} + +// Inter-thread and inter-process notifications of changes are done using a +// named pipe in the filesystem next to the Realm file. Everyone who wants to be +// notified of commits waits for data to become available on the pipe, and anyone +// who commits a write transaction writes data to the pipe after releasing the +// write lock. Note that no one ever actually *reads* from the pipe: the data +// actually written is meaningless, and trying to read from a pipe from multiple +// processes at once is fraught with race conditions. + +// When a RLMRealm instance is created, we add a CFRunLoopSource to the current +// thread's runloop. On each cycle of the run loop, the run loop checks each of +// its sources for work to do, which in the case of CFRunLoopSource is just +// checking if CFRunLoopSourceSignal has been called since the last time it ran, +// and if so invokes the function pointer supplied when the source is created, +// which in our case just invokes `[realm handleExternalChange]`. + +// Listening for external changes is done using kqueue() on a background thread. +// kqueue() lets us efficiently wait until the amount of data which can be read +// from one or more file descriptors has changed, and tells us which of the file +// descriptors it was that changed. We use this to wait on both the shared named +// pipe, and a local anonymous pipe. When data is written to the named pipe, we +// signal the runloop source and wake up the target runloop, and when data is +// written to the anonymous pipe the background thread removes the runloop +// source from the runloop and and shuts down. +ExternalCommitHelper::ExternalCommitHelper(RealmCoordinator& parent) +: m_parent(parent) +{ + m_kq = kqueue(); + if (m_kq == -1) { + throw std::system_error(errno, std::system_category()); + } + +#if !TARGET_OS_TV + auto path = parent.get_path() + ".note"; + + // Create and open the named pipe + int ret = mkfifo(path.c_str(), 0600); + if (ret == -1) { + int err = errno; + if (err == ENOTSUP) { + // Filesystem doesn't support named pipes, so try putting it in tmp instead + // Hash collisions are okay here because they just result in doing + // extra work, as opposed to correctness problems + std::ostringstream ss; + ss << getenv("TMPDIR"); + ss << "realm_" << std::hash()(path) << ".note"; + path = ss.str(); + ret = mkfifo(path.c_str(), 0600); + err = errno; + } + // the fifo already existing isn't an error + if (ret == -1 && err != EEXIST) { + throw std::system_error(err, std::system_category()); + } + } + + m_notify_fd = open(path.c_str(), O_RDWR); + if (m_notify_fd == -1) { + throw std::system_error(errno, std::system_category()); + } + + // Make writing to the pipe return -1 when the pipe's buffer is full + // rather than blocking until there's space available + ret = fcntl(m_notify_fd, F_SETFL, O_NONBLOCK); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + +#else // !TARGET_OS_TV + + // tvOS does not support named pipes, so use an anonymous pipe instead + int notification_pipe[2]; + int ret = pipe(notification_pipe); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + + m_notify_fd = notification_pipe[0]; + m_notify_fd_write = notification_pipe[1]; + +#endif // TARGET_OS_TV + + // Create the anonymous pipe for shutdown notifications + int shutdown_pipe[2]; + ret = pipe(shutdown_pipe); + if (ret == -1) { + throw std::system_error(errno, std::system_category()); + } + + m_shutdown_read_fd = shutdown_pipe[0]; + m_shutdown_write_fd = shutdown_pipe[1]; + + m_thread = std::async(std::launch::async, [=] { + try { + listen(); + } + catch (std::exception const& e) { + fprintf(stderr, "uncaught exception in notifier thread: %s: %s\n", typeid(e).name(), e.what()); + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread: %s: %s", typeid(e).name(), e.what()); + throw; + } + catch (...) { + fprintf(stderr, "uncaught exception in notifier thread\n"); + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "uncaught exception in notifier thread"); + throw; + } + }); +} + +ExternalCommitHelper::~ExternalCommitHelper() +{ + notify_fd(m_shutdown_write_fd, m_shutdown_read_fd); + m_thread.wait(); // Wait for the thread to exit +} + +void ExternalCommitHelper::listen() +{ + pthread_setname_np("RLMRealm notification listener"); + + // Set up the kqueue + // EVFILT_READ indicates that we care about data being available to read + // on the given file descriptor. + // EV_CLEAR makes it wait for the amount of data available to be read to + // change rather than just returning when there is any data to read. + struct kevent ke[2]; + EV_SET(&ke[0], m_notify_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); + EV_SET(&ke[1], m_shutdown_read_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); + int ret = kevent(m_kq, ke, 2, nullptr, 0, nullptr); + assert(ret == 0); + + while (true) { + struct kevent event; + // Wait for data to become on either fd + // Return code is number of bytes available or -1 on error + ret = kevent(m_kq, nullptr, 0, &event, 1, nullptr); + assert(ret >= 0); + if (ret == 0) { + // Spurious wakeup; just wait again + continue; + } + + // Check which file descriptor had activity: if it's the shutdown + // pipe, then someone called -stop; otherwise it's the named pipe + // and someone committed a write transaction + if (event.ident == (uint32_t)m_shutdown_read_fd) { + return; + } + assert(event.ident == (uint32_t)m_notify_fd); + + m_parent.on_change(); + } +} + +void ExternalCommitHelper::notify_others() +{ + if (m_notify_fd_write != -1) { + notify_fd(m_notify_fd_write, m_notify_fd); + } + else { + notify_fd(m_notify_fd, m_notify_fd); + } +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp new file mode 100644 index 0000000..b12128d --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/apple/keychain_helper.cpp @@ -0,0 +1,115 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/apple/keychain_helper.hpp" + +#include "util/format.hpp" + +#include +#include + +#include + +#include + +using realm::util::CFPtr; +using realm::util::adoptCF; + +namespace realm { +namespace keychain { + +KeychainAccessException::KeychainAccessException(int32_t error_code) +: std::runtime_error(util::format("Keychain returned unexpected status code: %1", error_code)) { } + +CFPtr convert_string(const std::string& string); +CFPtr build_search_dictionary(const std::string& account, const std::string& service, + util::Optional group); + +CFPtr convert_string(const std::string& string) +{ + auto result = adoptCF(CFStringCreateWithBytes(nullptr, reinterpret_cast(string.data()), + string.size(), kCFStringEncodingASCII, false)); + if (!result) { + throw std::bad_alloc(); + } + return result; +} + +CFPtr build_search_dictionary(const std::string& account, const std::string& service, + __unused util::Optional group) +{ + auto d = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + if (!d) { + throw std::bad_alloc(); + } + CFDictionaryAddValue(d.get(), kSecClass, kSecClassGenericPassword); + CFDictionaryAddValue(d.get(), kSecReturnData, kCFBooleanTrue); + CFDictionaryAddValue(d.get(), kSecAttrAccessible, kSecAttrAccessibleAlways); + CFDictionaryAddValue(d.get(), kSecAttrAccount, convert_string(account).get()); + CFDictionaryAddValue(d.get(), kSecAttrService, convert_string(service).get()); +#if TARGET_IPHONE_SIMULATOR +#else + if (group) { + CFDictionaryAddValue(d.get(), kSecAttrAccessGroup, convert_string(*group).get()); + } +#endif + return d; +} + +std::vector metadata_realm_encryption_key() +{ + const size_t key_size = 64; + const std::string service = "io.realm.sync.keychain"; + const std::string account = "metadata"; + + auto search_dictionary = build_search_dictionary(account, service, none); + CFDataRef retained_key_data; + if (OSStatus status = SecItemCopyMatching(search_dictionary.get(), (CFTypeRef *)&retained_key_data)) { + if (status != errSecItemNotFound) { + throw KeychainAccessException(status); + } + + // Key was not found. Generate a new key, store it, and return it. + std::vector key(key_size); + arc4random_buf(key.data(), key_size); + auto key_data = adoptCF(CFDataCreate(nullptr, reinterpret_cast(key.data()), key_size)); + if (!key_data) { + throw std::bad_alloc(); + } + + CFDictionaryAddValue(search_dictionary.get(), kSecValueData, key_data.get()); + if (OSStatus status = SecItemAdd(search_dictionary.get(), nullptr)) { + throw KeychainAccessException(status); + } + + return key; + } + CFPtr key_data = adoptCF(retained_key_data); + + // Key was previously stored. Extract it. + if (key_size != CFDataGetLength(key_data.get())) { + throw std::runtime_error("Password stored in keychain was not expected size."); + } + + auto key_bytes = reinterpret_cast(CFDataGetBytePtr(key_data.get())); + return std::vector(key_bytes, key_bytes + key_size); +} + +} +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp new file mode 100644 index 0000000..41e30df --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/collection_change_builder.cpp @@ -0,0 +1,806 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/collection_change_builder.hpp" + +#include +#include + +#include + +using namespace realm; +using namespace realm::_impl; + +CollectionChangeBuilder::CollectionChangeBuilder(IndexSet deletions, + IndexSet insertions, + IndexSet modifications, + std::vector moves) +: CollectionChangeSet({std::move(deletions), std::move(insertions), std::move(modifications), {}, std::move(moves)}) +{ + for (auto&& move : this->moves) { + this->deletions.add(move.from); + this->insertions.add(move.to); + } +} + +void CollectionChangeBuilder::merge(CollectionChangeBuilder&& c) +{ + if (c.empty()) + return; + if (empty()) { + *this = std::move(c); + return; + } + + verify(); + c.verify(); + + // First update any old moves + if (!c.moves.empty() || !c.deletions.empty() || !c.insertions.empty()) { + auto it = std::remove_if(begin(moves), end(moves), [&](auto& old) { + // Check if the moved row was moved again, and if so just update the destination + auto it = find_if(begin(c.moves), end(c.moves), [&](auto const& m) { + return old.to == m.from; + }); + if (it != c.moves.end()) { + if (modifications.contains(it->from)) + c.modifications.add(it->to); + old.to = it->to; + *it = c.moves.back(); + c.moves.pop_back(); + ++it; + return false; + } + + // Check if the destination was deleted + // Removing the insert for this move will happen later + if (c.deletions.contains(old.to)) + return true; + + // Update the destination to adjust for any new insertions and deletions + old.to = c.insertions.shift(c.deletions.unshift(old.to)); + return false; + }); + moves.erase(it, end(moves)); + } + + // Ignore new moves of rows which were previously inserted (the implicit + // delete from the move will remove the insert) + if (!insertions.empty() && !c.moves.empty()) { + c.moves.erase(std::remove_if(begin(c.moves), end(c.moves), + [&](auto const& m) { return insertions.contains(m.from); }), + end(c.moves)); + } + + // Ensure that any previously modified rows which were moved are still modified + if (!modifications.empty() && !c.moves.empty()) { + for (auto const& move : c.moves) { + if (modifications.contains(move.from)) + c.modifications.add(move.to); + } + } + + // Update the source position of new moves to compensate for the changes made + // in the old changeset + if (!deletions.empty() || !insertions.empty()) { + for (auto& move : c.moves) + move.from = deletions.shift(insertions.unshift(move.from)); + } + + moves.insert(end(moves), begin(c.moves), end(c.moves)); + + // New deletion indices have been shifted by the insertions, so unshift them + // before adding + deletions.add_shifted_by(insertions, c.deletions); + + // Drop any inserted-then-deleted rows, then merge in new insertions + insertions.erase_at(c.deletions); + insertions.insert_at(c.insertions); + + clean_up_stale_moves(); + + modifications.erase_at(c.deletions); + modifications.shift_for_insert_at(c.insertions); + modifications.add(c.modifications); + + c = {}; + verify(); +} + +void CollectionChangeBuilder::clean_up_stale_moves() +{ + // Look for moves which are now no-ops, and remove them plus the associated + // insert+delete. Note that this isn't just checking for from == to due to + // that rows can also be shifted by other inserts and deletes + moves.erase(std::remove_if(begin(moves), end(moves), [&](auto const& move) { + if (move.from - deletions.count(0, move.from) != move.to - insertions.count(0, move.to)) + return false; + deletions.remove(move.from); + insertions.remove(move.to); + return true; + }), end(moves)); +} + +void CollectionChangeBuilder::parse_complete() +{ + moves.reserve(m_move_mapping.size()); + for (auto move : m_move_mapping) { + REALM_ASSERT_DEBUG(deletions.contains(move.second)); + REALM_ASSERT_DEBUG(insertions.contains(move.first)); + if (move.first == move.second) { + deletions.remove(move.second); + insertions.remove(move.first); + } + else + moves.push_back({move.second, move.first}); + } + m_move_mapping.clear(); + std::sort(begin(moves), end(moves), + [](auto const& a, auto const& b) { return a.from < b.from; }); +} + +void CollectionChangeBuilder::modify(size_t ndx) +{ + modifications.add(ndx); +} + +void CollectionChangeBuilder::insert(size_t index, size_t count, bool track_moves) +{ + modifications.shift_for_insert_at(index, count); + if (!track_moves) + return; + + insertions.insert_at(index, count); + + for (auto& move : moves) { + if (move.to >= index) + ++move.to; + } +} + +void CollectionChangeBuilder::erase(size_t index) +{ + modifications.erase_at(index); + size_t unshifted = insertions.erase_or_unshift(index); + if (unshifted != IndexSet::npos) + deletions.add_shifted(unshifted); + + for (size_t i = 0; i < moves.size(); ++i) { + auto& move = moves[i]; + if (move.to == index) { + moves.erase(moves.begin() + i); + --i; + } + else if (move.to > index) + --move.to; + } +} + +void CollectionChangeBuilder::clear(size_t old_size) +{ + if (old_size != std::numeric_limits::max()) { + for (auto range : deletions) + old_size += range.second - range.first; + for (auto range : insertions) + old_size -= range.second - range.first; + } + + modifications.clear(); + insertions.clear(); + moves.clear(); + m_move_mapping.clear(); + deletions.set(old_size); +} + +void CollectionChangeBuilder::move(size_t from, size_t to) +{ + REALM_ASSERT(from != to); + + bool updated_existing_move = false; + for (auto& move : moves) { + if (move.to != from) { + // Shift other moves if this row is moving from one side of them + // to the other + if (move.to >= to && move.to < from) + ++move.to; + else if (move.to <= to && move.to > from) + --move.to; + continue; + } + REALM_ASSERT(!updated_existing_move); + + // Collapse A -> B, B -> C into a single A -> C move + move.to = to; + updated_existing_move = true; + + insertions.erase_at(from); + insertions.insert_at(to); + } + + if (!updated_existing_move) { + auto shifted_from = insertions.erase_or_unshift(from); + insertions.insert_at(to); + + // Don't report deletions/moves for newly inserted rows + if (shifted_from != IndexSet::npos) { + shifted_from = deletions.add_shifted(shifted_from); + moves.push_back({shifted_from, to}); + } + } + + bool modified = modifications.contains(from); + modifications.erase_at(from); + + if (modified) + modifications.insert_at(to); + else + modifications.shift_for_insert_at(to); +} + +void CollectionChangeBuilder::move_over(size_t row_ndx, size_t last_row, bool track_moves) +{ + REALM_ASSERT(row_ndx <= last_row); + REALM_ASSERT(insertions.empty() || prev(insertions.end())->second - 1 <= last_row); + REALM_ASSERT(modifications.empty() || prev(modifications.end())->second - 1 <= last_row); + + if (row_ndx == last_row) { + if (track_moves) { + auto shifted_from = insertions.erase_or_unshift(row_ndx); + if (shifted_from != IndexSet::npos) + deletions.add_shifted(shifted_from); + m_move_mapping.erase(row_ndx); + } + modifications.remove(row_ndx); + return; + } + + bool modified = modifications.contains(last_row); + if (modified) { + modifications.remove(last_row); + modifications.add(row_ndx); + } + else + modifications.remove(row_ndx); + + if (!track_moves) + return; + + bool row_is_insertion = insertions.contains(row_ndx); + bool last_is_insertion = !insertions.empty() && prev(insertions.end())->second == last_row + 1; + REALM_ASSERT_DEBUG(insertions.empty() || prev(insertions.end())->second <= last_row + 1); + + // Collapse A -> B, B -> C into a single A -> C move + bool last_was_already_moved = false; + if (last_is_insertion) { + auto it = m_move_mapping.find(last_row); + if (it != m_move_mapping.end() && it->first == last_row) { + m_move_mapping[row_ndx] = it->second; + m_move_mapping.erase(it); + last_was_already_moved = true; + } + } + + // Remove moves to the row being deleted + if (row_is_insertion && !last_was_already_moved) { + auto it = m_move_mapping.find(row_ndx); + if (it != m_move_mapping.end() && it->first == row_ndx) + m_move_mapping.erase(it); + } + + // Don't report deletions/moves if last_row is newly inserted + if (last_is_insertion) { + insertions.remove(last_row); + } + // If it was previously moved, the unshifted source row has already been marked as deleted + else if (!last_was_already_moved) { + auto shifted_last_row = insertions.unshift(last_row); + shifted_last_row = deletions.add_shifted(shifted_last_row); + m_move_mapping[row_ndx] = shifted_last_row; + } + + // Don't mark the moved-over row as deleted if it was a new insertion + if (!row_is_insertion) { + deletions.add_shifted(insertions.unshift(row_ndx)); + insertions.add(row_ndx); + } + verify(); +} + +void CollectionChangeBuilder::swap(size_t ndx_1, size_t ndx_2, bool track_moves) +{ + REALM_ASSERT(ndx_1 != ndx_2); + // The order of the two indices doesn't matter semantically, but making them + // consistent simplifies the logic + if (ndx_1 > ndx_2) + std::swap(ndx_1, ndx_2); + + bool row_1_modified = modifications.contains(ndx_1); + bool row_2_modified = modifications.contains(ndx_2); + if (row_1_modified != row_2_modified) { + if (row_1_modified) { + modifications.remove(ndx_1); + modifications.add(ndx_2); + } + else { + modifications.remove(ndx_2); + modifications.add(ndx_1); + } + } + + if (!track_moves) + return; + + auto update_move = [&](auto existing_it, auto ndx_1, auto ndx_2) { + // update the existing move to ndx_2 to point at ndx_1 + auto original = existing_it->second; + m_move_mapping.erase(existing_it); + m_move_mapping[ndx_1] = original; + + // add a move from 1 -> 2 unless 1 was a new insertion + if (!insertions.contains(ndx_1)) { + m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1)); + insertions.add(ndx_1); + } + REALM_ASSERT_DEBUG(insertions.contains(ndx_2)); + }; + + auto move_1 = m_move_mapping.find(ndx_1); + auto move_2 = m_move_mapping.find(ndx_2); + bool have_move_1 = move_1 != end(m_move_mapping) && move_1->first == ndx_1; + bool have_move_2 = move_2 != end(m_move_mapping) && move_2->first == ndx_2; + if (have_move_1 && have_move_2) { + // both are already moves, so just swap the destinations + std::swap(move_1->second, move_2->second); + } + else if (have_move_1) { + update_move(move_1, ndx_2, ndx_1); + } + else if (have_move_2) { + update_move(move_2, ndx_1, ndx_2); + } + else { + // ndx_2 needs to be done before 1 to avoid incorrect shifting + if (!insertions.contains(ndx_2)) { + m_move_mapping[ndx_1] = deletions.add_shifted(insertions.unshift(ndx_2)); + insertions.add(ndx_2); + } + if (!insertions.contains(ndx_1)) { + m_move_mapping[ndx_2] = deletions.add_shifted(insertions.unshift(ndx_1)); + insertions.add(ndx_1); + } + } +} + +void CollectionChangeBuilder::subsume(size_t old_ndx, size_t new_ndx, bool track_moves) +{ + REALM_ASSERT(old_ndx != new_ndx); + + if (modifications.contains(old_ndx)) { + modifications.add(new_ndx); + } + + if (!track_moves) + return; + + REALM_ASSERT_DEBUG(insertions.contains(new_ndx)); + REALM_ASSERT_DEBUG(!m_move_mapping.count(new_ndx)); + + // If the source row was already moved, update the existing move + auto it = m_move_mapping.find(old_ndx); + if (it != m_move_mapping.end() && it->first == old_ndx) { + m_move_mapping[new_ndx] = it->second; + m_move_mapping.erase(it); + } + // otherwise add a new move unless it was a new insertion + else if (!insertions.contains(old_ndx)) { + m_move_mapping[new_ndx] = deletions.shift(insertions.unshift(old_ndx)); + } + + verify(); +} + +void CollectionChangeBuilder::verify() +{ +#ifdef REALM_DEBUG + for (auto&& move : moves) { + REALM_ASSERT(deletions.contains(move.from)); + REALM_ASSERT(insertions.contains(move.to)); + } +#endif +} + +namespace { +struct RowInfo { + size_t row_index; + size_t prev_tv_index; + size_t tv_index; + size_t shifted_tv_index; +}; + +// Calculates the insertions/deletions required for a query on a table without +// a sort, where `removed` includes the rows which were modified to no longer +// match the query (but not outright deleted rows, which are filtered out long +// before any of this logic), and `move_candidates` tracks the rows which may +// be the result of a move. +// +// This function is not strictly required, as calculate_moves_sorted() will +// produce correct results even for the scenarios where this function is used. +// However, this function has asymptotically better worst-case performance and +// extremely cheap best-case performance, and is guaranteed to produce a minimal +// diff when the only row moves are due to move_last_over(). +void calculate_moves_unsorted(std::vector& new_rows, IndexSet& removed, + IndexSet const& move_candidates, + CollectionChangeSet& changeset) +{ + // Here we track which row we expect to see, which in the absence of swap() + // is always the row immediately after the last row which was not moved. + size_t expected = 0; + for (auto& row : new_rows) { + if (row.shifted_tv_index == expected) { + ++expected; + continue; + } + + // We didn't find the row we were expecting to find, which means that + // either a row was moved forward to here, the row we were expecting was + // removed, or the row we were expecting moved back. + + // First check if this row even could have moved. If it can't, just + // treat it as a match and move on, and we'll handle the row we were + // expecting when we hit it later. + if (!move_candidates.contains(row.row_index)) { + expected = row.shifted_tv_index + 1; + continue; + } + + // Next calculate where we expect this row to be based on the insertions + // and removals (i.e. rows changed to not match the query), as it could + // be that the row actually ends up in this spot due to the rows before + // it being removed. + size_t calc_expected = row.tv_index - changeset.insertions.count(0, row.tv_index) + removed.count(0, row.prev_tv_index); + if (row.shifted_tv_index == calc_expected) { + expected = calc_expected + 1; + continue; + } + + // The row still isn't the expected one, so record it as a move + changeset.moves.push_back({row.prev_tv_index, row.tv_index}); + changeset.insertions.add(row.tv_index); + removed.add(row.prev_tv_index); + } +} + +class LongestCommonSubsequenceCalculator { +public: + // A pair of an index in the table and an index in the table view + struct Row { + size_t row_index; + size_t tv_index; + }; + + struct Match { + // The index in `a` at which this match begins + size_t i; + // The index in `b` at which this match begins + size_t j; + // The length of this match + size_t size; + // The number of rows in this block which were modified + size_t modified; + }; + std::vector m_longest_matches; + + LongestCommonSubsequenceCalculator(std::vector& a, std::vector& b, + size_t start_index, + IndexSet const& modifications) + : m_modified(modifications) + , a(a), b(b) + { + find_longest_matches(start_index, a.size(), + start_index, b.size()); + m_longest_matches.push_back({a.size(), b.size(), 0}); + } + +private: + IndexSet const& m_modified; + + // The two arrays of rows being diffed + // a is sorted by tv_index, b is sorted by row_index + std::vector &a, &b; + + // Find the longest matching range in (a + begin1, a + end1) and (b + begin2, b + end2) + // "Matching" is defined as "has the same row index"; the TV index is just + // there to let us turn an index in a/b into an index which can be reported + // in the output changeset. + // + // This is done with the O(N) space variant of the dynamic programming + // algorithm for longest common subsequence, where N is the maximum number + // of the most common row index (which for everything but linkview-derived + // TVs will be 1). + Match find_longest_match(size_t begin1, size_t end1, size_t begin2, size_t end2) + { + struct Length { + size_t j, len; + }; + // The length of the matching block for each `j` for the previously checked row + std::vector prev; + // The length of the matching block for each `j` for the row currently being checked + std::vector cur; + + // Calculate the length of the matching block *ending* at b[j], which + // is 1 if b[j - 1] did not match, and b[j - 1] + 1 otherwise. + auto length = [&](size_t j) -> size_t { + for (auto const& pair : prev) { + if (pair.j + 1 == j) + return pair.len + 1; + } + return 1; + }; + + // Iterate over each `j` which has the same row index as a[i] and falls + // within the range begin2 <= j < end2 + auto for_each_b_match = [&](size_t i, auto&& f) { + size_t ai = a[i].row_index; + // Find the TV indicies at which this row appears in the new results + // There should always be at least one (or it would have been + // filtered out earlier), but there can be multiple if there are dupes + auto it = lower_bound(begin(b), end(b), ai, + [](auto lft, auto rgt) { return lft.row_index < rgt; }); + REALM_ASSERT(it != end(b) && it->row_index == ai); + for (; it != end(b) && it->row_index == ai; ++it) { + size_t j = it->tv_index; + if (j < begin2) + continue; + if (j >= end2) + break; // b is sorted by tv_index so this can't transition from false to true + f(j); + } + }; + + Match best = {begin1, begin2, 0, 0}; + for (size_t i = begin1; i < end1; ++i) { + // prev = std::move(cur), but avoids discarding prev's heap allocation + cur.swap(prev); + cur.clear(); + + for_each_b_match(i, [&](size_t j) { + size_t size = length(j); + + cur.push_back({j, size}); + + // If the matching block ending at a[i] and b[j] is longer than + // the previous one, select it as the best + if (size > best.size) + best = {i - size + 1, j - size + 1, size, IndexSet::npos}; + // Given two equal-length matches, prefer the one with fewer modified rows + else if (size == best.size) { + if (best.modified == IndexSet::npos) + best.modified = m_modified.count(best.j - size + 1, best.j + 1); + auto count = m_modified.count(j - size + 1, j + 1); + if (count < best.modified) + best = {i - size + 1, j - size + 1, size, count}; + } + + // The best block should always fall within the range being searched + REALM_ASSERT(best.i >= begin1 && best.i + best.size <= end1); + REALM_ASSERT(best.j >= begin2 && best.j + best.size <= end2); + }); + } + return best; + } + + void find_longest_matches(size_t begin1, size_t end1, size_t begin2, size_t end2) + { + // FIXME: recursion could get too deep here + // recursion depth worst case is currently O(N) and each recursion uses 320 bytes of stack + // could reduce worst case to O(sqrt(N)) (and typical case to O(log N)) + // biasing equal selections towards the middle, but that's still + // insufficient for Android's 8 KB stacks + auto m = find_longest_match(begin1, end1, begin2, end2); + if (!m.size) + return; + if (m.i > begin1 && m.j > begin2) + find_longest_matches(begin1, m.i, begin2, m.j); + m_longest_matches.push_back(m); + if (m.i + m.size < end2 && m.j + m.size < end2) + find_longest_matches(m.i + m.size, end1, m.j + m.size, end2); + } +}; + +void calculate_moves_sorted(std::vector& rows, CollectionChangeSet& changeset) +{ + // The RowInfo array contains information about the old and new TV indices of + // each row, which we need to turn into two sequences of rows, which we'll + // then find matches in + std::vector a, b; + + a.reserve(rows.size()); + for (auto& row : rows) { + a.push_back({row.row_index, row.prev_tv_index}); + } + std::sort(begin(a), end(a), [](auto lft, auto rgt) { + return std::tie(lft.tv_index, lft.row_index) < std::tie(rgt.tv_index, rgt.row_index); + }); + + // Before constructing `b`, first find the first index in `a` which will + // actually differ in `b`, and skip everything else if there aren't any + size_t first_difference = IndexSet::npos; + for (size_t i = 0; i < a.size(); ++i) { + if (a[i].row_index != rows[i].row_index) { + first_difference = i; + break; + } + } + if (first_difference == IndexSet::npos) + return; + + // Note that `b` is sorted by row_index, while `a` is sorted by tv_index + b.reserve(rows.size()); + for (size_t i = 0; i < rows.size(); ++i) + b.push_back({rows[i].row_index, i}); + std::sort(begin(b), end(b), [](auto lft, auto rgt) { + return std::tie(lft.row_index, lft.tv_index) < std::tie(rgt.row_index, rgt.tv_index); + }); + + // Calculate the LCS of the two sequences + auto matches = LongestCommonSubsequenceCalculator(a, b, first_difference, + changeset.modifications).m_longest_matches; + + // And then insert and delete rows as needed to align them + size_t i = first_difference, j = first_difference; + for (auto match : matches) { + for (; i < match.i; ++i) + changeset.deletions.add(a[i].tv_index); + for (; j < match.j; ++j) + changeset.insertions.add(rows[j].tv_index); + i += match.size; + j += match.size; + } +} + +} // Anonymous namespace + +CollectionChangeBuilder CollectionChangeBuilder::calculate(std::vector const& prev_rows, + std::vector const& next_rows, + std::function row_did_change, + util::Optional const& move_candidates) +{ + REALM_ASSERT_DEBUG(!move_candidates || std::is_sorted(begin(next_rows), end(next_rows))); + + CollectionChangeBuilder ret; + + size_t deleted = 0; + std::vector old_rows; + old_rows.reserve(prev_rows.size()); + for (size_t i = 0; i < prev_rows.size(); ++i) { + if (prev_rows[i] == IndexSet::npos) { + ++deleted; + ret.deletions.add(i); + } + else + old_rows.push_back({prev_rows[i], IndexSet::npos, i, i - deleted}); + } + std::sort(begin(old_rows), end(old_rows), [](auto& lft, auto& rgt) { + return lft.row_index < rgt.row_index; + }); + + std::vector new_rows; + new_rows.reserve(next_rows.size()); + for (size_t i = 0; i < next_rows.size(); ++i) { + new_rows.push_back({next_rows[i], IndexSet::npos, i, 0}); + } + std::sort(begin(new_rows), end(new_rows), [](auto& lft, auto& rgt) { + return lft.row_index < rgt.row_index; + }); + + // Don't add rows which were modified to not match the query to `deletions` + // immediately because the unsorted move logic needs to be able to + // distinguish them from rows which were outright deleted + IndexSet removed; + + // Now that our old and new sets of rows are sorted by row index, we can + // iterate over them and either record old+new TV indices for rows present + // in both, or mark them as inserted/deleted if they appear only in one + size_t i = 0, j = 0; + while (i < old_rows.size() && j < new_rows.size()) { + auto old_index = old_rows[i]; + auto new_index = new_rows[j]; + if (old_index.row_index == new_index.row_index) { + new_rows[j].prev_tv_index = old_rows[i].tv_index; + new_rows[j].shifted_tv_index = old_rows[i].shifted_tv_index; + ++i; + ++j; + } + else if (old_index.row_index < new_index.row_index) { + removed.add(old_index.tv_index); + ++i; + } + else { + ret.insertions.add(new_index.tv_index); + ++j; + } + } + + for (; i < old_rows.size(); ++i) + removed.add(old_rows[i].tv_index); + for (; j < new_rows.size(); ++j) + ret.insertions.add(new_rows[j].tv_index); + + // Filter out the new insertions since we don't need them for any of the + // further calculations + new_rows.erase(std::remove_if(begin(new_rows), end(new_rows), + [](auto& row) { return row.prev_tv_index == IndexSet::npos; }), + end(new_rows)); + std::sort(begin(new_rows), end(new_rows), + [](auto& lft, auto& rgt) { return lft.tv_index < rgt.tv_index; }); + + for (auto& row : new_rows) { + if (row_did_change(row.row_index)) { + ret.modifications.add(row.tv_index); + } + } + + if (move_candidates) { + calculate_moves_unsorted(new_rows, removed, *move_candidates, ret); + } + else { + calculate_moves_sorted(new_rows, ret); + } + ret.deletions.add(removed); + ret.verify(); + +#ifdef REALM_DEBUG + { // Verify that applying the calculated change to prev_rows actually produces next_rows + auto rows = prev_rows; + auto it = util::make_reverse_iterator(ret.deletions.end()); + auto end = util::make_reverse_iterator(ret.deletions.begin()); + for (; it != end; ++it) { + rows.erase(rows.begin() + it->first, rows.begin() + it->second); + } + + for (auto i : ret.insertions.as_indexes()) { + rows.insert(rows.begin() + i, next_rows[i]); + } + + REALM_ASSERT(rows == next_rows); + } +#endif + + return ret; +} + +CollectionChangeSet CollectionChangeBuilder::finalize() && +{ + // Calculate which indices in the old collection were modified + auto modifications_in_old = modifications; + modifications_in_old.erase_at(insertions); + modifications_in_old.shift_for_insert_at(deletions); + + // During changeset calculation we allow marking a row as both inserted and + // modified in case changeset merging results in it no longer being an insert, + // but we don't want inserts in the final modification set + modifications.remove(insertions); + + return { + std::move(deletions), + std::move(insertions), + std::move(modifications_in_old), + std::move(modifications), + std::move(moves) + }; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp new file mode 100644 index 0000000..c7b76f0 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/collection_notifier.cpp @@ -0,0 +1,338 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/collection_notifier.hpp" + +#include "impl/realm_coordinator.hpp" +#include "shared_realm.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +std::function +CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info, + Table const& root_table) +{ + // First check if any of the tables accessible from the root table were + // actually modified. This can be false if there were only insertions, or + // deletions which were not linked to by any row in the linking table + auto table_modified = [&](auto& tbl) { + return tbl.table_ndx < info.tables.size() + && !info.tables[tbl.table_ndx].modifications.empty(); + }; + if (!any_of(begin(m_related_tables), end(m_related_tables), table_modified)) { + return [](size_t) { return false; }; + } + + return DeepChangeChecker(info, root_table, m_related_tables); +} + +void DeepChangeChecker::find_related_tables(std::vector& out, Table const& table) +{ + auto table_ndx = table.get_index_in_group(); + if (any_of(begin(out), end(out), [=](auto& tbl) { return tbl.table_ndx == table_ndx; })) + return; + + // We need to add this table to `out` before recurring so that the check + // above works, but we can't store a pointer to the thing being populated + // because the recursive calls may resize `out`, so instead look it up by + // index every time + size_t out_index = out.size(); + out.push_back({table_ndx, {}}); + + for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { + auto type = table.get_column_type(i); + if (type == type_Link || type == type_LinkList) { + out[out_index].links.push_back({i, type == type_LinkList}); + find_related_tables(out, *table.get_link_target(i)); + } + } +} + +DeepChangeChecker::DeepChangeChecker(TransactionChangeInfo const& info, + Table const& root_table, + std::vector const& related_tables) +: m_info(info) +, m_root_table(root_table) +, m_root_table_ndx(root_table.get_index_in_group()) +, m_root_modifications(m_root_table_ndx < info.tables.size() ? &info.tables[m_root_table_ndx].modifications : nullptr) +, m_related_tables(related_tables) +{ +} + +bool DeepChangeChecker::check_outgoing_links(size_t table_ndx, + Table const& table, + size_t row_ndx, size_t depth) +{ + auto it = find_if(begin(m_related_tables), end(m_related_tables), + [&](auto&& tbl) { return tbl.table_ndx == table_ndx; }); + if (it == m_related_tables.end()) + return false; + + // Check if we're already checking if the destination of the link is + // modified, and if not add it to the stack + auto already_checking = [&](size_t col) { + for (auto p = m_current_path.begin(); p < m_current_path.begin() + depth; ++p) { + if (p->table == table_ndx && p->row == row_ndx && p->col == col) + return true; + } + m_current_path[depth] = {table_ndx, row_ndx, col, false}; + return false; + }; + + for (auto const& link : it->links) { + if (already_checking(link.col_ndx)) + continue; + if (!link.is_list) { + if (table.is_null_link(link.col_ndx, row_ndx)) + continue; + auto dst = table.get_link(link.col_ndx, row_ndx); + return check_row(*table.get_link_target(link.col_ndx), dst, depth + 1); + } + + auto& target = *table.get_link_target(link.col_ndx); + auto lvr = table.get_linklist(link.col_ndx, row_ndx); + for (size_t j = 0, size = lvr->size(); j < size; ++j) { + size_t dst = lvr->get(j).get_index(); + if (check_row(target, dst, depth + 1)) + return true; + } + } + + return false; +} + +bool DeepChangeChecker::check_row(Table const& table, size_t idx, size_t depth) +{ + // Arbitrary upper limit on the maximum depth to search + if (depth >= m_current_path.size()) { + // Don't mark any of the intermediate rows checked along the path as + // not modified, as a search starting from them might hit a modification + for (size_t i = 1; i < m_current_path.size(); ++i) + m_current_path[i].depth_exceeded = true; + return false; + } + + size_t table_ndx = table.get_index_in_group(); + if (depth > 0 && table_ndx < m_info.tables.size() && m_info.tables[table_ndx].modifications.contains(idx)) + return true; + + if (m_not_modified.size() <= table_ndx) + m_not_modified.resize(table_ndx + 1); + if (m_not_modified[table_ndx].contains(idx)) + return false; + + bool ret = check_outgoing_links(table_ndx, table, idx, depth); + if (!ret && !m_current_path[depth].depth_exceeded) + m_not_modified[table_ndx].add(idx); + return ret; +} + +bool DeepChangeChecker::operator()(size_t ndx) +{ + if (m_root_modifications && m_root_modifications->contains(ndx)) + return true; + return check_row(m_root_table, ndx, 0); +} + +CollectionNotifier::CollectionNotifier(std::shared_ptr realm) +: m_realm(std::move(realm)) +, m_sg_version(Realm::Internal::get_shared_group(*m_realm).get_version_of_current_transaction()) +{ +} + +CollectionNotifier::~CollectionNotifier() +{ + // Need to do this explicitly to ensure m_realm is destroyed with the mutex + // held to avoid potential double-deletion + unregister(); +} + +size_t CollectionNotifier::add_callback(CollectionChangeCallback callback) +{ + m_realm->verify_thread(); + + auto next_token = [=] { + size_t token = 0; + for (auto& callback : m_callbacks) { + if (token <= callback.token) { + token = callback.token + 1; + } + } + return token; + }; + + std::lock_guard lock(m_callback_mutex); + auto token = next_token(); + m_callbacks.push_back({std::move(callback), token, false}); + if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications + Realm::Internal::get_coordinator(*m_realm).send_commit_notifications(*m_realm); + m_have_callbacks = true; + } + return token; +} + +void CollectionNotifier::remove_callback(size_t token) +{ + Callback old; + { + std::lock_guard lock(m_callback_mutex); + REALM_ASSERT(m_error || m_callbacks.size() > 0); + + auto it = find_if(begin(m_callbacks), end(m_callbacks), + [=](const auto& c) { return c.token == token; }); + // We should only fail to find the callback if it was removed due to an error + REALM_ASSERT(m_error || it != end(m_callbacks)); + if (it == end(m_callbacks)) { + return; + } + + size_t idx = distance(begin(m_callbacks), it); + if (m_callback_index != npos && m_callback_index >= idx) { + --m_callback_index; + } + + old = std::move(*it); + m_callbacks.erase(it); + + m_have_callbacks = !m_callbacks.empty(); + } +} + +void CollectionNotifier::unregister() noexcept +{ + std::lock_guard lock(m_realm_mutex); + m_realm = nullptr; +} + +bool CollectionNotifier::is_alive() const noexcept +{ + std::lock_guard lock(m_realm_mutex); + return m_realm != nullptr; +} + +std::unique_lock CollectionNotifier::lock_target() +{ + return std::unique_lock{m_realm_mutex}; +} + +void CollectionNotifier::set_table(Table const& table) +{ + m_related_tables.clear(); + DeepChangeChecker::find_related_tables(m_related_tables, table); +} + +void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info) +{ + if (!do_add_required_change_info(info)) { + return; + } + + auto max = max_element(begin(m_related_tables), end(m_related_tables), + [](auto&& a, auto&& b) { return a.table_ndx < b.table_ndx; }); + + if (max->table_ndx >= info.table_modifications_needed.size()) + info.table_modifications_needed.resize(max->table_ndx + 1, false); + for (auto& tbl : m_related_tables) { + info.table_modifications_needed[tbl.table_ndx] = true; + } +} + +void CollectionNotifier::prepare_handover() +{ + REALM_ASSERT(m_sg); + m_sg_version = m_sg->get_version_of_current_transaction(); + do_prepare_handover(*m_sg); +} + +void CollectionNotifier::before_advance() +{ + while (auto fn = next_callback(!m_changes_to_deliver.empty(), true)) + fn.before(m_changes_to_deliver); +} + +void CollectionNotifier::after_advance() +{ + while (auto fn = next_callback(!m_changes_to_deliver.empty(), false)) + fn.after(m_changes_to_deliver); + m_changes_to_deliver = {}; +} + +void CollectionNotifier::deliver_error(std::exception_ptr error) +{ + while (auto fn = next_callback(true, false)) { + fn.error(error); + } + + // Remove all the callbacks as we never need to call anything ever again + // after delivering an error + std::lock_guard callback_lock(m_callback_mutex); + m_callbacks.clear(); + m_error = true; +} + +SharedGroup::VersionID CollectionNotifier::package_for_delivery(Realm& realm) +{ + { + std::lock_guard lock(m_realm_mutex); + if (m_realm.get() != &realm) { + return SharedGroup::VersionID{}; + } + } + + if (!prepare_to_deliver()) { + return SharedGroup::VersionID{}; + } + m_changes_to_deliver = std::move(m_accumulated_changes).finalize(); + return version(); +} + +CollectionChangeCallback CollectionNotifier::next_callback(bool has_changes, bool pre) +{ + std::lock_guard callback_lock(m_callback_mutex); + + for (++m_callback_index; m_callback_index < m_callbacks.size(); ++m_callback_index) { + auto& callback = m_callbacks[m_callback_index]; + if (callback.initial_delivered && !has_changes) { + continue; + } + if (!pre) + callback.initial_delivered = true; + return callback.fn; + } + + m_callback_index = npos; + return nullptr; +} + +void CollectionNotifier::attach_to(SharedGroup& sg) +{ + REALM_ASSERT(!m_sg); + + m_sg = &sg; + do_attach_to(sg); +} + +void CollectionNotifier::detach() +{ + REALM_ASSERT(m_sg); + do_detach_from(*m_sg); + m_sg = nullptr; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/handover.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/handover.cpp new file mode 100644 index 0000000..41a8a0e --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/handover.cpp @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/handover.hpp" + +using namespace realm; +using namespace realm::_impl; + +AnyHandover::AnyHandover(AnyHandover&& handover) +{ + switch (handover.m_type) { + case AnyThreadConfined::Type::Object: + new (&m_object.row_handover) RowHandover(std::move(handover.m_object.row_handover)); + new (&m_object.object_schema_name) std::string(std::move(handover.m_object.object_schema_name)); + break; + + case AnyThreadConfined::Type::List: + new (&m_list.link_view_handover) LinkViewHandover(std::move(handover.m_list.link_view_handover)); + break; + + case AnyThreadConfined::Type::Results: + new (&m_results.query_handover) QueryHandover(std::move(handover.m_results.query_handover)); + new (&m_results.sort_order) SortDescriptor::HandoverPatch(std::move(handover.m_results.sort_order)); + break; + } + new (&m_type) AnyThreadConfined::Type(handover.m_type); +} + +AnyHandover& AnyHandover::operator=(AnyHandover&& handover) +{ + this->~AnyHandover(); + new (this) AnyHandover(std::move(handover)); + return *this; +} + +AnyHandover::~AnyHandover() +{ + switch (m_type) { + case AnyThreadConfined::Type::Object: + m_object.row_handover.~unique_ptr(); + break; + + case AnyThreadConfined::Type::List: + m_list.link_view_handover.~unique_ptr(); + break; + + case AnyThreadConfined::Type::Results: + m_results.query_handover.~unique_ptr(); + m_results.sort_order.~unique_ptr(); + break; + } +} + +AnyThreadConfined AnyHandover::import_from_handover(SharedRealm realm) && +{ + SharedGroup& shared_group = Realm::Internal::get_shared_group(*realm); + switch (m_type) { + case AnyThreadConfined::Type::Object: { + auto row = shared_group.import_from_handover(std::move(m_object.row_handover)); + auto object_schema = realm->schema().find(m_object.object_schema_name); + REALM_ASSERT_DEBUG(object_schema != realm->schema().end()); + return AnyThreadConfined(Object(std::move(realm), *object_schema, std::move(*row))); + } + case AnyThreadConfined::Type::List: { + auto link_view_ref = shared_group.import_linkview_from_handover(std::move(m_list.link_view_handover)); + return AnyThreadConfined(List(std::move(realm), std::move(link_view_ref))); + } + case AnyThreadConfined::Type::Results: { + auto query = shared_group.import_from_handover(std::move(m_results.query_handover)); + auto& table = *query->get_table(); + return AnyThreadConfined(Results(std::move(realm), std::move(*query), + SortDescriptor::create_from_and_consume_patch(m_results.sort_order, table))); + } + } + REALM_UNREACHABLE(); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp new file mode 100644 index 0000000..beec1d5 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/list_notifier.cpp @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/list_notifier.hpp" + +#include "shared_realm.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +ListNotifier::ListNotifier(LinkViewRef lv, std::shared_ptr realm) +: CollectionNotifier(std::move(realm)) +, m_prev_size(lv->size()) +{ + set_table(lv->get_target_table()); + + auto& sg = Realm::Internal::get_shared_group(*get_realm()); + m_lv_handover = sg.export_linkview_for_handover(lv); +} + +void ListNotifier::release_data() noexcept +{ + m_lv.reset(); +} + +void ListNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_lv_handover); + REALM_ASSERT(!m_lv); + m_lv = sg.import_linkview_from_handover(std::move(m_lv_handover)); +} + +void ListNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(!m_lv_handover); + if (m_lv) { + m_lv_handover = sg.export_linkview_for_handover(m_lv); + m_lv = {}; + } +} + +bool ListNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(!m_lv_handover); + if (!m_lv || !m_lv->is_attached()) { + return false; // origin row was deleted after the notification was added + } + + // Find the lv's column, since that isn't tracked directly + auto& table = m_lv->get_origin_table(); + size_t row_ndx = m_lv->get_origin_row_index(); + size_t col_ndx = not_found; + for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { + if (table.get_column_type(i) == type_LinkList && table.get_linklist(i, row_ndx) == m_lv) { + col_ndx = i; + break; + } + } + REALM_ASSERT(col_ndx != not_found); + info.lists.push_back({table.get_index_in_group(), row_ndx, col_ndx, &m_change}); + + m_info = &info; + return true; +} + +void ListNotifier::run() +{ + if (!m_lv || !m_lv->is_attached()) { + // LV was deleted, so report all of the rows being removed if this is + // the first run after that + if (m_prev_size) { + m_change.deletions.set(m_prev_size); + m_prev_size = 0; + } + else { + m_change = {}; + } + return; + } + + auto row_did_change = get_modification_checker(*m_info, m_lv->get_target_table()); + for (size_t i = 0; i < m_lv->size(); ++i) { + if (m_change.modifications.contains(i)) + continue; + if (row_did_change(m_lv->get(i).get_index())) + m_change.modifications.add(i); + } + + for (auto const& move : m_change.moves) { + if (m_change.modifications.contains(move.to)) + continue; + if (row_did_change(m_lv->get(move.to).get_index())) + m_change.modifications.add(move.to); + } + + m_prev_size = m_lv->size(); +} + +void ListNotifier::do_prepare_handover(SharedGroup&) +{ + add_changes(std::move(m_change)); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp new file mode 100644 index 0000000..b183c65 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/realm_coordinator.cpp @@ -0,0 +1,669 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/realm_coordinator.hpp" + +#include "impl/collection_notifier.hpp" +#include "impl/external_commit_helper.hpp" +#include "impl/transact_log_handler.hpp" +#include "impl/weak_realm_notifier.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "schema.hpp" + +#if REALM_ENABLE_SYNC +#include "sync_config.hpp" +#include "sync_manager.hpp" +#include "sync_session.hpp" +#endif + +#include +#include +#include + +#include +#include + +using namespace realm; +using namespace realm::_impl; + +static std::mutex s_coordinator_mutex; +static std::unordered_map> s_coordinators_per_path; + +std::shared_ptr RealmCoordinator::get_coordinator(StringData path) +{ + std::lock_guard lock(s_coordinator_mutex); + + auto& weak_coordinator = s_coordinators_per_path[path]; + if (auto coordinator = weak_coordinator.lock()) { + return coordinator; + } + + auto coordinator = std::make_shared(); + weak_coordinator = coordinator; + return coordinator; +} + +std::shared_ptr RealmCoordinator::get_coordinator(const Realm::Config& config) +{ + auto coordinator = get_coordinator(config.path); + std::lock_guard lock(coordinator->m_realm_mutex); + coordinator->set_config(config); + return coordinator; +} + +std::shared_ptr RealmCoordinator::get_existing_coordinator(StringData path) +{ + std::lock_guard lock(s_coordinator_mutex); + auto it = s_coordinators_per_path.find(path); + return it == s_coordinators_per_path.end() ? nullptr : it->second.lock(); +} + +void RealmCoordinator::create_sync_session() +{ +#if REALM_ENABLE_SYNC + if (m_sync_session) + return; + + m_sync_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config); + + std::weak_ptr weak_self = shared_from_this(); + SyncSession::Internal::set_sync_transact_callback(*m_sync_session, + [weak_self](VersionID old_version, VersionID new_version) { + if (auto self = weak_self.lock()) { + if (self->m_transaction_callback) + self->m_transaction_callback(old_version, new_version); + self->notify_others(); + } + }); + if (m_config.sync_config->error_handler) { + SyncSession::Internal::set_error_handler(*m_sync_session, m_config.sync_config->error_handler); + } +#endif +} + +void RealmCoordinator::set_config(const Realm::Config& config) +{ + if ((!m_config.read_only() && !m_notifier) || (m_config.read_only() && m_weak_realm_notifiers.empty())) { + m_config = config; + } + else { + if (m_config.read_only() != config.read_only()) { + throw MismatchedConfigException("Realm at path '%1' already opened with different read permissions.", config.path); + } + if (m_config.in_memory != config.in_memory) { + throw MismatchedConfigException("Realm at path '%1' already opened with different inMemory settings.", config.path); + } + if (m_config.encryption_key != config.encryption_key) { + throw MismatchedConfigException("Realm at path '%1' already opened with a different encryption key.", config.path); + } + if (m_config.schema_mode != config.schema_mode) { + throw MismatchedConfigException("Realm at path '%1' already opened with a different schema mode.", config.path); + } + if (m_config.schema_version != config.schema_version && config.schema_version != ObjectStore::NotVersioned) { + throw MismatchedConfigException("Realm at path '%1' already opened with different schema version.", config.path); + } + +#if REALM_ENABLE_SYNC + if (bool(m_config.sync_config) != bool(config.sync_config)) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync configurations.", config.path); + } + + if (config.sync_config) { + if (m_config.sync_config->user_tag != config.sync_config->user_tag) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync user identifier.", config.path); + } + if (m_config.sync_config->realm_url != config.sync_config->realm_url) { + throw MismatchedConfigException("Realm at path '%1' already opened with different sync server URL.", config.path); + } + } +#endif + + // Realm::update_schema() handles complaining about schema mismatches + } + + if (config.sync_config) { + create_sync_session(); + } +} + +std::shared_ptr RealmCoordinator::get_realm(Realm::Config config) +{ + std::lock_guard lock(m_realm_mutex); + + set_config(config); + + if (config.cache) { + for (auto& cached_realm : m_weak_realm_notifiers) { + if (cached_realm.is_cached_for_current_thread()) { + // can be null if we jumped in between ref count hitting zero and + // unregister_realm() getting the lock + if (auto realm = cached_realm.realm()) { + return realm; + } + } + } + } + + auto realm = Realm::make_shared_realm(std::move(config)); + if (!config.read_only() && !m_notifier && config.automatic_change_notifications) { + try { + m_notifier = std::make_unique(*this); + } + catch (std::system_error const& ex) { + throw RealmFileException(RealmFileException::Kind::AccessError, config.path, ex.code().message(), ""); + } + } + realm->init(shared_from_this()); + + m_weak_realm_notifiers.emplace_back(realm, m_config.cache); + return realm; +} + +std::shared_ptr RealmCoordinator::get_realm() +{ + return get_realm(m_config); +} + +const Schema* RealmCoordinator::get_schema() const noexcept +{ + return m_schema_version == uint64_t(-1) ? nullptr : &m_schema; +} + +void RealmCoordinator::update_schema(Schema const& schema, uint64_t schema_version) +{ + if (m_schema_version != uint64_t(-1) && m_schema_version != schema_version && m_weak_realm_notifiers.size() > 1) { + throw MismatchedConfigException("Realm at path '%1' already opened with a different schema version.", m_config.path); + } + + m_schema = schema; + m_schema_version = schema_version; + + // FIXME: notify realms of the schema change +} + +RealmCoordinator::RealmCoordinator() = default; + +RealmCoordinator::~RealmCoordinator() +{ + std::lock_guard coordinator_lock(s_coordinator_mutex); + for (auto it = s_coordinators_per_path.begin(); it != s_coordinators_per_path.end(); ) { + if (it->second.expired()) { + it = s_coordinators_per_path.erase(it); + } + else { + ++it; + } + } +} + +void RealmCoordinator::unregister_realm(Realm* realm) +{ + std::lock_guard lock(m_realm_mutex); + auto new_end = remove_if(begin(m_weak_realm_notifiers), end(m_weak_realm_notifiers), + [=](auto& notifier) { return notifier.expired() || notifier.is_for_realm(realm); }); + m_weak_realm_notifiers.erase(new_end, end(m_weak_realm_notifiers)); +} + +void RealmCoordinator::clear_cache() +{ + std::vector realms_to_close; + { + std::lock_guard lock(s_coordinator_mutex); + + for (auto& weak_coordinator : s_coordinators_per_path) { + auto coordinator = weak_coordinator.second.lock(); + if (!coordinator) { + continue; + } + + coordinator->m_notifier = nullptr; + + // Gather a list of all of the realms which will be removed + for (auto& weak_realm_notifier : coordinator->m_weak_realm_notifiers) { + if (auto realm = weak_realm_notifier.realm()) { + realms_to_close.push_back(realm); + } + } + } + + s_coordinators_per_path.clear(); + } + + // Close all of the previously cached Realms. This can't be done while + // s_coordinator_mutex is held as it may try to re-lock it. + for (auto& weak_realm : realms_to_close) { + if (auto realm = weak_realm.lock()) { + realm->close(); + } + } +} + +void RealmCoordinator::clear_all_caches() +{ + std::vector> to_clear; + { + std::lock_guard lock(s_coordinator_mutex); + for (auto iter : s_coordinators_per_path) { + to_clear.push_back(iter.second); + } + } + for (auto weak_coordinator : to_clear) { + if (auto coordinator = weak_coordinator.lock()) { + coordinator->clear_cache(); + } + } +} + +void RealmCoordinator::send_commit_notifications(Realm& source_realm) +{ + REALM_ASSERT(!m_config.read_only()); + if (m_notifier) { + m_notifier->notify_others(); + } +#if REALM_ENABLE_SYNC + if (m_sync_session) { + auto& sg = Realm::Internal::get_shared_group(source_realm); + auto version = LangBindHelper::get_version_of_latest_snapshot(sg); + SyncSession::Internal::nonsync_transact_notify(*m_sync_session, version); + } +#else + // Silence "unused parameter 'source_realm'" warning + (void)source_realm; +#endif +} + +void RealmCoordinator::pin_version(uint_fast64_t version, uint_fast32_t index) +{ + if (m_async_error) { + return; + } + + SharedGroup::VersionID versionid(version, index); + if (!m_advancer_sg) { + try { + std::unique_ptr read_only_group; + Realm::open_with_config(m_config, m_advancer_history, m_advancer_sg, read_only_group, nullptr); + REALM_ASSERT(!read_only_group); + m_advancer_sg->begin_read(versionid); + } + catch (...) { + m_async_error = std::current_exception(); + m_advancer_sg = nullptr; + m_advancer_history = nullptr; + } + } + else if (m_new_notifiers.empty()) { + // If this is the first notifier then we don't already have a read transaction + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Ready); + m_advancer_sg->begin_read(versionid); + } + else { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + if (versionid < m_advancer_sg->get_version_of_current_transaction()) { + // Ensure we're holding a readlock on the oldest version we have a + // handover object for, as handover objects don't + m_advancer_sg->end_read(); + m_advancer_sg->begin_read(versionid); + } + } +} + +void RealmCoordinator::register_notifier(std::shared_ptr notifier) +{ + auto version = notifier->version(); + auto& self = Realm::Internal::get_coordinator(*notifier->get_realm()); + { + std::lock_guard lock(self.m_notifier_mutex); + self.pin_version(version.version, version.index); + self.m_new_notifiers.push_back(std::move(notifier)); + } +} + +void RealmCoordinator::clean_up_dead_notifiers() +{ + auto swap_remove = [&](auto& container) { + bool did_remove = false; + for (size_t i = 0; i < container.size(); ++i) { + if (container[i]->is_alive()) + continue; + + // Ensure the notifier is destroyed here even if there's lingering refs + // to the async notifier elsewhere + container[i]->release_data(); + + if (container.size() > i + 1) + container[i] = std::move(container.back()); + container.pop_back(); + --i; + did_remove = true; + } + return did_remove; + }; + + if (swap_remove(m_notifiers)) { + // Make sure we aren't holding on to read versions needlessly if there + // are no notifiers left, but don't close them entirely as opening shared + // groups is expensive + if (m_notifiers.empty() && m_notifier_sg) { + REALM_ASSERT_3(m_notifier_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + m_notifier_sg->end_read(); + } + } + if (swap_remove(m_new_notifiers)) { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + if (m_new_notifiers.empty() && m_advancer_sg) { + m_advancer_sg->end_read(); + } + } +} + +void RealmCoordinator::on_change() +{ + run_async_notifiers(); + + std::lock_guard lock(m_realm_mutex); + for (auto& realm : m_weak_realm_notifiers) { + realm.notify(); + } +} + +namespace { +class IncrementalChangeInfo { +public: + IncrementalChangeInfo(SharedGroup& sg, + SchemaMode schema_mode, + std::vector>& notifiers) + : m_sg(sg), m_schema_mode(schema_mode) + { + if (notifiers.empty()) + return; + + auto cmp = [&](auto&& lft, auto&& rgt) { + return lft->version() < rgt->version(); + }; + + // Sort the notifiers by their source version so that we can pull them + // all forward to the latest version in a single pass over the transaction log + std::sort(notifiers.begin(), notifiers.end(), cmp); + + // Preallocate the required amount of space in the vector so that we can + // safely give out pointers to within the vector + size_t count = 1; + for (auto it = notifiers.begin(), next = it + 1; next != notifiers.end(); ++it, ++next) { + if (cmp(*it, *next)) + ++count; + } + m_info.reserve(count); + m_info.resize(1); + m_current = &m_info[0]; + } + + TransactionChangeInfo& current() const { return *m_current; } + + bool advance_incremental(SharedGroup::VersionID version) + { + if (version != m_sg.get_version_of_current_transaction()) { + transaction::advance(m_sg, *m_current, version); + m_info.push_back({ + m_current->table_modifications_needed, + m_current->table_moves_needed, + std::move(m_current->lists)}); + m_current = &m_info.back(); + return true; + } + return false; + } + + void advance_to_final(SharedGroup::VersionID version) + { + if (!m_current) { + transaction::advance(m_sg, nullptr, m_schema_mode, version); + return; + } + + transaction::advance(m_sg, *m_current, version); + + // We now need to combine the transaction change info objects so that all of + // the notifiers see the complete set of changes from their first version to + // the most recent one + for (size_t i = m_info.size() - 1; i > 0; --i) { + auto& cur = m_info[i]; + if (cur.tables.empty()) + continue; + auto& prev = m_info[i - 1]; + if (prev.tables.empty()) { + prev.tables = cur.tables; + continue; + } + + for (size_t j = 0; j < prev.tables.size() && j < cur.tables.size(); ++j) { + prev.tables[j].merge(CollectionChangeBuilder{cur.tables[j]}); + } + prev.tables.reserve(cur.tables.size()); + while (prev.tables.size() < cur.tables.size()) { + prev.tables.push_back(cur.tables[prev.tables.size()]); + } + } + + // Copy the list change info if there are multiple LinkViews for the same LinkList + auto id = [](auto const& list) { return std::tie(list.table_ndx, list.col_ndx, list.row_ndx); }; + for (size_t i = 1; i < m_current->lists.size(); ++i) { + for (size_t j = i; j > 0; --j) { + if (id(m_current->lists[i]) == id(m_current->lists[j - 1])) { + m_current->lists[j - 1].changes->merge(CollectionChangeBuilder{*m_current->lists[i].changes}); + } + } + } + } + +private: + std::vector m_info; + TransactionChangeInfo* m_current = nullptr; + SharedGroup& m_sg; + SchemaMode m_schema_mode; +}; +} // anonymous namespace + +void RealmCoordinator::run_async_notifiers() +{ + std::unique_lock lock(m_notifier_mutex); + + clean_up_dead_notifiers(); + + if (m_notifiers.empty() && m_new_notifiers.empty()) { + return; + } + + if (!m_async_error) { + open_helper_shared_group(); + } + + if (m_async_error) { + std::move(m_new_notifiers.begin(), m_new_notifiers.end(), std::back_inserter(m_notifiers)); + m_new_notifiers.clear(); + return; + } + + SharedGroup::VersionID version; + + // Advance all of the new notifiers to the most recent version, if any + auto new_notifiers = std::move(m_new_notifiers); + IncrementalChangeInfo new_notifier_change_info(*m_advancer_sg, m_config.schema_mode, new_notifiers); + + if (!new_notifiers.empty()) { + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Reading); + REALM_ASSERT_3(m_advancer_sg->get_version_of_current_transaction().version, + <=, new_notifiers.front()->version().version); + + // The advancer SG can be at an older version than the oldest new notifier + // if a notifier was added and then removed before it ever got the chance + // to run, as we don't move the pin forward when removing dead notifiers + transaction::advance(*m_advancer_sg, nullptr, m_config.schema_mode, new_notifiers.front()->version()); + + // Advance each of the new notifiers to the latest version, attaching them + // to the SG at their handover version. This requires a unique + // TransactionChangeInfo for each source version, so that things don't + // see changes from before the version they were handed over from. + // Each Info has all of the changes between that source version and the + // next source version, and they'll be merged together later after + // releasing the lock + for (auto& notifier : new_notifiers) { + new_notifier_change_info.advance_incremental(notifier->version()); + notifier->attach_to(*m_advancer_sg); + notifier->add_required_change_info(new_notifier_change_info.current()); + } + new_notifier_change_info.advance_to_final(SharedGroup::VersionID{}); + + for (auto& notifier : new_notifiers) { + notifier->detach(); + } + version = m_advancer_sg->get_version_of_current_transaction(); + m_advancer_sg->end_read(); + } + REALM_ASSERT_3(m_advancer_sg->get_transact_stage(), ==, SharedGroup::transact_Ready); + + // Make a copy of the notifiers vector and then release the lock to avoid + // blocking other threads trying to register or unregister notifiers while we run them + auto notifiers = m_notifiers; + lock.unlock(); + + // Advance the non-new notifiers to the same version as we advanced the new + // ones to (or the latest if there were no new ones) + IncrementalChangeInfo change_info(*m_notifier_sg, m_config.schema_mode, notifiers); + for (auto& notifier : notifiers) { + notifier->add_required_change_info(change_info.current()); + } + change_info.advance_to_final(version); + + // Attach the new notifiers to the main SG and move them to the main list + for (auto& notifier : new_notifiers) { + notifier->attach_to(*m_notifier_sg); + } + std::move(new_notifiers.begin(), new_notifiers.end(), std::back_inserter(notifiers)); + + // Change info is now all ready, so the notifiers can now perform their + // background work + for (auto& notifier : notifiers) { + notifier->run(); + } + + // Reacquire the lock while updating the fields that are actually read on + // other threads + lock.lock(); + for (auto& notifier : notifiers) { + notifier->prepare_handover(); + } + m_notifiers = std::move(notifiers); + clean_up_dead_notifiers(); +} + +void RealmCoordinator::open_helper_shared_group() +{ + if (!m_notifier_sg) { + try { + std::unique_ptr read_only_group; + Realm::open_with_config(m_config, m_notifier_history, m_notifier_sg, read_only_group, nullptr); + REALM_ASSERT(!read_only_group); + m_notifier_sg->begin_read(); + } + catch (...) { + // Store the error to be passed to the async notifiers + m_async_error = std::current_exception(); + m_notifier_sg = nullptr; + m_notifier_history = nullptr; + } + } + else if (m_notifiers.empty()) { + m_notifier_sg->begin_read(); + } +} + + +std::vector> RealmCoordinator::notifiers_to_deliver(Realm& realm) +{ + std::unique_lock lock(m_notifier_mutex); + decltype(m_notifiers) notifiers; + if (m_async_error) { + auto error = m_async_error; + notifiers = m_notifiers; + lock.unlock(); + for (auto& notifier : notifiers) + notifier->deliver_error(error); + return {}; + } + + for (auto& notifier : m_notifiers) { + auto notifier_version = notifier->package_for_delivery(realm); + if (notifier_version == SharedGroup::VersionID{}) + continue; + notifiers.push_back(notifier); + } + + return notifiers; +} + +void RealmCoordinator::advance_to_ready(Realm& realm) +{ + auto& sg = Realm::Internal::get_shared_group(realm); + auto notifiers = notifiers_to_deliver(realm); + if (notifiers.empty()) { + transaction::advance(sg, realm.m_binding_context.get(), m_config.schema_mode); + return; + } + + auto version = notifiers[0]->version(); + if (version <= sg.get_version_of_current_transaction()) + return; + + for (auto& notifier : notifiers) + notifier->before_advance(); + transaction::advance(sg, realm.m_binding_context.get(), m_config.schema_mode, version); + for (auto& notifier : notifiers) + notifier->deliver(sg); + for (auto& notifier : notifiers) + notifier->after_advance(); +} + +void RealmCoordinator::process_available_async(Realm& realm) +{ + auto notifiers = notifiers_to_deliver(realm); + if (notifiers.empty()) + return; + + auto& sg = Realm::Internal::get_shared_group(realm); + auto version = notifiers[0]->version(); + if (version != sg.get_version_of_current_transaction()) + return; + + for (auto& notifier : notifiers) + notifier->deliver(sg); + for (auto& notifier : notifiers) + notifier->after_advance(); +} + +void RealmCoordinator::notify_others() +{ + if (m_notifier) + m_notifier->notify_others(); +} + +void RealmCoordinator::set_transaction_callback(std::function fn) +{ + m_transaction_callback = std::move(fn); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp new file mode 100644 index 0000000..60aeb1a --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/results_notifier.cpp @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/results_notifier.hpp" + +using namespace realm; +using namespace realm::_impl; + +ResultsNotifier::ResultsNotifier(Results& target) +: CollectionNotifier(target.get_realm()) +, m_target_results(&target) +, m_target_is_in_table_order(target.is_in_table_order()) +{ + Query q = target.get_query(); + set_table(*q.get_table()); + m_query_handover = Realm::Internal::get_shared_group(*get_realm()).export_for_handover(q, MutableSourcePayload::Move); + SortDescriptor::generate_patch(target.get_sort(), m_sort_handover); +} + +void ResultsNotifier::target_results_moved(Results& old_target, Results& new_target) +{ + auto lock = lock_target(); + + REALM_ASSERT(m_target_results == &old_target); + m_target_results = &new_target; +} + +void ResultsNotifier::release_data() noexcept +{ + m_query = nullptr; +} + +// Most of the inter-thread synchronization for run(), prepare_handover(), +// attach_to(), detach(), release_data() and deliver() is done by +// RealmCoordinator external to this code, which has some potentially +// non-obvious results on which members are and are not safe to use without +// holding a lock. +// +// add_required_change_info(), attach_to(), detach(), run(), +// prepare_handover(), and release_data() are all only ever called on a single +// background worker thread. call_callbacks() and deliver() are called on the +// target thread. Calls to prepare_handover() and deliver() are guarded by a +// lock. +// +// In total, this means that the safe data flow is as follows: +// - add_Required_change_info(), prepare_handover(), attach_to(), detach() and +// release_data() can read members written by each other +// - deliver() can read members written to in prepare_handover(), deliver(), +// and call_callbacks() +// - call_callbacks() and read members written to in deliver() +// +// Separately from the handover data flow, m_target_results is guarded by the target lock + +bool ResultsNotifier::do_add_required_change_info(TransactionChangeInfo& info) +{ + REALM_ASSERT(m_query); + m_info = &info; + + auto table_ndx = m_query->get_table()->get_index_in_group(); + if (info.table_moves_needed.size() <= table_ndx) + info.table_moves_needed.resize(table_ndx + 1); + info.table_moves_needed[table_ndx] = true; + + return m_initial_run_complete && have_callbacks(); +} + +bool ResultsNotifier::need_to_run() +{ + REALM_ASSERT(m_info); + REALM_ASSERT(!m_tv.is_attached()); + + { + auto lock = lock_target(); + // Don't run the query if the results aren't actually going to be used + if (!get_realm() || (!have_callbacks() && !m_target_results->wants_background_updates())) { + return false; + } + } + + // If we've run previously, check if we need to rerun + if (m_initial_run_complete && m_query->sync_view_if_needed() == m_last_seen_version) { + return false; + } + + return true; +} + +void ResultsNotifier::calculate_changes() +{ + size_t table_ndx = m_query->get_table()->get_index_in_group(); + if (m_initial_run_complete) { + auto changes = table_ndx < m_info->tables.size() ? &m_info->tables[table_ndx] : nullptr; + + std::vector next_rows; + next_rows.reserve(m_tv.size()); + for (size_t i = 0; i < m_tv.size(); ++i) + next_rows.push_back(m_tv[i].get_index()); + + util::Optional move_candidates; + if (changes) { + auto const& moves = changes->moves; + for (auto& idx : m_previous_rows) { + auto it = lower_bound(begin(moves), end(moves), idx, + [](auto const& a, auto b) { return a.from < b; }); + if (it != moves.end() && it->from == idx) + idx = it->to; + else if (changes->deletions.contains(idx)) + idx = npos; + else + REALM_ASSERT_DEBUG(!changes->insertions.contains(idx)); + } + if (m_target_is_in_table_order && !m_sort) + move_candidates = changes->insertions; + } + + m_changes = CollectionChangeBuilder::calculate(m_previous_rows, next_rows, + get_modification_checker(*m_info, *m_query->get_table()), + move_candidates); + + m_previous_rows = std::move(next_rows); + } + else { + m_previous_rows.resize(m_tv.size()); + for (size_t i = 0; i < m_tv.size(); ++i) + m_previous_rows[i] = m_tv[i].get_index(); + } +} + +void ResultsNotifier::run() +{ + if (!need_to_run()) + return; + + m_query->sync_view_if_needed(); + m_tv = m_query->find_all(); + if (m_sort) { + m_tv.sort(m_sort); + } + m_last_seen_version = m_tv.sync_if_needed(); + + calculate_changes(); +} + +void ResultsNotifier::do_prepare_handover(SharedGroup& sg) +{ + if (!m_tv.is_attached()) { + // if the table version didn't change we can just reuse the same handover + // object and bump its version to the current SG version + if (m_tv_handover) + m_tv_handover->version = sg.get_version_of_current_transaction(); + return; + } + + REALM_ASSERT(m_tv.is_in_sync()); + + m_initial_run_complete = true; + m_tv_handover = sg.export_for_handover(m_tv, MutableSourcePayload::Move); + + add_changes(std::move(m_changes)); + REALM_ASSERT(m_changes.empty()); + + // detach the TableView as we won't need it again and keeping it around + // makes advance_read() much more expensive + m_tv = {}; +} + +void ResultsNotifier::deliver(SharedGroup& sg) +{ + auto lock = lock_target(); + + // Target realm being null here indicates that we were unregistered while we + // were in the process of advancing the Realm version and preparing for + // delivery, i.e. the results was destroyed from the "wrong" thread + if (!get_realm()) { + return; + } + + REALM_ASSERT(!m_query_handover); + if (m_tv_to_deliver) { + Results::Internal::set_table_view(*m_target_results, + std::move(*sg.import_from_handover(std::move(m_tv_to_deliver)))); + } + REALM_ASSERT(!m_tv_to_deliver); +} + +bool ResultsNotifier::prepare_to_deliver() +{ + auto lock = lock_target(); + // We can get called before the query has actually had the chance to run if + // we're added immediately before a different set of async results are + // delivered + if (!get_realm() || !m_initial_run_complete) + return false; + m_tv_to_deliver = std::move(m_tv_handover); + return true; +} + +void ResultsNotifier::do_attach_to(SharedGroup& sg) +{ + REALM_ASSERT(m_query_handover); + m_query = sg.import_from_handover(std::move(m_query_handover)); + m_sort = SortDescriptor::create_from_and_consume_patch(m_sort_handover, *m_query->get_table()); +} + +void ResultsNotifier::do_detach_from(SharedGroup& sg) +{ + REALM_ASSERT(m_query); + REALM_ASSERT(!m_tv.is_attached()); + + SortDescriptor::generate_patch(m_sort, m_sort_handover); + m_query_handover = sg.export_for_handover(*m_query, MutableSourcePayload::Move); + m_query = nullptr; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp new file mode 100644 index 0000000..e998e2b --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/transact_log_handler.cpp @@ -0,0 +1,845 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/transact_log_handler.hpp" + +#include "binding_context.hpp" +#include "impl/collection_notifier.hpp" +#include "index_set.hpp" +#include "shared_realm.hpp" + +#include +#include +#include + +using namespace realm; + +namespace { +template +struct MarkDirtyMixin { + bool mark_dirty(size_t row, size_t col, _impl::Instruction instr=_impl::instr_Set) + { + // Ignore SetDefault and SetUnique as those conceptually cannot be + // changes to existing rows + if (instr == _impl::instr_Set) + static_cast(this)->mark_dirty(row, col); + return true; + } + + bool set_int(size_t c, size_t r, int_fast64_t, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + bool set_bool(size_t c, size_t r, bool, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_float(size_t c, size_t r, float, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_double(size_t c, size_t r, double, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_string(size_t c, size_t r, StringData, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + bool set_binary(size_t c, size_t r, BinaryData, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_olddatetime(size_t c, size_t r, OldDateTime, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_timestamp(size_t c, size_t r, Timestamp, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_table(size_t c, size_t r, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_mixed(size_t c, size_t r, const Mixed&, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_link(size_t c, size_t r, size_t, size_t, _impl::Instruction i) { return mark_dirty(r, c, i); } + bool set_null(size_t c, size_t r, _impl::Instruction i, size_t) { return mark_dirty(r, c, i); } + + bool add_int(size_t col, size_t row, size_t) { return mark_dirty(row, col); } + bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } + bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); } + bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); } + + bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; } + bool set_string_unique(size_t, size_t, size_t, StringData) { return true; } +}; + +class TransactLogValidationMixin { + // Index of currently selected table + size_t m_current_table = 0; + + // Tables which were created during the transaction being processed, which + // can have columns inserted without a schema version bump + std::vector m_new_tables; + + REALM_NORETURN + REALM_NOINLINE + void schema_error() + { + throw std::logic_error("Schema mismatch detected: another process has modified the Realm file's schema in an incompatible way"); + } + + // Throw an exception if the currently modified table already existed before + // the current set of modifications + bool schema_error_unless_new_table() + { + if (schema_mode == SchemaMode::Additive) { + return true; + } + if (std::find(begin(m_new_tables), end(m_new_tables), m_current_table) != end(m_new_tables)) { + return true; + } + schema_error(); + } + +protected: + size_t current_table() const noexcept { return m_current_table; } + +public: + SchemaMode schema_mode; + + // Schema changes which don't involve a change in the schema version are + // allowed + bool add_search_index(size_t) { return true; } + bool remove_search_index(size_t) { return true; } + + // Creating entirely new tables without a schema version bump is allowed, so + // we need to track if new columns are being added to a new table or an + // existing one + bool insert_group_level_table(size_t table_ndx, size_t, StringData) + { + // Shift any previously added tables after the new one + for (auto& table : m_new_tables) { + if (table >= table_ndx) + ++table; + } + m_new_tables.push_back(table_ndx); + m_current_table = table_ndx; + return true; + } + bool insert_column(size_t, DataType, StringData, bool) { return schema_error_unless_new_table(); } + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return schema_error_unless_new_table(); } + bool set_link_type(size_t, LinkType) { return schema_error_unless_new_table(); } + bool move_column(size_t, size_t) { return schema_error_unless_new_table(); } + bool move_group_level_table(size_t, size_t) { return schema_error_unless_new_table(); } + + // Removing or renaming things while a Realm is open is never supported + bool erase_group_level_table(size_t, size_t) { schema_error(); } + bool rename_group_level_table(size_t, StringData) { schema_error(); } + bool erase_column(size_t) { schema_error(); } + bool erase_link_column(size_t, size_t, size_t) { schema_error(); } + bool rename_column(size_t, StringData) { schema_error(); } + + bool select_descriptor(int levels, const size_t*) + { + // subtables not supported + return levels == 0; + } + + bool select_table(size_t group_level_ndx, int, const size_t*) noexcept + { + m_current_table = group_level_ndx; + return true; + } + + bool select_link_list(size_t, size_t, size_t) { return true; } + + // Non-schema changes are all allowed + void parse_complete() { } + bool insert_empty_rows(size_t, size_t, size_t, bool) { return true; } + bool erase_rows(size_t, size_t, size_t, bool) { return true; } + bool swap_rows(size_t, size_t) { return true; } + bool clear_table() noexcept { return true; } + bool link_list_set(size_t, size_t, size_t) { return true; } + bool link_list_insert(size_t, size_t, size_t) { return true; } + bool link_list_erase(size_t, size_t) { return true; } + bool link_list_nullify(size_t, size_t) { return true; } + bool link_list_clear(size_t) { return true; } + bool link_list_move(size_t, size_t) { return true; } + bool link_list_swap(size_t, size_t) { return true; } + bool merge_rows(size_t, size_t) { return true; } + bool optimize_table() { return true; } + +}; + + +// A transaction log handler that just validates that all operations made are +// ones supported by the object store +struct TransactLogValidator : public TransactLogValidationMixin, public MarkDirtyMixin { + TransactLogValidator(SchemaMode schema_mode) { this->schema_mode = schema_mode; } + void mark_dirty(size_t, size_t) { } +}; + +// Move the value at container[from] to container[to], shifting everything in +// between, or do nothing if either are out of bounds +template +void rotate(Container& container, size_t from, size_t to) +{ + REALM_ASSERT(from != to); + if (from >= container.size() && to >= container.size()) + return; + if (from >= container.size() || to >= container.size()) + container.resize(std::max(from, to) + 1); + if (from < to) + std::rotate(begin(container) + from, begin(container) + to, begin(container) + to + 1); + else + std::rotate(begin(container) + to, begin(container) + from, begin(container) + from + 1); +} + +// Insert a default-initialized value at pos if there is anything after pos in the container. +template +void insert_empty_at(Container& container, size_t pos) +{ + if (pos < container.size()) + container.insert(container.begin() + pos, typename Container::value_type{}); +} + +// Shift `value` to reflect a move from `from` to `to` +void adjust_for_move(size_t& value, size_t from, size_t to) +{ + if (value == from) + value = to; + else if (value > from && value < to) + --value; + else if (value < from && value >= to) + ++value; +} + +// Extends TransactLogValidator to also track changes and report it to the +// binding context if any properties are being observed +class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin { + using ColumnInfo = BindingContext::ColumnInfo; + using ObserverState = BindingContext::ObserverState; + + // Observed table rows which need change information + std::vector m_observers; + // Userdata pointers for rows which have been deleted + std::vector invalidated; + // Delegate to send change information to + BindingContext* m_context; + + // Change information for the currently selected LinkList, if any + ColumnInfo* m_active_linklist = nullptr; + + // Get the change info for the given column, creating it if needed + static ColumnInfo& get_change(ObserverState& state, size_t i) + { + expand_to(state, i); + return state.changes[i]; + } + + static void expand_to(ObserverState& state, size_t i) + { + auto old_size = state.changes.size(); + if (old_size <= i) { + auto new_size = std::max(state.changes.size() * 2, i + 1); + state.changes.resize(new_size); + size_t base = old_size == 0 ? 0 : state.changes[old_size - 1].initial_column_index + 1; + for (size_t i = old_size; i < new_size; ++i) + state.changes[i].initial_column_index = i - old_size + base; + } + } + + // Remove the given observer from the list of observed objects and add it + // to the listed of invalidated objects + void invalidate(ObserverState *o) + { + invalidated.push_back(o->info); + m_observers.erase(m_observers.begin() + (o - &m_observers[0])); + } + +public: + template + TransactLogObserver(BindingContext* context, SharedGroup& sg, Func&& func, util::Optional schema_mode) + : m_context(context) + { + auto old_version = sg.get_version_of_current_transaction(); + if (context) { + m_observers = context->get_observed_rows(); + } + if (m_observers.empty()) { + if (schema_mode) { + func(TransactLogValidator(*schema_mode)); + } + else { + func(); + } + if (context && old_version != sg.get_version_of_current_transaction()) { + context->did_change({}, {}); + } + return; + } + + std::sort(begin(m_observers), end(m_observers)); + func(*this); + context->did_change(m_observers, invalidated); + } + + // Mark the given row/col as needing notifications sent + void mark_dirty(size_t row_ndx, size_t col_ndx) + { + auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{current_table(), row_ndx, nullptr}); + if (it != end(m_observers) && it->table_ndx == current_table() && it->row_ndx == row_ndx) { + get_change(*it, col_ndx).kind = ColumnInfo::Kind::Set; + } + } + + // Called at the end of the transaction log immediately before the version + // is advanced + void parse_complete() + { + m_context->will_change(m_observers, invalidated); + } + + bool insert_group_level_table(size_t table_ndx, size_t prior_size, StringData name) + { + for (auto& observer : m_observers) { + if (observer.table_ndx >= table_ndx) + ++observer.table_ndx; + } + TransactLogValidationMixin::insert_group_level_table(table_ndx, prior_size, name); + return true; + } + + bool insert_empty_rows(size_t row_ndx, size_t num_rows, size_t prior_size, bool) + { + if (row_ndx != prior_size) { + for (auto& observer : m_observers) { + if (observer.table_ndx == current_table() && observer.row_ndx >= row_ndx) + observer.row_ndx += num_rows; + } + } + return true; + } + + bool erase_rows(size_t row_ndx, size_t rows_to_erase, size_t prior_size, bool unordered) + { + REALM_ASSERT(unordered || rows_to_erase == 1); + size_t last_row_ndx = prior_size - 1; + + if (unordered) { + auto end = m_observers.end(); + auto row_it = lower_bound(begin(m_observers), end, ObserverState{current_table(), row_ndx, nullptr}); + auto last_it = lower_bound(row_it, end, ObserverState{current_table(), last_row_ndx, nullptr}); + bool have_row = row_it != end && row_it->table_ndx == current_table() && row_it->row_ndx == row_ndx; + bool have_last = last_it != end && last_it->table_ndx == current_table() && last_it->row_ndx == last_row_ndx; + if (have_row && have_last) { + invalidated.push_back(row_it->info); + row_it->info = last_it->info; + row_it->changes = std::move(last_it->changes); + m_observers.erase(last_it); + } + else if (have_row) { + invalidated.push_back(row_it->info); + m_observers.erase(row_it); + } + else if (have_last) { + last_it->row_ndx = row_ndx; + std::rotate(row_it, last_it, end); + } + } + else { + for (size_t i = 0; i < m_observers.size(); ++i) { + auto& o = m_observers[i]; + if (o.table_ndx == current_table()) { + if (o.row_ndx == row_ndx) { + invalidate(&o); + --i; + } + else if (o.row_ndx > row_ndx) { + o.row_ndx -= rows_to_erase; + } + } + } + } + return true; + } + + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) + { + REALM_ASSERT(row_ndx_1 < row_ndx_2); // this is enforced by core + + auto end = m_observers.end(); + auto it_1 = lower_bound(begin(m_observers), end, ObserverState{current_table(), row_ndx_1, nullptr}); + auto it_2 = lower_bound(it_1, end, ObserverState{current_table(), row_ndx_2, nullptr}); + bool have_row_1 = it_1 != end && it_1->table_ndx == current_table() && it_1->row_ndx == row_ndx_1; + bool have_row_2 = it_2 != end && it_2->table_ndx == current_table() && it_2->row_ndx == row_ndx_2; + + if (have_row_1 && have_row_2) { + std::swap(it_1->info, it_2->info); + std::swap(it_1->changes, it_2->changes); + } + else if (have_row_1) { + it_1->row_ndx = row_ndx_2; + std::rotate(it_1, it_1 + 1, it_2); + } + else if (have_row_2) { + it_2->row_ndx = row_ndx_1; + std::rotate(it_1, it_2, end); + } + + return true; + } + + bool merge_rows(size_t from, size_t to) + { + REALM_ASSERT(from != to); + + auto end = m_observers.end(); + auto from_it = lower_bound(begin(m_observers), end, ObserverState{current_table(), from, nullptr}); + if (from_it == end || from_it->table_ndx != current_table() || from_it->row_ndx != from) + return true; + + auto to_it = lower_bound(begin(m_observers), end, ObserverState{current_table(), to, nullptr}); + // an observer for the subsuming row should not already exist + REALM_ASSERT_DEBUG(to_it == end || (ObserverState{current_table(), to, nullptr}) < *to_it); + + from_it->row_ndx = to; + if (from < to) + std::rotate(from_it, from_it + 1, to_it); + else + std::rotate(to_it, from_it, from_it + 1); + return true; + } + + bool clear_table() + { + for (size_t i = 0; i < m_observers.size(); ) { + auto& o = m_observers[i]; + if (o.table_ndx == current_table()) { + invalidate(&o); + } + else { + ++i; + } + } + return true; + } + + bool select_link_list(size_t col, size_t row, size_t) + { + m_active_linklist = nullptr; + for (auto& o : m_observers) { + if (o.table_ndx == current_table() && o.row_ndx == row) { + m_active_linklist = &get_change(o, col); + break; + } + } + return true; + } + + void append_link_list_change(ColumnInfo::Kind kind, size_t index) { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + // Active LinkList isn't observed or already has multiple kinds of changes + return; + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = kind; + o->indices.add(index); + } + else if (o->kind == kind) { + if (kind == ColumnInfo::Kind::Remove) { + o->indices.add_shifted(index); + } + else if (kind == ColumnInfo::Kind::Insert) { + o->indices.insert_at(index); + } + else { + o->indices.add(index); + } + } + else { + // Array KVO can only send a single kind of change at a time, so + // if there are multiple just give up and send "Set" + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + } + + bool link_list_set(size_t index, size_t, size_t) + { + append_link_list_change(ColumnInfo::Kind::Set, index); + return true; + } + + bool link_list_insert(size_t index, size_t, size_t) + { + append_link_list_change(ColumnInfo::Kind::Insert, index); + return true; + } + + bool link_list_erase(size_t index, size_t) + { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_nullify(size_t index, size_t) + { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_swap(size_t index1, size_t index2) + { + append_link_list_change(ColumnInfo::Kind::Set, index1); + append_link_list_change(ColumnInfo::Kind::Set, index2); + return true; + } + + bool link_list_clear(size_t old_size) + { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + + if (o->kind == ColumnInfo::Kind::Remove) + old_size += o->indices.count(); + else if (o->kind == ColumnInfo::Kind::Insert) + old_size -= o->indices.count(); + + o->indices.set(old_size); + + o->kind = ColumnInfo::Kind::Remove; + return true; + } + + bool link_list_move(size_t from, size_t to) + { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + if (from > to) { + std::swap(from, to); + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = ColumnInfo::Kind::Set; + } + if (o->kind == ColumnInfo::Kind::Set) { + for (size_t i = from; i <= to; ++i) + o->indices.add(i); + } + else { + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + return true; + } + + bool insert_column(size_t ndx, DataType, StringData, bool) + { + for (auto& observer : m_observers) { + if (observer.table_ndx == current_table()) { + expand_to(observer, ndx); + insert_empty_at(observer.changes, ndx); + } + } + return true; + } + + bool move_column(size_t from, size_t to) + { + for (auto& observer : m_observers) { + if (observer.table_ndx == current_table()) { + // have to initialize the columns one past the moved one so that + // we can later initialize any more columns after that + expand_to(observer, std::max(from, to) + 1); + rotate(observer.changes, from, to); + } + } + return true; + } + + bool move_group_level_table(size_t from, size_t to) + { + for (auto& observer : m_observers) + adjust_for_move(observer.table_ndx, from, to); + return true; + } + + bool insert_link_column(size_t ndx, DataType type, StringData name, size_t, size_t) { return insert_column(ndx, type, name, false); } + +}; + +// Extends TransactLogValidator to track changes made to LinkViews +class LinkViewObserver : public TransactLogValidationMixin, public MarkDirtyMixin { + _impl::TransactionChangeInfo& m_info; + _impl::CollectionChangeBuilder* m_active = nullptr; + + _impl::CollectionChangeBuilder* get_change() + { + auto tbl_ndx = current_table(); + if (!m_info.track_all && (tbl_ndx >= m_info.table_modifications_needed.size() || !m_info.table_modifications_needed[tbl_ndx])) + return nullptr; + if (m_info.tables.size() <= tbl_ndx) { + m_info.tables.resize(std::max(m_info.tables.size() * 2, tbl_ndx + 1)); + } + return &m_info.tables[tbl_ndx]; + } + + bool need_move_info() const + { + auto tbl_ndx = current_table(); + return m_info.track_all || (tbl_ndx < m_info.table_moves_needed.size() && m_info.table_moves_needed[tbl_ndx]); + } + +public: + LinkViewObserver(_impl::TransactionChangeInfo& info) + : m_info(info) { } + + void mark_dirty(size_t row, size_t) + { + if (auto change = get_change()) + change->modify(row); + } + + void parse_complete() + { + for (auto& table : m_info.tables) { + table.parse_complete(); + } + for (auto& list : m_info.lists) { + list.changes->clean_up_stale_moves(); + } + } + + bool select_link_list(size_t col, size_t row, size_t) + { + mark_dirty(row, col); + + m_active = nullptr; + // When there are multiple source versions there could be multiple + // change objects for a single LinkView, in which case we need to use + // the last one + for (auto it = m_info.lists.rbegin(), end = m_info.lists.rend(); it != end; ++it) { + if (it->table_ndx == current_table() && it->row_ndx == row && it->col_ndx == col) { + m_active = it->changes; + break; + } + } + return true; + } + + bool link_list_set(size_t index, size_t, size_t) + { + if (m_active) + m_active->modify(index); + return true; + } + + bool link_list_insert(size_t index, size_t, size_t) + { + if (m_active) + m_active->insert(index); + return true; + } + + bool link_list_erase(size_t index, size_t) + { + if (m_active) + m_active->erase(index); + return true; + } + + bool link_list_nullify(size_t index, size_t prior_size) + { + return link_list_erase(index, prior_size); + } + + bool link_list_swap(size_t index1, size_t index2) + { + link_list_set(index1, 0, npos); + link_list_set(index2, 0, npos); + return true; + } + + bool link_list_clear(size_t old_size) + { + if (m_active) + m_active->clear(old_size); + return true; + } + + bool link_list_move(size_t from, size_t to) + { + if (m_active) + m_active->move(from, to); + return true; + } + + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t, bool unordered) + { + REALM_ASSERT(!unordered); + if (auto change = get_change()) + change->insert(row_ndx, num_rows_to_insert, need_move_info()); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.row_ndx >= row_ndx) + list.row_ndx += num_rows_to_insert; + } + + return true; + } + + bool erase_rows(size_t row_ndx, size_t, size_t prior_num_rows, bool unordered) + { + REALM_ASSERT(unordered); + size_t last_row = prior_num_rows - 1; + + for (auto it = begin(m_info.lists); it != end(m_info.lists); ) { + if (it->table_ndx == current_table()) { + if (it->row_ndx == row_ndx) { + *it = std::move(m_info.lists.back()); + m_info.lists.pop_back(); + continue; + } + if (it->row_ndx == last_row) + it->row_ndx = row_ndx; + } + ++it; + } + + if (auto change = get_change()) + change->move_over(row_ndx, last_row, need_move_info()); + return true; + } + + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) { + REALM_ASSERT(row_ndx_1 < row_ndx_2); + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table()) { + if (list.row_ndx == row_ndx_1) + list.row_ndx = row_ndx_2; + else if (list.row_ndx == row_ndx_2) + list.row_ndx = row_ndx_1; + } + } + if (auto change = get_change()) + change->swap(row_ndx_1, row_ndx_2, need_move_info()); + return true; + } + + bool merge_rows(size_t from, size_t to) + { + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.row_ndx == from) + list.row_ndx = to; + } + if (auto change = get_change()) + change->subsume(from, to, need_move_info()); + return true; + } + + bool clear_table() + { + auto tbl_ndx = current_table(); + auto it = remove_if(begin(m_info.lists), end(m_info.lists), + [&](auto const& lv) { return lv.table_ndx == tbl_ndx; }); + m_info.lists.erase(it, end(m_info.lists)); + if (auto change = get_change()) + change->clear(std::numeric_limits::max()); + return true; + } + + bool insert_column(size_t ndx, DataType, StringData, bool) + { + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table() && list.col_ndx >= ndx) + ++list.col_ndx; + } + return true; + } + + bool insert_group_level_table(size_t ndx, size_t, StringData) + { + for (auto& list : m_info.lists) { + if (list.table_ndx >= ndx) + ++list.table_ndx; + } + insert_empty_at(m_info.tables, ndx); + insert_empty_at(m_info.table_moves_needed, ndx); + insert_empty_at(m_info.table_modifications_needed, ndx); + return true; + } + + bool move_column(size_t from, size_t to) + { + for (auto& list : m_info.lists) { + if (list.table_ndx == current_table()) + adjust_for_move(list.col_ndx, from, to); + } + return true; + } + + bool move_group_level_table(size_t from, size_t to) + { + for (auto& list : m_info.lists) + adjust_for_move(list.table_ndx, from, to); + rotate(m_info.tables, from, to); + rotate(m_info.table_modifications_needed, from, to); + rotate(m_info.table_moves_needed, from, to); + return true; + } + + bool insert_link_column(size_t ndx, DataType type, StringData name, size_t, size_t) { return insert_column(ndx, type, name, false); } + +}; +} // anonymous namespace + +namespace realm { +namespace _impl { +namespace transaction { +void advance(SharedGroup& sg, BindingContext* context, SchemaMode schema_mode, SharedGroup::VersionID version) +{ + TransactLogObserver(context, sg, [&](auto&&... args) { + LangBindHelper::advance_read(sg, std::move(args)..., version); + }, schema_mode); +} + +void begin_without_validation(SharedGroup& sg) +{ + LangBindHelper::promote_to_write(sg); +} + +void begin(SharedGroup& sg, BindingContext* context, SchemaMode schema_mode) +{ + TransactLogObserver(context, sg, [&](auto&&... args) { + LangBindHelper::promote_to_write(sg, std::move(args)...); + }, schema_mode); +} + +void commit(SharedGroup& sg, BindingContext* context) +{ + LangBindHelper::commit_and_continue_as_read(sg); + + if (context) { + context->did_change({}, {}); + } +} + +void cancel(SharedGroup& sg, BindingContext* context) +{ + TransactLogObserver(context, sg, [&](auto&&... args) { + LangBindHelper::rollback_and_continue_as_read(sg, std::move(args)...); + }, util::none); +} + +void advance(SharedGroup& sg, + TransactionChangeInfo& info, + SharedGroup::VersionID version) +{ + if (!info.track_all && info.table_modifications_needed.empty() && info.lists.empty()) { + LangBindHelper::advance_read(sg, version); + } + else { + LangBindHelper::advance_read(sg, LinkViewObserver(info), version); + } + +} + +} // namespace transaction +} // namespace _impl +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp b/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp new file mode 100644 index 0000000..d181667 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/impl/weak_realm_notifier.cpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "impl/weak_realm_notifier.hpp" + +#include "shared_realm.hpp" +#include "util/event_loop_signal.hpp" + +using namespace realm; +using namespace realm::_impl; + +WeakRealmNotifier::WeakRealmNotifier(const std::shared_ptr& realm, bool cache) +: m_realm(realm) +, m_realm_key(realm.get()) +, m_cache(cache) +, m_signal(std::make_shared>(Callback{realm})) +{ +} + +WeakRealmNotifier::~WeakRealmNotifier() = default; + +void WeakRealmNotifier::Callback::operator()() +{ + if (auto realm = weak_realm.lock()) { + realm->notify(); + } +} + +void WeakRealmNotifier::notify() +{ + m_signal->notify(); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/index_set.cpp b/Pods/Realm/Realm/ObjectStore/src/index_set.cpp new file mode 100644 index 0000000..a5c30c2 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/index_set.cpp @@ -0,0 +1,707 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "index_set.hpp" + +#include + +#include + +using namespace realm; +using namespace realm::_impl; + +const size_t IndexSet::npos; + +template +void MutableChunkedRangeVectorIterator::set(size_t front, size_t back) +{ + this->m_outer->count -= this->m_inner->second - this->m_inner->first; + if (this->offset() == 0) { + this->m_outer->begin = front; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end = back; + } + this->m_outer->count += back - front; + this->m_inner->first = front; + this->m_inner->second = back; +} + +template +void MutableChunkedRangeVectorIterator::adjust(ptrdiff_t front, ptrdiff_t back) +{ + if (this->offset() == 0) { + this->m_outer->begin += front; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end += back; + } + this->m_outer->count += -front + back; + this->m_inner->first += front; + this->m_inner->second += back; +} + +template +void MutableChunkedRangeVectorIterator::shift(ptrdiff_t distance) +{ + if (this->offset() == 0) { + this->m_outer->begin += distance; + } + if (this->m_inner == &this->m_outer->data.back()) { + this->m_outer->end += distance; + } + this->m_inner->first += distance; + this->m_inner->second += distance; +} + +void ChunkedRangeVector::push_back(value_type value) +{ + if (!empty() && m_data.back().data.size() < max_size) { + auto& range = m_data.back(); + REALM_ASSERT(range.end <= value.first); + + range.data.push_back(value); + range.count += value.second - value.first; + range.end = value.second; + } + else { + m_data.push_back({{std::move(value)}, value.first, value.second, value.second - value.first}); + } + verify(); +} + +ChunkedRangeVector::iterator ChunkedRangeVector::insert(iterator pos, value_type value) +{ + if (pos.m_outer == m_data.end()) { + push_back(std::move(value)); + return std::prev(end()); + } + + pos = ensure_space(pos); + auto& chunk = *pos.m_outer; + pos.m_inner = &*chunk.data.insert(pos.m_outer->data.begin() + pos.offset(), value); + chunk.count += value.second - value.first; + chunk.begin = std::min(chunk.begin, value.first); + chunk.end = std::max(chunk.end, value.second); + + verify(); + return pos; +} + +ChunkedRangeVector::iterator ChunkedRangeVector::ensure_space(iterator pos) +{ + if (pos.m_outer->data.size() + 1 <= max_size) + return pos; + + auto offset = pos.offset(); + + // Split the chunk in half to make space for the new insertion + auto new_pos = m_data.insert(pos.m_outer + 1, Chunk{}); + auto prev = new_pos - 1; + auto to_move = max_size / 2; + new_pos->data.reserve(to_move); + new_pos->data.assign(prev->data.end() - to_move, prev->data.end()); + prev->data.resize(prev->data.size() - to_move); + + size_t moved_count = 0; + for (auto range : new_pos->data) + moved_count += range.second - range.first; + + prev->end = prev->data.back().second; + prev->count -= moved_count; + new_pos->begin = new_pos->data.front().first; + new_pos->end = new_pos->data.back().second; + new_pos->count = moved_count; + + if (offset >= to_move) { + pos.m_outer = new_pos; + offset -= to_move; + } + else { + pos.m_outer = prev; + } + pos.m_end = m_data.end(); + pos.m_inner = &pos.m_outer->data[offset]; + verify(); + return pos; +} + +ChunkedRangeVector::iterator ChunkedRangeVector::erase(iterator pos) +{ + auto offset = pos.offset(); + auto& chunk = *pos.m_outer; + chunk.count -= pos->second - pos->first; + chunk.data.erase(chunk.data.begin() + offset); + + if (chunk.data.size() == 0) { + pos.m_outer = m_data.erase(pos.m_outer); + pos.m_end = m_data.end(); + pos.m_inner = pos.m_outer == m_data.end() ? nullptr : &pos.m_outer->data.front(); + verify(); + return pos; + } + + chunk.begin = chunk.data.front().first; + chunk.end = chunk.data.back().second; + if (offset < chunk.data.size()) + pos.m_inner = &chunk.data[offset]; + else { + ++pos.m_outer; + pos.m_inner = pos.m_outer == pos.m_end ? nullptr : &pos.m_outer->data.front(); + } + + verify(); + return pos; +} + +void ChunkedRangeVector::verify() const noexcept +{ +#ifdef REALM_DEBUG + size_t prev_end = -1; + for (auto range : *this) { + REALM_ASSERT(range.first < range.second); + REALM_ASSERT(prev_end == size_t(-1) || range.first > prev_end); + prev_end = range.second; + } + + for (auto& chunk : m_data) { + REALM_ASSERT(!chunk.data.empty()); + REALM_ASSERT(chunk.data.front().first == chunk.begin); + REALM_ASSERT(chunk.data.back().second == chunk.end); + REALM_ASSERT(chunk.count <= chunk.end - chunk.begin); + size_t count = 0; + for (auto range : chunk.data) + count += range.second - range.first; + REALM_ASSERT(count == chunk.count); + } +#endif +} + +namespace { +class ChunkedRangeVectorBuilder { +public: + using value_type = std::pair; + + ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected); + void push_back(size_t index); + void push_back(std::pair range); + std::vector finalize(); +private: + std::vector m_data; + size_t m_outer_pos = 0; +}; + +ChunkedRangeVectorBuilder::ChunkedRangeVectorBuilder(ChunkedRangeVector const& expected) +{ + size_t size = 0; + for (auto const& chunk : expected.m_data) + size += chunk.data.size(); + m_data.resize(size / ChunkedRangeVector::max_size + 1); + for (size_t i = 0; i < m_data.size() - 1; ++i) + m_data[i].data.reserve(ChunkedRangeVector::max_size); +} + +void ChunkedRangeVectorBuilder::push_back(size_t index) +{ + push_back({index, index + 1}); +} + +void ChunkedRangeVectorBuilder::push_back(std::pair range) +{ + auto& chunk = m_data[m_outer_pos]; + if (chunk.data.empty()) { + chunk.data.push_back(range); + chunk.count = range.second - range.first; + chunk.begin = range.first; + } + else if (range.first == chunk.data.back().second) { + chunk.data.back().second = range.second; + chunk.count += range.second - range.first; + } + else if (chunk.data.size() < ChunkedRangeVector::max_size) { + chunk.data.push_back(range); + chunk.count += range.second - range.first; + } + else { + chunk.end = chunk.data.back().second; + ++m_outer_pos; + if (m_outer_pos >= m_data.size()) + m_data.push_back({{range}, range.first, 0, 1}); + else { + auto& chunk = m_data[m_outer_pos]; + chunk.data.push_back(range); + chunk.begin = range.first; + chunk.count = range.second - range.first; + } + } +} + +std::vector ChunkedRangeVectorBuilder::finalize() +{ + if (!m_data.empty()) { + m_data.resize(m_outer_pos + 1); + if (m_data.back().data.empty()) + m_data.pop_back(); + else + m_data.back().end = m_data.back().data.back().second; + } + return std::move(m_data); +} +} + +IndexSet::IndexSet(std::initializer_list values) +{ + for (size_t v : values) + add(v); +} + +bool IndexSet::contains(size_t index) const +{ + auto it = const_cast(this)->find(index); + return it != end() && it->first <= index; +} + +size_t IndexSet::count(size_t start_index, size_t end_index) const +{ + auto it = const_cast(this)->find(start_index); + const auto end = this->end(); + if (it == end || it->first >= end_index) { + return 0; + } + if (it->second >= end_index) + return std::min(it->second, end_index) - std::max(it->first, start_index); + + size_t ret = 0; + + if (start_index > it->first || it.offset() != 0) { + // Start index is in the middle of a chunk, so start by counting the + // rest of that chunk + ret = it->second - std::max(it->first, start_index); + for (++it; it != end && it->second < end_index && it.offset() != 0; ++it) { + ret += it->second - it->first; + } + if (it != end && it->first < end_index && it.offset() != 0) + ret += end_index - it->first; + if (it == end || it->second >= end_index) + return ret; + } + + // Now count all complete chunks that fall within the range + while (it != end && it.outer()->end <= end_index) { + REALM_ASSERT_DEBUG(it.offset() == 0); + ret += it.outer()->count; + it.next_chunk(); + } + + // Cound all complete ranges within the last chunk + while (it != end && it->second <= end_index) { + ret += it->second - it->first; + ++it; + } + + // And finally add in the partial last range + if (it != end && it->first < end_index) + ret += end_index - it->first; + return ret; +} + +IndexSet::iterator IndexSet::find(size_t index) +{ + return find(index, begin()); +} + +IndexSet::iterator IndexSet::find(size_t index, iterator begin) +{ + auto it = std::find_if(begin.outer(), m_data.end(), + [&](auto const& lft) { return lft.end > index; }); + if (it == m_data.end()) + return end(); + if (index < it->begin) + return iterator(it, m_data.end(), &it->data[0]); + auto inner_begin = it->data.begin(); + if (it == begin.outer()) + inner_begin += begin.offset(); + auto inner = std::lower_bound(inner_begin, it->data.end(), index, + [&](auto const& lft, auto) { return lft.second <= index; }); + REALM_ASSERT_DEBUG(inner != it->data.end()); + + return iterator(it, m_data.end(), &*inner); +} + +void IndexSet::add(size_t index) +{ + do_add(find(index), index); +} + +void IndexSet::add(IndexSet const& other) +{ + auto it = begin(); + for (size_t index : other.as_indexes()) { + it = do_add(find(index, it), index); + } +} + +size_t IndexSet::add_shifted(size_t index) +{ + iterator it = begin(), end = this->end(); + + // Shift for any complete chunks before the target + for (; it != end && it.outer()->end <= index; it.next_chunk()) + index += it.outer()->count; + + // And any ranges within the last partial chunk + for (; it != end && it->first <= index; ++it) + index += it->second - it->first; + + do_add(it, index); + return index; +} + +void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values) +{ + if (values.empty()) + return; + +#ifdef REALM_DEBUG + size_t expected = std::distance(as_indexes().begin(), as_indexes().end()); + for (auto index : values.as_indexes()) { + if (!shifted_by.contains(index)) + ++expected; + } +#endif + + ChunkedRangeVectorBuilder builder(*this); + + auto old_it = cbegin(), old_end = cend(); + auto shift_it = shifted_by.cbegin(), shift_end = shifted_by.cend(); + + size_t skip_until = 0; + size_t old_shift = 0; + size_t new_shift = 0; + for (size_t index : values.as_indexes()) { + for (; shift_it != shift_end && shift_it->first <= index; ++shift_it) { + new_shift += shift_it->second - shift_it->first; + skip_until = shift_it->second; + } + if (index < skip_until) + continue; + + for (; old_it != old_end && old_it->first <= index - new_shift + old_shift; ++old_it) { + for (size_t i = old_it->first; i < old_it->second; ++i) + builder.push_back(i); + old_shift += old_it->second - old_it->first; + } + + REALM_ASSERT(index >= new_shift); + builder.push_back(index - new_shift + old_shift); + } + + copy(old_it, old_end, std::back_inserter(builder)); + m_data = builder.finalize(); + +#ifdef REALM_DEBUG + REALM_ASSERT((size_t)std::distance(as_indexes().begin(), as_indexes().end()) == expected); +#endif +} + +void IndexSet::set(size_t len) +{ + clear(); + if (len) { + push_back({0, len}); + } +} + +void IndexSet::insert_at(size_t index, size_t count) +{ + REALM_ASSERT(count > 0); + + auto pos = find(index); + auto end = this->end(); + bool in_existing = false; + if (pos != end) { + if (pos->first <= index) { + in_existing = true; + pos.adjust(0, count); + } + else { + pos.shift(count); + } + for (auto it = std::next(pos); it != end; ++it) + it.shift(count); + } + if (!in_existing) { + for (size_t i = 0; i < count; ++i) + pos = std::next(do_add(pos, index + i)); + } + + verify(); +} + +void IndexSet::insert_at(IndexSet const& positions) +{ + if (positions.empty()) + return; + if (empty()) { + *this = positions; + return; + } + + IndexIterator begin1 = cbegin(), begin2 = positions.cbegin(); + IndexIterator end1 = cend(), end2 = positions.cend(); + + ChunkedRangeVectorBuilder builder(*this); + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 + shift < *begin2) { + builder.push_back(*begin1++ + shift); + } + else { + ++shift; + builder.push_back(*begin2++); + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 + shift); + for (; begin2 != end2; ++begin2) + builder.push_back(*begin2); + + m_data = builder.finalize(); +} + +void IndexSet::shift_for_insert_at(size_t index, size_t count) +{ + REALM_ASSERT(count > 0); + + auto it = find(index); + if (it == end()) + return; + + for (auto pos = it, end = this->end(); pos != end; ++pos) + pos.shift(count); + + // If the range contained the insertion point, split the range and move + // the part of it before the insertion point back + if (it->first < index + count) { + auto old_second = it->second; + it.set(it->first - count, index); + insert(std::next(it), {index + count, old_second}); + } + verify(); +} + +void IndexSet::shift_for_insert_at(realm::IndexSet const& values) +{ + if (empty() || values.empty()) + return; + if (values.m_data.front().begin >= m_data.back().end) + return; + + IndexIterator begin1 = cbegin(), begin2 = values.cbegin(); + IndexIterator end1 = cend(), end2 = values.cend(); + + ChunkedRangeVectorBuilder builder(*this); + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 + shift < *begin2) { + builder.push_back(*begin1++ + shift); + } + else { + ++shift; + begin2++; + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 + shift); + + m_data = builder.finalize(); +} + +void IndexSet::erase_at(size_t index) +{ + auto it = find(index); + if (it != end()) + do_erase(it, index); +} + +void IndexSet::erase_at(IndexSet const& positions) +{ + if (empty() || positions.empty()) + return; + + ChunkedRangeVectorBuilder builder(*this); + + IndexIterator begin1 = cbegin(), begin2 = positions.cbegin(); + IndexIterator end1 = cend(), end2 = positions.cend(); + + size_t shift = 0; + while (begin1 != end1 && begin2 != end2) { + if (*begin1 < *begin2) { + builder.push_back(*begin1++ - shift); + } + else if (*begin1 == *begin2) { + ++shift; + ++begin1; + ++begin2; + } + else { + ++shift; + ++begin2; + } + } + for (; begin1 != end1; ++begin1) + builder.push_back(*begin1 - shift); + + m_data = builder.finalize(); +} + +size_t IndexSet::erase_or_unshift(size_t index) +{ + auto shifted = index; + iterator it = begin(), end = this->end(); + + // Shift for any complete chunks before the target + for (; it != end && it.outer()->end <= index; it.next_chunk()) + shifted -= it.outer()->count; + + // And any ranges within the last partial chunk + for (; it != end && it->second <= index; ++it) + shifted -= it->second - it->first; + + if (it == end) + return shifted; + + if (it->first <= index) + shifted = npos; + + do_erase(it, index); + + return shifted; +} + +void IndexSet::do_erase(iterator it, size_t index) +{ + if (it->first <= index) { + if (it->first + 1 == it->second) { + it = erase(it); + } + else { + it.adjust(0, -1); + ++it; + } + } + else if (it != begin() && std::prev(it)->second + 1 == it->first) { + std::prev(it).adjust(0, it->second - it->first); + it = erase(it); + } + + for (; it != end(); ++it) + it.shift(-1); +} + +IndexSet::iterator IndexSet::do_remove(iterator it, size_t begin, size_t end) +{ + for (it = find(begin, it); it != this->end() && it->first < end; it = find(begin, it)) { + // Trim off any part of the range to remove that's before the matching range + begin = std::max(it->first, begin); + + // If the matching range extends to both sides of the range to remove, + // split it on the range to remove + if (it->first < begin && it->second > end) { + auto old_second = it->second; + it.set(it->first, begin); + it = std::prev(insert(std::next(it), {end, old_second})); + } + // Range to delete now coverages (at least) one end of the matching range + else if (begin == it->first && end >= it->second) + it = erase(it); + else if (begin == it->first) + it.set(end, it->second); + else + it.set(it->first, begin); + } + return it; +} + +void IndexSet::remove(size_t index, size_t count) +{ + do_remove(find(index), index, index + count); +} + +void IndexSet::remove(realm::IndexSet const& values) +{ + auto it = begin(); + for (auto range : values) { + it = do_remove(it, range.first, range.second); + if (it == end()) + return; + } +} + +size_t IndexSet::shift(size_t index) const +{ + // FIXME: optimize + for (auto range : *this) { + if (range.first > index) + break; + index += range.second - range.first; + } + return index; +} + +size_t IndexSet::unshift(size_t index) const +{ + REALM_ASSERT_DEBUG(!contains(index)); + return index - count(0, index); +} + +void IndexSet::clear() +{ + m_data.clear(); +} + +IndexSet::iterator IndexSet::do_add(iterator it, size_t index) +{ + verify(); + bool more_before = it != begin(), valid = it != end(); + REALM_ASSERT(!more_before || index >= std::prev(it)->second); + if (valid && it->first <= index && it->second > index) { + // index is already in set + return it; + } + if (more_before && std::prev(it)->second == index) { + auto prev = std::prev(it); + // index is immediately after an existing range + prev.adjust(0, 1); + + if (valid && prev->second == it->first) { + // index joins two existing ranges + prev.adjust(0, it->second - it->first); + return std::prev(erase(it)); + } + return prev; + } + if (valid && it->first == index + 1) { + // index is immediately before an existing range + it.adjust(-1, 0); + return it; + } + + // index is not next to an existing range + return insert(it, {index, index + 1}); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/list.cpp b/Pods/Realm/Realm/ObjectStore/src/list.cpp new file mode 100644 index 0000000..4bab43a --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/list.cpp @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "list.hpp" + +#include "impl/list_notifier.hpp" +#include "impl/realm_coordinator.hpp" +#include "object_store.hpp" +#include "results.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" +#include "util/format.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +List::List() noexcept = default; +List::~List() = default; + +List::List(const List&) = default; +List& List::operator=(const List&) = default; +List::List(List&&) = default; +List& List::operator=(List&&) = default; + +List::List(std::shared_ptr r, LinkViewRef l) noexcept +: m_realm(std::move(r)) +, m_link_view(std::move(l)) +{ +} + +const ObjectSchema& List::get_object_schema() const +{ + verify_attached(); + + if (!m_object_schema) { + auto object_type = ObjectStore::object_type_for_table_name(m_link_view->get_target_table().get_name()); + auto it = m_realm->schema().find(object_type); + REALM_ASSERT(it != m_realm->schema().end()); + m_object_schema = &*it; + } + return *m_object_schema; +} + +Query List::get_query() const +{ + verify_attached(); + return m_link_view->get_target_table().where(m_link_view); +} + +size_t List::get_origin_row_index() const +{ + verify_attached(); + return m_link_view->get_origin_row_index(); +} + +void List::verify_valid_row(size_t row_ndx, bool insertion) const +{ + size_t size = m_link_view->size(); + if (row_ndx > size || (!insertion && row_ndx == size)) { + throw OutOfBoundsIndexException{row_ndx, size + insertion}; + } +} + +bool List::is_valid() const +{ + m_realm->verify_thread(); + return m_link_view && m_link_view->is_attached(); +} + +void List::verify_attached() const +{ + if (!is_valid()) { + throw InvalidatedException(); + } +} + +void List::verify_in_transaction() const +{ + verify_attached(); + if (!m_realm->is_in_transaction()) { + throw InvalidTransactionException("Must be in a write transaction"); + } +} + +size_t List::size() const +{ + verify_attached(); + return m_link_view->size(); +} + +RowExpr List::get(size_t row_ndx) const +{ + verify_attached(); + verify_valid_row(row_ndx); + return m_link_view->get(row_ndx); +} + +size_t List::get_unchecked(size_t row_ndx) const noexcept +{ + return m_link_view->get(row_ndx).get_index(); +} + +size_t List::find(ConstRow const& row) const +{ + verify_attached(); + + if (!row.is_attached() || row.get_table() != &m_link_view->get_target_table()) { + return not_found; + } + + return m_link_view->find(row.get_index()); +} + +void List::add(size_t target_row_ndx) +{ + verify_in_transaction(); + m_link_view->add(target_row_ndx); +} + +void List::insert(size_t row_ndx, size_t target_row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx, true); + m_link_view->insert(row_ndx, target_row_ndx); +} + +void List::move(size_t source_ndx, size_t dest_ndx) +{ + verify_in_transaction(); + verify_valid_row(source_ndx); + verify_valid_row(dest_ndx); // Can't be one past end due to removing one earlier + m_link_view->move(source_ndx, dest_ndx); +} + +void List::remove(size_t row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + m_link_view->remove(row_ndx); +} + +void List::remove_all() +{ + verify_in_transaction(); + m_link_view->clear(); +} + +void List::set(size_t row_ndx, size_t target_row_ndx) +{ + verify_in_transaction(); + verify_valid_row(row_ndx); + m_link_view->set(row_ndx, target_row_ndx); +} + +void List::swap(size_t ndx1, size_t ndx2) +{ + verify_in_transaction(); + verify_valid_row(ndx1); + verify_valid_row(ndx2); + m_link_view->swap(ndx1, ndx2); +} + +void List::delete_all() +{ + verify_in_transaction(); + m_link_view->remove_all_target_rows(); +} + +Results List::sort(SortDescriptor order) +{ + verify_attached(); + return Results(m_realm, m_link_view, util::none, std::move(order)); +} + +Results List::filter(Query q) +{ + verify_attached(); + return Results(m_realm, m_link_view, get_query().and_query(std::move(q))); +} + +Results List::snapshot() const +{ + verify_attached(); + return Results(m_realm, m_link_view).snapshot(); +} + +// These definitions rely on that LinkViews are interned by core +bool List::operator==(List const& rgt) const noexcept +{ + return m_link_view.get() == rgt.m_link_view.get(); +} + +namespace std { +size_t hash::operator()(realm::List const& list) const +{ + return std::hash()(list.m_link_view.get()); +} +} + +NotificationToken List::add_notification_callback(CollectionChangeCallback cb) +{ + verify_attached(); + if (!m_notifier) { + m_notifier = std::make_shared(m_link_view, m_realm); + RealmCoordinator::register_notifier(m_notifier); + } + return {m_notifier, m_notifier->add_callback(std::move(cb))}; +} + +List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +, requested(r), valid_count(c) {} diff --git a/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp b/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp new file mode 100644 index 0000000..dee4f44 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/object_schema.cpp @@ -0,0 +1,222 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "object_schema.hpp" + +#include "object_store.hpp" +#include "property.hpp" +#include "schema.hpp" + +#include "util/format.hpp" + +#include +#include +#include + +using namespace realm; + +#define ASSERT_PROPERTY_TYPE_VALUE(property, type) \ + static_assert(static_cast(PropertyType::property) == type_##type, \ + "PropertyType and DataType must have the same values") + +ASSERT_PROPERTY_TYPE_VALUE(Int, Int); +ASSERT_PROPERTY_TYPE_VALUE(Bool, Bool); +ASSERT_PROPERTY_TYPE_VALUE(Float, Float); +ASSERT_PROPERTY_TYPE_VALUE(Double, Double); +ASSERT_PROPERTY_TYPE_VALUE(Data, Binary); +ASSERT_PROPERTY_TYPE_VALUE(Date, Timestamp); +ASSERT_PROPERTY_TYPE_VALUE(Any, Mixed); +ASSERT_PROPERTY_TYPE_VALUE(Object, Link); +ASSERT_PROPERTY_TYPE_VALUE(Array, LinkList); + +ObjectSchema::ObjectSchema() = default; +ObjectSchema::~ObjectSchema() = default; + +ObjectSchema::ObjectSchema(std::string name, std::initializer_list persisted_properties) +: name(std::move(name)) +, persisted_properties(persisted_properties) +{ + for (auto const& prop : persisted_properties) { + if (prop.is_primary) { + primary_key = prop.name; + } + } +} + +ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : name(name) { + ConstTableRef table; + if (index < group.size()) { + table = group.get_table(index); + } + else { + table = ObjectStore::table_for_object_type(group, name); + } + + size_t count = table->get_column_count(); + persisted_properties.reserve(count); + for (size_t col = 0; col < count; col++) { + Property property; + property.name = table->get_column_name(col).data(); + property.type = (PropertyType)table->get_column_type(col); + property.is_indexed = table->has_search_index(col); + property.is_nullable = table->is_nullable(col) || property.type == PropertyType::Object; + property.table_column = col; + if (property.type == PropertyType::Object || property.type == PropertyType::Array) { + // set link type for objects and arrays + ConstTableRef linkTable = table->get_link_target(col); + property.object_type = ObjectStore::object_type_for_table_name(linkTable->get_name().data()); + } + persisted_properties.push_back(std::move(property)); + } + + primary_key = realm::ObjectStore::get_primary_key_for_object(group, name); + set_primary_key_property(); +} + +Property *ObjectSchema::property_for_name(StringData name) { + for (auto& prop : persisted_properties) { + if (StringData(prop.name) == name) { + return ∝ + } + } + for (auto& prop : computed_properties) { + if (StringData(prop.name) == name) { + return ∝ + } + } + return nullptr; +} + +const Property *ObjectSchema::property_for_name(StringData name) const { + return const_cast(this)->property_for_name(name); +} + +void ObjectSchema::set_primary_key_property() +{ + if (primary_key.length()) { + if (auto primary_key_prop = primary_key_property()) { + primary_key_prop->is_primary = true; + } + } +} + +static void validate_property(Schema const& schema, + std::string const& object_name, + Property const& prop, + Property const** primary, + std::vector& exceptions) +{ + // check nullablity + if (prop.is_nullable && !prop.type_is_nullable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be nullable.", + object_name, prop.name, string_for_property_type(prop.type)); + } + else if (prop.type == PropertyType::Object && !prop.is_nullable) { + exceptions.emplace_back("Property '%1.%2' of type 'Object' must be nullable.", object_name, prop.name); + } + + // check primary keys + if (prop.is_primary) { + if (!prop.is_indexable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be made the primary key.", + object_name, prop.name, string_for_property_type(prop.type)); + } + if (*primary) { + exceptions.emplace_back("Properties'%1' and '%2' are both marked as the primary key of '%3'.", + prop.name, (*primary)->name, object_name); + } + *primary = ∝ + } + + // check indexable + if (prop.is_indexed && !prop.is_indexable()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be indexed.", + object_name, prop.name, string_for_property_type(prop.type)); + } + + // check that only link properties have object types + if (prop.type != PropertyType::LinkingObjects && !prop.link_origin_property_name.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an origin property name.", + object_name, prop.name, string_for_property_type(prop.type)); + } + else if (prop.type == PropertyType::LinkingObjects && prop.link_origin_property_name.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' must have an origin property name.", + object_name, prop.name, string_for_property_type(prop.type)); + } + + if (prop.type != PropertyType::Object && prop.type != PropertyType::Array && prop.type != PropertyType::LinkingObjects) { + if (!prop.object_type.empty()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.", + object_name, prop.name, string_for_property_type(prop.type)); + } + return; + } + + + // check that the object_type is valid for link properties + auto it = schema.find(prop.object_type); + if (it == schema.end()) { + exceptions.emplace_back("Property '%1.%2' of type '%3' has unknown object type '%4'", + object_name, prop.name, string_for_property_type(prop.type), prop.object_type); + return; + } + if (prop.type != PropertyType::LinkingObjects) { + return; + } + + const Property *origin_property = it->property_for_name(prop.link_origin_property_name); + if (!origin_property) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name); + } + else if (origin_property->type != PropertyType::Object && origin_property->type != PropertyType::Array) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name); + } + else if (origin_property->object_type != object_name) { + exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to type '%5'", + prop.object_type, prop.link_origin_property_name, + object_name, prop.name, origin_property->object_type); + } +} + +void ObjectSchema::validate(Schema const& schema, std::vector& exceptions) const +{ + const Property *primary = nullptr; + for (auto const& prop : persisted_properties) { + validate_property(schema, name, prop, &primary, exceptions); + } + for (auto const& prop : computed_properties) { + validate_property(schema, name, prop, &primary, exceptions); + } + + if (!primary_key.empty() && !primary && !primary_key_property()) { + exceptions.emplace_back("Specified primary key '%1.%2' does not exist.", name, primary_key); + } +} + +namespace realm { +bool operator==(ObjectSchema const& a, ObjectSchema const& b) +{ + return std::tie(a.name, a.primary_key, a.persisted_properties, a.computed_properties) + == std::tie(b.name, b.primary_key, b.persisted_properties, b.computed_properties); + +} +} diff --git a/Pods/Realm/Realm/ObjectStore/src/object_store.cpp b/Pods/Realm/Realm/ObjectStore/src/object_store.cpp new file mode 100644 index 0000000..e86f4dd --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/object_store.cpp @@ -0,0 +1,810 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "object_store.hpp" + +#include "object_schema.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" +#include "util/format.hpp" + +#include +#include +#include +#include + +#include + +using namespace realm; + +const uint64_t ObjectStore::NotVersioned = std::numeric_limits::max(); + +namespace { +const char * const c_metadataTableName = "metadata"; +const char * const c_versionColumnName = "version"; +const size_t c_versionColumnIndex = 0; + +const char * const c_primaryKeyTableName = "pk"; +const char * const c_primaryKeyObjectClassColumnName = "pk_table"; +const size_t c_primaryKeyObjectClassColumnIndex = 0; +const char * const c_primaryKeyPropertyNameColumnName = "pk_property"; +const size_t c_primaryKeyPropertyNameColumnIndex = 1; + +const size_t c_zeroRowIndex = 0; + +const char c_object_table_prefix[] = "class_"; + +void create_metadata_tables(Group& group) { + TableRef table = group.get_or_add_table(c_primaryKeyTableName); + if (table->get_column_count() == 0) { + table->add_column(type_String, c_primaryKeyObjectClassColumnName); + table->add_column(type_String, c_primaryKeyPropertyNameColumnName); + } + table->add_search_index(table->get_column_index(c_primaryKeyObjectClassColumnName)); + + table = group.get_or_add_table(c_metadataTableName); + if (table->get_column_count() == 0) { + table->add_column(type_Int, c_versionColumnName); + + // set initial version + table->add_empty_row(); + table->set_int(c_versionColumnIndex, c_zeroRowIndex, ObjectStore::NotVersioned); + } +} + +void set_schema_version(Group& group, uint64_t version) { + TableRef table = group.get_or_add_table(c_metadataTableName); + table->set_int(c_versionColumnIndex, c_zeroRowIndex, version); +} + +template +auto table_for_object_schema(Group& group, ObjectSchema const& object_schema) +{ + return ObjectStore::table_for_object_type(group, object_schema.name); +} + +void add_index(Table& table, size_t col) +{ + try { + table.add_search_index(col); + } + catch (LogicError const&) { + throw std::logic_error(util::format("Cannot index property '%1.%2': indexing properties of type '%3' is not yet implemented.", + ObjectStore::object_type_for_table_name(table.get_name()), + table.get_column_name(col), + string_for_property_type((PropertyType)table.get_column_type(col)))); + } +} + +void insert_column(Group& group, Table& table, Property const& property, size_t col_ndx) +{ + if (property.type == PropertyType::Object || property.type == PropertyType::Array) { + auto target_name = ObjectStore::table_name_for_object_type(property.object_type); + TableRef link_table = group.get_or_add_table(target_name); + table.insert_column_link(col_ndx, DataType(property.type), property.name, *link_table); + } + else { + table.insert_column(col_ndx, DataType(property.type), property.name, property.is_nullable); + if (property.requires_index()) + add_index(table, col_ndx); + } +} + +void add_column(Group& group, Table& table, Property const& property) +{ + insert_column(group, table, property, table.get_column_count()); +} + +void replace_column(Group& group, Table& table, Property const& old_property, Property const& new_property) +{ + insert_column(group, table, new_property, old_property.table_column); + table.remove_column(old_property.table_column + 1); +} + +TableRef create_table(Group& group, ObjectSchema const& object_schema) +{ + auto name = ObjectStore::table_name_for_object_type(object_schema.name); + auto table = group.get_or_add_table(name); + if (table->get_column_count() > 0) { + return table; + } + + for (auto const& prop : object_schema.persisted_properties) { + add_column(group, *table, prop); + } + + ObjectStore::set_primary_key_for_object(group, object_schema.name, object_schema.primary_key); + + return table; +} + +void copy_property_values(Property const& prop, Table& table) +{ + auto copy_property_values = [&](auto getter, auto setter) { + for (size_t i = 0, count = table.size(); i < count; i++) { + bool is_default = false; + (table.*setter)(prop.table_column, i, (table.*getter)(prop.table_column + 1, i), + is_default); + } + }; + + switch (prop.type) { + case PropertyType::Int: + copy_property_values(&Table::get_int, &Table::set_int); + break; + case PropertyType::Bool: + copy_property_values(&Table::get_bool, &Table::set_bool); + break; + case PropertyType::Float: + copy_property_values(&Table::get_float, &Table::set_float); + break; + case PropertyType::Double: + copy_property_values(&Table::get_double, &Table::set_double); + break; + case PropertyType::String: + copy_property_values(&Table::get_string, &Table::set_string); + break; + case PropertyType::Data: + copy_property_values(&Table::get_binary, &Table::set_binary); + break; + case PropertyType::Date: + copy_property_values(&Table::get_timestamp, &Table::set_timestamp); + break; + default: + break; + } +} + +void make_property_optional(Group& group, Table& table, Property property) +{ + property.is_nullable = true; + insert_column(group, table, property, property.table_column); + copy_property_values(property, table); + table.remove_column(property.table_column + 1); +} + +void make_property_required(Group& group, Table& table, Property property) +{ + property.is_nullable = false; + insert_column(group, table, property, property.table_column); + table.remove_column(property.table_column + 1); +} + +void validate_primary_column_uniqueness(Group const& group, StringData object_type, StringData primary_property) +{ + auto table = ObjectStore::table_for_object_type(group, object_type); + if (table->get_distinct_view(table->get_column_index(primary_property)).size() != table->size()) { + throw DuplicatePrimaryKeyValueException(object_type, primary_property); + } +} + +void validate_primary_column_uniqueness(Group const& group) +{ + auto pk_table = group.get_table(c_primaryKeyTableName); + for (size_t i = 0, count = pk_table->size(); i < count; ++i) { + validate_primary_column_uniqueness(group, + pk_table->get_string(c_primaryKeyObjectClassColumnIndex, i), + pk_table->get_string(c_primaryKeyPropertyNameColumnIndex, i)); + } +} +} // anonymous namespace + +uint64_t ObjectStore::get_schema_version(Group const& group) { + ConstTableRef table = group.get_table(c_metadataTableName); + if (!table || table->get_column_count() == 0) { + return ObjectStore::NotVersioned; + } + return table->get_int(c_versionColumnIndex, c_zeroRowIndex); +} + +StringData ObjectStore::get_primary_key_for_object(Group const& group, StringData object_type) { + ConstTableRef table = group.get_table(c_primaryKeyTableName); + if (!table) { + return ""; + } + size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); + if (row == not_found) { + return ""; + } + return table->get_string(c_primaryKeyPropertyNameColumnIndex, row); +} + +void ObjectStore::set_primary_key_for_object(Group& group, StringData object_type, StringData primary_key) { + TableRef table = group.get_table(c_primaryKeyTableName); + + // get row or create if new object and populate + size_t row = table->find_first_string(c_primaryKeyObjectClassColumnIndex, object_type); + if (row == not_found && primary_key.size()) { + row = table->add_empty_row(); + row = table->set_string_unique(c_primaryKeyObjectClassColumnIndex, row, object_type); + } + + // set if changing, or remove if setting to nil + if (primary_key.size() == 0) { + if (row != not_found) { + table->move_last_over(row); + } + } + else { + table->set_string(c_primaryKeyPropertyNameColumnIndex, row, primary_key); + } +} + +StringData ObjectStore::object_type_for_table_name(StringData table_name) { + if (table_name.begins_with(c_object_table_prefix)) { + return table_name.substr(sizeof(c_object_table_prefix) - 1); + } + return StringData(); +} + +std::string ObjectStore::table_name_for_object_type(StringData object_type) { + return std::string(c_object_table_prefix) + std::string(object_type); +} + +TableRef ObjectStore::table_for_object_type(Group& group, StringData object_type) { + auto name = table_name_for_object_type(object_type); + return group.get_table(name); +} + +ConstTableRef ObjectStore::table_for_object_type(Group const& group, StringData object_type) { + auto name = table_name_for_object_type(object_type); + return group.get_table(name); +} + +namespace { +struct SchemaDifferenceExplainer { + std::vector errors; + + void operator()(schema_change::AddTable op) + { + errors.emplace_back("Class '%1' has been added.", op.object->name); + } + + void operator()(schema_change::AddProperty op) + { + errors.emplace_back("Property '%1.%2' has been added.", op.object->name, op.property->name); + } + + void operator()(schema_change::RemoveProperty op) + { + errors.emplace_back("Property '%1.%2' has been removed.", op.object->name, op.property->name); + } + + void operator()(schema_change::ChangePropertyType op) + { + errors.emplace_back("Property '%1.%2' has been changed from '%3' to '%4'.", + op.object->name, op.new_property->name, + string_for_property_type(op.old_property->type), + string_for_property_type(op.new_property->type)); + } + + void operator()(schema_change::MakePropertyNullable op) + { + errors.emplace_back("Property '%1.%2' has been made optional.", op.object->name, op.property->name); + } + + void operator()(schema_change::MakePropertyRequired op) + { + errors.emplace_back("Property '%1.%2' has been made required.", op.object->name, op.property->name); + } + + void operator()(schema_change::ChangePrimaryKey op) + { + if (op.property && !op.object->primary_key.empty()) { + errors.emplace_back("Primary Key for class '%1 has changed from '%2' to '%3'.", + op.object->name, op.object->primary_key, op.property->name); + } + else if (op.property) { + errors.emplace_back("Primary Key for class '%1 has been added.", op.object->name); + } + else { + errors.emplace_back("Primary Key for class '%1 has been removed.", op.object->name); + } + } + + void operator()(schema_change::AddIndex op) + { + errors.emplace_back("Property '%1.%2' has been made indexed.", op.object->name, op.property->name); + } + + void operator()(schema_change::RemoveIndex op) + { + errors.emplace_back("Property '%1.%2' has been made unindexed.", op.object->name, op.property->name); + } +}; + +class TableHelper { +public: + TableHelper(Group& g) : m_group(g) { } + + Table& operator()(const ObjectSchema* object_schema) + { + if (object_schema != m_current_object_schema) { + m_current_table = table_for_object_schema(m_group, *object_schema); + m_current_object_schema = object_schema; + } + REALM_ASSERT(m_current_table); + return *m_current_table; + } + +private: + Group& m_group; + const ObjectSchema* m_current_object_schema = nullptr; + TableRef m_current_table; +}; + +template +void verify_no_errors(Verifier&& verifier, std::vector const& changes) +{ + for (auto& change : changes) { + change.visit(verifier); + } + + if (!verifier.errors.empty()) { + throw ErrorType(verifier.errors); + } +} +} // anonymous namespace + +bool ObjectStore::needs_migration(std::vector const& changes) +{ + using namespace schema_change; + struct Visitor { + bool operator()(AddIndex) { return false; } + bool operator()(AddProperty) { return true; } + bool operator()(AddTable) { return false; } + bool operator()(ChangePrimaryKey) { return true; } + bool operator()(ChangePropertyType) { return true; } + bool operator()(MakePropertyNullable) { return true; } + bool operator()(MakePropertyRequired) { return true; } + bool operator()(RemoveIndex) { return false; } + bool operator()(RemoveProperty) { return true; } + }; + + return std::any_of(begin(changes), end(changes), + [](auto&& change) { return change.visit(Visitor()); }); +} + +void ObjectStore::verify_no_changes_required(std::vector const& changes) +{ + verify_no_errors(SchemaDifferenceExplainer(), changes); +} + +void ObjectStore::verify_no_migration_required(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + // Adding a table or adding/removing indexes can be done automatically. + // All other changes require migrations. + void operator()(AddTable) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + } verifier; + verify_no_errors(verifier, changes); +} + +void ObjectStore::verify_valid_additive_changes(std::vector const& changes) +{ + using namespace schema_change; + struct Verifier : SchemaDifferenceExplainer { + using SchemaDifferenceExplainer::operator(); + + // Additive mode allows adding things, extra columns, and adding/removing indexes + void operator()(AddTable) { } + void operator()(AddProperty) { } + void operator()(RemoveProperty) { } + void operator()(AddIndex) { } + void operator()(RemoveIndex) { } + } verifier; + verify_no_errors(verifier, changes); +} + +static void apply_non_migration_changes(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier : SchemaDifferenceExplainer { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + // Produce an exception listing the unsupported schema changes for + // everything but the explicitly supported ones + using SchemaDifferenceExplainer::operator(); + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + } applier{group}; + verify_no_errors(applier, changes); +} + +static void create_initial_tables(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + void operator()(AddTable op) { create_table(group, *op.object); } + + // Note that in normal operation none of these will be hit, as if we're + // creating the initial tables there shouldn't be anything to update. + // Implementing these makes us better able to handle weird + // not-quite-correct files produced by other things and has no obvious + // downside. + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(RemoveProperty op) { table(op.object).remove_column(op.property->table_column); } + void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } + void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } + void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? StringData{op.property->name} : ""); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + + void operator()(ChangePropertyType op) + { + insert_column(group, table(op.object), *op.new_property, op.old_property->table_column); + table(op.object).remove_column(op.old_property->table_column + 1); + } + } applier{group}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_additive_changes(Group& group, std::vector const& changes, bool update_indexes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group, bool update_indexes) : group{group}, table{group}, update_indexes{update_indexes} { } + Group& group; + TableHelper table; + bool update_indexes; + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(AddIndex op) { if (update_indexes) add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { if (update_indexes) table(op.object).remove_search_index(op.property->table_column); } + void operator()(RemoveProperty) { } + + // No need for errors for these, as we've already verified that they aren't present + void operator()(ChangePrimaryKey) { } + void operator()(ChangePropertyType) { } + void operator()(MakePropertyNullable) { } + void operator()(MakePropertyRequired) { } + } applier{group, update_indexes}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_pre_migration_changes(Group& group, std::vector const& changes) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group) : group{group}, table{group} { } + Group& group; + TableHelper table; + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddProperty op) { add_column(group, table(op.object), *op.property); } + void operator()(RemoveProperty) { /* delayed until after the migration */ } + void operator()(ChangePropertyType op) { replace_column(group, table(op.object), *op.old_property, *op.new_property); } + void operator()(MakePropertyNullable op) { make_property_optional(group, table(op.object), *op.property); } + void operator()(MakePropertyRequired op) { make_property_required(group, table(op.object), *op.property); } + void operator()(ChangePrimaryKey op) { ObjectStore::set_primary_key_for_object(group, op.object->name, op.property ? op.property->name : ""); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + } applier{group}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +static void apply_post_migration_changes(Group& group, std::vector const& changes, Schema const& initial_schema) +{ + using namespace schema_change; + struct Applier { + Applier(Group& group, Schema const& initial_schema) : group{group}, initial_schema(initial_schema), table(group) { } + Group& group; + Schema const& initial_schema; + TableHelper table; + + void operator()(RemoveProperty op) + { + if (!initial_schema.empty() && !initial_schema.find(op.object->name)->property_for_name(op.property->name)) + throw std::logic_error(util::format("Renamed property '%1.%2' does not exist.", op.object->name, op.property->name)); + auto table = table_for_object_schema(group, *op.object); + table->remove_column(op.property->table_column); + } + + void operator()(ChangePrimaryKey op) + { + if (op.property) { + validate_primary_column_uniqueness(group, op.object->name, op.property->name); + } + } + + void operator()(AddTable op) { create_table(group, *op.object); } + void operator()(AddIndex op) { add_index(table(op.object), op.property->table_column); } + void operator()(RemoveIndex op) { table(op.object).remove_search_index(op.property->table_column); } + + void operator()(ChangePropertyType) { } + void operator()(MakePropertyNullable) { } + void operator()(MakePropertyRequired) { } + void operator()(AddProperty) { } + } applier{group, initial_schema}; + + for (auto& change : changes) { + change.visit(applier); + } +} + +void ObjectStore::apply_schema_changes(Group& group, Schema& schema, uint64_t& schema_version, + Schema const& target_schema, uint64_t target_schema_version, + SchemaMode mode, std::vector const& changes, + std::function migration_function) +{ + create_metadata_tables(group); + + if (schema_version == ObjectStore::NotVersioned) { + create_initial_tables(group, changes); + set_schema_version(group, target_schema_version); + schema_version = target_schema_version; + schema = target_schema; + set_schema_columns(group, schema); + return; + } + + if (mode == SchemaMode::Additive) { + apply_additive_changes(group, changes, schema_version < target_schema_version); + + if (schema_version < target_schema_version) { + schema_version = target_schema_version; + set_schema_version(group, target_schema_version); + } + + schema = target_schema; + set_schema_columns(group, schema); + return; + } + + if (mode == SchemaMode::Manual) { + // Have to update the schema on the Realm before calling the migration + // function as the migration will need it + auto old_version = schema_version; + auto old_schema = schema; + schema_version = target_schema_version; + schema = target_schema; + set_schema_columns(group, schema); + + try { + migration_function(); + verify_no_changes_required(schema_from_group(group).compare(schema)); + validate_primary_column_uniqueness(group); + } + catch (...) { + schema = move(old_schema); + schema_version = old_version; + throw; + } + + set_schema_columns(group, schema); + set_schema_version(group, target_schema_version); + return; + } + + if (schema_version == target_schema_version) { + apply_non_migration_changes(group, changes); + schema = target_schema; + set_schema_columns(group, schema); + return; + } + + apply_pre_migration_changes(group, changes); + if (migration_function) { + // Have to update the schema on the Realm before calling the migration + // function as the migration will need it + auto old_version = schema_version; + auto old_schema = schema; + schema_version = target_schema_version; + schema = target_schema; + set_schema_columns(group, schema); + + try { + migration_function(); + + // Migration function may have changed the schema, so we need to re-read it + schema = schema_from_group(group); + apply_post_migration_changes(group, schema.compare(target_schema), old_schema); + validate_primary_column_uniqueness(group); + } + catch (...) { + schema = move(old_schema); + schema_version = old_version; + throw; + } + } + else { + apply_post_migration_changes(group, changes, {}); + } + + set_schema_version(group, target_schema_version); + schema_version = target_schema_version; + schema = target_schema; + set_schema_columns(group, schema); +} + +Schema ObjectStore::schema_from_group(Group const& group) { + std::vector schema; + schema.reserve(group.size()); + for (size_t i = 0; i < group.size(); i++) { + auto object_type = object_type_for_table_name(group.get_table_name(i)); + if (object_type.size()) { + schema.emplace_back(group, object_type, i); + } + } + return schema; +} + +void ObjectStore::set_schema_columns(Group const& group, Schema& schema) +{ + for (auto& object_schema : schema) { + auto table = table_for_object_schema(group, object_schema); + if (!table) { + continue; + } + for (auto& property : object_schema.persisted_properties) { + property.table_column = table->get_column_index(property.name); + } + } +} + +void ObjectStore::delete_data_for_object(Group& group, StringData object_type) { + if (TableRef table = table_for_object_type(group, object_type)) { + group.remove_table(table->get_index_in_group()); + ObjectStore::set_primary_key_for_object(group, object_type, ""); + } +} + +bool ObjectStore::is_empty(Group const& group) { + for (size_t i = 0; i < group.size(); i++) { + ConstTableRef table = group.get_table(i); + std::string object_type = object_type_for_table_name(table->get_name()); + if (!object_type.length()) { + continue; + } + if (!table->is_empty()) { + return false; + } + } + return true; +} + +void ObjectStore::rename_property(Group& group, Schema& target_schema, StringData object_type, StringData old_name, StringData new_name) +{ + TableRef table = table_for_object_type(group, object_type); + if (!table) { + throw std::logic_error(util::format("Cannot rename properties for type '%1' because it does not exist.", object_type)); + } + + auto target_object_schema = target_schema.find(object_type); + if (target_object_schema == target_schema.end()) { + throw std::logic_error(util::format("Cannot rename properties for type '%1' because it has been removed from the Realm.", object_type)); + } + + if (target_object_schema->property_for_name(old_name)) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because the source property still exists.", + object_type, old_name, new_name)); + } + + ObjectSchema table_object_schema(group, object_type); + Property *old_property = table_object_schema.property_for_name(old_name); + if (!old_property) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' because it does not exist.", object_type, old_name)); + } + + Property *new_property = table_object_schema.property_for_name(new_name); + if (!new_property) { + // New property doesn't exist in the table, which means we're probably + // renaming to an intermediate property in a multi-version migration. + // This is safe because the migration will fail schema validation unless + // this property is renamed again to a valid name before the end. + table->rename_column(old_property->table_column, new_name); + return; + } + + if (old_property->type != new_property->type || old_property->object_type != new_property->object_type) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from type '%4' to '%5'.", + object_type, old_name, new_name, old_property->type_string(), new_property->type_string())); + } + + if (old_property->is_nullable && !new_property->is_nullable) { + throw std::logic_error(util::format("Cannot rename property '%1.%2' to '%3' because it would change from optional to required.", + object_type, old_name, new_name)); + } + + size_t column_to_remove = new_property->table_column; + table->rename_column(old_property->table_column, new_name); + table->remove_column(column_to_remove); + + // update table_column for each property since it may have shifted + for (auto& current_prop : target_object_schema->persisted_properties) { + if (current_prop.table_column == column_to_remove) + current_prop.table_column = old_property->table_column; + else if (current_prop.table_column > column_to_remove) + --current_prop.table_column; + } + + // update nullability for column + if (new_property->is_nullable && !old_property->is_nullable) { + auto prop = *new_property; + prop.table_column = old_property->table_column; + make_property_optional(group, *table, prop); + } +} + +InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) +: logic_error(util::format("Provided schema version %1 is less than last set version %2.", new_version, old_version)) +, m_old_version(old_version), m_new_version(new_version) +{ +} + +DuplicatePrimaryKeyValueException::DuplicatePrimaryKeyValueException(std::string object_type, std::string property) +: logic_error(util::format("Primary key property '%1.%2' has duplicate values after migration.", object_type, property)) +, m_object_type(object_type), m_property(property) +{ +} + +SchemaValidationException::SchemaValidationException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "Schema validation failed due to the following errors:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} + +SchemaMismatchException::SchemaMismatchException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "Migration is required due to the following errors:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} + +InvalidSchemaChangeException::InvalidSchemaChangeException(std::vector const& errors) +: std::logic_error([&] { + std::string message = "The following changes cannot be made in additive-only schema mode:"; + for (auto const& error : errors) { + message += std::string("\n- ") + error.what(); + } + return message; +}()) +{ +} diff --git a/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp b/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp new file mode 100644 index 0000000..8936534 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/placeholder.cpp @@ -0,0 +1 @@ +// This file is intentionally left blank. diff --git a/Pods/Realm/Realm/ObjectStore/src/results.cpp b/Pods/Realm/Realm/ObjectStore/src/results.cpp new file mode 100644 index 0000000..5585f32 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/results.cpp @@ -0,0 +1,601 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "results.hpp" + +#include "impl/realm_coordinator.hpp" +#include "impl/results_notifier.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "schema.hpp" +#include "util/compiler.hpp" +#include "util/format.hpp" + +#include + +using namespace realm; + +Results::Results() = default; +Results::~Results() = default; + +Results::Results(SharedRealm r, Query q, SortDescriptor s) +: m_realm(std::move(r)) +, m_query(std::move(q)) +, m_table(m_query.get_table().get()) +, m_sort(std::move(s)) +, m_mode(Mode::Query) +{ +} + +Results::Results(SharedRealm r, Table& table) +: m_realm(std::move(r)) +, m_table(&table) +, m_mode(Mode::Table) +{ +} + +Results::Results(SharedRealm r, LinkViewRef lv, util::Optional q, SortDescriptor s) +: m_realm(std::move(r)) +, m_link_view(lv) +, m_table(&lv->get_target_table()) +, m_sort(std::move(s)) +, m_mode(Mode::LinkView) +{ + if (q) { + m_query = std::move(*q); + m_mode = Mode::Query; + } +} + +Results::Results(SharedRealm r, TableView tv, SortDescriptor s) +: m_realm(std::move(r)) +, m_table_view(std::move(tv)) +, m_table(&m_table_view.get_parent()) +, m_sort(std::move(s)) +, m_mode(Mode::TableView) +{ +} + +Results::Results(const Results&) = default; +Results& Results::operator=(const Results&) = default; + +Results::Results(Results&& other) +: m_realm(std::move(other.m_realm)) +, m_object_schema(std::move(other.m_object_schema)) +, m_query(std::move(other.m_query)) +, m_table_view(std::move(other.m_table_view)) +, m_link_view(std::move(other.m_link_view)) +, m_table(other.m_table) +, m_sort(std::move(other.m_sort)) +, m_notifier(std::move(other.m_notifier)) +, m_mode(other.m_mode) +, m_update_policy(other.m_update_policy) +, m_has_used_table_view(other.m_has_used_table_view) +, m_wants_background_updates(other.m_wants_background_updates) +{ + if (m_notifier) { + m_notifier->target_results_moved(other, *this); + } +} + +Results& Results::operator=(Results&& other) +{ + this->~Results(); + new (this) Results(std::move(other)); + return *this; +} + +bool Results::is_valid() const +{ + if (m_realm) + m_realm->verify_thread(); + + if (m_table && !m_table->is_attached()) + return false; + + return true; +} + +void Results::validate_read() const +{ + // is_valid ensures that we're on the correct thread. + if (!is_valid()) + throw InvalidatedException(); +} + +void Results::validate_write() const +{ + validate_read(); + if (!m_realm || !m_realm->is_in_transaction()) + throw InvalidTransactionException("Must be in a write transaction"); +} + +size_t Results::size() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: return 0; + case Mode::Table: return m_table->size(); + case Mode::LinkView: return m_link_view->size(); + case Mode::Query: + m_query.sync_view_if_needed(); + return m_query.count(); + case Mode::TableView: + update_tableview(); + return m_table_view.size(); + } + REALM_UNREACHABLE(); +} + +const ObjectSchema& Results::get_object_schema() const +{ + validate_read(); + + if (!m_object_schema) { + REALM_ASSERT(m_realm); + auto it = m_realm->schema().find(get_object_type()); + REALM_ASSERT(it != m_realm->schema().end()); + m_object_schema = &*it; + } + + return *m_object_schema; +} + + +StringData Results::get_object_type() const noexcept +{ + if (!m_table) { + return StringData(); + } + + return ObjectStore::object_type_for_table_name(m_table->get_name()); +} + +RowExpr Results::get(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: break; + case Mode::Table: + if (row_ndx < m_table->size()) + return m_table->get(row_ndx); + break; + case Mode::LinkView: + if (update_linkview()) { + if (row_ndx < m_link_view->size()) + return m_link_view->get(row_ndx); + break; + } + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + if (row_ndx >= m_table_view.size()) + break; + if (m_update_policy == UpdatePolicy::Never && !m_table_view.is_row_attached(row_ndx)) + return {}; + return m_table_view.get(row_ndx); + } + + throw OutOfBoundsIndexException{row_ndx, size()}; +} + +util::Optional Results::first() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->front()); + case Mode::LinkView: + if (update_linkview()) + return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(0)); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.front()); + } + REALM_UNREACHABLE(); +} + +util::Optional Results::last() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + return m_table->size() == 0 ? util::none : util::make_optional(m_table->back()); + case Mode::LinkView: + if (update_linkview()) + return m_link_view->size() == 0 ? util::none : util::make_optional(m_link_view->get(m_link_view->size() - 1)); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view.size() == 0 ? util::none : util::make_optional(m_table_view.back()); + } + REALM_UNREACHABLE(); +} + +bool Results::update_linkview() +{ + REALM_ASSERT(m_update_policy == UpdatePolicy::Auto); + + if (m_sort) { + m_query = get_query(); + m_mode = Mode::Query; + update_tableview(); + return false; + } + return true; +} + +void Results::update_tableview(bool wants_notifications) +{ + if (m_update_policy == UpdatePolicy::Never) { + REALM_ASSERT(m_mode == Mode::TableView); + return; + } + + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + case Mode::LinkView: + return; + case Mode::Query: + m_query.sync_view_if_needed(); + m_table_view = m_query.find_all(); + if (m_sort) { + m_table_view.sort(m_sort); + } + m_mode = Mode::TableView; + REALM_FALLTHROUGH; + case Mode::TableView: + if (wants_notifications && !m_notifier && !m_realm->is_in_transaction() && m_realm->can_deliver_notifications()) { + m_notifier = std::make_shared<_impl::ResultsNotifier>(*this); + _impl::RealmCoordinator::register_notifier(m_notifier); + } + m_has_used_table_view = true; + m_table_view.sync_if_needed(); + break; + } +} + +size_t Results::index_of(Row const& row) +{ + validate_read(); + if (!row) { + throw DetatchedAccessorException{}; + } + if (m_table && row.get_table() != m_table) { + throw IncorrectTableException( + ObjectStore::object_type_for_table_name(m_table->get_name()), + ObjectStore::object_type_for_table_name(row.get_table()->get_name()), + "Attempting to get the index of a Row of the wrong type" + ); + } + return index_of(row.get_index()); +} + +size_t Results::index_of(size_t row_ndx) +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return not_found; + case Mode::Table: + return row_ndx; + case Mode::LinkView: + if (update_linkview()) + return m_link_view->find(row_ndx); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view.find_by_source_ndx(row_ndx); + } + REALM_UNREACHABLE(); +} + +template +util::Optional Results::aggregate(size_t column, bool return_none_for_empty, + const char* name, + Int agg_int, Float agg_float, + Double agg_double, Timestamp agg_timestamp) +{ + validate_read(); + if (!m_table) + return none; + if (column > m_table->get_column_count()) + throw OutOfBoundsIndexException{column, m_table->get_column_count()}; + + auto do_agg = [&](auto const& getter) -> util::Optional { + switch (m_mode) { + case Mode::Empty: + return none; + case Mode::Table: + if (return_none_for_empty && m_table->size() == 0) + return none; + return util::Optional(getter(*m_table)); + case Mode::LinkView: + m_query = this->get_query(); + m_mode = Mode::Query; + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + this->update_tableview(); + if (return_none_for_empty && m_table_view.size() == 0) + return none; + return util::Optional(getter(m_table_view)); + } + REALM_UNREACHABLE(); + }; + + switch (m_table->get_column_type(column)) + { + case type_Timestamp: return do_agg(agg_timestamp); + case type_Double: return do_agg(agg_double); + case type_Float: return do_agg(agg_float); + case type_Int: return do_agg(agg_int); + default: + throw UnsupportedColumnTypeException{column, m_table, name}; + } +} + +util::Optional Results::max(size_t column) +{ + return aggregate(column, true, "max", + [=](auto const& table) { return table.maximum_int(column); }, + [=](auto const& table) { return table.maximum_float(column); }, + [=](auto const& table) { return table.maximum_double(column); }, + [=](auto const& table) { return table.maximum_timestamp(column); }); +} + +util::Optional Results::min(size_t column) +{ + return aggregate(column, true, "min", + [=](auto const& table) { return table.minimum_int(column); }, + [=](auto const& table) { return table.minimum_float(column); }, + [=](auto const& table) { return table.minimum_double(column); }, + [=](auto const& table) { return table.minimum_timestamp(column); }); +} + +util::Optional Results::sum(size_t column) +{ + return aggregate(column, false, "sum", + [=](auto const& table) { return table.sum_int(column); }, + [=](auto const& table) { return table.sum_float(column); }, + [=](auto const& table) { return table.sum_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "sum"}; }); +} + +util::Optional Results::average(size_t column) +{ + return aggregate(column, true, "average", + [=](auto const& table) { return table.average_int(column); }, + [=](auto const& table) { return table.average_float(column); }, + [=](auto const& table) { return table.average_double(column); }, + [=](auto const&) -> util::None { throw UnsupportedColumnTypeException{column, m_table, "average"}; }); +} + +void Results::clear() +{ + switch (m_mode) { + case Mode::Empty: + return; + case Mode::Table: + validate_write(); + m_table->clear(); + break; + case Mode::Query: + // Not using Query:remove() because building the tableview and + // clearing it is actually significantly faster + case Mode::TableView: + validate_write(); + update_tableview(); + + switch (m_update_policy) { + case UpdatePolicy::Auto: + m_table_view.clear(RemoveMode::unordered); + break; + case UpdatePolicy::Never: { + // Copy the TableView because a frozen Results shouldn't let its size() change. + TableView copy(m_table_view); + copy.clear(RemoveMode::unordered); + break; + } + } + break; + case Mode::LinkView: + validate_write(); + m_link_view->remove_all_target_rows(); + break; + } +} + +Query Results::get_query() const +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + case Mode::Query: + return m_query; + case Mode::TableView: { + // A TableView has an associated Query if it was produced by Query::find_all. This is indicated + // by TableView::get_query returning a Query with a non-null table. + Query query = m_table_view.get_query(); + if (query.get_table()) { + return query; + } + + // The TableView has no associated query so create one with no conditions that is restricted + // to the rows in the TableView. + if (m_update_policy == UpdatePolicy::Auto) { + m_table_view.sync_if_needed(); + } + return Query(*m_table, std::unique_ptr(new TableView(m_table_view))); + } + case Mode::LinkView: + return m_table->where(m_link_view); + case Mode::Table: + return m_table->where(); + } + REALM_UNREACHABLE(); +} + +TableView Results::get_tableview() +{ + validate_read(); + switch (m_mode) { + case Mode::Empty: + return {}; + case Mode::LinkView: + if (update_linkview()) + return m_table->where(m_link_view).find_all(); + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(); + return m_table_view; + case Mode::Table: + return m_table->where().find_all(); + } + REALM_UNREACHABLE(); +} + +Results Results::sort(realm::SortDescriptor&& sort) const +{ + return Results(m_realm, get_query(), std::move(sort)); +} + +Results Results::filter(Query&& q) const +{ + return Results(m_realm, get_query().and_query(std::move(q)), m_sort); +} + +Results Results::snapshot() const & +{ + validate_read(); + + return Results(*this).snapshot(); +} + +Results Results::snapshot() && +{ + validate_read(); + + switch (m_mode) { + case Mode::Empty: + return Results(); + + case Mode::Table: + case Mode::LinkView: + m_query = get_query(); + m_mode = Mode::Query; + + REALM_FALLTHROUGH; + case Mode::Query: + case Mode::TableView: + update_tableview(false); + m_notifier.reset(); + m_update_policy = UpdatePolicy::Never; + return std::move(*this); + } + REALM_UNREACHABLE(); +} + +void Results::prepare_async() +{ + if (m_realm->config().read_only()) { + throw InvalidTransactionException("Cannot create asynchronous query for read-only Realms"); + } + if (m_realm->is_in_transaction()) { + throw InvalidTransactionException("Cannot create asynchronous query while in a write transaction"); + } + if (m_update_policy == UpdatePolicy::Never) { + throw std::logic_error("Cannot create asynchronous query for snapshotted Results."); + } + + if (!m_notifier) { + m_wants_background_updates = true; + m_notifier = std::make_shared<_impl::ResultsNotifier>(*this); + _impl::RealmCoordinator::register_notifier(m_notifier); + } +} + +NotificationToken Results::async(std::function target) +{ + prepare_async(); + auto wrap = [=](CollectionChangeSet, std::exception_ptr e) { target(e); }; + return {m_notifier, m_notifier->add_callback(wrap)}; +} + +NotificationToken Results::add_notification_callback(CollectionChangeCallback cb) +{ + prepare_async(); + return {m_notifier, m_notifier->add_callback(std::move(cb))}; +} + +bool Results::is_in_table_order() const +{ + switch (m_mode) { + case Mode::Empty: + case Mode::Table: + return true; + case Mode::LinkView: + return false; + case Mode::Query: + return m_query.produces_results_in_table_order() && !m_sort; + case Mode::TableView: + return m_table_view.is_in_table_order(); + } + REALM_UNREACHABLE(); // keep gcc happy +} + +void Results::Internal::set_table_view(Results& results, realm::TableView &&tv) +{ + REALM_ASSERT(results.m_update_policy != UpdatePolicy::Never); + // If the previous TableView was never actually used, then stop generating + // new ones until the user actually uses the Results object again + if (results.m_mode == Mode::TableView) { + results.m_wants_background_updates = results.m_has_used_table_view; + } + + results.m_table_view = std::move(tv); + results.m_mode = Mode::TableView; + results.m_has_used_table_view = false; + REALM_ASSERT(results.m_table_view.is_in_sync()); + REALM_ASSERT(results.m_table_view.is_attached()); +} + +Results::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c) +: std::out_of_range(util::format("Requested index %1 greater than max %2", r, c)) +, requested(r), valid_count(c) {} + +Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation) +: std::logic_error(util::format("Cannot %1 property '%2': operation not supported for '%3' properties", + operation, table->get_column_name(column), + string_for_property_type(static_cast(table->get_column_type(column))))) +, column_index(column) +, column_name(table->get_column_name(column)) +, column_type(table->get_column_type(column)) +{ +} diff --git a/Pods/Realm/Realm/ObjectStore/src/schema.cpp b/Pods/Realm/Realm/ObjectStore/src/schema.cpp new file mode 100644 index 0000000..f45dd13 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/schema.cpp @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "schema.hpp" + +#include "object_schema.hpp" +#include "object_store.hpp" +#include "object_schema.hpp" +#include "property.hpp" + +#include + +using namespace realm; + +namespace realm { +bool operator==(Schema const& a, Schema const& b) +{ + return static_cast(a) == static_cast(b); +} +} + +Schema::Schema() = default; +Schema::~Schema() = default; +Schema::Schema(Schema const&) = default; +Schema::Schema(Schema &&) = default; +Schema& Schema::operator=(Schema const&) = default; +Schema& Schema::operator=(Schema&&) = default; + +Schema::Schema(std::initializer_list types) : Schema(base(types)) { } + +Schema::Schema(base types) : base(std::move(types)) +{ + std::sort(begin(), end(), [](ObjectSchema const& lft, ObjectSchema const& rgt) { + return lft.name < rgt.name; + }); +} + +Schema::iterator Schema::find(StringData name) +{ + auto it = std::lower_bound(begin(), end(), name, [](ObjectSchema const& lft, StringData rgt) { + return lft.name < rgt; + }); + if (it != end() && it->name != name) { + it = end(); + } + return it; +} + +Schema::const_iterator Schema::find(StringData name) const +{ + return const_cast(this)->find(name); +} + +Schema::iterator Schema::find(ObjectSchema const& object) noexcept +{ + return find(object.name); +} + +Schema::const_iterator Schema::find(ObjectSchema const& object) const noexcept +{ + return const_cast(this)->find(object); +} + +void Schema::validate() const +{ + std::vector exceptions; + for (auto const& object : *this) { + object.validate(*this, exceptions); + } + + if (exceptions.size()) { + throw SchemaValidationException(exceptions); + } +} + +namespace { +struct IsNotRemoveProperty { + bool operator()(SchemaChange sc) const { return sc.visit(*this); } + bool operator()(schema_change::RemoveProperty) const { return false; } + template bool operator()(T) const { return true; } +}; +struct GetRemovedColumn { + size_t operator()(SchemaChange sc) const { return sc.visit(*this); } + size_t operator()(schema_change::RemoveProperty p) const { return p.property->table_column; } + template size_t operator()(T) const { __builtin_unreachable(); } +}; +} + +static void compare(ObjectSchema const& existing_schema, + ObjectSchema const& target_schema, + std::vector& changes) +{ + for (auto& current_prop : existing_schema.persisted_properties) { + auto target_prop = target_schema.property_for_name(current_prop.name); + + if (!target_prop) { + changes.emplace_back(schema_change::RemoveProperty{&existing_schema, ¤t_prop}); + continue; + } + if (current_prop.type != target_prop->type || current_prop.object_type != target_prop->object_type) { + changes.emplace_back(schema_change::ChangePropertyType{&existing_schema, ¤t_prop, target_prop}); + continue; + } + if (current_prop.is_nullable != target_prop->is_nullable) { + if (current_prop.is_nullable) + changes.emplace_back(schema_change::MakePropertyRequired{&existing_schema, ¤t_prop}); + else + changes.emplace_back(schema_change::MakePropertyNullable{&existing_schema, ¤t_prop}); + } + if (target_prop->requires_index()) { + if (!current_prop.is_indexed) + changes.emplace_back(schema_change::AddIndex{&existing_schema, ¤t_prop}); + } + else if (current_prop.requires_index()) { + changes.emplace_back(schema_change::RemoveIndex{&existing_schema, ¤t_prop}); + } + } + + if (existing_schema.primary_key != target_schema.primary_key) { + changes.emplace_back(schema_change::ChangePrimaryKey{&existing_schema, target_schema.primary_key_property()}); + } + + for (auto& target_prop : target_schema.persisted_properties) { + if (!existing_schema.property_for_name(target_prop.name)) { + changes.emplace_back(schema_change::AddProperty{&existing_schema, &target_prop}); + } + } + + // Move all RemovePropertys to the end and sort in descending order of + // column index, as removing a column will shift all columns after that one + auto it = std::partition(begin(changes), end(changes), IsNotRemoveProperty{}); + std::sort(it, end(changes), + [](auto a, auto b) { return GetRemovedColumn()(a) > GetRemovedColumn()(b); }); +} + +std::vector Schema::compare(Schema const& target_schema) const +{ + std::vector changes; + for (auto &object_schema : target_schema) { + auto matching_schema = find(object_schema); + if (matching_schema == end()) { + changes.emplace_back(schema_change::AddTable{&object_schema}); + continue; + } + + ::compare(*matching_schema, object_schema, changes); + } + return changes; +} + +void Schema::copy_table_columns_from(realm::Schema const& other) +{ + for (auto& source_schema : other) { + auto matching_schema = find(source_schema); + if (matching_schema == end()) { + continue; + } + + for (auto& current_prop : source_schema.persisted_properties) { + auto target_prop = matching_schema->property_for_name(current_prop.name); + if (target_prop) { + target_prop->table_column = current_prop.table_column; + } + } + } +} + +namespace realm { +bool operator==(SchemaChange const& lft, SchemaChange const& rgt) +{ + if (lft.m_kind != rgt.m_kind) + return false; + + using namespace schema_change; + struct Visitor { + SchemaChange const& value; + + #define REALM_SC_COMPARE(type, ...) \ + bool operator()(type rgt) const \ + { \ + auto cmp = [](auto&& v) { return std::tie(__VA_ARGS__); }; \ + return cmp(value.type) == cmp(rgt); \ + } + + REALM_SC_COMPARE(AddIndex, v.object, v.property) + REALM_SC_COMPARE(AddProperty, v.object, v.property) + REALM_SC_COMPARE(AddTable, v.object) + REALM_SC_COMPARE(ChangePrimaryKey, v.object, v.property) + REALM_SC_COMPARE(ChangePropertyType, v.object, v.old_property, v.new_property) + REALM_SC_COMPARE(MakePropertyNullable, v.object, v.property) + REALM_SC_COMPARE(MakePropertyRequired, v.object, v.property) + REALM_SC_COMPARE(RemoveIndex, v.object, v.property) + REALM_SC_COMPARE(RemoveProperty, v.object, v.property) + + #undef REALM_SC_COMPARE + } visitor{lft}; + return rgt.visit(visitor); +} +} // namespace realm diff --git a/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp b/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp new file mode 100644 index 0000000..58bcc1d --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/shared_realm.cpp @@ -0,0 +1,702 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "shared_realm.hpp" + +#include "impl/handover.hpp" +#include "impl/realm_coordinator.hpp" +#include "impl/transact_log_handler.hpp" + +#include "binding_context.hpp" +#include "object_schema.hpp" +#include "object_store.hpp" +#include "schema.hpp" +#include "thread_confined.hpp" + +#include "util/format.hpp" + +#include +#include + +#if REALM_ENABLE_SYNC +#include +#endif + +using namespace realm; +using namespace realm::_impl; + +Realm::Realm(Config config) +: m_config(std::move(config)) +{ + open_with_config(m_config, m_history, m_shared_group, m_read_only_group, this); + + if (m_read_only_group) { + m_group = m_read_only_group.get(); + } +} + +void Realm::init(std::shared_ptr<_impl::RealmCoordinator> coordinator) +{ + // if there is an existing realm at the current path steal its schema/column mapping + if (auto existing = coordinator ? coordinator->get_schema() : nullptr) { + m_schema = *existing; + m_schema_version = coordinator->get_schema_version(); + } + else { + // otherwise get the schema from the group + m_schema_version = ObjectStore::get_schema_version(read_group()); + m_schema = ObjectStore::schema_from_group(read_group()); + + if (m_shared_group) { + m_schema_transaction_version = m_shared_group->get_version_of_current_transaction().version; + m_shared_group->end_read(); + m_group = nullptr; + } + } + + m_coordinator = std::move(coordinator); + + if (m_config.schema) { + try { + auto schema = std::move(*m_config.schema); + m_config.schema = util::none; + update_schema(std::move(schema), m_config.schema_version, + std::move(m_config.migration_function)); + } + catch (...) { + m_coordinator = nullptr; // don't try to unregister in the destructor as it'll deadlock + throw; + } + } + +} + +REALM_NOINLINE static void translate_file_exception(StringData path, bool read_only=false) +{ + try { + throw; + } + catch (util::File::PermissionDenied const& ex) { + throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), + util::format("Unable to open a realm at path '%1'. Please use a path where your app has %2 permissions.", + ex.get_path(), read_only ? "read" : "read-write"), + ex.what()); + } + catch (util::File::Exists const& ex) { + throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(), + util::format("File at path '%1' already exists.", ex.get_path()), + ex.what()); + } + catch (util::File::NotFound const& ex) { + throw RealmFileException(RealmFileException::Kind::NotFound, ex.get_path(), + util::format("Directory at path '%1' does not exist.", ex.get_path()), ex.what()); + } + catch (util::File::AccessError const& ex) { + // Errors for `open()` include the path, but other errors don't. We + // don't want two copies of the path in the error, so strip it out if it + // appears, and then include it in our prefix. + std::string underlying = ex.what(); + auto pos = underlying.find(ex.get_path()); + if (pos != std::string::npos && pos > 0) { + // One extra char at each end for the quotes + underlying.replace(pos - 1, ex.get_path().size() + 2, ""); + } + throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(), + util::format("Unable to open a realm at path '%1': %2.", ex.get_path(), underlying), ex.what()); + } + catch (IncompatibleLockFile const& ex) { + throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, path, + "Realm file is currently open in another process " + "which cannot share access with this process. " + "All processes sharing a single file must be the same architecture.", + ex.what()); + } + catch (FileFormatUpgradeRequired const& ex) { + throw RealmFileException(RealmFileException::Kind::FormatUpgradeRequired, path, + "The Realm file format must be allowed to be upgraded " + "in order to proceed.", + ex.what()); + } +} + +void Realm::open_with_config(const Config& config, + std::unique_ptr& history, + std::unique_ptr& shared_group, + std::unique_ptr& read_only_group, + Realm* realm) +{ + if (config.encryption_key.data() && config.encryption_key.size() != 64) { + throw InvalidEncryptionKeyException(); + } + try { + if (config.read_only()) { + read_only_group = std::make_unique(config.path, config.encryption_key.data(), Group::mode_ReadOnly); + } + else { + // FIXME: The SharedGroup constructor, when called below, will + // throw a C++ exception if server_synchronization_mode is + // inconsistent with the accessed Realm file. This exception + // probably has to be transmuted to an NSError. + bool server_synchronization_mode = bool(config.sync_config); + if (server_synchronization_mode) { +#if REALM_ENABLE_SYNC + history = realm::sync::make_sync_history(config.path); +#else + REALM_TERMINATE("Realm was not built with sync enabled"); +#endif + } + else { + history = realm::make_in_realm_history(config.path); + } + + SharedGroupOptions options; + options.durability = config.in_memory ? SharedGroupOptions::Durability::MemOnly : + SharedGroupOptions::Durability::Full; + options.encryption_key = config.encryption_key.data(); + options.allow_file_format_upgrade = !config.disable_format_upgrade; + options.upgrade_callback = [&](int from_version, int to_version) { + if (realm) { + realm->upgrade_initial_version = from_version; + realm->upgrade_final_version = to_version; + } + }; + shared_group = std::make_unique(*history, options); + } + } + catch (...) { + translate_file_exception(config.path, config.read_only()); + } +} + +Realm::~Realm() +{ + if (m_coordinator) { + m_coordinator->unregister_realm(this); + } +} + +Group& Realm::read_group() +{ + if (!m_group) { + m_group = &const_cast(m_shared_group->begin_read()); + add_schema_change_handler(); + } + return *m_group; +} + +void Realm::Internal::begin_read(Realm& realm, VersionID version_id) +{ + REALM_ASSERT(!realm.m_group); + realm.m_group = &const_cast(realm.m_shared_group->begin_read(version_id)); + realm.add_schema_change_handler(); +} + +SharedRealm Realm::get_shared_realm(Config config) +{ + auto coordinator = RealmCoordinator::get_coordinator(config.path); + return coordinator->get_realm(std::move(config)); +} + +void Realm::set_schema(Schema schema, uint64_t version) +{ + schema.copy_table_columns_from(m_schema); + m_schema = schema; + m_coordinator->update_schema(schema, version); +} + +bool Realm::read_schema_from_group_if_needed() +{ + // schema of read-only Realms can't change + if (m_read_only_group) + return false; + + Group& group = read_group(); + auto current_version = m_shared_group->get_version_of_current_transaction().version; + if (m_schema_transaction_version == current_version) + return false; + + m_schema = ObjectStore::schema_from_group(group); + m_schema_version = ObjectStore::get_schema_version(group); + m_schema_transaction_version = current_version; + return true; +} + +void Realm::reset_file_if_needed(Schema const& schema, uint64_t version, std::vector& required_changes) +{ + if (m_schema_version == ObjectStore::NotVersioned) + return; + if (m_schema_version == version && !ObjectStore::needs_migration(required_changes)) + return; + + // FIXME: this does not work if multiple processes try to open the file at + // the same time, or even multiple threads if there is not any external + // synchronization. The latter is probably fixable, but making it + // multi-process-safe requires some sort of multi-process exclusive lock + m_group = nullptr; + m_shared_group = nullptr; + m_history = nullptr; + util::File::remove(m_config.path); + + open_with_config(m_config, m_history, m_shared_group, m_read_only_group, this); + m_schema = ObjectStore::schema_from_group(read_group()); + m_schema_version = ObjectStore::get_schema_version(read_group()); + required_changes = m_schema.compare(schema); +} + +void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction migration_function) +{ + schema.validate(); + read_schema_from_group_if_needed(); + std::vector required_changes = m_schema.compare(schema); + + auto no_changes_required = [&] { + switch (m_config.schema_mode) { + case SchemaMode::Automatic: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) { + throw InvalidSchemaVersionException(m_schema_version, version); + } + if (version == m_schema_version) { + if (required_changes.empty()) { + set_schema(std::move(schema), version); + return true; + } + ObjectStore::verify_no_migration_required(required_changes); + } + return false; + + case SchemaMode::ReadOnly: + if (version != m_schema_version) + throw InvalidSchemaVersionException(m_schema_version, version); + ObjectStore::verify_no_migration_required(m_schema.compare(schema)); + set_schema(std::move(schema), version); + return true; + + case SchemaMode::ResetFile: + reset_file_if_needed(schema, version, required_changes); + return required_changes.empty(); + + case SchemaMode::Additive: + if (required_changes.empty()) { + set_schema(std::move(schema), version); + return version == m_schema_version; + } + ObjectStore::verify_valid_additive_changes(required_changes); + return false; + + case SchemaMode::Manual: + if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned) { + throw InvalidSchemaVersionException(m_schema_version, version); + } + if (version == m_schema_version) { + ObjectStore::verify_no_changes_required(required_changes); + return true; + } + return false; + } + __builtin_unreachable(); + }; + + if (no_changes_required()) + return; + // Either the schema version has changed or we need to do non-migration changes + + m_group->set_schema_change_notification_handler(nullptr); + transaction::begin_without_validation(*m_shared_group); + add_schema_change_handler(); + + // Cancel the write transaction if we exit this function before committing it + struct WriteTransactionGuard { + Realm& realm; + ~WriteTransactionGuard() { if (realm.is_in_transaction()) realm.cancel_transaction(); } + } write_transaction_guard{*this}; + + // If beginning the write transaction advanced the version, then someone else + // may have updated the schema and we need to re-read it + // We can't just begin the write transaction before checking anything because + // that means that write transactions would block opening Realms in other processes + if (read_schema_from_group_if_needed()) { + required_changes = m_schema.compare(schema); + if (no_changes_required()) + return; + } + + bool additive = m_config.schema_mode == SchemaMode::Additive; + if (migration_function && !additive) { + auto wrapper = [&] { + SharedRealm old_realm(new Realm(m_config)); + old_realm->init(nullptr); + // Need to open in read-write mode so that it uses a SharedGroup, but + // users shouldn't actually be able to write via the old realm + old_realm->m_config.schema_mode = SchemaMode::ReadOnly; + + migration_function(old_realm, shared_from_this(), m_schema); + }; + ObjectStore::apply_schema_changes(read_group(), m_schema, m_schema_version, + schema, version, m_config.schema_mode, required_changes, wrapper); + } + else { + ObjectStore::apply_schema_changes(read_group(), m_schema, m_schema_version, + schema, version, m_config.schema_mode, required_changes); + REALM_ASSERT_DEBUG(additive || (required_changes = ObjectStore::schema_from_group(read_group()).compare(schema)).empty()); + } + + commit_transaction(); + m_coordinator->update_schema(m_schema, version); +} + +void Realm::add_schema_change_handler() +{ + if (m_config.schema_mode == SchemaMode::Additive) { + m_group->set_schema_change_notification_handler([&] { + auto new_schema = ObjectStore::schema_from_group(read_group()); + auto required_changes = m_schema.compare(new_schema); + ObjectStore::verify_valid_additive_changes(required_changes); + m_schema.copy_table_columns_from(new_schema); + m_coordinator->update_schema(m_schema, m_schema_version); + }); + } +} + +static void check_read_write(Realm *realm) +{ + if (realm->config().read_only()) { + throw InvalidTransactionException("Can't perform transactions on read-only Realms."); + } +} + +void Realm::verify_thread() const +{ + if (m_thread_id != std::this_thread::get_id()) { + throw IncorrectThreadException(); + } +} + +void Realm::verify_in_write() const +{ + if (!is_in_transaction()) { + throw InvalidTransactionException("Cannot modify managed objects outside of a write transaction."); + } +} + +bool Realm::is_in_transaction() const noexcept +{ + if (!m_shared_group) { + return false; + } + return m_shared_group->get_transact_stage() == SharedGroup::transact_Writing; +} + +void Realm::begin_transaction() +{ + check_read_write(this); + verify_thread(); + + if (is_in_transaction()) { + throw InvalidTransactionException("The Realm is already in a write transaction"); + } + + // make sure we have a read transaction + read_group(); + + transaction::begin(*m_shared_group, m_binding_context.get(), m_config.schema_mode); +} + +void Realm::commit_transaction() +{ + check_read_write(this); + verify_thread(); + + if (!is_in_transaction()) { + throw InvalidTransactionException("Can't commit a non-existing write transaction"); + } + + transaction::commit(*m_shared_group, m_binding_context.get()); + m_coordinator->send_commit_notifications(*this); +} + +void Realm::cancel_transaction() +{ + check_read_write(this); + verify_thread(); + + if (!is_in_transaction()) { + throw InvalidTransactionException("Can't cancel a non-existing write transaction"); + } + + transaction::cancel(*m_shared_group, m_binding_context.get()); +} + +void Realm::invalidate() +{ + verify_thread(); + check_read_write(this); + + if (is_in_transaction()) { + cancel_transaction(); + } + if (!m_group) { + return; + } + + m_shared_group->end_read(); + m_group = nullptr; +} + +bool Realm::compact() +{ + verify_thread(); + + if (m_config.read_only()) { + throw InvalidTransactionException("Can't compact a read-only Realm"); + } + if (is_in_transaction()) { + throw InvalidTransactionException("Can't compact a Realm within a write transaction"); + } + + Group& group = read_group(); + for (auto &object_schema : m_schema) { + ObjectStore::table_for_object_type(group, object_schema.name)->optimize(); + } + m_shared_group->end_read(); + m_group = nullptr; + + return m_shared_group->compact(); +} + +void Realm::write_copy(StringData path, BinaryData key) +{ + if (key.data() && key.size() != 64) { + throw InvalidEncryptionKeyException(); + } + verify_thread(); + try { + read_group().write(path, key.data()); + } + catch (...) { + translate_file_exception(path); + } +} + +void Realm::notify() +{ + if (is_closed()) { + return; + } + + verify_thread(); + + if (m_shared_group->has_changed()) { // Throws + if (m_binding_context) { + m_binding_context->changes_available(); + } + if (m_auto_refresh) { + if (m_group) { + m_coordinator->advance_to_ready(*this); + } + else if (m_binding_context) { + m_binding_context->did_change({}, {}); + } + } + } + else { + m_coordinator->process_available_async(*this); + } +} + +bool Realm::refresh() +{ + verify_thread(); + check_read_write(this); + + // can't be any new changes if we're in a write transaction + if (is_in_transaction()) { + return false; + } + + // advance transaction if database has changed + if (!m_shared_group->has_changed()) { // Throws + return false; + } + + if (m_group) { + transaction::advance(*m_shared_group, m_binding_context.get(), m_config.schema_mode); + m_coordinator->process_available_async(*this); + } + else { + // Create the read transaction + read_group(); + } + + return true; +} + +bool Realm::can_deliver_notifications() const noexcept +{ + if (m_config.read_only()) { + return false; + } + + if (m_binding_context && !m_binding_context->can_deliver_notifications()) { + return false; + } + + return true; +} + +uint64_t Realm::get_schema_version(const realm::Realm::Config &config) +{ + auto coordinator = RealmCoordinator::get_existing_coordinator(config.path); + if (coordinator) { + return coordinator->get_schema_version(); + } + + return ObjectStore::get_schema_version(Realm(config).read_group()); +} + +void Realm::close() +{ + if (m_coordinator) { + m_coordinator->unregister_realm(this); + } + + m_group = nullptr; + m_shared_group = nullptr; + m_history = nullptr; + m_read_only_group = nullptr; + m_binding_context = nullptr; + m_coordinator = nullptr; +} + +util::Optional Realm::file_format_upgraded_from_version() const +{ + if (upgrade_initial_version != upgrade_final_version) { + return upgrade_initial_version; + } + return util::none; +} + +Realm::HandoverPackage::HandoverPackage(HandoverPackage&&) = default; +Realm::HandoverPackage& Realm::HandoverPackage::operator=(HandoverPackage&&) = default; +Realm::HandoverPackage::VersionID::VersionID() : VersionID(SharedGroup::VersionID()) { } + +// Precondition: `m_version` is not greater than `new_version` +// Postcondition: `m_version` is equal to `new_version` +void Realm::HandoverPackage::advance_to_version(VersionID new_version) +{ + if (SharedGroup::VersionID(new_version) == SharedGroup::VersionID(m_version_id)) { + return; + } + REALM_ASSERT_DEBUG((SharedGroup::VersionID(new_version) > SharedGroup::VersionID(m_version_id))); + + // Open `Realm` at handover version + _impl::RealmCoordinator& coordinator = get_coordinator(); + Realm::Config config = coordinator.get_config(); + config.cache = false; + SharedRealm realm = coordinator.get_realm(config); + REALM_ASSERT(!realm->is_in_read_transaction()); + realm->m_group = &const_cast(realm->m_shared_group->begin_read(m_version_id)); + + // Import handover, advance version, and then repackage for handover + auto objects = realm->accept_handover(std::move(*this)); + transaction::advance(*realm->m_shared_group, realm->m_binding_context.get(), + realm->m_config.schema_mode, new_version); + *this = realm->package_for_handover(std::move(objects)); +} + +Realm::HandoverPackage::~HandoverPackage() +{ + if (is_awaiting_import()) { + get_coordinator().get_realm()->m_shared_group->unpin_version(m_version_id); + mark_not_awaiting_import(); + } +} + +Realm::HandoverPackage Realm::package_for_handover(std::vector objects_to_hand_over) +{ + verify_thread(); + if (is_in_transaction()) { + throw InvalidTransactionException("Cannot package handover during a write transaction."); + } + + HandoverPackage handover; + auto version_id = m_shared_group->pin_version(); + handover.m_version_id = version_id; + handover.m_source_realm = shared_from_this(); + // Since `m_source_realm` is used to determine if we need to unpin when destroyed, + // `m_source_realm` should only be set after `pin_version` succeeds in case it throws. + + handover.m_objects.reserve(objects_to_hand_over.size()); + for (auto &object : objects_to_hand_over) { + REALM_ASSERT(object.get_realm().get() == this); + handover.m_objects.push_back(object.export_for_handover()); + } + + return handover; +} + +std::vector Realm::accept_handover(Realm::HandoverPackage handover) +{ + verify_thread(); + + if (!handover.is_awaiting_import()) { + throw std::logic_error("Handover package must not be imported more than once."); + } + + auto unpin_version = util::make_scope_exit([&]() noexcept { + m_shared_group->unpin_version(handover.m_version_id); + handover.mark_not_awaiting_import(); + }); + + if (is_in_transaction()) { + throw InvalidTransactionException("Cannot accept handover during a write transaction."); + } + + // Ensure we're on the same version as the handover + if (!m_group) { + // A read transaction doesn't yet exist, so create at the handover version + m_group = &const_cast(m_shared_group->begin_read(handover.m_version_id)); + add_schema_change_handler(); + } + else { + auto current_version = m_shared_group->get_version_of_current_transaction(); + + if (SharedGroup::VersionID(handover.m_version_id) <= current_version) { + // The handover is behind, so advance it to our version + handover.advance_to_version(current_version); + } else { + // We're behind, so advance to the handover's version + transaction::advance(*m_shared_group, m_binding_context.get(), + m_config.schema_mode, handover.m_version_id); + m_coordinator->process_available_async(*this); + } + } + + std::vector objects; + objects.reserve(handover.m_objects.size()); + for (auto &object : handover.m_objects) { + objects.push_back(std::move(object).import_from_handover(shared_from_this())); + } + + // Avoid weird partial-refresh semantics when importing old packages + refresh(); + + return objects; +} + +MismatchedConfigException::MismatchedConfigException(StringData message, StringData path) +: std::logic_error(util::format(message.data(), path)) { } diff --git a/Pods/Realm/Realm/ObjectStore/src/sync_manager.cpp b/Pods/Realm/Realm/ObjectStore/src/sync_manager.cpp new file mode 100644 index 0000000..b9bdc0b --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync_manager.cpp @@ -0,0 +1,190 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync_manager.hpp" + +#include "impl/sync_client.hpp" +#include "sync_session.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; + +SyncManager& SyncManager::shared() +{ + static SyncManager& manager = *new SyncManager; + return manager; +} + +void SyncManager::set_log_level(util::Logger::Level level) noexcept +{ + std::lock_guard lock(m_mutex); + m_log_level = level; +} + +void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept +{ + std::lock_guard lock(m_mutex); + m_logger_factory = &factory; +} + +void SyncManager::set_error_handler(std::function handler) +{ + std::lock_guard lock(m_mutex); + auto wrapped_handler = [=](int error_code, std::string message) { + // FIXME: If the sync team decides to route all errors through the session-level error handler, the client-level + // error handler might go away altogether. + switch (error_code) { + case 100: // Connection closed (no error) + case 101: // Unspecified non-critical error + return; + default: + handler(error_code, message); + } + }; + m_error_handler = std::move(wrapped_handler); +} + +void SyncManager::set_login_function(SyncLoginFunction login_function) +{ + std::lock_guard lock(m_mutex); + m_login_function = std::move(login_function); +} + +SyncLoginFunction& SyncManager::get_sync_login_function() +{ + std::lock_guard lock(m_mutex); + // Precondition: binding must set a login callback before connecting any synced Realms. + REALM_ASSERT(m_login_function); + return m_login_function; +} + +void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately) +{ + std::lock_guard lock(m_mutex); + using Reconnect = sync::Client::Reconnect; + m_client_reconnect_mode = reconnect_immediately ? Reconnect::immediately : Reconnect::normal; +} + +void SyncManager::set_client_should_validate_ssl(bool validate_ssl) +{ + std::lock_guard lock(m_mutex); + m_client_validate_ssl = validate_ssl; +} + +std::shared_ptr SyncManager::get_existing_active_session(const std::string& path) const +{ + std::lock_guard lock(m_session_mutex); + return get_existing_active_session_locked(path); +} + +std::shared_ptr SyncManager::get_existing_active_session_locked(const std::string& path) const +{ + REALM_ASSERT(!m_session_mutex.try_lock()); + auto it = m_active_sessions.find(path); + if (it == m_active_sessions.end()) { + return nullptr; + } + if (auto session = it->second.lock()) { + return session; + } + return nullptr; +} + +std::unique_ptr SyncManager::get_existing_inactive_session_locked(const std::string& path) +{ + REALM_ASSERT(!m_session_mutex.try_lock()); + auto it = m_inactive_sessions.find(path); + if (it == m_inactive_sessions.end()) { + return nullptr; + } + auto ret = std::move(it->second); + m_inactive_sessions.erase(it); + return ret; +} + +std::shared_ptr SyncManager::get_session(const std::string& path, const SyncConfig& sync_config) +{ + auto client = get_sync_client(); // Throws + + std::lock_guard lock(m_session_mutex); + if (auto session = get_existing_active_session_locked(path)) { + return session; + } + + std::unique_ptr session = get_existing_inactive_session_locked(path); + if (!session) + session.reset(new SyncSession(std::move(client), path, sync_config)); + session->revive_if_needed(); + + auto session_deleter = [this](SyncSession *session) { dropped_last_reference_to_session(session); }; + auto shared_session = std::shared_ptr(session.release(), std::move(session_deleter)); + m_active_sessions[path] = shared_session; + return shared_session; +} + +void SyncManager::dropped_last_reference_to_session(SyncSession* session) +{ + { + std::lock_guard lock(m_session_mutex); + auto path = session->path(); + REALM_ASSERT_DEBUG(m_active_sessions.count(path)); + m_active_sessions.erase(path); + m_inactive_sessions[path].reset(session); + } + session->close(); +} + +void SyncManager::unregister_session(const std::string& path) +{ + std::lock_guard lock(m_session_mutex); + if (m_active_sessions.count(path)) + return; + auto it = m_inactive_sessions.find(path); + REALM_ASSERT(it != m_inactive_sessions.end()); + if (it->second->is_inactive()) + m_inactive_sessions.erase(path); +} + +std::shared_ptr SyncManager::get_sync_client() const +{ + std::lock_guard lock(m_mutex); + if (!m_sync_client) + m_sync_client = create_sync_client(); // Throws + return m_sync_client; +} + +std::shared_ptr SyncManager::create_sync_client() const +{ + REALM_ASSERT(!m_mutex.try_lock()); + + std::unique_ptr logger; + if (m_logger_factory) { + logger = m_logger_factory->make_logger(m_log_level); // Throws + } + else { + auto stderr_logger = std::make_unique(); // Throws + stderr_logger->set_level_threshold(m_log_level); + logger = std::move(stderr_logger); + } + return std::make_shared(std::move(logger), + std::move(m_error_handler), + m_client_reconnect_mode, + m_client_validate_ssl); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/sync_metadata.cpp b/Pods/Realm/Realm/ObjectStore/src/sync_metadata.cpp new file mode 100644 index 0000000..7ba3c03 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync_metadata.cpp @@ -0,0 +1,235 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync_metadata.hpp" + +#include "object_schema.hpp" +#include "object_store.hpp" +#include "property.hpp" +#include "results.hpp" +#include "schema.hpp" +#if REALM_PLATFORM_APPLE +#include "impl/apple/keychain_helper.hpp" +#endif + +#include +#include + +namespace realm { + +static const char * const c_sync_userMetadata = "UserMetadata"; +static const char * const c_sync_marked_for_removal = "marked_for_removal"; +static const char * const c_sync_identity = "identity"; +static const char * const c_sync_auth_server_url = "auth_server_url"; +static const char * const c_sync_user_token = "user_token"; + +SyncMetadataManager::SyncMetadataManager(std::string path, + bool should_encrypt, + util::Optional> encryption_key) +{ + std::lock_guard lock(m_metadata_lock); + + auto nullable_string_property = [](std::string name)->Property { + Property p = { name, PropertyType::String }; + p.is_nullable = true; + return p; + }; + + Property primary_key = { c_sync_identity, PropertyType::String }; + primary_key.is_indexed = true; + primary_key.is_primary = true; + + Realm::Config config; + config.path = std::move(path); + Schema schema = { + { c_sync_userMetadata, + { + primary_key, + { c_sync_marked_for_removal, PropertyType::Bool }, + nullable_string_property(c_sync_auth_server_url), + nullable_string_property(c_sync_user_token), + } + } + }; + config.schema = std::move(schema); + config.schema_mode = SchemaMode::Additive; +#if REALM_PLATFORM_APPLE + if (should_encrypt && !encryption_key) { + encryption_key = keychain::metadata_realm_encryption_key(); + } +#endif + if (should_encrypt) { + if (!encryption_key) { + throw std::invalid_argument("Metadata Realm encryption was specified, but no encryption key was provided."); + } + config.encryption_key = std::move(*encryption_key); + } + + // Open the Realm. + SharedRealm realm = Realm::get_shared_realm(config); + + // Get data about the (hardcoded) schema. + DescriptorRef descriptor = ObjectStore::table_for_object_type(realm->read_group(), + c_sync_userMetadata)->get_descriptor(); + m_schema = { + descriptor->get_column_index(c_sync_identity), + descriptor->get_column_index(c_sync_marked_for_removal), + descriptor->get_column_index(c_sync_user_token), + descriptor->get_column_index(c_sync_auth_server_url) + }; + + m_metadata_config = std::move(config); +} + +Realm::Config SyncMetadataManager::get_configuration() const +{ + std::lock_guard lock(m_metadata_lock); + return m_metadata_config; +} + +SyncUserMetadataResults SyncMetadataManager::all_unmarked_users() const +{ + return get_users(false); +} + +SyncUserMetadataResults SyncMetadataManager::all_users_marked_for_removal() const +{ + return get_users(true); +} + +SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const +{ + // Open the Realm. + SharedRealm realm = Realm::get_shared_realm(get_configuration()); + + TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata); + Query query = table->where().equal(m_schema.idx_marked_for_removal, marked); + + Results results(realm, std::move(query)); + return SyncUserMetadataResults(std::move(results), std::move(realm), m_schema); +} + +SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row) +: m_invalid(row.get_bool(schema.idx_marked_for_removal)) +, m_schema(std::move(schema)) +, m_realm(std::move(realm)) +, m_row(row) +{ } + +SyncUserMetadata::SyncUserMetadata(SyncMetadataManager& manager, std::string identity, bool make_if_absent) +: m_schema(manager.m_schema) +{ + // Open the Realm. + m_realm = Realm::get_shared_realm(manager.get_configuration()); + + // Retrieve or create the row for this object. + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); + size_t row_idx = table->find_first_string(m_schema.idx_identity, identity); + if (row_idx == not_found) { + if (!make_if_absent) { + m_invalid = true; + m_realm = nullptr; + return; + } + m_realm->begin_transaction(); + row_idx = table->find_first_string(m_schema.idx_identity, identity); + if (row_idx == not_found) { + row_idx = table->add_empty_row(); + table->set_string(m_schema.idx_identity, row_idx, identity); + m_realm->commit_transaction(); + } else { + // Someone beat us to adding this user. + m_realm->cancel_transaction(); + } + } + m_row = table->get(row_idx); + if (make_if_absent) { + // User existed in the table, but had been marked for deletion. Unmark it. + m_realm->begin_transaction(); + table->set_bool(m_schema.idx_marked_for_removal, row_idx, false); + m_realm->commit_transaction(); + m_invalid = false; + } else { + m_invalid = m_row.get_bool(m_schema.idx_marked_for_removal); + } +} + +bool SyncUserMetadata::is_valid() const +{ + return !m_invalid; +} + +std::string SyncUserMetadata::identity() const +{ + m_realm->verify_thread(); + StringData result = m_row.get_string(m_schema.idx_identity); + return result; +} + +util::Optional SyncUserMetadata::get_optional_string_field(size_t col_idx) const +{ + REALM_ASSERT(!m_invalid); + m_realm->verify_thread(); + StringData result = m_row.get_string(col_idx); + return result.is_null() ? util::none : util::make_optional(std::string(result)); +} + +util::Optional SyncUserMetadata::server_url() const +{ + return get_optional_string_field(m_schema.idx_auth_server_url); +} + +util::Optional SyncUserMetadata::user_token() const +{ + return get_optional_string_field(m_schema.idx_user_token); +} + +void SyncUserMetadata::set_state(util::Optional server_url, util::Optional user_token) +{ + if (m_invalid) { + return; + } + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_string(m_schema.idx_user_token, *user_token); + m_row.set_string(m_schema.idx_auth_server_url, *server_url); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::mark_for_removal() +{ + if (m_invalid) { + return; + } + m_realm->verify_thread(); + m_realm->begin_transaction(); + m_row.set_bool(m_schema.idx_marked_for_removal, true); + m_realm->commit_transaction(); +} + +void SyncUserMetadata::remove() +{ + m_invalid = true; + m_realm->begin_transaction(); + TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata); + table->move_last_over(m_row.get_index()); + m_realm->commit_transaction(); + m_realm = nullptr; +} + +} diff --git a/Pods/Realm/Realm/ObjectStore/src/sync_session.cpp b/Pods/Realm/Realm/ObjectStore/src/sync_session.cpp new file mode 100644 index 0000000..a666bc5 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/sync_session.cpp @@ -0,0 +1,474 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "sync_session.hpp" + +#include "impl/sync_client.hpp" +#include "sync_manager.hpp" + +#include + +using namespace realm; +using namespace realm::_impl; +using namespace realm::_impl::sync_session_states; + +/// A state which a `SyncSession` can currently be within. State classes handle various actions +/// and state transitions. +/// +/// STATES: +/// +/// WAITING_FOR_ACCESS_TOKEN: upon entering this state, the binding is informed +/// that the session wants an access token. The session is now waiting for the +/// binding to provide the token. +/// From: initial, INACTIVE +/// To: +/// * ACTIVE: when the binding successfully refreshes the token +/// * INACTIVE: if asked to log out, or if asked to close and the stop policy +/// is Immediate. +/// * DYING: if asked to close and the stop policy is AfterChangesUploaded +/// * ERROR: if a fatal error occurs +/// +/// ACTIVE: the session is connected to the Realm Object Server and is actively +/// transferring data. +/// From: WAITING_FOR_ACCESS_TOKEN, DYING +/// To: +/// * WAITING_FOR_ACCESS_TOKEN: if the session is informed (through the error +/// handler) that the token expired +/// * INACTIVE: if asked to log out, or if asked to close and the stop policy +/// is Immediate. +/// * DYING: if asked to close and the stop policy is AfterChangesUploaded +/// * ERROR: if a fatal error occurs +/// +/// DYING: the session is performing clean-up work in preparation to be destroyed. +/// From: ACTIVE +/// To: +/// * INACTIVE: when the clean-up work completes, if the session wasn't +/// revived, or if explicitly asked to log out before the +/// clean-up work begins +/// * ACTIVE: if the session is revived +/// * ERROR: if a fatal error occurs +/// +/// INACTIVE: the user owning this session has logged out, the `sync::Session` +/// owned by this session is destroyed, and the session is quiescent. +/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING +/// To: +/// * WAITING_FOR_ACCESS_TOKEN: if the session is revived +/// * ERROR: if a fatal error occurs +/// +/// ERROR: a non-recoverable error has occurred, and this session is semantically +/// invalid. The binding must create a new session with a different configuration. +/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING, INACTIVE +/// To: +/// * (none, this is a terminal state) +/// +struct SyncSession::State { + virtual ~State() { } + + virtual void enter_state(std::unique_lock&, SyncSession&) const { } + + virtual void refresh_access_token(std::unique_lock&, + SyncSession&, const std::string&, + const util::Optional&) const { } + + virtual void access_token_expired(std::unique_lock&, SyncSession&) const { } + + virtual void nonsync_transact_notify(std::unique_lock&, SyncSession&, sync::Session::version_type) const { } + + virtual bool revive_if_needed(std::unique_lock&, SyncSession&) const { return false; } + + virtual void log_out(std::unique_lock&, SyncSession&) const { } + + virtual void close_if_connecting(std::unique_lock&, SyncSession&) const { } + + virtual void close(std::unique_lock&, SyncSession&) const { } + + static const State& waiting_for_access_token; + static const State& active; + static const State& dying; + static const State& inactive; + static const State& error; +}; + +struct sync_session_states::WaitingForAccessToken : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + session.m_deferred_close = false; + } + + void refresh_access_token(std::unique_lock& lock, SyncSession& session, + const std::string& access_token, + const util::Optional& server_url) const override + { + // Since the sync session was previously unbound, it's safe to do this from the + // calling thread. + if (!session.m_server_url) { + session.m_server_url = std::move(server_url); + } + session.m_session->bind(*session.m_server_url, std::move(access_token)); + if (session.m_deferred_commit_notification) { + session.m_session->nonsync_transact_notify(*session.m_deferred_commit_notification); + session.m_deferred_commit_notification = util::none; + } + session.advance_state(lock, active); + if (session.m_deferred_close) { + session.m_deferred_close = false; + session.m_state->close(lock, session); + } + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } + + void nonsync_transact_notify(std::unique_lock&, + SyncSession& session, + sync::Session::version_type version) const override + { + // Notify at first available opportunity. + session.m_deferred_commit_notification = version; + } + + void close_if_connecting(std::unique_lock& lock, SyncSession& session) const override + { + // Ignore the sync configuration's stop policy as we're not yet connected. + session.advance_state(lock, inactive); + } + + void close(std::unique_lock&, SyncSession& session) const override + { + session.m_deferred_close = true; + } +}; + +struct sync_session_states::Active : public SyncSession::State { + void refresh_access_token(std::unique_lock&, SyncSession& session, + const std::string& access_token, + const util::Optional&) const override + { + session.m_session->refresh(std::move(access_token)); + } + + void access_token_expired(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, waiting_for_access_token); + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } + + void nonsync_transact_notify(std::unique_lock&, SyncSession& session, + sync::Session::version_type version) const override + { + // Fully ready sync session, notify immediately. + session.m_session->nonsync_transact_notify(version); + } + + void close(std::unique_lock& lock, SyncSession& session) const override + { + switch (session.m_config.stop_policy) { + case SyncSessionStopPolicy::Immediately: + session.advance_state(lock, inactive); + break; + case SyncSessionStopPolicy::LiveIndefinitely: + // Don't do anything; session lives forever. + break; + case SyncSessionStopPolicy::AfterChangesUploaded: + // Wait for all pending changes to upload. + session.advance_state(lock, dying); + break; + } + } +}; + +struct sync_session_states::Dying : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + ++session.m_pending_upload_threads; + std::thread([session=&session] { + std::unique_lock lock(session->m_state_mutex); + if (session->m_pending_upload_threads != 1) { + --session->m_pending_upload_threads; + return; + } + + if (session->m_state != &State::dying) { + // The session was revived. Don't kill it. + --session->m_pending_upload_threads; + return; + } + + session->m_session->wait_for_upload_complete_or_client_stopped(); + --session->m_pending_upload_threads; + session->advance_state(lock, inactive); + }).detach(); + } + + bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override + { + // Revive. + session.advance_state(lock, active); + return false; + } + + void log_out(std::unique_lock& lock, SyncSession& session) const override + { + session.advance_state(lock, inactive); + } +}; + +struct sync_session_states::Inactive : public SyncSession::State { + void enter_state(std::unique_lock& lock, SyncSession& session) const override + { + session.m_session = nullptr; + session.m_server_url = util::none; + session.unregister(lock); + } + + bool revive_if_needed(std::unique_lock& lock, SyncSession& session) const override + { + // Revive. + session.create_sync_session(); + session.advance_state(lock, waiting_for_access_token); + return true; + } +}; + +struct sync_session_states::Error : public SyncSession::State { + void enter_state(std::unique_lock&, SyncSession& session) const override + { + session.m_session = nullptr; + } + + // Everything else is a no-op when in the error state. +}; + + +const SyncSession::State& SyncSession::State::waiting_for_access_token = WaitingForAccessToken(); +const SyncSession::State& SyncSession::State::active = Active(); +const SyncSession::State& SyncSession::State::dying = Dying(); +const SyncSession::State& SyncSession::State::inactive = Inactive(); +const SyncSession::State& SyncSession::State::error = Error(); + + +SyncSession::SyncSession(std::shared_ptr client, std::string realm_path, SyncConfig config) +: m_state(&State::inactive) +, m_config(std::move(config)) +, m_realm_path(std::move(realm_path)) +, m_client(std::move(client)) +{ + revive_if_needed(); +} + +void SyncSession::create_sync_session() +{ + REALM_ASSERT(!m_session); + m_session = std::make_unique(m_client->client, m_realm_path); + + // Set up the wrapped handler + auto wrapped_handler = [this](int error_code, std::string message) { + using Error = realm::sync::Error; + + SyncSessionError error_type; + // Precondition: error_code is a valid realm::sync::Error raw value. + Error strong_code = static_cast(error_code); + + switch (strong_code) { + // Client errors; all ignored (for now) + case Error::connection_closed: + case Error::other_error: + case Error::unknown_message: + case Error::bad_syntax: + case Error::limits_exceeded: + case Error::wrong_protocol_version: + case Error::bad_session_ident: + case Error::reuse_of_session_ident: + case Error::bound_in_other_session: + case Error::bad_message_order: + return; + // Session errors + case Error::session_closed: + case Error::other_session_error: + // The binding doesn't need to be aware of these because they are strictly informational, and do not + // represent actual errors. + return; + case Error::token_expired: { + std::unique_lock lock(m_state_mutex); + // This isn't an error from the binding's point of view. If we're connected we'll + // simply ask the binding to log in again. + m_state->access_token_expired(lock, *this); + return; + } + case Error::bad_authentication: { + std::unique_lock lock(m_state_mutex); + error_type = SyncSessionError::UserFatal; + advance_state(lock, State::error); + break; + } + case Error::illegal_realm_path: + case Error::no_such_realm: + case Error::bad_server_file_ident: + case Error::diverging_histories: + case Error::bad_changeset: { + std::unique_lock lock(m_state_mutex); + error_type = SyncSessionError::SessionFatal; + advance_state(lock, State::error); + break; + } + case Error::permission_denied: + error_type = SyncSessionError::AccessDenied; + break; + case Error::bad_client_file_ident: + case Error::bad_server_version: + case Error::bad_client_version: + error_type = SyncSessionError::Debug; + break; + } + if (m_error_handler) { + m_error_handler(error_code, message, error_type); + } + }; + m_session->set_error_handler(std::move(wrapped_handler)); + + // Set up the wrapped sync transact callback + auto wrapped_callback = [this](VersionID old_version, VersionID new_version) { + if (m_sync_transact_callback) { + m_sync_transact_callback(old_version, new_version); + } + }; + m_session->set_sync_transact_callback(std::move(wrapped_callback)); +} + +void SyncSession::set_sync_transact_callback(std::function callback) +{ + m_sync_transact_callback = std::move(callback); +} + +void SyncSession::set_error_handler(std::function handler) +{ + m_error_handler = std::move(handler); +} + +void SyncSession::advance_state(std::unique_lock& lock, const State& state) +{ + REALM_ASSERT(lock.owns_lock()); + REALM_ASSERT(&state != m_state); + m_state = &state; + m_state->enter_state(lock, *this); +} + +void SyncSession::nonsync_transact_notify(sync::Session::version_type version) +{ + std::unique_lock lock(m_state_mutex); + m_state->nonsync_transact_notify(lock, *this, version); +} + +void SyncSession::revive_if_needed() +{ + bool need_login; + { + std::unique_lock lock(m_state_mutex); + need_login = m_state->revive_if_needed(lock, *this); + } + if (need_login) + SyncManager::shared().get_sync_login_function()(m_realm_path, m_config); +} + +void SyncSession::log_out() +{ + std::unique_lock lock(m_state_mutex); + m_state->log_out(lock, *this); +} + +void SyncSession::close() +{ + std::unique_lock lock(m_state_mutex); + m_state->close(lock, *this); +} + +void SyncSession::close_if_connecting() +{ + std::unique_lock lock(m_state_mutex); + m_state->close_if_connecting(lock, *this); +} + +void SyncSession::unregister(std::unique_lock& lock) +{ + REALM_ASSERT(lock.owns_lock()); + REALM_ASSERT(m_state == &State::inactive); // Must stop an active session before unregistering. + + lock.unlock(); + SyncManager::shared().unregister_session(m_realm_path); +} + +void SyncSession::wait_for_upload_completion(std::function callback) +{ + REALM_ASSERT(shared_from_this()); + auto thread = std::thread([this, callback=std::move(callback), self=shared_from_this()]() { + { + std::unique_lock lock(m_state_mutex); + if (m_session) { + m_session->wait_for_upload_complete_or_client_stopped(); + } + } + + callback(); + }); + thread.detach(); +} + +void SyncSession::wait_for_download_completion(std::function callback) +{ + REALM_ASSERT(shared_from_this()); + auto thread = std::thread([this, callback=std::move(callback), self=shared_from_this()]() { + { + std::unique_lock lock(m_state_mutex); + if (m_session) { + m_session->wait_for_download_complete_or_client_stopped(); + } + } + + callback(); + }); + thread.detach(); +} + +void SyncSession::refresh_access_token(std::string access_token, util::Optional server_url) +{ + if (!m_server_url && !server_url) { + // The first time this method is called, the server URL must be provided. + return; + } + + std::unique_lock lock(m_state_mutex); + m_state->refresh_access_token(lock, *this, access_token, server_url); +} + +bool SyncSession::is_valid() const +{ + std::unique_lock lock(m_state_mutex); + return m_state != &State::error; +} + +bool SyncSession::is_inactive() const +{ + std::unique_lock lock(m_state_mutex); + return m_state == &State::inactive && m_pending_upload_threads == 0; +} diff --git a/Pods/Realm/Realm/ObjectStore/src/thread_confined.cpp b/Pods/Realm/Realm/ObjectStore/src/thread_confined.cpp new file mode 100644 index 0000000..0db0d57 --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/thread_confined.cpp @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "thread_confined.hpp" + +#include "impl/handover.hpp" + +using namespace realm; + +AnyThreadConfined::AnyThreadConfined(const AnyThreadConfined& thread_confined) +{ + switch (thread_confined.m_type) { + case Type::Object: + new (&m_object) Object(thread_confined.m_object); + break; + + case Type::List: + new (&m_list) List(thread_confined.m_list); + break; + + case Type::Results: + new (&m_results) Results(thread_confined.m_results); + break; + } + new (&m_type) Type(thread_confined.m_type); +} + +AnyThreadConfined& AnyThreadConfined::operator=(const AnyThreadConfined& thread_confined) +{ + this->~AnyThreadConfined(); + new (this) AnyThreadConfined(thread_confined); + return *this; +} + +AnyThreadConfined::AnyThreadConfined(AnyThreadConfined&& thread_confined) +{ + switch (thread_confined.m_type) { + case Type::Object: + new (&m_object) Object(std::move(thread_confined.m_object)); + break; + + case Type::List: + new (&m_list) List(std::move(thread_confined.m_list)); + break; + + case Type::Results: + new (&m_results) Results(std::move(thread_confined.m_results)); + break; + } + new (&m_type) Type(std::move(thread_confined.m_type)); +} + +AnyThreadConfined& AnyThreadConfined::operator=(AnyThreadConfined&& thread_confined) +{ + this->~AnyThreadConfined(); + new (this) AnyThreadConfined(std::move(thread_confined)); + return *this; +} + +AnyThreadConfined::~AnyThreadConfined() +{ + switch (m_type) { + case Type::Object: + m_object.~Object(); + break; + + case Type::List: + m_list.~List(); + break; + + case Type::Results: + m_results.~Results(); + break; + } +} + +SharedRealm AnyThreadConfined::get_realm() const +{ + switch (m_type) { + case Type::Object: + return m_object.realm(); + + case Type::List: + return m_list.get_realm(); + + case Type::Results: + return m_results.get_realm(); + } + REALM_UNREACHABLE(); +} + +_impl::AnyHandover AnyThreadConfined::export_for_handover() const +{ + SharedGroup& shared_group = Realm::Internal::get_shared_group(*get_realm()); + switch (m_type) { + case AnyThreadConfined::Type::Object: + return _impl::AnyHandover(shared_group.export_for_handover(m_object.row()), + m_object.get_object_schema().name); + + case AnyThreadConfined::Type::List: + return _impl::AnyHandover(shared_group.export_linkview_for_handover(m_list.m_link_view)); + + case AnyThreadConfined::Type::Results: { + SortDescriptor::HandoverPatch sort_order; + SortDescriptor::generate_patch(m_results.get_sort(), sort_order); + return _impl::AnyHandover(shared_group.export_for_handover(m_results.get_query(), ConstSourcePayload::Copy), + std::move(sort_order)); + } + } + REALM_UNREACHABLE(); +} diff --git a/Pods/Realm/Realm/ObjectStore/src/util/format.cpp b/Pods/Realm/Realm/ObjectStore/src/util/format.cpp new file mode 100644 index 0000000..4103c8d --- /dev/null +++ b/Pods/Realm/Realm/ObjectStore/src/util/format.cpp @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "util/format.hpp" + +#include + +#include +#include + +namespace realm { namespace _impl { +Printable::Printable(StringData value) : m_type(Type::String), m_string(value.data()) { } + +void Printable::print(std::ostream& out) const +{ + switch (m_type) { + case Printable::Type::Bool: + out << (m_uint ? "true" : "false"); + break; + case Printable::Type::Uint: + out << m_uint; + break; + case Printable::Type::Int: + out << m_int; + break; + case Printable::Type::String: + out << m_string; + break; + } +} + +std::string format(const char* fmt, std::initializer_list values) +{ + std::stringstream ss; + while (*fmt) { + auto next = strchr(fmt, '%'); + + // emit the rest of the format string if there are no more percents + if (!next) { + ss << fmt; + break; + } + + // emit everything up to the next percent + ss.write(fmt, next - fmt); + ++next; + REALM_ASSERT(*next); + + // %% produces a single escaped % + if (*next == '%') { + ss << '%'; + fmt = next + 1; + continue; + } + REALM_ASSERT(isdigit(*next)); + + // The const_cast is safe because stroul does not actually modify + // the pointed-to string, but it lacks a const overload + auto index = strtoul(next, const_cast(&fmt), 10) - 1; + REALM_ASSERT(index < values.size()); + (values.begin() + index)->print(ss); + } + return ss.str(); +} + +} // namespace _impl +} // namespace realm diff --git a/Pods/Realm/Realm/RLMAccessor.mm b/Pods/Realm/Realm/RLMAccessor.mm new file mode 100644 index 0000000..724ccf0 --- /dev/null +++ b/Pods/Realm/Realm/RLMAccessor.mm @@ -0,0 +1,801 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMAccessor.h" + +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.h" +#import "RLMSchema_Private.h" +#import "RLMUtil.hpp" +#import "results.hpp" +#import "property.hpp" + +#import +#import + +typedef NS_ENUM(char, RLMAccessorCode) { + RLMAccessorCodeByte, + RLMAccessorCodeShort, + RLMAccessorCodeInt, + RLMAccessorCodeLong, + RLMAccessorCodeLongLong, + RLMAccessorCodeFloat, + RLMAccessorCodeDouble, + RLMAccessorCodeBool, + RLMAccessorCodeString, + RLMAccessorCodeDate, + RLMAccessorCodeData, + RLMAccessorCodeLink, + RLMAccessorCodeArray, + RLMAccessorCodeLinkingObjects, + RLMAccessorCodeAny, + + RLMAccessorCodeIntObject, + RLMAccessorCodeFloatObject, + RLMAccessorCodeDoubleObject, + RLMAccessorCodeBoolObject, +}; + +template +static T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { + RLMVerifyAttached(obj); + return obj->_row.get_table()->get(obj->_info->objectSchema->persisted_properties[index].table_column, obj->_row.get_index()); +} + +template +static NSNumber *getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { + RLMVerifyAttached(obj); + auto col = obj->_info->objectSchema->persisted_properties[index].table_column; + if (obj->_row.is_null(col)) { + return nil; + } + return @(obj->_row.get_table()->get(col, obj->_row.get_index())); +} + + +// long getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, long long val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), val, setDefault); +} + +// float getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, float val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), val, setDefault); +} + +// double getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, double val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), val, setDefault); +} + +// bool getter/setter +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, BOOL val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), val, setDefault); +} + +// string getter/setter +static inline NSString *RLMGetString(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMStringDataToNSString(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + try { + obj->_row.get_table()->set_string(colIndex, obj->_row.get_index(), RLMStringDataWithNSString(val), setDefault); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +// date getter/setter +static inline NSDate *RLMGetDate(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMTimestampToNSDate(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSDate *const date, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + if (date) { + obj->_row.get_table()->set_timestamp(colIndex, obj->_row.get_index(), RLMTimestampForNSDate(date), setDefault); + } + else { + obj->_row.set_null(colIndex); + } +} + +// data getter/setter +static inline NSData *RLMGetData(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + return RLMBinaryDataToNSData(get(obj, colIndex)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSData *const data, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + try { + obj->_row.get_table()->set_binary(colIndex, obj->_row.get_index(), RLMBinaryDataForNSData(data), setDefault); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, + __unsafe_unretained NSString *const className, + __unsafe_unretained id const value, + RLMCreationOptions creationOptions) NS_RETURNS_RETAINED; +static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, + __unsafe_unretained NSString *const className, + __unsafe_unretained id const value, + RLMCreationOptions creationOptions) { + RLMObjectBase *link = RLMDynamicCast(value); + if (!link || ![link->_objectSchema.className isEqualToString:className]) { + // create from non-rlmobject + return RLMCreateObjectInRealmWithValue(realm, className, value, creationOptions & RLMCreationOptionsCreateOrUpdate); + } + + if (link.isInvalidated) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + + if (link->_realm == realm) { + return link; + } + + if (creationOptions & RLMCreationOptionsPromoteUnmanaged) { + if (!link->_realm) { + RLMAddObjectToRealm(link, realm, creationOptions & RLMCreationOptionsCreateOrUpdate); + return link; + } + @throw RLMException(@"Can not add objects from a different Realm"); + } + + // copy from another realm or copy from unmanaged + return RLMCreateObjectInRealmWithValue(realm, className, link, creationOptions & RLMCreationOptionsCreateOrUpdate); +} + +// link getter/setter +static inline RLMObjectBase *RLMGetLink(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + RLMVerifyAttached(obj); + auto col = obj->_info->objectSchema->persisted_properties[colIndex].table_column; + + if (obj->_row.is_null_link(col)) { + return nil; + } + NSUInteger index = obj->_row.get_link(col); + return RLMCreateObjectAccessor(obj->_realm, obj->_info->linkTargetType(colIndex), index); +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained RLMObjectBase *const val, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + if (!val) { + obj->_row.nullify_link(colIndex); + return; + } + + RLMObjectBase *link = RLMGetLinkedObjectForValue(obj->_realm, val->_objectSchema.className, + val, RLMCreationOptionsPromoteUnmanaged); + + // make sure it is the correct type + if (link->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { + @throw RLMException(@"Can't set object of type '%@' to property of type '%@'", + val->_objectSchema.className, + obj->_info->propertyForTableColumn(colIndex).objectClassName); + } + obj->_row.get_table()->set_link(colIndex, obj->_row.get_index(), link->_row.get_index(), setDefault); +} + +// array getter/setter +static inline RLMArray *RLMGetArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { + RLMVerifyAttached(obj); + auto prop = obj->_info->rlmObjectSchema.properties[colIndex]; + return [[RLMArrayLinkView alloc] initWithParent:obj property:prop]; +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained id const array, __unused bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex); + // remove all old + // FIXME: make sure delete rules don't purge objects + linkView->clear(); + for (RLMObjectBase *link in array) { + RLMObjectBase * addedLink = RLMGetLinkedObjectForValue(obj->_realm, link->_objectSchema.className, link, RLMCreationOptionsPromoteUnmanaged); + linkView->add(addedLink->_row.get_index()); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const intObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (intObject) { + obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), intObject.longLongValue, setDefault); + } + else { + obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const floatObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (floatObject) { + obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), floatObject.floatValue, setDefault); + } + else { + obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const doubleObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (doubleObject) { + obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), doubleObject.doubleValue, setDefault); + } + else { + obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const boolObject, bool setDefault) { + RLMVerifyInWriteTransaction(obj); + + if (boolObject) { + obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), boolObject.boolValue, setDefault); + } + else { + obj->_row.get_table()->set_null(colIndex, obj->_row.get_index(), setDefault); + } +} + +static inline RLMLinkingObjects *RLMGetLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const property) { + auto& objectInfo = obj->_realm->_info[property.objectClassName]; + auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String); + auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), objectInfo.table(), linkingProperty->table_column); + realm::Results results(obj->_realm->_realm, std::move(backlinkView)); + return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)]; +} + +// any getter/setter +static inline id RLMGetAnyProperty(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx) { + RLMVerifyAttached(obj); + return RLMMixedToObjc(obj->_row.get_mixed(col_ndx)); +} +static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger, __unsafe_unretained id, bool) { + RLMVerifyInWriteTransaction(obj); + @throw RLMException(@"Modifying Mixed properties is not supported"); +} + +// dynamic getter with column closure +static IMP RLMAccessorGetter(RLMProperty *prop, RLMAccessorCode accessorCode) { + NSUInteger index = prop.index; + switch (accessorCode) { + case RLMAccessorCodeByte: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return (char)get(obj, index); + }); + case RLMAccessorCodeShort: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return (short)get(obj, index); + }); + case RLMAccessorCodeInt: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return (int)get(obj, index); + }); + case RLMAccessorCodeLongLong: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }); + case RLMAccessorCodeLong: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return (long)get(obj, index); + }); + case RLMAccessorCodeFloat: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }); + case RLMAccessorCodeDouble: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }); + case RLMAccessorCodeBool: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return get(obj, index); + }); + case RLMAccessorCodeString: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetString(obj, index); + }); + case RLMAccessorCodeDate: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetDate(obj, index); + }); + case RLMAccessorCodeData: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetData(obj, index); + }); + case RLMAccessorCodeLink: + return imp_implementationWithBlock(^id(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetLink(obj, index); + }); + case RLMAccessorCodeArray: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetArray(obj, index); + }); + case RLMAccessorCodeAny: + @throw RLMException(@"Cannot create accessor class for schema with Mixed properties"); + case RLMAccessorCodeIntObject: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }); + case RLMAccessorCodeFloatObject: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }); + case RLMAccessorCodeDoubleObject: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }); + case RLMAccessorCodeBoolObject: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }); + case RLMAccessorCodeLinkingObjects: + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj) { + return RLMGetLinkingObjects(obj, prop); + }); + } +} + +template +static void RLMWrapSetter(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const name, Function&& f) { + if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, obj->_row.get_index(), *obj->_info)) { + info->willChange(name); + f(); + info->didChange(name); + } + else { + f(); + } +} + +template +static IMP RLMMakeSetter(RLMProperty *prop) { + NSUInteger index = prop.index; + NSString *name = prop.name; + if (prop.isPrimary) { + return imp_implementationWithBlock(^(__unused RLMObjectBase *obj, __unused ArgType val) { + @throw RLMException(@"Primary key can't be changed after an object is inserted."); + }); + } + return imp_implementationWithBlock(^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) { + RLMWrapSetter(obj, name, [&] { + RLMSetValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, static_cast(val), false); + }); + }); +} + +// dynamic setter with column closure +static IMP RLMAccessorSetter(RLMProperty *prop, RLMAccessorCode accessorCode) { + switch (accessorCode) { + case RLMAccessorCodeByte: return RLMMakeSetter(prop); + case RLMAccessorCodeShort: return RLMMakeSetter(prop); + case RLMAccessorCodeInt: return RLMMakeSetter(prop); + case RLMAccessorCodeLong: return RLMMakeSetter(prop); + case RLMAccessorCodeLongLong: return RLMMakeSetter(prop); + case RLMAccessorCodeFloat: return RLMMakeSetter(prop); + case RLMAccessorCodeDouble: return RLMMakeSetter(prop); + case RLMAccessorCodeBool: return RLMMakeSetter(prop); + case RLMAccessorCodeString: return RLMMakeSetter(prop); + case RLMAccessorCodeDate: return RLMMakeSetter(prop); + case RLMAccessorCodeData: return RLMMakeSetter(prop); + case RLMAccessorCodeLink: return RLMMakeSetter(prop); + case RLMAccessorCodeArray: return RLMMakeSetter(prop); + case RLMAccessorCodeAny: return RLMMakeSetter(prop); + case RLMAccessorCodeIntObject: return RLMMakeSetter *>(prop); + case RLMAccessorCodeFloatObject: return RLMMakeSetter *>(prop); + case RLMAccessorCodeDoubleObject: return RLMMakeSetter *>(prop); + case RLMAccessorCodeBoolObject: return RLMMakeSetter *>(prop); + case RLMAccessorCodeLinkingObjects: return nil; + } +} + +// call getter for superclass for property at colIndex +static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { + typedef id (*getter_type)(RLMObjectBase *, SEL); + RLMProperty *prop = obj->_objectSchema[propName]; + Class superClass = class_getSuperclass(obj.class); + getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel]; + return superGetter(obj, prop.getterSel); +} + +// call setter for superclass for property at colIndex +static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { + typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar); + RLMProperty *prop = obj->_objectSchema[propName]; + Class superClass = class_getSuperclass(obj.class); + setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel]; + superSetter(obj, prop.setterSel, val); +} + +// getter/setter for unmanaged object +static IMP RLMAccessorUnmanagedGetter(RLMProperty *prop, RLMAccessorCode accessorCode) { + // only override getters for RLMArray and linking objects properties + if (accessorCode == RLMAccessorCodeArray) { + NSString *objectClassName = prop.objectClassName; + NSString *propName = prop.name; + + return imp_implementationWithBlock(^(RLMObjectBase *obj) { + id val = RLMSuperGet(obj, propName); + if (!val) { + val = [[RLMArray alloc] initWithObjectClassName:objectClassName]; + RLMSuperSet(obj, propName, val); + } + return val; + }); + } + else if (accessorCode == RLMAccessorCodeLinkingObjects) { + return imp_implementationWithBlock(^(RLMObjectBase *){ + return [RLMResults emptyDetachedResults]; + }); + } + return nil; +} +static IMP RLMAccessorUnmanagedSetter(RLMProperty *prop, RLMAccessorCode accessorCode) { + // only override getters for RLMArray and linking objects properties + if (accessorCode == RLMAccessorCodeArray) { + NSString *propName = prop.name; + NSString *objectClassName = prop.objectClassName; + return imp_implementationWithBlock(^(RLMObjectBase *obj, id ar) { + // make copy when setting (as is the case for all other variants) + RLMArray *unmanagedAr = [[RLMArray alloc] initWithObjectClassName:objectClassName]; + [unmanagedAr addObjects:ar]; + RLMSuperSet(obj, propName, unmanagedAr); + }); + } + return nil; +} + +// macros/helpers to generate objc type strings for registering methods +#define GETTER_TYPES(C) C "@:" +#define SETTER_TYPES(C) "v@:" C + +// getter type strings +// NOTE: this typecode is really the the first charachter of the objc/runtime.h type +// the @ type maps to multiple core types (string, date, array, mixed, any which are id in objc) +static const char *getterTypeStringForObjcCode(char code) { + switch (code) { + case 's': return GETTER_TYPES("s"); + case 'i': return GETTER_TYPES("i"); + case 'l': return GETTER_TYPES("l"); + case 'q': return GETTER_TYPES("q"); + case 'f': return GETTER_TYPES("f"); + case 'd': return GETTER_TYPES("d"); + case 'B': return GETTER_TYPES("B"); + case 'c': return GETTER_TYPES("c"); + case '@': return GETTER_TYPES("@"); + default: @throw RLMException(@"Invalid accessor code"); + } +} + +// setter type strings +// NOTE: this typecode is really the the first charachter of the objc/runtime.h type +// the @ type maps to multiple core types (string, date, array, mixed, any which are id in objc) +static const char *setterTypeStringForObjcCode(char code) { + switch (code) { + case 's': return SETTER_TYPES("s"); + case 'i': return SETTER_TYPES("i"); + case 'l': return SETTER_TYPES("l"); + case 'q': return SETTER_TYPES("q"); + case 'f': return SETTER_TYPES("f"); + case 'd': return SETTER_TYPES("d"); + case 'B': return SETTER_TYPES("B"); + case 'c': return SETTER_TYPES("c"); + case '@': return SETTER_TYPES("@"); + default: @throw RLMException(@"Invalid accessor code"); + } +} + +// get accessor lookup code based on objc type and rlm type +static RLMAccessorCode accessorCodeForType(char objcTypeCode, RLMPropertyType rlmType) { + switch (objcTypeCode) { + case 't': return RLMAccessorCodeArray; + case '@': // custom accessors for strings and subtables + switch (rlmType) { // custom accessor codes for types that map to objc objects + case RLMPropertyTypeObject: return RLMAccessorCodeLink; + case RLMPropertyTypeString: return RLMAccessorCodeString; + case RLMPropertyTypeArray: return RLMAccessorCodeArray; + case RLMPropertyTypeDate: return RLMAccessorCodeDate; + case RLMPropertyTypeData: return RLMAccessorCodeData; + case RLMPropertyTypeAny: return RLMAccessorCodeAny; + + case RLMPropertyTypeBool: return RLMAccessorCodeBoolObject; + case RLMPropertyTypeDouble: return RLMAccessorCodeDoubleObject; + case RLMPropertyTypeFloat: return RLMAccessorCodeFloatObject; + case RLMPropertyTypeInt: return RLMAccessorCodeIntObject; + + case RLMPropertyTypeLinkingObjects: return RLMAccessorCodeLinkingObjects; + } + case 'c': + switch (rlmType) { + case RLMPropertyTypeInt: return RLMAccessorCodeByte; + case RLMPropertyTypeBool: return RLMAccessorCodeBool; + default: + @throw RLMException(@"Unexpected property type for Objective-C type code"); + } + case 'B': return RLMAccessorCodeBool; + case 's': return RLMAccessorCodeShort; + case 'i': return RLMAccessorCodeInt; + case 'l': return RLMAccessorCodeLong; + case 'q': return RLMAccessorCodeLongLong; + case 'f': return RLMAccessorCodeFloat; + case 'd': return RLMAccessorCodeDouble; + default: + @throw RLMException(@"Invalid type for objc typecode"); + } +} + +// implement the class method className on accessors to return the className of the +// base object +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class){ return className; }); + class_addMethod(metaClass, @selector(className), imp, "@@:"); +} + +// implement the shared schema method +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class cls) { + if (cls == accessorClass) { + return schema; + } + + // If we aren't being called directly on the class this was overriden + // for, the class is either a subclass which we haven't initialized yet, + // or it's a runtime-generated class which should use the parent's + // schema. We check for the latter by checking if the immediate + // descendent of the desired class is a class generated by us (there + // may be further subclasses not generated by us for things like KVO). + Class parent = class_getSuperclass(cls); + while (parent != accessorClass) { + cls = parent; + parent = class_getSuperclass(cls); + } + if (RLMIsGeneratedClass(cls)) { + return schema; + } + + return [RLMSchema sharedSchemaForClass:cls]; + }); + class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); +} + +static NSMutableSet *s_generatedClasses = [NSMutableSet new]; +static void RLMMarkClassAsGenerated(Class cls) { + @synchronized (s_generatedClasses) { + [s_generatedClasses addObject:cls]; + } +} + +bool RLMIsGeneratedClass(Class cls) { + @synchronized (s_generatedClasses) { + return [s_generatedClasses containsObject:cls]; + } +} + +static Class RLMCreateAccessorClass(Class objectClass, + RLMObjectSchema *schema, + NSString *accessorClassPrefix, + IMP (*getterGetter)(RLMProperty *, RLMAccessorCode), + IMP (*setterGetter)(RLMProperty *, RLMAccessorCode)) { + // throw if no schema, prefix, or object class + if (!objectClass || !schema || !accessorClassPrefix) { + @throw RLMException(@"Missing arguments"); + } + if (!RLMIsObjectOrSubclass(objectClass)) { + @throw RLMException(@"objectClass must derive from RLMObject or Object"); + } + + // create and register proxy class which derives from object class + NSString *accessorClassName = [accessorClassPrefix stringByAppendingString:schema.className]; + Class accClass = objc_getClass(accessorClassName.UTF8String); + if (!accClass) { + accClass = objc_allocateClassPair(objectClass, accessorClassName.UTF8String, 0); + objc_registerClassPair(accClass); + } + + // override getters/setters for each propery + NSArray *allProperties = [schema.properties arrayByAddingObjectsFromArray:schema.computedProperties]; + for (RLMProperty *prop in allProperties) { + RLMAccessorCode accessorCode = accessorCodeForType(prop.objcType, prop.type); + if (prop.getterSel && getterGetter) { + IMP getterImp = getterGetter(prop, accessorCode); + if (getterImp) { + class_replaceMethod(accClass, prop.getterSel, getterImp, getterTypeStringForObjcCode(prop.objcType)); + } + } + if (prop.setterSel && setterGetter) { + IMP setterImp = setterGetter(prop, accessorCode); + if (setterImp) { + class_replaceMethod(accClass, prop.setterSel, setterImp, setterTypeStringForObjcCode(prop.objcType)); + } + } + } + + RLMMarkClassAsGenerated(accClass); + + return accClass; +} + +Class RLMAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, NSString *prefix) { + return RLMCreateAccessorClass(objectClass, schema, prefix, RLMAccessorGetter, RLMAccessorSetter); +} + +Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) { + return RLMCreateAccessorClass(objectClass, schema, @"RLMUnmanaged_", + RLMAccessorUnmanagedGetter, RLMAccessorUnmanagedSetter); +} + +void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) { + RLMObjectSchema *schema = obj->_objectSchema; + RLMProperty *prop = schema[propName]; + if (!prop) { + @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + } + if (prop.isPrimary) { + @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val); + } + if (!RLMIsObjectValidForProperty(val, prop)) { + @throw RLMException(@"Invalid property value '%@' for property '%@' of class '%@'", val, propName, obj->_objectSchema.className); + } + + RLMDynamicSet(obj, prop, RLMCoerceToNil(val), RLMCreationOptionsPromoteUnmanaged); +} + +// Precondition: the property is not a primary key +void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained id const val, RLMCreationOptions creationOptions) { + REALM_ASSERT_DEBUG(!prop.isPrimary); + bool setDefault = creationOptions & RLMCreationOptionsSetDefault; + + auto col = obj->_info->tableColumn(prop); + RLMWrapSetter(obj, prop.name, [&] { + switch (accessorCodeForType(prop.objcType, prop.type)) { + case RLMAccessorCodeByte: + case RLMAccessorCodeShort: + case RLMAccessorCodeInt: + case RLMAccessorCodeLong: + case RLMAccessorCodeLongLong: + RLMSetValue(obj, col, [val longLongValue], setDefault); + break; + case RLMAccessorCodeFloat: + RLMSetValue(obj, col, [val floatValue], setDefault); + break; + case RLMAccessorCodeDouble: + RLMSetValue(obj, col, [val doubleValue], setDefault); + break; + case RLMAccessorCodeBool: + RLMSetValue(obj, col, [val boolValue], setDefault); + break; + case RLMAccessorCodeIntObject: + RLMSetValue(obj, col, (NSNumber *)val, setDefault); + break; + case RLMAccessorCodeFloatObject: + RLMSetValue(obj, col, (NSNumber *)val, setDefault); + break; + case RLMAccessorCodeDoubleObject: + RLMSetValue(obj, col, (NSNumber *)val, setDefault); + break; + case RLMAccessorCodeBoolObject: + RLMSetValue(obj, col, (NSNumber *)val, setDefault); + break; + case RLMAccessorCodeString: + RLMSetValue(obj, col, (NSString *)val, setDefault); + break; + case RLMAccessorCodeDate: + RLMSetValue(obj, col, (NSDate *)val, setDefault); + break; + case RLMAccessorCodeData: + RLMSetValue(obj, col, (NSData *)val, setDefault); + break; + case RLMAccessorCodeLink: { + if (!val || val == NSNull.null) { + RLMSetValue(obj, col, (RLMObjectBase *)nil, setDefault); + } + else { + RLMSetValue(obj, col, RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, val, creationOptions), setDefault); + } + break; + } + case RLMAccessorCodeArray: + if (!val || val == NSNull.null) { + RLMSetValue(obj, col, (id)nil, setDefault); + } + else { + id rawLinks = val; + NSMutableArray *links = [NSMutableArray array]; + for (id rawLink in rawLinks) { + [links addObject:RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, rawLink, creationOptions)]; + } + RLMSetValue(obj, col, links, setDefault); + } + break; + case RLMAccessorCodeAny: + RLMSetValue(obj, col, val, setDefault); + break; + case RLMAccessorCodeLinkingObjects: + @throw RLMException(@"Linking objects properties are read-only"); + } + }); +} + +id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) { + auto index = prop.index; + switch (accessorCodeForType(prop.objcType, prop.type)) { + case RLMAccessorCodeIntObject: + case RLMAccessorCodeByte: + case RLMAccessorCodeShort: + case RLMAccessorCodeInt: + case RLMAccessorCodeLong: + case RLMAccessorCodeLongLong: return getBoxed(obj, index); + case RLMAccessorCodeFloatObject: + case RLMAccessorCodeFloat: return getBoxed(obj, index); + case RLMAccessorCodeDoubleObject: + case RLMAccessorCodeDouble: return getBoxed(obj, index); + case RLMAccessorCodeBoolObject: + case RLMAccessorCodeBool: return getBoxed(obj, index); + case RLMAccessorCodeString: return RLMGetString(obj, index); + case RLMAccessorCodeDate: return RLMGetDate(obj, index); + case RLMAccessorCodeData: return RLMGetData(obj, index); + case RLMAccessorCodeLink: return RLMGetLink(obj, index); + case RLMAccessorCodeArray: return RLMGetArray(obj, index); + case RLMAccessorCodeAny: return RLMGetAnyProperty(obj, index); + case RLMAccessorCodeLinkingObjects: return RLMGetLinkingObjects(obj, prop); + } +} + +id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const propName, bool asList) { + RLMProperty *prop = obj->_objectSchema[propName]; + if (!prop) { + @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + } + if (asList && prop.type == RLMPropertyTypeArray && prop.swiftIvar) { + RLMListBase *list = object_getIvar(obj, prop.swiftIvar); + if (!list._rlmArray) { + list._rlmArray = RLMDynamicGet(obj, prop); + } + return list; + } + return RLMDynamicGet(obj, prop); +} diff --git a/Pods/Realm/Realm/RLMAnalytics.mm b/Pods/Realm/Realm/RLMAnalytics.mm new file mode 100644 index 0000000..88d9812 --- /dev/null +++ b/Pods/Realm/Realm/RLMAnalytics.mm @@ -0,0 +1,243 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously submits build information to Realm if running in an iOS +// simulator or on OS X if a debugger is attached. Does nothing if running on an +// iOS / watchOS device or if a debugger is *not* attached. +// +// To be clear: this does *not* run when your app is in production or on +// your end-user’s devices; it will only run in the simulator or when a debugger +// is attached. +// +// Why are we doing this? In short, because it helps us build a better product +// for you. None of the data personally identifies you, your employer or your +// app, but it *will* help us understand what language you use, what iOS +// versions you target, etc. Having this info will help prioritizing our time, +// adding new features and deprecating old features. Collecting an anonymized +// bundle & anonymized MAC is the only way for us to count actual usage of the +// other metrics accurately. If we don’t have a way to deduplicate the info +// reported, it will be useless, as a single developer building their Swift app +// 10 times would report 10 times more than a single Objective-C developer that +// only builds once, making the data all but useless. +// No one likes sharing data unless it’s necessary, we get it, and we’ve +// debated adding this for a long long time. Since Realm is a free product +// without an email signup, we feel this is a necessary step so we can collect +// relevant data to build a better product for you. If you truly, absolutely +// feel compelled to not send this data back to Realm, then you can set an env +// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe +// letting these analytics run is a small price to pay for the product & support +// we give you. +// +// Currently the following information is reported: +// - What version of Realm is being used, and from which language (obj-c or Swift). +// - What version of OS X it's running on (in case Xcode aggressively drops +// support for older versions again, we need to know what we need to support). +// - The minimum iOS/OS X version that the application is targeting (again, to +// help us decide what versions we need to support). +// - An anonymous MAC address and bundle ID to aggregate the other information on. +// - What version of Swift is being used (if applicable). + +#import "RLMAnalytics.hpp" + +#import + +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_MAC || (TARGET_OS_WATCH && TARGET_OS_SIMULATOR) || (TARGET_OS_TV && TARGET_OS_SIMULATOR) +#import "RLMRealm.h" +#import "RLMUtil.hpp" + +#import +#import +#import +#import +#import + +#import + +#ifndef REALM_COCOA_VERSION +#import "RLMVersion.h" +#endif + +#import + +// Declared for RealmSwiftObjectUtil +@interface NSObject (SwiftVersion) ++ (NSString *)swiftVersion; +@end + +// Wrapper for sysctl() that handles the memory management stuff +static auto RLMSysCtl(int *mib, u_int mibSize, size_t *bufferSize) { + std::unique_ptr buffer(nullptr, &free); + + int ret = sysctl(mib, mibSize, nullptr, bufferSize, nullptr, 0); + if (ret != 0) { + return buffer; + } + + buffer.reset(malloc(*bufferSize)); + if (!buffer) { + return buffer; + } + + ret = sysctl(mib, mibSize, buffer.get(), bufferSize, nullptr, 0); + if (ret != 0) { + buffer.reset(); + } + + return buffer; +} + +// Get the version of OS X we're running on (even in the simulator this gives +// the OS X version and not the simulated iOS version) +static NSString *RLMOSVersion() { + std::array mib = {CTL_KERN, KERN_OSRELEASE}; + size_t bufferSize; + auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize); + if (!buffer) { + return nil; + } + + return [[NSString alloc] initWithBytesNoCopy:buffer.release() + length:bufferSize - 1 + encoding:NSUTF8StringEncoding + freeWhenDone:YES]; +} + +// Hash the data in the given buffer and convert it to a hex-format string +static NSString *RLMHashData(const void *bytes, size_t length) { + unsigned char buffer[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(bytes, static_cast(length), buffer); + + char formatted[CC_SHA256_DIGEST_LENGTH * 2 + 1]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) { + sprintf(formatted + i * 2, "%02x", buffer[i]); + } + + return [[NSString alloc] initWithBytes:formatted + length:CC_SHA256_DIGEST_LENGTH * 2 + encoding:NSUTF8StringEncoding]; +} + +// Returns the hash of the MAC address of the first network adaptor since the +// vendorIdentifier isn't constant between iOS simulators. +static NSString *RLMMACAddress() { + int en0 = static_cast(if_nametoindex("en0")); + if (!en0) { + return nil; + } + + std::array mib = {CTL_NET, PF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, en0}; + size_t bufferSize; + auto buffer = RLMSysCtl(&mib[0], mib.size(), &bufferSize); + if (!buffer) { + return nil; + } + + // sockaddr_dl struct is immediately after the if_msghdr struct in the buffer + auto sockaddr = reinterpret_cast(static_cast(buffer.get()) + 1); + auto mac = reinterpret_cast(sockaddr->sdl_data + sockaddr->sdl_nlen); + + return RLMHashData(mac, 6); +} + +static NSDictionary *RLMAnalyticsPayload() { + NSBundle *appBundle = NSBundle.mainBundle; + NSString *hashedBundleID = appBundle.bundleIdentifier; + + // Main bundle isn't always the one of interest (e.g. when running tests + // it's xctest rather than the app's bundle), so look for one with a bundle ID + if (!hashedBundleID) { + for (NSBundle *bundle in NSBundle.allBundles) { + if ((hashedBundleID = bundle.bundleIdentifier)) { + appBundle = bundle; + break; + } + } + } + + // If we found a bundle ID anywhere, hash it as it could contain sensitive + // information (e.g. the name of an unnanounced product) + if (hashedBundleID) { + NSData *data = [hashedBundleID dataUsingEncoding:NSUTF8StringEncoding]; + hashedBundleID = RLMHashData(data.bytes, data.length); + } + + NSString *osVersionString = [[NSProcessInfo processInfo] operatingSystemVersionString]; + Class swiftObjectUtilClass = NSClassFromString(@"RealmSwiftObjectUtil"); + BOOL isSwift = swiftObjectUtilClass != nil; + NSString *swiftVersion = isSwift ? [swiftObjectUtilClass swiftVersion] : @"N/A"; + + static NSString *kUnknownString = @"unknown"; + NSString *hashedMACAddress = RLMMACAddress() ?: kUnknownString; + + return @{ + @"event": @"Run", + @"properties": @{ + // MixPanel properties + @"token": @"ce0fac19508f6c8f20066d345d360fd0", + + // Anonymous identifiers to deduplicate events + @"distinct_id": hashedMACAddress, + @"Anonymized MAC Address": hashedMACAddress, + @"Anonymized Bundle ID": hashedBundleID ?: kUnknownString, + + // Which version of Realm is being used + @"Binding": @"cocoa", + @"Language": isSwift ? @"swift" : @"objc", + @"Realm Version": REALM_COCOA_VERSION, + @"Sync Version": @(REALM_SYNC_VER_STRING), +#if TARGET_OS_WATCH + @"Target OS Type": @"watchos", +#elif TARGET_OS_TV + @"Target OS Type": @"tvos", +#elif TARGET_OS_IPHONE + @"Target OS Type": @"ios", +#else + @"Target OS Type": @"osx", +#endif + @"Swift Version": swiftVersion, + // Current OS version the app is targetting + @"Target OS Version": osVersionString, + // Minimum OS version the app is targetting + @"Target OS Minimum Version": appBundle.infoDictionary[@"MinimumOSVersion"] ?: kUnknownString, + + // Host OS version being built on + @"Host OS Type": @"osx", + @"Host OS Version": RLMOSVersion() ?: kUnknownString, + } + }; +} + +void RLMSendAnalytics() { + if (getenv("REALM_DISABLE_ANALYTICS") || !RLMIsDebuggerAttached() || RLMIsRunningInPlayground()) { + return; + } + + + NSData *payload = [NSJSONSerialization dataWithJSONObject:RLMAnalyticsPayload() options:0 error:nil]; + NSString *url = [NSString stringWithFormat:@"https://api.mixpanel.com/track/?data=%@&ip=1", [payload base64EncodedStringWithOptions:0]]; + + // No error handling or anything because logging errors annoyed people for no + // real benefit, and it's not clear what else we could do + [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url]] resume]; +} + +#else + +void RLMSendAnalytics() {} + +#endif diff --git a/Pods/Realm/Realm/RLMArray.mm b/Pods/Realm/Realm/RLMArray.mm new file mode 100644 index 0000000..2cfccf9 --- /dev/null +++ b/Pods/Realm/Realm/RLMArray.mm @@ -0,0 +1,439 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMArray_Private.hpp" + +#import "RLMObject_Private.h" +#import "RLMObjectStore.h" +#import "RLMObjectSchema.h" +#import "RLMQueryUtil.hpp" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import + +// See -countByEnumeratingWithState:objects:count +@interface RLMArrayHolder : NSObject { +@public + std::unique_ptr items; +} +@end +@implementation RLMArrayHolder +@end + +@implementation RLMArray { +@public + // Backing array when this instance is unmanaged + NSMutableArray *_backingArray; +} + +template +static void changeArray(__unsafe_unretained RLMArray *const ar, + NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { + if (!ar->_backingArray) { + ar->_backingArray = [NSMutableArray new]; + } + + if (RLMObjectBase *parent = ar->_parentObject) { + NSIndexSet *indexes = is(); + [parent willChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + f(); + [parent didChange:kind valuesAtIndexes:indexes forKey:ar->_key]; + } + else { + f(); + } +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); +} + +static void changeArray(__unsafe_unretained RLMArray *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return is; }); +} + +- (instancetype)initWithObjectClassName:(__unsafe_unretained NSString *const)objectClassName { + self = [super init]; + if (self) { + _objectClassName = objectClassName; + } + return self; +} + +- (RLMRealm *)realm { + return nil; +} + +// +// Generic implementations for all RLMArray variants +// + +- (id)firstObject { + if (self.count) { + return [self objectAtIndex:0]; + } + return nil; +} + +- (id)lastObject { + NSUInteger count = self.count; + if (count) { + return [self objectAtIndex:count-1]; + } + return nil; +} + +- (void)addObjects:(id)objects { + for (id obj in objects) { + [self addObject:obj]; + } +} + +- (void)addObject:(RLMObject *)object { + [self insertObject:object atIndex:self.count]; +} + +- (void)removeLastObject { + NSUInteger count = self.count; + if (count) { + [self removeObjectAtIndex:count-1]; + } +} + +- (id)objectAtIndexedSubscript:(NSUInteger)index { + return [self objectAtIndex:index]; +} + +- (void)setObject:(id)newValue atIndexedSubscript:(NSUInteger)index { + [self replaceObjectAtIndex:index withObject:newValue]; +} + +// +// Unmanaged RLMArray implementation +// + +static void RLMValidateMatchingObjectType(RLMArray *array, RLMObject *object) { + if (!object) { + @throw RLMException(@"Object must not be nil"); + } + if (!object->_objectSchema) { + @throw RLMException(@"Object cannot be inserted unless the schema is initialized. " + "This can happen if you try to insert objects into a RLMArray / List from a default value or from an overriden unmanaged initializer (`init()`)."); + } + if (![array->_objectClassName isEqualToString:object->_objectSchema.className]) { + @throw RLMException(@"Object type '%@' does not match RLMArray type '%@'.", + object->_objectSchema.className, array->_objectClassName); + } +} + +static void RLMValidateArrayBounds(__unsafe_unretained RLMArray *const ar, + NSUInteger index, bool allowOnePastEnd=false) { + NSUInteger max = ar->_backingArray.count + allowOnePastEnd; + if (index >= max) { + @throw RLMException(@"Index %llu is out of bounds (must be less than %llu).", + (unsigned long long)index, (unsigned long long)max); + } +} + +- (id)objectAtIndex:(NSUInteger)index { + RLMValidateArrayBounds(self, index); + if (!_backingArray) { + _backingArray = [NSMutableArray new]; + } + return [_backingArray objectAtIndex:index]; +} + +- (NSUInteger)count { + return _backingArray.count; +} + +- (BOOL)isInvalidated { + return NO; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unused __unsafe_unretained id [])buffer count:(__unused NSUInteger)len { + if (state->state != 0) { + return 0; + } + + // We need to enumerate a copy of the backing array so that it doesn't + // reflect changes made during enumeration. This copy has to be autoreleased + // (since there's nowhere for us to store a strong reference), and uses + // RLMArrayHolder rather than an NSArray because NSArray doesn't guarantee + // that it'll use a single contiguous block of memory, and if it doesn't + // we'd need to forward multiple calls to this method to the same NSArray, + // which would require holding a reference to it somewhere. + __autoreleasing RLMArrayHolder *copy = [[RLMArrayHolder alloc] init]; + copy->items = std::make_unique(self.count); + + NSUInteger i = 0; + for (id object in _backingArray) { + copy->items[i++] = object; + } + + state->itemsPtr = (__unsafe_unretained id *)(void *)copy->items.get(); + // needs to point to something valid, but the whole point of this is so + // that it can't be changed + state->mutationsPtr = state->extra; + state->state = i; + + return i; +} + +- (void)addObjectsFromArray:(NSArray *)array { + for (id obj in array) { + RLMValidateMatchingObjectType(self, obj); + } + changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(_backingArray.count, array.count), ^{ + [_backingArray addObjectsFromArray:array]; + }); +} + +- (void)insertObject:(RLMObject *)anObject atIndex:(NSUInteger)index { + RLMValidateMatchingObjectType(self, anObject); + RLMValidateArrayBounds(self, index, true); + changeArray(self, NSKeyValueChangeInsertion, index, ^{ + [_backingArray insertObject:anObject atIndex:index]; + }); +} + +- (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ + NSUInteger currentIndex = [indexes firstIndex]; + for (RLMObject *obj in objects) { + RLMValidateMatchingObjectType(self, obj); + [_backingArray insertObject:obj atIndex:currentIndex]; + currentIndex = [indexes indexGreaterThanIndex:currentIndex]; + } + }); +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + RLMValidateArrayBounds(self, index); + changeArray(self, NSKeyValueChangeRemoval, index, ^{ + [_backingArray removeObjectAtIndex:index]; + }); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeRemoval, indexes, ^{ + [_backingArray removeObjectsAtIndexes:indexes]; + }); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + RLMValidateMatchingObjectType(self, anObject); + RLMValidateArrayBounds(self, index); + changeArray(self, NSKeyValueChangeReplacement, index, ^{ + [_backingArray replaceObjectAtIndex:index withObject:anObject]; + }); +} + +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex { + RLMValidateArrayBounds(self, sourceIndex); + RLMValidateArrayBounds(self, destinationIndex); + RLMObjectBase *original = _backingArray[sourceIndex]; + + auto start = std::min(sourceIndex, destinationIndex); + auto len = std::max(sourceIndex, destinationIndex) - start + 1; + changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{ + [_backingArray removeObjectAtIndex:sourceIndex]; + [_backingArray insertObject:original atIndex:destinationIndex]; + }); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 { + RLMValidateArrayBounds(self, index1); + RLMValidateArrayBounds(self, index2); + + changeArray(self, NSKeyValueChangeReplacement, ^{ + [_backingArray exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; + }, [=] { + NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1]; + [set addIndex:index2]; + return set; + }); +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + RLMValidateMatchingObjectType(self, object); + NSUInteger index = 0; + for (RLMObject *cmp in _backingArray) { + if (RLMObjectBaseAreEqual(object, cmp)) { + return index; + } + index++; + } + return NSNotFound; +} + +- (void)removeAllObjects { + changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, _backingArray.count), ^{ + [_backingArray removeAllObjects]; + }); +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... +{ + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args +{ + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if (!_backingArray) { + return [super valueForKeyPath:keyPath]; + } + // Although delegating to valueForKeyPath: here would allow to support + // nested key paths as well, limiting functionality gives consistency + // between unmanaged and managed arrays. + if ([keyPath characterAtIndex:0] == '@') { + NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; + if (operatorRange.location != NSNotFound) { + NSString *operatorKeyPath = [keyPath substringFromIndex:operatorRange.location + 1]; + if ([operatorKeyPath rangeOfString:@"."].location != NSNotFound) { + @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); + } + } + } + return [_backingArray valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + if ([key isEqualToString:RLMInvalidatedKey]) { + return @NO; // Unmanaged arrays are never invalidated + } + if (!_backingArray) { + return @[]; + } + return [_backingArray valueForKey:key]; +} + +- (void)setValue:(id)value forKey:(NSString *)key { + [_backingArray setValue:value forKey:key]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + if (!_backingArray) { + return NSNotFound; + } + return [_backingArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger, BOOL *) { + return [predicate evaluateWithObject:obj]; + }]; +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + if (!_backingArray) { + _backingArray = [NSMutableArray new]; + } + return [_backingArray objectsAtIndexes:indexes]; +} + +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { + RLMValidateArrayObservationKey(keyPath, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +// +// Methods unsupported on unmanaged RLMArray instances +// + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate +{ + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} + +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending +{ + return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithProperty:property ascending:ascending]]]; +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties +{ + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { + @throw RLMException(@"This method may only be called on RLMArray instances retrieved from an RLMRealm"); +} +#pragma clang diagnostic pop + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... +{ + va_list args; + va_start(args, predicateFormat); + NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; + va_end(args); + return index; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args +{ + return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat + arguments:args]]; +} + +#pragma mark - Superclass Overrides + +- (NSString *)description { + return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; +} + +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { + return RLMDescriptionWithMaxDepth(@"RLMArray", self, depth); +} +@end + +@interface RLMSortDescriptor () +@property (nonatomic, strong) NSString *property; +@property (nonatomic, assign) BOOL ascending; +@end + +@implementation RLMSortDescriptor ++ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending { + RLMSortDescriptor *desc = [[RLMSortDescriptor alloc] init]; + desc->_property = propertyName; + desc->_ascending = ascending; + return desc; +} + +- (instancetype)reversedSortDescriptor { + return [self.class sortDescriptorWithProperty:_property ascending:!_ascending]; +} + +@end diff --git a/Pods/Realm/Realm/RLMArrayLinkView.mm b/Pods/Realm/Realm/RLMArrayLinkView.mm new file mode 100644 index 0000000..af62781 --- /dev/null +++ b/Pods/Realm/Realm/RLMArrayLinkView.mm @@ -0,0 +1,429 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMArray_Private.hpp" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema.h" +#import "RLMUtil.hpp" + +#import "list.hpp" +#import "results.hpp" + +#import +#import + +// +// RLMArray implementation +// +@implementation RLMArrayLinkView { +@public + realm::List _backingList; + RLMRealm *_realm; + RLMClassInfo *_objectInfo; + RLMClassInfo *_ownerInfo; + std::unique_ptr _observationInfo; +} + +- (RLMArrayLinkView *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject + property:(__unsafe_unretained RLMProperty *const)property { + self = [self initWithObjectClassName:property.objectClassName]; + if (self) { + _realm = parentObject->_realm; + _backingList = realm::List(_realm->_realm, parentObject->_row.get_linklist(parentObject->_info->tableColumn(property))); + _objectInfo = &parentObject->_info->linkTargetType(property.index); + _ownerInfo = parentObject->_info; + _key = property.name; + } + return self; +} + +void RLMValidateArrayObservationKey(__unsafe_unretained NSString *const keyPath, + __unsafe_unretained RLMArray *const array) { + if (![keyPath isEqualToString:RLMInvalidatedKey]) { + @throw RLMException(@"[<%@ %p> addObserver:forKeyPath:options:context:] is not supported. Key path: %@", + [array class], array, keyPath); + } +} + +void RLMEnsureArrayObservationInfo(std::unique_ptr& info, + __unsafe_unretained NSString *const keyPath, + __unsafe_unretained RLMArray *const array, + __unsafe_unretained id const observed) { + RLMValidateArrayObservationKey(keyPath, array); + if (!info && array.class == [RLMArrayLinkView class]) { + RLMArrayLinkView *lv = static_cast(array); + info = std::make_unique(*lv->_ownerInfo, + lv->_backingList.get_origin_row_index(), + observed); + } +} + +// +// validation helpers +// +[[gnu::noinline]] +[[noreturn]] +static void throwError() { + try { + throw; + } + catch (realm::InvalidTransactionException const&) { + @throw RLMException(@"Cannot modify managed RLMArray outside of a write transaction"); + } + catch (realm::IncorrectThreadException const&) { + @throw RLMException(@"Realm accessed from incorrect thread"); + } + catch (realm::List::InvalidatedException const&) { + @throw RLMException(@"RLMArray has been invalidated or the containing object has been deleted"); + } + catch (realm::List::OutOfBoundsIndexException const& e) { + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + e.requested, e.valid_count); + } +} + +template +static auto translateErrors(Function&& f) { + try { + return f(); + } + catch (...) { + throwError(); + } +} + +static void validateObjectToAdd(__unsafe_unretained RLMArrayLinkView *const ar, + __unsafe_unretained RLMObject *const obj) { + if (!obj) { + @throw RLMException(@"Cannot add `nil` to RLMArray<%@>", ar->_objectClassName); + } + + NSString *objectClassName = obj->_objectSchema.className; + if (![objectClassName isEqualToString:ar->_objectClassName]) { + @throw RLMException(@"Cannot add object of type '%@' to RLMArray<%@>", + objectClassName, ar->_objectClassName); + } + + if (obj->_realm != ar.realm) { + [ar.realm addObject:obj]; + } + else if (obj->_realm && !obj->_row.is_attached()) { + @throw RLMException(@"Object has been deleted or invalidated."); + } +} + +template +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, + NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { + translateErrors([&] { ar->_backingList.verify_in_transaction(); }); + RLMObservationInfo *info = RLMGetObservationInfo(ar->_observationInfo.get(), + ar->_backingList.get_origin_row_index(), + *ar->_ownerInfo); + if (info) { + NSIndexSet *indexes = is(); + info->willChange(ar->_key, kind, indexes); + try { + f(); + } + catch (...) { + info->didChange(ar->_key, kind, indexes); + throwError(); + } + info->didChange(ar->_key, kind, indexes); + } + else { + translateErrors([&] { f(); }); + } +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSUInteger index, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndex:index]; }); +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSRange range, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return [NSIndexSet indexSetWithIndexesInRange:range]; }); +} + +static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, NSIndexSet *is, dispatch_block_t f) { + changeArray(ar, kind, f, [=] { return is; }); +} + +// +// public method implementations +// +- (RLMRealm *)realm { + return _realm; +} + +- (NSUInteger)count { + return translateErrors([&] { return _backingList.size(); }); +} + +- (BOOL)isInvalidated { + return translateErrors([&] { return !_backingList.is_valid(); }); +} + +- (RLMClassInfo *)objectInfo { + return _objectInfo; +} + +- (BOOL)isEqual:(id)object { + if (RLMArrayLinkView *linkView = RLMDynamicCast(object)) { + return linkView->_backingList == _backingList; + } + return NO; +} + +- (NSUInteger)hash { + return std::hash()(_backingList); +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unused __unsafe_unretained id [])buffer + count:(NSUInteger)len { + __autoreleasing RLMFastEnumerator *enumerator; + if (state->state == 0) { + translateErrors([&] { _backingList.verify_attached(); }); + + enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_objectInfo]; + state->extra[0] = (long)enumerator; + state->extra[1] = self.count; + } + else { + enumerator = (__bridge id)(void *)state->extra[0]; + } + + return [enumerator countByEnumeratingWithState:state count:len]; +} + +- (id)objectAtIndex:(NSUInteger)index { + return RLMCreateObjectAccessor(_realm, *_objectInfo, + translateErrors([&] { return _backingList.get(index).get_index(); })); +} + +static void RLMInsertObject(RLMArrayLinkView *ar, RLMObject *object, NSUInteger index) { + if (index == NSUIntegerMax) { + index = translateErrors([&] { return ar->_backingList.size(); }); + } + + validateObjectToAdd(ar, object); + changeArray(ar, NSKeyValueChangeInsertion, index, ^{ + ar->_backingList.insert(index, object->_row.get_index()); + }); +} + +- (void)addObject:(RLMObject *)object { + RLMInsertObject(self, object, NSUIntegerMax); +} + +- (void)insertObject:(RLMObject *)object atIndex:(NSUInteger)index { + RLMInsertObject(self, object, index); +} + +- (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ + NSUInteger index = [indexes firstIndex]; + for (RLMObject *obj in objects) { + validateObjectToAdd(self, obj); + _backingList.insert(index, obj->_row.get_index()); + index = [indexes indexGreaterThanIndex:index]; + } + }); +} + + +- (void)removeObjectAtIndex:(NSUInteger)index { + changeArray(self, NSKeyValueChangeRemoval, index, ^{ + _backingList.remove(index); + }); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + changeArray(self, NSKeyValueChangeRemoval, indexes, ^{ + [indexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *) { + _backingList.remove(idx); + }]; + }); +} + +- (void)addObjectsFromArray:(NSArray *)array { + changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{ + for (RLMObject *obj in array) { + validateObjectToAdd(self, obj); + _backingList.add(obj->_row.get_index()); + } + }); +} + +- (void)removeAllObjects { + changeArray(self, NSKeyValueChangeRemoval, NSMakeRange(0, self.count), ^{ + _backingList.remove_all(); + }); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObject *)object { + validateObjectToAdd(self, object); + changeArray(self, NSKeyValueChangeReplacement, index, ^{ + _backingList.set(index, object->_row.get_index()); + }); +} + +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex { + auto start = std::min(sourceIndex, destinationIndex); + auto len = std::max(sourceIndex, destinationIndex) - start + 1; + changeArray(self, NSKeyValueChangeReplacement, {start, len}, ^{ + _backingList.move(sourceIndex, destinationIndex); + }); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2 { + changeArray(self, NSKeyValueChangeReplacement, ^{ + _backingList.swap(index1, index2); + }, [=] { + NSMutableIndexSet *set = [[NSMutableIndexSet alloc] initWithIndex:index1]; + [set addIndex:index2]; + return set; + }); +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + if (object.invalidated) { + @throw RLMException(@"Object has been deleted or invalidated"); + } + + // check that object types align + if (![_objectClassName isEqualToString:object->_objectSchema.className]) { + @throw RLMException(@"Object of type (%@) does not match RLMArray type (%@)", + object->_objectSchema.className, _objectClassName); + } + + return translateErrors([&] { return RLMConvertNotFound(_backingList.find(object->_row)); }); +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if ([keyPath hasPrefix:@"@"]) { + // Delegate KVC collection operators to RLMResults + auto query = translateErrors([&] { return _backingList.get_query(); }); + RLMResults *results = [RLMResults resultsWithObjectInfo:*_objectInfo + results:realm::Results(_realm->_realm, std::move(query))]; + return [results valueForKeyPath:keyPath]; + } + return [super valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + // Ideally we'd use "@invalidated" for this so that "invalidated" would use + // normal array KVC semantics, but observing @things works very oddly (when + // it's part of a key path, it's triggered automatically when array index + // changes occur, and can't be sent explicitly, but works normally when it's + // the entire key path), and an RLMArrayLinkView *can't* have objects where + // invalidated is true, so we're not losing much. + if ([key isEqualToString:RLMInvalidatedKey]) { + return @(!_backingList.is_valid()); + } + + translateErrors([&] { _backingList.verify_attached(); }); + return RLMCollectionValueForKey(self, key); +} + +- (void)setValue:(id)value forKey:(NSString *)key { + translateErrors([&] { _backingList.verify_in_transaction(); }); + RLMCollectionSetValueForKey(self, key, value); +} + +- (void)deleteObjectsFromRealm { + // delete all target rows from the realm + RLMTrackDeletions(_realm, ^{ + translateErrors([&] { _backingList.delete_all(); }); + }); +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { + if (properties.count == 0) { + auto results = translateErrors([&] { return _backingList.filter({}); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; + } + + auto order = RLMSortDescriptorFromDescriptors(*_objectInfo->table(), properties); + auto results = translateErrors([&] { return _backingList.sort(std::move(order)); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; +} + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + auto query = RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group); + auto results = translateErrors([&] { return _backingList.filter(std::move(query)); }); + return [RLMResults resultsWithObjectInfo:*_objectInfo results:std::move(results)]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + auto query = translateErrors([&] { return _backingList.get_query(); }); + query.and_query(RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group)); +#if REALM_VER_MAJOR >= 2 + auto indexInTable = query.find(); + if (indexInTable == realm::not_found) { + return NSNotFound; + } + auto row = query.get_table()->get(indexInTable); + return _backingList.find(row); +#else + return RLMConvertNotFound(query.find()); +#endif +} + +- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes { + // FIXME: this is called by KVO when array changes are made. It's not clear + // why, and returning nil seems to work fine. + return nil; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + RLMEnsureArrayObservationInfo(_observationInfo, keyPath, self, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +- (NSUInteger)indexInSource:(NSUInteger)index { + return _backingList.get_unchecked(index); +} + +- (realm::TableView)tableView { + return translateErrors([&] { return _backingList.get_query(); }).find_all(); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollectionChange *, NSError *))block { + [_realm verifyNotificationsAreSupported]; + return RLMAddNotificationBlock(self, _backingList, block); +} +#pragma clang diagnostic pop + +@end diff --git a/Pods/Realm/Realm/RLMAuthResponseModel.m b/Pods/Realm/Realm/RLMAuthResponseModel.m new file mode 100644 index 0000000..2eb5b7c --- /dev/null +++ b/Pods/Realm/Realm/RLMAuthResponseModel.m @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMAuthResponseModel.h" + +#import "RLMTokenModels.h" +#import "RLMSyncUtil_Private.h" + +static const NSString *const kRLMSyncAccessTokenKey = @"access_token"; +static const NSString *const kRLMSyncRefreshTokenKey = @"refresh_token"; + +@interface RLMAuthResponseModel () + +@property (nonatomic, readwrite) RLMTokenModel *accessToken; +@property (nonatomic, readwrite) RLMTokenModel *refreshToken; + +@end + +@implementation RLMAuthResponseModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken { + if (self = [super init]) { + // Get the access token. + if (requireAccessToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncAccessTokenKey, RLMTokenModel, accessToken); + } + // Get the refresh token. + if (requireRefreshToken) { + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } else { + RLM_SYNC_PARSE_OPTIONAL_MODEL(jsonDictionary, kRLMSyncRefreshTokenKey, RLMTokenModel, refreshToken); + } + return self; + } + return nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMClassInfo.mm b/Pods/Realm/Realm/RLMClassInfo.mm new file mode 100644 index 0000000..09ad9d5 --- /dev/null +++ b/Pods/Realm/Realm/RLMClassInfo.mm @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMClassInfo.hpp" + +#import "RLMRealm_Private.hpp" +#import "RLMObjectSchema.h" +#import "RLMSchema.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMUtil.hpp" + +#import "object_schema.hpp" +#import "object_store.hpp" +#import "schema.hpp" + +#import + +using namespace realm; + +RLMClassInfo::RLMClassInfo(RLMRealm *realm, RLMObjectSchema *rlmObjectSchema, + const realm::ObjectSchema *objectSchema) +: realm(realm), rlmObjectSchema(rlmObjectSchema), objectSchema(objectSchema) { } + +realm::Table *RLMClassInfo::table() const { + if (!m_table) { + m_table = ObjectStore::table_for_object_type(realm.group, objectSchema->name).get(); + } + return m_table; +} + +RLMProperty *RLMClassInfo::propertyForTableColumn(NSUInteger col) const noexcept { + auto const& props = objectSchema->persisted_properties; + for (size_t i = 0; i < props.size(); ++i) { + if (props[i].table_column == col) { + return rlmObjectSchema.properties[i]; + } + } + return nil; +} + +RLMProperty *RLMClassInfo::propertyForPrimaryKey() const noexcept { + return rlmObjectSchema.primaryKeyProperty; +} + +NSUInteger RLMClassInfo::tableColumn(NSString *propertyName) const { + return tableColumn(RLMValidatedProperty(rlmObjectSchema, propertyName)); +} + +NSUInteger RLMClassInfo::tableColumn(RLMProperty *property) const { + return objectSchema->persisted_properties[property.index].table_column; +} + +RLMClassInfo &RLMClassInfo::linkTargetType(size_t index) { + if (index < m_linkTargets.size() && m_linkTargets[index]) { + return *m_linkTargets[index]; + } + if (m_linkTargets.size() <= index) { + m_linkTargets.resize(index + 1); + } + m_linkTargets[index] = &realm->_info[rlmObjectSchema.properties[index].objectClassName]; + return *m_linkTargets[index]; +} + +RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); } +RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); } +RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); } +RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::end() const noexcept { return m_objects.end(); } + +RLMClassInfo& RLMSchemaInfo::operator[](NSString *name) { + auto it = m_objects.find(name); + if (it == m_objects.end()) { + @throw RLMException(@"Object type '%@' is not managed by the Realm. " + @"If using a custom `objectClasses` / `objectTypes` array in your configuration, " + @"add `%@` to the list of `objectClasses` / `objectTypes`.", + name, name); + } + return *&it->second; +} + +RLMSchemaInfo::RLMSchemaInfo(RLMRealm *realm, RLMSchema *rlmSchema, realm::Schema const& schema) { + REALM_ASSERT(rlmSchema.objectSchema.count == schema.size()); + REALM_ASSERT(m_objects.empty()); + + m_objects.reserve(schema.size()); + for (RLMObjectSchema *rlmObjectSchema in rlmSchema.objectSchema) { + m_objects.emplace(std::piecewise_construct, + std::forward_as_tuple(rlmObjectSchema.className), + std::forward_as_tuple(realm, rlmObjectSchema, + &*schema.find(rlmObjectSchema.className.UTF8String))); + } +} diff --git a/Pods/Realm/Realm/RLMCollection.mm b/Pods/Realm/Realm/RLMCollection.mm new file mode 100644 index 0000000..8b1bfe1 --- /dev/null +++ b/Pods/Realm/Realm/RLMCollection.mm @@ -0,0 +1,344 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMCollection_Private.hpp" + +#import "RLMArray_Private.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" + +#import "collection_notifications.hpp" +#import "list.hpp" +#import "results.hpp" + +#import + +static const int RLMEnumerationBufferSize = 16; + +@implementation RLMFastEnumerator { + // The buffer supplied by fast enumeration does not retain the objects given + // to it, but because we create objects on-demand and don't want them + // autoreleased (a table can have more rows than the device has memory for + // accessor objects) we need a thing to retain them. + id _strongBuffer[RLMEnumerationBufferSize]; + + RLMRealm *_realm; + RLMClassInfo *_info; + + // Collection being enumerated. Only one of these two will be valid: when + // possible we enumerate the collection directly, but when in a write + // transaction we instead create a frozen TableView and enumerate that + // instead so that mutating the collection during enumeration works. + id _collection; + realm::TableView _tableView; +} + +- (instancetype)initWithCollection:(id)collection objectSchema:(RLMClassInfo&)info { + self = [super init]; + if (self) { + _realm = collection.realm; + _info = &info; + + if (_realm.inWriteTransaction) { + _tableView = [collection tableView]; + } + else { + _collection = collection; + [_realm registerEnumerator:self]; + } + } + return self; +} + +- (void)dealloc { + if (_collection) { + [_realm unregisterEnumerator:self]; + } +} + +- (void)detach { + _tableView = [_collection tableView]; + _collection = nil; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + count:(NSUInteger)len { + [_realm verifyThread]; + if (!_tableView.is_attached() && !_collection) { + @throw RLMException(@"Collection is no longer valid"); + } + // The fast enumeration buffer size is currently a hardcoded number in the + // compiler so this can't actually happen, but just in case it changes in + // the future... + if (len > RLMEnumerationBufferSize) { + len = RLMEnumerationBufferSize; + } + + NSUInteger batchCount = 0, count = state->extra[1]; + + Class accessorClass = _info->rlmObjectSchema.accessorClass; + for (NSUInteger index = state->state; index < count && batchCount < len; ++index) { + RLMObject *accessor = RLMCreateManagedAccessor(accessorClass, _realm, _info); + if (_collection) { + accessor->_row = (*_info->table())[[_collection indexInSource:index]]; + } + else if (_tableView.is_row_attached(index)) { + accessor->_row = (*_info->table())[_tableView.get_source_ndx(index)]; + } + RLMInitializeSwiftAccessorGenerics(accessor); + _strongBuffer[batchCount] = accessor; + batchCount++; + } + + for (NSUInteger i = batchCount; i < len; ++i) { + _strongBuffer[i] = nil; + } + + if (batchCount == 0) { + // Release our data if we're done, as we're autoreleased and so may + // stick around for a while + _collection = nil; + if (_tableView.is_attached()) { + _tableView = {}; + } + else { + [_realm unregisterEnumerator:self]; + } + } + + state->itemsPtr = (__unsafe_unretained id *)(void *)_strongBuffer; + state->state += batchCount; + state->mutationsPtr = state->extra+1; + + return batchCount; +} +@end + + +NSArray *RLMCollectionValueForKey(id collection, NSString *key) { + size_t count = collection.count; + if (count == 0) { + return @[]; + } + + RLMRealm *realm = collection.realm; + RLMClassInfo *info = collection.objectInfo; + + NSMutableArray *results = [NSMutableArray arrayWithCapacity:count]; + if ([key isEqualToString:@"self"]) { + for (size_t i = 0; i < count; i++) { + size_t rowIndex = [collection indexInSource:i]; + [results addObject:RLMCreateObjectAccessor(realm, *info, rowIndex) ?: NSNull.null]; + } + return results; + } + + RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info); + realm::Table *table = info->table(); + for (size_t i = 0; i < count; i++) { + size_t rowIndex = [collection indexInSource:i]; + accessor->_row = (*table)[rowIndex]; + RLMInitializeSwiftAccessorGenerics(accessor); + [results addObject:[accessor valueForKey:key] ?: NSNull.null]; + } + + return results; +} + +void RLMCollectionSetValueForKey(id collection, NSString *key, id value) { + realm::TableView tv = [collection tableView]; + if (tv.size() == 0) { + return; + } + + RLMRealm *realm = collection.realm; + RLMClassInfo *info = collection.objectInfo; + RLMObject *accessor = RLMCreateManagedAccessor(info->rlmObjectSchema.accessorClass, realm, info); + for (size_t i = 0; i < tv.size(); i++) { + accessor->_row = tv[i]; + RLMInitializeSwiftAccessorGenerics(accessor); + [accessor setValue:value forKey:key]; + } +} + +NSString *RLMDescriptionWithMaxDepth(NSString *name, + id collection, + NSUInteger depth) { + if (depth == 0) { + return @""; + } + + const NSUInteger maxObjects = 100; + auto str = [NSMutableString stringWithFormat:@"%@ <%p> (\n", name, (void *)collection]; + size_t index = 0, skipped = 0; + for (id obj in collection) { + NSString *sub; + if ([obj respondsToSelector:@selector(descriptionWithMaxDepth:)]) { + sub = [obj descriptionWithMaxDepth:depth - 1]; + } + else { + sub = [obj description]; + } + + // Indent child objects + NSString *objDescription = [sub stringByReplacingOccurrencesOfString:@"\n" + withString:@"\n\t"]; + [str appendFormat:@"\t[%zu] %@,\n", index++, objDescription]; + if (index >= maxObjects) { + skipped = collection.count - maxObjects; + break; + } + } + + // Remove last comma and newline characters + if (collection.count > 0) { + [str deleteCharactersInRange:NSMakeRange(str.length-2, 2)]; + } + if (skipped) { + [str appendFormat:@"\n\t... %zu objects skipped.", skipped]; + } + [str appendFormat:@"\n)"]; + return str; +} + +@implementation RLMCancellationToken { + realm::NotificationToken _token; +} +- (instancetype)initWithToken:(realm::NotificationToken)token { + self = [super init]; + if (self) { + _token = std::move(token); + } + return self; +} + +- (void)stop { + _token = {}; +} + +@end + +@implementation RLMCollectionChange { + realm::CollectionChangeSet _indices; +} + +- (instancetype)initWithChanges:(realm::CollectionChangeSet)indices { + self = [super init]; + if (self) { + _indices = std::move(indices); + } + return self; +} + +static NSArray *toArray(realm::IndexSet const& set) { + NSMutableArray *ret = [NSMutableArray new]; + for (auto index : set.as_indexes()) { + [ret addObject:@(index)]; + } + return ret; +} + +- (NSArray *)insertions { + return toArray(_indices.insertions); +} + +- (NSArray *)deletions { + return toArray(_indices.deletions); +} + +- (NSArray *)modifications { + return toArray(_indices.modifications); +} + +static NSArray *toIndexPathArray(realm::IndexSet const& set, NSUInteger section) { + NSMutableArray *ret = [NSMutableArray new]; + NSUInteger path[2] = {section, 0}; + for (auto index : set.as_indexes()) { + path[1] = index; + [ret addObject:[NSIndexPath indexPathWithIndexes:path length:2]]; + } + return ret; +} + +- (NSArray *)deletionsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.deletions, section); +} + +- (NSArray *)insertionsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.insertions, section); + +} + +- (NSArray *)modificationsInSection:(NSUInteger)section { + return toIndexPathArray(_indices.modifications, section); + +} +@end + +template +RLMNotificationToken *RLMAddNotificationBlock(id objcCollection, + Collection& collection, + void (^block)(id, RLMCollectionChange *, NSError *), + bool suppressInitialChange) { + struct IsValid { + static bool call(realm::List const& list) { + return list.is_valid(); + } + static bool call(realm::Results const&) { + return true; + } + }; + + auto skip = suppressInitialChange ? std::make_shared(true) : nullptr; + auto cb = [=, &collection](realm::CollectionChangeSet const& changes, + std::exception_ptr err) { + if (err) { + try { + rethrow_exception(err); + } + catch (...) { + NSError *error = nil; + RLMRealmTranslateException(&error); + block(nil, nil, error); + return; + } + } + + if (!IsValid::call(collection)) { + return; + } + + if (skip && *skip) { + *skip = false; + block(objcCollection, nil, nil); + } + else if (changes.empty()) { + block(objcCollection, nil, nil); + } + else { + block(objcCollection, [[RLMCollectionChange alloc] initWithChanges:changes], nil); + } + }; + + return [[RLMCancellationToken alloc] initWithToken:collection.add_notification_callback(cb)]; +} + +// Explicitly instantiate the templated function for the two types we'll use it on +template RLMNotificationToken *RLMAddNotificationBlock(id, realm::List&, void (^)(id, RLMCollectionChange *, NSError *), bool); +template RLMNotificationToken *RLMAddNotificationBlock(id, realm::Results&, void (^)(id, RLMCollectionChange *, NSError *), bool); diff --git a/Pods/Realm/Realm/RLMConstants.m b/Pods/Realm/Realm/RLMConstants.m new file mode 100644 index 0000000..7136127 --- /dev/null +++ b/Pods/Realm/Realm/RLMConstants.m @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +RLMNotification const RLMRealmRefreshRequiredNotification = @"RLMRealmRefreshRequiredNotification"; +RLMNotification const RLMRealmDidChangeNotification = @"RLMRealmDidChangeNotification"; + +NSString * const RLMErrorDomain = @"io.realm"; + +NSString * const RLMUnknownSystemErrorDomain = @"io.realm.unknown"; + +NSString * const RLMExceptionName = @"RLMException"; + +NSString * const RLMRealmVersionKey = @"RLMRealmVersion"; + +NSString * const RLMRealmCoreVersionKey = @"RLMRealmCoreVersion"; + +NSString * const RLMInvalidatedKey = @"invalidated"; diff --git a/Pods/Realm/Realm/RLMListBase.mm b/Pods/Realm/Realm/RLMListBase.mm new file mode 100644 index 0000000..78cf91a --- /dev/null +++ b/Pods/Realm/Realm/RLMListBase.mm @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMListBase.h" + +#import "RLMArray_Private.hpp" +#import "RLMObservation.hpp" + +@interface RLMArray (KVO) +- (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes; +@end + +@implementation RLMListBase { + std::unique_ptr _observationInfo; +} + +- (instancetype)initWithArray:(RLMArray *)array { + self = [super init]; + if (self) { + __rlmArray = array; + } + return self; +} + +- (id)valueForKey:(NSString *)key { + return [__rlmArray valueForKey:key]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len { + return [__rlmArray countByEnumeratingWithState:state objects:buffer count:len]; +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + return [__rlmArray objectsAtIndexes:indexes]; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + RLMEnsureArrayObservationInfo(_observationInfo, keyPath, __rlmArray, self); + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +@end diff --git a/Pods/Realm/Realm/RLMMigration.mm b/Pods/Realm/Realm/RLMMigration.mm new file mode 100644 index 0000000..6d8da01 --- /dev/null +++ b/Pods/Realm/Realm/RLMMigration.mm @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMMigration_Private.h" + +#import "RLMAccessor.h" +#import "RLMObject_Private.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMProperty_Private.h" +#import "RLMRealm_Dynamic.h" +#import "RLMRealm_Private.hpp" +#import "RLMResults_Private.h" +#import "RLMSchema_Private.hpp" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "shared_realm.hpp" +#import "schema.hpp" + +#import + +using namespace realm; + +// The source realm for a migration has to use a SharedGroup to be able to share +// the file with the destination realm, but we don't want to let the user call +// beginWriteTransaction on it as that would make no sense. +@interface RLMMigrationRealm : RLMRealm +@end + +@implementation RLMMigrationRealm +- (BOOL)readonly { + return YES; +} + +- (void)beginWriteTransaction { + @throw RLMException(@"Cannot modify the source Realm in a migration"); +} +@end + +@implementation RLMMigration { + realm::Schema *_schema; +} + +- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema { + self = [super init]; + if (self) { + _realm = realm; + _oldRealm = oldRealm; + _schema = &schema; + object_setClass(_oldRealm, RLMMigrationRealm.class); + } + return self; +} + +- (RLMSchema *)oldSchema { + return self.oldRealm.schema; +} + +- (RLMSchema *)newSchema { + return self.realm.schema; +} + +- (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block { + // get all objects + RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil; + RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil; + + if (objects && oldObjects) { + for (long i = oldObjects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(oldObjects[i], objects[i]); + } + } + } + else if (objects) { + for (long i = objects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(nil, objects[i]); + } + } + } + else if (oldObjects) { + for (long i = oldObjects.count - 1; i >= 0; i--) { + @autoreleasepool { + block(oldObjects[i], nil); + } + } + } +} + +- (void)execute:(RLMMigrationBlock)block { + @autoreleasepool { + // disable all primary keys for migration and use DynamicObject for all types + for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) { + objectSchema.accessorClass = RLMDynamicObject.class; + objectSchema.primaryKeyProperty.isPrimary = NO; + } + for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) { + objectSchema.accessorClass = RLMDynamicObject.class; + } + + block(self, _oldRealm->_realm->schema_version()); + + _oldRealm = nil; + _realm = nil; + } +} + +- (RLMObject *)createObject:(NSString *)className withValue:(id)value { + return [_realm createObject:className withValue:value]; +} + +- (RLMObject *)createObject:(NSString *)className withObject:(id)object { + return [self createObject:className withValue:object]; +} + +- (void)deleteObject:(RLMObject *)object { + [_realm deleteObject:object]; +} + +- (BOOL)deleteDataForClassName:(NSString *)name { + if (!name) { + return false; + } + + TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String); + if (!table) { + return false; + } + + if ([_realm.schema schemaForClassName:name]) { + table->clear(); + } + else { + realm::ObjectStore::delete_data_for_object(_realm.group, name.UTF8String); + } + + return true; +} + +- (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName { + const char *objectType = className.UTF8String; + realm::ObjectStore::rename_property(_realm.group, *_schema, objectType, oldName.UTF8String, newName.UTF8String); +} + +@end diff --git a/Pods/Realm/Realm/RLMNetworkClient.m b/Pods/Realm/Realm/RLMNetworkClient.m new file mode 100644 index 0000000..f6cd317 --- /dev/null +++ b/Pods/Realm/Realm/RLMNetworkClient.m @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMNetworkClient.h" + +#import "RLMRealmConfiguration.h" +#import "RLMSyncUtil_Private.h" + +typedef void(^RLMServerURLSessionCompletionBlock)(NSData *, NSURLResponse *, NSError *); + +static NSUInteger const kHTTPCodeRange = 100; + +typedef enum : NSUInteger { + Informational = 1, // 1XX + Success = 2, // 2XX + Redirection = 3, // 3XX + ClientError = 4, // 4XX + ServerError = 5, // 5XX +} RLMServerHTTPErrorCodeType; + +static NSRange RLM_rangeForErrorType(RLMServerHTTPErrorCodeType type) { + return NSMakeRange(type*100, kHTTPCodeRange); +} + +@implementation RLMNetworkClient + ++ (NSURLSession *)session { + return [NSURLSession sharedSession]; +} + ++ (NSURL *)urlForServer:(NSURL *)serverURL endpoint:(RLMServerEndpoint)endpoint { + NSString *pathComponent = nil; + switch (endpoint) { + case RLMServerEndpointAuth: + pathComponent = @"auth"; + break; + case RLMServerEndpointLogout: + // TODO: fix this + pathComponent = @"logout"; + NSAssert(NO, @"logout endpoint isn't implemented yet, don't use it"); + break; + case RLMServerEndpointAddCredential: + // TODO: fix this + pathComponent = @"addCredential"; + NSAssert(NO, @"add credential endpoint isn't implemented yet, don't use it"); + break; + case RLMServerEndpointRemoveCredential: + // TODO: fix this + pathComponent = @"removeCredential"; + NSAssert(NO, @"remove credential endpoint isn't implemented yet, don't use it"); + break; + } + NSAssert(pathComponent != nil, @"Unrecognized value for RLMServerEndpoint enum"); + return [serverURL URLByAppendingPathComponent:pathComponent]; +} + ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + completion:(RLMSyncCompletionBlock)completionBlock { + static NSTimeInterval const defaultTimeout = 60; + [self postRequestToEndpoint:endpoint + server:serverURL + JSON:jsonDictionary + timeout:defaultTimeout + completion:completionBlock]; +} + +// FIXME: should completion argument also pass back the NSURLResponse and/or the raw data? ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + timeout:(NSTimeInterval)timeout + completion:(RLMSyncCompletionBlock)completionBlock { + + NSError *localError = nil; + + // Attempt to convert the JSON + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary + options:(NSJSONWritingOptions)0 + error:&localError]; + if (!jsonData) { + completionBlock(localError, nil); + return; + } + + // Create the request + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self urlForServer:serverURL endpoint:endpoint]]; + request.HTTPBody = jsonData; + request.HTTPMethod = @"POST"; + request.timeoutInterval = MAX(timeout, 10); + [request addValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"]; + [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + RLMServerURLSessionCompletionBlock handler = ^(NSData *data, + NSURLResponse *response, + NSError *error) { + NSError *localError = nil; + + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *actualResponse = (NSHTTPURLResponse *)response; + BOOL badResponse = (NSLocationInRange(actualResponse.statusCode, RLM_rangeForErrorType(ClientError)) + || NSLocationInRange(actualResponse.statusCode, RLM_rangeForErrorType(ServerError))); + if (badResponse) { + // Client or server error + localError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorHTTPStatusCodeError + userInfo:@{@"statusCode": @(actualResponse.statusCode)}]; + completionBlock(localError, nil); + return; + } + } + + // Parse out the JSON + if (data && !error) { + id json = [NSJSONSerialization JSONObjectWithData:data + options:(NSJSONReadingOptions)0 + error:&localError]; + if (!json || localError) { + // JSON parsing error + completionBlock(localError, nil); + } else if (![json isKindOfClass:[NSDictionary class]]) { + // JSON response malformed + localError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + completionBlock(localError, nil); + } else { + // JSON parsed successfully + completionBlock(nil, (NSDictionary *)json); + } + } else { + // Network error + completionBlock(error, nil); + } + }; + + // Add the request to a task and start it + NSURLSessionTask *task = [self.session dataTaskWithRequest:request + completionHandler:handler]; + [task resume]; +} + +@end diff --git a/Pods/Realm/Realm/RLMObject.mm b/Pods/Realm/Realm/RLMObject.mm new file mode 100644 index 0000000..fb98d6c --- /dev/null +++ b/Pods/Realm/Realm/RLMObject.mm @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObject_Private.hpp" + +#import "RLMAccessor.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" + +// We declare things in RLMObject which are actually implemented in RLMObjectBase +// for documentation's sake, which leads to -Wunimplemented-method warnings. +// Other alternatives to this would be to disable -Wunimplemented-method for this +// file (but then we could miss legitimately missing things), or declaring the +// inherited things in a category (but they currently aren't nicely grouped for +// that). +@implementation RLMObject + +// synthesized in RLMObjectBase +@dynamic invalidated, realm, objectSchema; + +#pragma mark - Designated Initializers + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { + return [super initWithValue:value schema:schema]; +} + +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm schema:(RLMObjectSchema *)schema { + return [super initWithRealm:realm schema:schema]; +} + +#pragma mark - Convenience Initializers + +- (instancetype)initWithValue:(id)value { + [self.class sharedSchema]; // ensure this class' objectSchema is loaded in the partialSharedSchema + RLMSchema *schema = RLMSchema.partialSharedSchema; + return [super initWithValue:value schema:schema]; +} + +#pragma mark - Class-based Object Creation + ++ (instancetype)createInDefaultRealmWithValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue([RLMRealm defaultRealm], [self className], value, false); +} + ++ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, false); +} + ++ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value { + return [self createOrUpdateInRealm:[RLMRealm defaultRealm] withValue:value]; +} + ++ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value { + // verify primary key + RLMObjectSchema *schema = [self sharedSchema]; + if (!schema.primaryKeyProperty) { + NSString *reason = [NSString stringWithFormat:@"'%@' does not have a primary key and can not be updated", schema.className]; + @throw [NSException exceptionWithName:@"RLMExecption" reason:reason userInfo:nil]; + } + return (RLMObject *)RLMCreateObjectInRealmWithValue(realm, [self className], value, true); +} + +#pragma mark - Subscripting + +- (id)objectForKeyedSubscript:(NSString *)key { + return RLMObjectBaseObjectForKeyedSubscript(self, key); +} + +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { + RLMObjectBaseSetObjectForKeyedSubscript(self, key, obj); +} + +#pragma mark - Getting & Querying + ++ (RLMResults *)allObjects { + return RLMGetObjects(RLMRealm.defaultRealm, self.className, nil); +} + ++ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm { + return RLMGetObjects(realm, self.className, nil); +} + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsInRealm:realm where:predicateFormat args:args]; + va_end(args); + return results; +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args { + return [self objectsInRealm:realm withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + ++ (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + return RLMGetObjects(RLMRealm.defaultRealm, self.className, predicate); +} + ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(NSPredicate *)predicate { + return RLMGetObjects(realm, self.className, predicate); +} + ++ (instancetype)objectForPrimaryKey:(id)primaryKey { + return RLMGetObject(RLMRealm.defaultRealm, self.className, primaryKey); +} + ++ (instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(id)primaryKey { + return RLMGetObject(realm, self.className, primaryKey); +} + +#pragma mark - Other Instance Methods + +- (BOOL)isEqualToObject:(RLMObject *)object { + return [object isKindOfClass:RLMObject.class] && RLMObjectBaseAreEqual(self, object); +} + ++ (NSString *)className { + return [super className]; +} + +#pragma mark - Default values for schema definition + ++ (NSArray *)indexedProperties { + return @[]; +} + ++ (NSDictionary *)linkingObjectsProperties { + return @{}; +} + ++ (NSDictionary *)defaultPropertyValues { + return nil; +} + ++ (NSString *)primaryKey { + return nil; +} + ++ (NSArray *)ignoredProperties { + return nil; +} + ++ (NSArray *)requiredProperties { + return @[]; +} + +@end + +@implementation RLMDynamicObject + ++ (BOOL)shouldIncludeInDefaultSchema { + return NO; +} + +- (id)valueForUndefinedKey:(NSString *)key { + return RLMDynamicGetByName(self, key, false); +} + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key { + RLMDynamicValidatedSet(self, key, value); +} + +@end + +@implementation RLMWeakObjectHandle { + realm::Row _row; + RLMClassInfo *_info; + Class _objectClass; +} + +- (instancetype)initWithObject:(RLMObjectBase *)object { + if (!(self = [super init])) { + return nil; + } + + _row = object->_row; + _info = object->_info; + _objectClass = object.class; + + return self; +} + +- (RLMObjectBase *)object { + RLMObjectBase *object = RLMCreateManagedAccessor(_objectClass, _info->realm, _info); + object->_row = std::move(_row); + return object; +} + +@end diff --git a/Pods/Realm/Realm/RLMObjectBase.mm b/Pods/Realm/Realm/RLMObjectBase.mm new file mode 100644 index 0000000..80e5205 --- /dev/null +++ b/Pods/Realm/Realm/RLMObjectBase.mm @@ -0,0 +1,450 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObject_Private.hpp" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObservation.hpp" +#import "RLMOptionalBase.h" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +using namespace realm; + +const NSUInteger RLMDescriptionMaxDepth = 5; + +static bool maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) { + obj->_objectSchema = [obj.class sharedSchema]; + if (!obj->_objectSchema) { + return false; + } + + // set default values + if (!obj->_objectSchema.isSwiftClass) { + NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema); + for (NSString *key in dict) { + [obj setValue:dict[key] forKey:key]; + } + } + + // set unmanaged accessor class + object_setClass(obj, obj->_objectSchema.unmanagedClass); + return true; +} + +@implementation RLMObjectBase +// unmanaged init +- (instancetype)init { + if ((self = [super init])) { + maybeInitObjectSchemaForUnmanaged(self); + } + return self; +} + +- (void)dealloc { + // This can't be a unique_ptr because associated objects are removed + // *after* c++ members are destroyed and dealloc is called, and we need it + // to be in a validish state when that happens + delete _observationInfo; + _observationInfo = nullptr; +} + +static id validatedObjectForProperty(id obj, RLMProperty *prop, RLMSchema *schema) { + if (RLMIsObjectValidForProperty(obj, prop)) { + return obj; + } + + // check for object or array of properties + if (prop.type == RLMPropertyTypeObject) { + // for object create and try to initialize with obj + RLMObjectSchema *objSchema = schema[prop.objectClassName]; + return [[objSchema.objectClass alloc] initWithValue:obj schema:schema]; + } + else if (prop.type == RLMPropertyTypeArray && [obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + // for arrays, create objects for each element and return new array + RLMObjectSchema *objSchema = schema[prop.objectClassName]; + RLMArray *objects = [[RLMArray alloc] initWithObjectClassName:objSchema.className]; + for (id el in obj) { + [objects addObject:[[objSchema.objectClass alloc] initWithValue:el schema:schema]]; + } + return objects; + } + + // if not convertible to prop throw + @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); +} + +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema { + if (!(self = [super init])) { + return self; + } + + if (!maybeInitObjectSchemaForUnmanaged(self)) { + // Don't populate fields from the passed-in object if we're called + // during schema init + return self; + } + + NSArray *properties = _objectSchema.properties; + if (NSArray *array = RLMDynamicCast(value)) { + if (array.count != properties.count) { + @throw RLMException(@"Invalid array input. Number of array elements does not match number of properties."); + } + for (NSUInteger i = 0; i < array.count; i++) { + id propertyValue = validatedObjectForProperty(array[i], properties[i], schema); + [self setValue:RLMCoerceToNil(propertyValue) forKeyPath:[properties[i] name]]; + } + } + else if (value) { + // assume our object is an NSDictionary or an object with kvc properties + NSDictionary *defaultValues = nil; + for (RLMProperty *prop in properties) { + id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className); + + // get default for nil object + if (!obj) { + if (!defaultValues) { + defaultValues = RLMDefaultValuesForObjectSchema(_objectSchema); + } + obj = defaultValues[prop.name]; + } + + // don't set unspecified properties + if (!obj) { + continue; + } + + obj = validatedObjectForProperty(obj, prop, schema); + [self setValue:RLMCoerceToNil(obj) forKeyPath:prop.name]; + } + } else { + @throw RLMException(@"Must provide a non-nil value."); + } + + return self; +} + +id RLMCreateManagedAccessor(Class cls, __unsafe_unretained RLMRealm *realm, RLMClassInfo *info) { + RLMObjectBase *obj = [[cls alloc] initWithRealm:realm schema:info->rlmObjectSchema]; + obj->_info = info; + return obj; +} + +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema { + self = [super init]; + if (self) { + _realm = realm; + _objectSchema = schema; + } + return self; +} + +- (id)valueForKey:(NSString *)key { + if (_observationInfo) { + return _observationInfo->valueForKey(key); + } + return [super valueForKey:key]; +} + +// Generic Swift properties can't be dynamic, so KVO doesn't work for them by default +- (id)valueForUndefinedKey:(NSString *)key { + if (Ivar ivar = _objectSchema[key].swiftIvar) { + return RLMCoerceToNil(object_getIvar(self, ivar)); + } + return [super valueForUndefinedKey:key]; +} + +- (void)setValue:(id)value forUndefinedKey:(NSString *)key { + RLMProperty *property = _objectSchema[key]; + if (Ivar ivar = property.swiftIvar) { + if (property.type == RLMPropertyTypeArray && [value conformsToProtocol:@protocol(NSFastEnumeration)]) { + RLMArray *array = [object_getIvar(self, ivar) _rlmArray]; + [array removeAllObjects]; + [array addObjects:value]; + } + else if (property.optional) { + RLMOptionalBase *optional = object_getIvar(self, ivar); + optional.underlyingValue = value; + } + return; + } + [super setValue:value forUndefinedKey:key]; +} + +// overridden at runtime per-class for performance ++ (NSString *)className { + NSString *className = NSStringFromClass(self); + if ([RLMSwiftSupport isSwiftClassName:className]) { + className = [RLMSwiftSupport demangleClassName:className]; + } + return className; +} + +// overridden at runtime per-class for performance ++ (RLMObjectSchema *)sharedSchema { + return [RLMSchema sharedSchemaForClass:self.class]; +} + ++ (Class)objectUtilClass:(BOOL)isSwift { + return RLMObjectUtilClass(isSwift); +} + +- (NSString *)description +{ + if (self.isInvalidated) { + return @"[invalid object]"; + } + + return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth]; +} + +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth { + if (depth == 0) { + return @""; + } + + NSString *baseClassName = _objectSchema.className; + NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName]; + + for (RLMProperty *property in _objectSchema.properties) { + id object = RLMObjectBaseObjectForKeyedSubscript(self, property.name); + NSString *sub; + if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) { + sub = [object descriptionWithMaxDepth:depth - 1]; + } + else if (property.type == RLMPropertyTypeData) { + static NSUInteger maxPrintedDataLength = 24; + NSData *data = object; + NSUInteger length = data.length; + if (length > maxPrintedDataLength) { + data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength]; + } + NSString *dataDescription = [data description]; + sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length]; + } + else { + sub = [object description]; + } + [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + [mString appendString:@"}"]; + + return [NSString stringWithString:mString]; +} + +- (RLMRealm *)realm { + return _realm; +} + +- (RLMObjectSchema *)objectSchema { + return _objectSchema; +} + +- (BOOL)isInvalidated { + // if not unmanaged and our accessor has been detached, we have been deleted + return self.class == _objectSchema.accessorClass && !_row.is_attached(); +} + +- (BOOL)isEqual:(id)object { + if (RLMObjectBase *other = RLMDynamicCast(object)) { + if (_objectSchema.primaryKeyProperty) { + return RLMObjectBaseAreEqual(self, other); + } + } + return [super isEqual:object]; +} + +- (NSUInteger)hash { + if (_objectSchema.primaryKeyProperty) { + id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name]; + + // modify the hash of our primary key value to avoid potential (although unlikely) collisions + return [primaryProperty hash] ^ 1; + } + else { + return [super hash]; + } +} + ++ (BOOL)shouldIncludeInDefaultSchema { + return RLMIsObjectSubclass(self); +} + +- (id)mutableArrayValueForKey:(NSString *)key { + id obj = [self valueForKey:key]; + if ([obj isKindOfClass:[RLMArray class]]) { + return obj; + } + return [super mutableArrayValueForKey:key]; +} + +- (void)addObserver:(id)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + if (!_observationInfo) { + _observationInfo = new RLMObservationInfo(self); + } + _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath); + + [super addObserver:observer forKeyPath:keyPath options:options context:context]; +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { + [super removeObserver:observer forKeyPath:keyPath]; + if (_observationInfo) + _observationInfo->removeObserver(); +} + ++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { + const char *className = class_getName(self); + const char accessorClassPrefix[] = "RLMAccessor_"; + if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { + if (self.sharedSchema[key]) { + return NO; + } + } + + return [super automaticallyNotifiesObserversForKey:key]; +} + +@end + +RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) { + return object ? object->_realm : nil; +} + +RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) { + return object ? object->_objectSchema : nil; +} + +id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) { + if (!object) { + return nil; + } + + if (object->_realm) { + return RLMDynamicGetByName(object, key, false); + } + else { + return [object valueForKey:key]; + } +} + +void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) { + if (!object) { + return; + } + + if (object->_realm) { + RLMDynamicValidatedSet(object, key, obj); + } + else { + [object setValue:obj forKey:key]; + } +} + + +BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) { + // if not the correct types throw + if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) { + @throw RLMException(@"Can only compare objects of class RLMObjectBase"); + } + // if identical object (or both are nil) + if (o1 == o2) { + return YES; + } + // if one is nil + if (o1 == nil || o2 == nil) { + return NO; + } + // if not in realm or differing realms + if (o1->_realm == nil || o1->_realm != o2->_realm) { + return NO; + } + // if either are detached + if (!o1->_row.is_attached() || !o2->_row.is_attached()) { + return NO; + } + // if table and index are the same + return o1->_row.get_table() == o2->_row.get_table() + && o1->_row.get_index() == o2->_row.get_index(); +} + +id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) { + @try { + return [object valueForKey:key]; + } + @catch (NSException *e) { + if ([e.name isEqualToString:NSUndefinedKeyException]) { + @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'", + object, className, key); + } + @throw; + } +} + +Class RLMObjectUtilClass(BOOL isSwift) { + static Class objectUtilObjc = [RLMObjectUtil class]; + static Class objectUtilSwift = NSClassFromString(@"RealmSwiftObjectUtil"); + return isSwift && objectUtilSwift ? objectUtilSwift : objectUtilObjc; +} + +@implementation RLMObjectUtil + ++ (NSArray *)ignoredPropertiesForClass:(Class)cls { + return [cls ignoredProperties]; +} + ++ (NSArray *)indexedPropertiesForClass:(Class)cls { + return [cls indexedProperties]; +} + ++ (NSDictionary *)linkingObjectsPropertiesForClass:(Class)cls { + return [cls linkingObjectsProperties]; +} + ++ (NSDictionary *)linkingObjectProperties:(__unused id)object { + return nil; +} + ++ (NSArray *)getGenericListPropertyNames:(__unused id)obj { + return nil; +} + ++ (NSDictionary *)getLinkingObjectsProperties:(__unused id)obj { + return nil; +} + ++ (NSDictionary *)getOptionalProperties:(__unused id)obj { + return nil; +} + ++ (NSArray *)requiredPropertiesForClass:(Class)cls { + return [cls requiredProperties]; +} + +@end diff --git a/Pods/Realm/Realm/RLMObjectSchema.mm b/Pods/Realm/Realm/RLMObjectSchema.mm new file mode 100644 index 0000000..8762dae --- /dev/null +++ b/Pods/Realm/Realm/RLMObjectSchema.mm @@ -0,0 +1,440 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObjectSchema_Private.hpp" + +#import "RLMArray.h" +#import "RLMListBase.h" +#import "RLMObject_Private.h" +#import "RLMProperty_Private.hpp" +#import "RLMRealm_Dynamic.h" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" + +using namespace realm; + +// private properties +@interface RLMObjectSchema () +@property (nonatomic, readwrite) NSDictionary *allPropertiesByName; +@property (nonatomic, readwrite) NSString *className; +@end + +@implementation RLMObjectSchema { + NSArray *_swiftGenericProperties; +} + +- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties { + self = [super init]; + self.className = objectClassName; + self.properties = properties; + self.objectClass = objectClass; + self.accessorClass = objectClass; + self.unmanagedClass = objectClass; + return self; +} + +// return properties by name +-(RLMProperty *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)key { + return _allPropertiesByName[key]; +} + +// create property map when setting property array +-(void)setProperties:(NSArray *)properties { + _properties = properties; + [self _propertiesDidChange]; +} + +- (void)setComputedProperties:(NSArray *)computedProperties { + _computedProperties = computedProperties; + [self _propertiesDidChange]; +} + +- (void)_propertiesDidChange { + NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:_properties.count + _computedProperties.count]; + NSUInteger index = 0; + for (RLMProperty *prop in _properties) { + prop.index = index++; + map[prop.name] = prop; + if (prop.isPrimary) { + self.primaryKeyProperty = prop; + } + } + for (RLMProperty *prop in _computedProperties) { + map[prop.name] = prop; + } + _allPropertiesByName = map; +} + + +- (void)setPrimaryKeyProperty:(RLMProperty *)primaryKeyProperty { + _primaryKeyProperty.isPrimary = NO; + primaryKeyProperty.isPrimary = YES; + _primaryKeyProperty = primaryKeyProperty; +} + ++ (instancetype)schemaForObjectClass:(Class)objectClass { + RLMObjectSchema *schema = [RLMObjectSchema new]; + + // determine classname from objectclass as className method has not yet been updated + NSString *className = NSStringFromClass(objectClass); + bool isSwift = [RLMSwiftSupport isSwiftClassName:className]; + if (isSwift) { + className = [RLMSwiftSupport demangleClassName:className]; + } + schema.className = className; + schema.objectClass = objectClass; + schema.accessorClass = objectClass; + schema.isSwiftClass = isSwift; + + // create array of RLMProperties, inserting properties of superclasses first + Class cls = objectClass; + Class superClass = class_getSuperclass(cls); + NSArray *allProperties = @[]; + while (superClass && superClass != RLMObjectBase.class) { + allProperties = [[RLMObjectSchema propertiesForClass:cls isSwift:isSwift] arrayByAddingObjectsFromArray:allProperties]; + cls = superClass; + superClass = class_getSuperclass(superClass); + } + NSArray *persistedProperties = [allProperties filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RLMProperty *property, NSDictionary *) { + return !RLMPropertyTypeIsComputed(property.type); + }]]; + schema.properties = persistedProperties; + + NSArray *computedProperties = [allProperties filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(RLMProperty *property, NSDictionary *) { + return RLMPropertyTypeIsComputed(property.type); + }]]; + schema.computedProperties = computedProperties; + + // verify that we didn't add any properties twice due to inheritance + if (allProperties.count != [NSSet setWithArray:[allProperties valueForKey:@"name"]].count) { + NSCountedSet *countedPropertyNames = [NSCountedSet setWithArray:[allProperties valueForKey:@"name"]]; + NSSet *duplicatePropertyNames = [countedPropertyNames filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *) { + return [countedPropertyNames countForObject:object] > 1; + }]]; + + if (duplicatePropertyNames.count == 1) { + @throw RLMException(@"Property '%@' is declared multiple times in the class hierarchy of '%@'", duplicatePropertyNames.allObjects.firstObject, className); + } else { + @throw RLMException(@"Object '%@' has properties that are declared multiple times in its class hierarchy: '%@'", className, [duplicatePropertyNames.allObjects componentsJoinedByString:@"', '"]); + } + } + + if (NSString *primaryKey = [objectClass primaryKey]) { + for (RLMProperty *prop in schema.properties) { + if ([primaryKey isEqualToString:prop.name]) { + prop.indexed = YES; + schema.primaryKeyProperty = prop; + break; + } + } + + if (!schema.primaryKeyProperty) { + @throw RLMException(@"Primary key property '%@' does not exist on object '%@'", primaryKey, className); + } + if (schema.primaryKeyProperty.type != RLMPropertyTypeInt && schema.primaryKeyProperty.type != RLMPropertyTypeString) { + @throw RLMException(@"Only 'string' and 'int' properties can be designated the primary key"); + } + } + + for (RLMProperty *prop in schema.properties) { + if (prop.optional && !RLMPropertyTypeIsNullable(prop.type)) { + @throw RLMException(@"Only 'string', 'binary', and 'object' properties can be made optional, and property '%@' is of type '%@'.", + prop.name, RLMTypeToString(prop.type)); + } + } + + return schema; +} + ++ (nullable NSString *)baseNameForLazySwiftProperty:(NSString *)propertyName { + // A Swift lazy var shows up as two separate children on the reflection tree: one named 'x', and another that is + // optional and is named 'x.storage'. Note that '.' is illegal in either a Swift or Objective-C property name. + NSString *const storageSuffix = @".storage"; + if ([propertyName hasSuffix:storageSuffix]) { + return [propertyName substringToIndex:propertyName.length - storageSuffix.length]; + } + return nil; +} + ++ (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass { + Class objectUtil = [objectClass objectUtilClass:isSwiftClass]; + NSArray *ignoredProperties = [objectUtil ignoredPropertiesForClass:objectClass]; + NSDictionary *linkingObjectsProperties = [objectUtil linkingObjectsPropertiesForClass:objectClass]; + + // For Swift classes we need an instance of the object when parsing properties + id swiftObjectInstance = isSwiftClass ? [[objectClass alloc] init] : nil; + + unsigned int count; + objc_property_t *props = class_copyPropertyList(objectClass, &count); + NSMutableArray *propArray = [NSMutableArray arrayWithCapacity:count]; + NSSet *indexed = [[NSSet alloc] initWithArray:[objectUtil indexedPropertiesForClass:objectClass]]; + for (unsigned int i = 0; i < count; i++) { + NSString *propertyName = @(property_getName(props[i])); + if ([ignoredProperties containsObject:propertyName]) { + continue; + } + + RLMProperty *prop = nil; + if (isSwiftClass) { + prop = [[RLMProperty alloc] initSwiftPropertyWithName:propertyName + indexed:[indexed containsObject:propertyName] + linkPropertyDescriptor:linkingObjectsProperties[propertyName] + property:props[i] + instance:swiftObjectInstance]; + } + else { + prop = [[RLMProperty alloc] initWithName:propertyName + indexed:[indexed containsObject:propertyName] + linkPropertyDescriptor:linkingObjectsProperties[propertyName] + property:props[i]]; + } + + if (prop) { + [propArray addObject:prop]; + } + } + free(props); + + if (isSwiftClass) { + // List<> properties don't show up as objective-C properties due to + // being generic, so use Swift reflection to get a list of them, and + // then access their ivars directly + for (NSString *propName in [objectUtil getGenericListPropertyNames:swiftObjectInstance]) { + Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); + id value = object_getIvar(swiftObjectInstance, ivar); + NSString *className = [value _rlmArray].objectClassName; + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propName]; + }]; + if (existing != NSNotFound) { + [propArray removeObjectAtIndex:existing]; + } + [propArray addObject:[[RLMProperty alloc] initSwiftListPropertyWithName:propName + ivar:ivar + objectClassName:className]]; + } + + // Ditto for LinkingObjects<> properties. + NSDictionary *linkingObjectsProperties = [objectUtil getLinkingObjectsProperties:swiftObjectInstance]; + for (NSString *propName in linkingObjectsProperties) { + NSDictionary *info = linkingObjectsProperties[propName]; + Ivar ivar = class_getInstanceVariable(objectClass, propName.UTF8String); + + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propName]; + }]; + if (existing != NSNotFound) { + [propArray removeObjectAtIndex:existing]; + } + + [propArray addObject:[[RLMProperty alloc] initSwiftLinkingObjectsPropertyWithName:propName + ivar:ivar + objectClassName:info[@"class"] + linkOriginPropertyName:info[@"property"]]]; + } + } + + if (auto optionalProperties = [objectUtil getOptionalProperties:swiftObjectInstance]) { + for (RLMProperty *property in propArray) { + property.optional = false; + } + [optionalProperties enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSNumber *propertyType, __unused BOOL *stop) { + if ([ignoredProperties containsObject:propertyName]) { + return; + } + NSUInteger existing = [propArray indexOfObjectPassingTest:^BOOL(RLMProperty *obj, __unused NSUInteger idx, __unused BOOL *stop) { + return [obj.name isEqualToString:propertyName]; + }]; + RLMProperty *property; + if (existing != NSNotFound) { + property = propArray[existing]; + property.optional = true; + } + if (auto type = RLMCoerceToNil(propertyType)) { + if (existing == NSNotFound) { + // Check to see if this optional property is an underlying storage property for a Swift lazy var. + // Managed lazy vars are't allowed. + // NOTE: Revisit this once property behaviors are implemented in Swift. + if (NSString *lazyPropertyBaseName = [self baseNameForLazySwiftProperty:propertyName]) { + if ([ignoredProperties containsObject:lazyPropertyBaseName]) { + // This property is the storage property for a ignored lazy Swift property. Just continue. + return; + } else { + @throw RLMException(@"Lazy managed property '%@' is not allowed on a Realm Swift object class. Either add the property to the ignored properties list or make it non-lazy.", lazyPropertyBaseName); + } + } + // The current property isn't a storage property for a lazy Swift property. + property = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:propertyName + indexed:[indexed containsObject:propertyName] + ivar:class_getInstanceVariable(objectClass, propertyName.UTF8String) + propertyType:RLMPropertyType(type.intValue)]; + [propArray addObject:property]; + } + else { + property.type = RLMPropertyType(type.intValue); + } + } + }]; + } + if (auto requiredProperties = [objectUtil requiredPropertiesForClass:objectClass]) { + for (RLMProperty *property in propArray) { + bool required = [requiredProperties containsObject:property.name]; + if (required && property.type == RLMPropertyTypeObject) { + @throw RLMException(@"Object properties cannot be made required, " + "but '+[%@ requiredProperties]' included '%@'", objectClass, property.name); + } + property.optional &= !required; + } + } + + for (RLMProperty *property in propArray) { + if (!property.optional && property.type == RLMPropertyTypeObject) { // remove if/when core supports required link columns + @throw RLMException(@"The `%@.%@` property must be marked as being optional.", [objectClass className], property.name); + } + } + + return propArray; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMObjectSchema *schema = [[RLMObjectSchema allocWithZone:zone] init]; + schema->_objectClass = _objectClass; + schema->_className = _className; + schema->_objectClass = _objectClass; + schema->_accessorClass = _objectClass; + schema->_unmanagedClass = _unmanagedClass; + schema->_isSwiftClass = _isSwiftClass; + + // call property setter to reset map and primary key + schema.properties = [[NSArray allocWithZone:zone] initWithArray:_properties copyItems:YES]; + schema.computedProperties = [[NSArray allocWithZone:zone] initWithArray:_computedProperties copyItems:YES]; + + return schema; +} + +- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema { + if (objectSchema.properties.count != _properties.count) { + return NO; + } + + if (![_properties isEqualToArray:objectSchema.properties]) { + return NO; + } + if (![_computedProperties isEqualToArray:objectSchema.computedProperties]) { + return NO; + } + + return YES; +} + +- (NSString *)description { + NSMutableString *propertiesString = [NSMutableString string]; + for (RLMProperty *property in self.properties) { + [propertiesString appendFormat:@"\t%@\n", [property.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + for (RLMProperty *property in self.computedProperties) { + [propertiesString appendFormat:@"\t%@\n", [property.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + return [NSString stringWithFormat:@"%@ {\n%@}", self.className, propertiesString]; +} + +- (realm::ObjectSchema)objectStoreCopy { + ObjectSchema objectSchema; + objectSchema.name = _className.UTF8String; + objectSchema.primary_key = _primaryKeyProperty ? _primaryKeyProperty.name.UTF8String : ""; + for (RLMProperty *prop in _properties) { + Property p = [prop objectStoreCopy]; + p.is_primary = (prop == _primaryKeyProperty); + objectSchema.persisted_properties.push_back(std::move(p)); + } + for (RLMProperty *prop in _computedProperties) { + objectSchema.computed_properties.push_back([prop objectStoreCopy]); + } + return objectSchema; +} + ++ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)objectSchema { + RLMObjectSchema *schema = [RLMObjectSchema new]; + schema.className = @(objectSchema.name.c_str()); + + // create array of RLMProperties + NSMutableArray *properties = [NSMutableArray arrayWithCapacity:objectSchema.persisted_properties.size()]; + for (const Property &prop : objectSchema.persisted_properties) { + RLMProperty *property = [RLMProperty propertyForObjectStoreProperty:prop]; + property.isPrimary = (prop.name == objectSchema.primary_key); + [properties addObject:property]; + } + schema.properties = properties; + + NSMutableArray *computedProperties = [NSMutableArray arrayWithCapacity:objectSchema.computed_properties.size()]; + for (const Property &prop : objectSchema.computed_properties) { + [computedProperties addObject:[RLMProperty propertyForObjectStoreProperty:prop]]; + } + schema.computedProperties = computedProperties; + + // get primary key from realm metadata + if (objectSchema.primary_key.length()) { + NSString *primaryKeyString = [NSString stringWithUTF8String:objectSchema.primary_key.c_str()]; + schema.primaryKeyProperty = schema[primaryKeyString]; + if (!schema.primaryKeyProperty) { + @throw RLMException(@"No property matching primary key '%@'", primaryKeyString); + } + } + + // for dynamic schema use vanilla RLMDynamicObject accessor classes + schema.objectClass = RLMObject.class; + schema.accessorClass = RLMDynamicObject.class; + schema.unmanagedClass = RLMObject.class; + + return schema; +} + +- (NSArray *)swiftGenericProperties { + if (_swiftGenericProperties) { + return _swiftGenericProperties; + } + + // This check isn't semantically required, but avoiding accessing the local + // static helps perf in the obj-c case + if (!_isSwiftClass) { + return _swiftGenericProperties = @[]; + } + + // Check if it's a swift class using the obj-c API + static Class s_swiftObjectClass = NSClassFromString(@"RealmSwiftObject"); + if (![_accessorClass isSubclassOfClass:s_swiftObjectClass]) { + return _swiftGenericProperties = @[]; + } + + NSMutableArray *genericProperties = [NSMutableArray new]; + for (RLMProperty *prop in _properties) { + if (prop->_swiftIvar) { + [genericProperties addObject:prop]; + } + } + // Currently all computed properties are Swift generics + [genericProperties addObjectsFromArray:_computedProperties]; + + return _swiftGenericProperties = genericProperties; +} + +@end diff --git a/Pods/Realm/Realm/RLMObjectStore.mm b/Pods/Realm/Realm/RLMObjectStore.mm new file mode 100644 index 0000000..4654100 --- /dev/null +++ b/Pods/Realm/Realm/RLMObjectStore.mm @@ -0,0 +1,550 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObjectStore.h" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObservation.hpp" +#import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMOptionalBase.h" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "results.hpp" +#import "shared_realm.hpp" + +#import + +using namespace realm; + +void RLMRealmCreateAccessors(RLMSchema *schema) { + for (RLMObjectSchema *objectSchema in schema.objectSchema) { + if (objectSchema.accessorClass != objectSchema.objectClass) { + continue; + } + + static unsigned long long count = 0; + NSString *prefix = [NSString stringWithFormat:@"RLMAccessor_%llu_", count++]; + objectSchema.accessorClass = RLMAccessorClassForObjectClass(objectSchema.objectClass, objectSchema, prefix); + } +} + +static inline void RLMVerifyRealmRead(__unsafe_unretained RLMRealm *const realm) { + if (!realm) { + @throw RLMException(@"Realm must not be nil"); + } + [realm verifyThread]; +} + +static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMRealm *const realm) { + RLMVerifyRealmRead(realm); + // if realm is not writable throw + if (!realm.inWriteTransaction) { + @throw RLMException(@"Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first."); + } +} + +void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const object) { + if (!object || !object->_row || !object->_objectSchema->_isSwiftClass) { + return; + } + if (![object isKindOfClass:object->_objectSchema.objectClass]) { + // It can be a different class if it's a dynamic object, and those don't + // require any init here (and would crash since they don't have the ivars) + return; + } + + for (RLMProperty *prop in object->_objectSchema.swiftGenericProperties) { + if (prop->_type == RLMPropertyTypeArray) { + RLMArray *array = [[RLMArrayLinkView alloc] initWithParent:object property:prop]; + [object_getIvar(object, prop.swiftIvar) set_rlmArray:array]; + } + else if (prop.type == RLMPropertyTypeLinkingObjects) { + id linkingObjects = object_getIvar(object, prop.swiftIvar); + [linkingObjects setObject:(id)[[RLMWeakObjectHandle alloc] initWithObject:object]]; + [linkingObjects setProperty:prop]; + } + else { + RLMOptionalBase *optional = object_getIvar(object, prop.swiftIvar); + optional.property = prop; + optional.object = object; + } + } +} + +static void validateValueForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const prop) { + switch (prop.type) { + case RLMPropertyTypeString: + case RLMPropertyTypeBool: + case RLMPropertyTypeDate: + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + case RLMPropertyTypeData: + if (!RLMIsObjectValidForProperty(obj, prop)) { + @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); + } + break; + case RLMPropertyTypeObject: + break; + case RLMPropertyTypeArray: { + if (obj != nil && obj != NSNull.null) { + if (![obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + @throw RLMException(@"Array property value (%@) is not enumerable.", obj); + } + } + break; + } + case RLMPropertyTypeAny: + case RLMPropertyTypeLinkingObjects: + @throw RLMException(@"Invalid value '%@' for property '%@'", obj, prop.name); + } +} + +static NSUInteger createRowForObject(RLMClassInfo const& info) { + try { + return info.table()->add_empty_row(); + } + catch (std::exception const& e) { + @throw RLMException(e); + } +} + +/* If a row exists with the specified primary key value, return its index. Otherwise, return `realm::not_found`. + * + * Precondition: `info` must refer to a class which has a primary key property + * Precondition: `primaryValue` is a validated property value that has been coerced to `nil` + */ +static NSUInteger getRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + + RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); + const NSUInteger primaryPropertyColumn = info.tableColumn(primaryProperty); + + switch (primaryProperty.type) { + case RLMPropertyTypeString: + return info.table()->find_first_string(primaryPropertyColumn, RLMStringDataWithNSString(primaryValue)); + + case RLMPropertyTypeInt: + if (primaryValue) { + return info.table()->find_first_int(primaryPropertyColumn, [primaryValue longLongValue]); + } else { + return info.table()->find_first_null(primaryPropertyColumn); + } + + default: + REALM_UNREACHABLE(); + } +} + +/* Create a row with the specified primary key value and return its index. + * + * Precondition: `info` must refer to a class which has a valid primary key property + * Precondition: a write transaction is in progress + * Precondition: no row already exists with the specified `primaryValue` for this model + */ +static NSUInteger createRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); + REALM_ASSERT_DEBUG(getRowForObjectWithPrimaryKey(info, primaryValue) == realm::not_found); + + RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); + const NSUInteger primaryColumnIndex = info.tableColumn(primaryProperty); + + // create row + const NSUInteger rowIndex = createRowForObject(info); + Row row = info.table()->get(rowIndex); + + // set value for primary key + validateValueForProperty(primaryValue, primaryProperty); + primaryValue = RLMCoerceToNil(primaryValue); + + try { + switch (primaryProperty.type) { + case RLMPropertyTypeString: + REALM_ASSERT_DEBUG(!primaryValue || [primaryValue isKindOfClass:NSString.class]); + row.set_string_unique(primaryColumnIndex, RLMStringDataWithNSString(primaryValue)); + break; + + case RLMPropertyTypeInt: + if (primaryValue) { + REALM_ASSERT_DEBUG([primaryValue isKindOfClass:NSNumber.class]); + row.set_int_unique(primaryColumnIndex, [primaryValue longLongValue]); + } else { + row.set_null(primaryColumnIndex); // FIXME: Use `set_null_unique` once Core supports it + } + break; + + default: + REALM_UNREACHABLE(); + } + } + catch (std::exception const& e) { + @throw RLMException(e); + } + return rowIndex; +} + +/* If a row exists with the specified primary key value, returns its index. Otherwise, creates a new row with the + * specified primary key value and returns its index. The out parameter `foundExisting` will be set to indicate + * whether or not a new row was created. + * + * Precondition: `info` must refer to a class which has a valid primary key property + * Precondition: a write transaction is in progress + */ +static NSUInteger createOrGetRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue, + bool* foundExisting = nullptr) { + REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); + REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); + + const NSUInteger existingRow = getRowForObjectWithPrimaryKey(info, primaryValue); + if (existingRow == realm::not_found) { + *foundExisting = false; + return createRowForObjectWithPrimaryKey(info, primaryValue); + } else { + *foundExisting = true; + return existingRow; + } +} + +/* If the class has a primary key, calls `valueForProperty` with that key and creates or gets the row with + * this primary key value. Otherwise if the class has no primary key, creates a new row. The out parameter + * `foundExisting` will be set to indicate whether or not a new row was created. + * + * Precondition: a write transaction is in progress + */ +template +static NSUInteger createOrGetRowForObject(RLMClassInfo const& info, F valueForProperty, + bool createOrUpdate, bool* foundExisting) { + // try to get existing row if this class has a primary key + if (RLMProperty *primaryProperty = info.propertyForPrimaryKey()) { + // get primary value + const id primaryValue = valueForProperty(primaryProperty); + + // search for existing object based on primary key type, creating a new row if one does not exist + NSUInteger rowIndex = createOrGetRowForObjectWithPrimaryKey(info, RLMCoerceToNil(primaryValue), foundExisting); + + // ensure that `createOrUpdate` is set if we found an existing row + if (*foundExisting && !createOrUpdate) { + @throw RLMException(@"Can't create object with existing primary key value '%@'.", primaryValue); + } + return rowIndex; + } + // if no existing, create row + else { + *foundExisting = false; + return createRowForObject(info); + } +} + +void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, + __unsafe_unretained RLMRealm *const realm, + bool createOrUpdate) { + RLMVerifyInWriteTransaction(realm); + + // verify that object is unmanaged + if (object.invalidated) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + if (object->_realm) { + if (object->_realm == realm) { + // no-op + return; + } + // for differing realms users must explicitly create the object in the second realm + @throw RLMException(@"Object is already managed by another Realm"); + } + if (object->_observationInfo && object->_observationInfo->hasObservers()) { + @throw RLMException(@"Cannot add an object with observers to a Realm"); + } + + // set the realm and schema + NSString *objectClassName = object->_objectSchema.className; + auto& info = realm->_info[objectClassName]; + object->_info = &info; + object->_objectSchema = info.rlmObjectSchema; + object->_realm = realm; + + // get or create row + bool foundExisting; + auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return [object valueForKey:p.name]; }; + object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; + + RLMCreationOptions creationOptions = RLMCreationOptionsPromoteUnmanaged; + if (createOrUpdate) { + creationOptions |= RLMCreationOptionsCreateOrUpdate; + } + + // populate all properties + for (RLMProperty *prop in info.rlmObjectSchema.properties) { + // get object from ivar using key value coding + id value = nil; + if (prop.swiftIvar) { + if (prop.type == RLMPropertyTypeArray) { + value = static_cast(object_getIvar(object, prop.swiftIvar))._rlmArray; + } + else { // optional + value = static_cast(object_getIvar(object, prop.swiftIvar)).underlyingValue; + } + } + else if ([object respondsToSelector:prop.getterSel]) { + value = [object valueForKey:prop.getterName]; + } + + if (!value && !prop.optional) { + @throw RLMException(@"No value or default value specified for property '%@' in '%@'", + prop.name, info.rlmObjectSchema.className); + } + + // set the ivars for object and array properties to nil as otherwise the + // accessors retain objects that are no longer accessible via the properties + // this is mainly an issue when the object graph being added has cycles, + // as it's not obvious that the user has to set the *ivars* to nil to + // avoid leaking memory + if (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray) { + if (!prop.swiftIvar) { + ((void(*)(id, SEL, id))objc_msgSend)(object, prop.setterSel, nil); + } + } + + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + // set in table with out validation + RLMDynamicSet(object, prop, RLMCoerceToNil(value), creationOptions); + } + + // set to proper accessor class + object_setClass(object, info.rlmObjectSchema.accessorClass); + + RLMInitializeSwiftAccessorGenerics(object); +} + +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id value, bool createOrUpdate = false) { + if (createOrUpdate && RLMIsObjectSubclass([value class])) { + RLMObjectBase *obj = value; + if ([obj->_objectSchema.className isEqualToString:className] && obj->_realm == realm) { + // This is a no-op if value is an RLMObject of the same type already backed by the target realm. + return value; + } + } + + // verify writable + RLMVerifyInWriteTransaction(realm); + + // create the object + auto& info = realm->_info[className]; + RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + + RLMCreationOptions creationOptions = createOrUpdate ? RLMCreationOptionsCreateOrUpdate : RLMCreationOptionsNone; + + // create row, and populate + if (NSArray *array = RLMDynamicCast(value)) { + // get or create our accessor + bool foundExisting; + NSArray *props = info.rlmObjectSchema.properties; + auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { + return array[[props indexOfObject:p]]; + }; + object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; + + // populate + for (NSUInteger i = 0; i < array.count; i++) { + RLMProperty *prop = props[i]; + + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + id val = array[i]; + validateValueForProperty(val, prop); + RLMDynamicSet(object, prop, RLMCoerceToNil(val), creationOptions); + } + } + else { + __block bool foundExisting = false; + __block NSDictionary *defaultValues = nil; + __block bool usedDefault = false; + auto getValue = ^(RLMProperty *prop) { + id propValue = RLMValidatedValueForProperty(value, prop.name, info.rlmObjectSchema.className); + usedDefault = !propValue && !foundExisting; + if (usedDefault) { + if (!defaultValues) { + defaultValues = RLMDefaultValuesForObjectSchema(info.rlmObjectSchema); + } + propValue = defaultValues[prop.name]; + if (!propValue && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray)) { + propValue = NSNull.null; + } + } + return propValue; + }; + // get or create our accessor + object->_row = (*info.table())[createOrGetRowForObject(info, getValue, createOrUpdate, &foundExisting)]; + + // populate + for (RLMProperty *prop in info.rlmObjectSchema.properties) { + // skip primary key when updating since it doesn't change + if (prop.isPrimary) + continue; + + if (id propValue = getValue(prop)) { + validateValueForProperty(propValue, prop); + // add SetDefault to creationoptions + RLMCreationOptions propertyCreationOptions = creationOptions; + if (usedDefault) { + propertyCreationOptions |= RLMCreationOptionsSetDefault; + } + RLMDynamicSet(object, prop, RLMCoerceToNil(propValue), propertyCreationOptions); + } + else if (!foundExisting && !prop.optional) { + @throw RLMException(@"Property '%@' of object of type '%@' cannot be nil.", prop.name, info.rlmObjectSchema.className); + } + } + } + + RLMInitializeSwiftAccessorGenerics(object); + return object; +} + +void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object, + __unsafe_unretained RLMRealm *const realm) { + if (realm != object->_realm) { + @throw RLMException(@"Can only delete an object from the Realm it belongs to."); + } + + RLMVerifyInWriteTransaction(object->_realm); + + // move last row to row we are deleting + if (object->_row.is_attached()) { + RLMTrackDeletions(realm, ^{ + object->_row.get_table()->move_last_over(object->_row.get_index()); + }); + } + + // set realm to nil + object->_realm = nil; +} + +void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) { + RLMVerifyInWriteTransaction(realm); + + // clear table for each object schema + for (auto& info : realm->_info) { + RLMClearTable(info.second); + } +} + +RLMResults *RLMGetObjects(RLMRealm *realm, NSString *objectClassName, NSPredicate *predicate) { + RLMVerifyRealmRead(realm); + + // create view from table and predicate + RLMClassInfo& info = realm->_info[objectClassName]; + if (!info.table()) { + // read-only realms may be missing tables since we can't add any + // missing ones on init + return [RLMResults resultsWithObjectInfo:info results:{}]; + } + + if (predicate) { + realm::Query query = RLMPredicateToQuery(predicate, info.rlmObjectSchema, realm.schema, realm.group); + return [RLMResults resultsWithObjectInfo:info + results:realm::Results(realm->_realm, std::move(query))]; + } + + return [RLMResults resultsWithObjectInfo:info + results:realm::Results(realm->_realm, *info.table())]; +} + +id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) { + RLMVerifyRealmRead(realm); + + RLMClassInfo& info = realm->_info[objectClassName]; + auto primaryProperty = info.objectSchema->primary_key_property(); + if (!primaryProperty) { + @throw RLMException(@"%@ does not have a primary key", objectClassName); + } + + auto table = info.table(); + if (!table) { + // read-only realms may be missing tables since we can't add any + // missing ones on init + return nil; + } + + key = RLMCoerceToNil(key); + if (!key && !primaryProperty->is_nullable) { + @throw RLMException(@"Invalid null value for non-nullable primary key."); + } + + size_t row = realm::not_found; + switch (primaryProperty->type) { + case PropertyType::String: { + NSString *string = RLMDynamicCast(key); + if (!key || string) { + row = table->find_first_string(primaryProperty->table_column, RLMStringDataWithNSString(string)); + } else { + @throw RLMException(@"Invalid value '%@' of type '%@' for string primary key.", key, [key class]); + } + break; + } + case PropertyType::Int: + if (NSNumber *number = RLMDynamicCast(key)) { + row = table->find_first_int(primaryProperty->table_column, number.longLongValue); + } else if (!key) { + row = table->find_first_null(primaryProperty->table_column); + } else { + @throw RLMException(@"Invalid value '%@' of type '%@' for int primary key.", key, [key class]); + } + break; + default: + REALM_UNREACHABLE(); + } + + if (row == realm::not_found) { + return nil; + } + + return RLMCreateObjectAccessor(realm, info, row); +} + +RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, + RLMClassInfo& info, + NSUInteger index) { + return RLMCreateObjectAccessor(realm, info, (*info.table())[index]); +} + +// Create accessor and register with realm +RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, + RLMClassInfo& info, + realm::RowExpr row) { + RLMObjectBase *accessor = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + accessor->_row = row; + RLMInitializeSwiftAccessorGenerics(accessor); + return accessor; +} diff --git a/Pods/Realm/Realm/RLMObservation.mm b/Pods/Realm/Realm/RLMObservation.mm new file mode 100644 index 0000000..e6538b6 --- /dev/null +++ b/Pods/Realm/Realm/RLMObservation.mm @@ -0,0 +1,500 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObservation.hpp" + +#import "RLMAccessor.h" +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" + +#import + +using namespace realm; + +namespace { + template + struct IteratorPair { + Iterator first; + Iterator second; + }; + template + Iterator begin(IteratorPair const& p) { + return p.first; + } + template + Iterator end(IteratorPair const& p) { + return p.second; + } + + template + auto reverse(Container const& c) { + return IteratorPair{c.rbegin(), c.rend()}; + } +} + +RLMObservationInfo::RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object) +: object(object) +, objectSchema(&objectSchema) +{ + setRow(*objectSchema.table(), row); +} + +RLMObservationInfo::RLMObservationInfo(id object) +: object(object) +{ +} + +RLMObservationInfo::~RLMObservationInfo() { + if (prev) { + // Not the head of the linked list, so just detach from the list + REALM_ASSERT_DEBUG(prev->next == this); + prev->next = next; + if (next) { + REALM_ASSERT_DEBUG(next->prev == this); + next->prev = prev; + } + } + else if (objectSchema) { + // The head of the list, so remove self from the object schema's array + // of observation info, either replacing self with the next info or + // removing entirely if there is no next + auto end = objectSchema->observedObjects.end(); + auto it = find(objectSchema->observedObjects.begin(), end, this); + if (it != end) { + if (next) { + *it = next; + next->prev = nullptr; + } + else { + iter_swap(it, std::prev(end)); + objectSchema->observedObjects.pop_back(); + } + } + } + // Otherwise the observed object was unmanaged, so nothing to do + +#ifdef DEBUG + // ensure that incorrect cleanup fails noisily + object = (__bridge id)(void *)-1; + prev = (RLMObservationInfo *)-1; + next = (RLMObservationInfo *)-1; +#endif +} + +NSString *RLMObservationInfo::columnName(size_t col) const noexcept { + return objectSchema->propertyForTableColumn(col).name; +} + +void RLMObservationInfo::willChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const { + if (indexes) { + forEach([=](__unsafe_unretained auto o) { + [o willChange:kind valuesAtIndexes:indexes forKey:key]; + }); + } + else { + forEach([=](__unsafe_unretained auto o) { + [o willChangeValueForKey:key]; + }); + } +} + +void RLMObservationInfo::didChange(NSString *key, NSKeyValueChange kind, NSIndexSet *indexes) const { + if (indexes) { + forEach([=](__unsafe_unretained auto o) { + [o didChange:kind valuesAtIndexes:indexes forKey:key]; + }); + } + else { + forEach([=](__unsafe_unretained auto o) { + [o didChangeValueForKey:key]; + }); + } +} + +void RLMObservationInfo::prepareForInvalidation() { + REALM_ASSERT_DEBUG(objectSchema); + REALM_ASSERT_DEBUG(!prev); + for (auto info = this; info; info = info->next) + info->invalidated = true; +} + +void RLMObservationInfo::setRow(realm::Table &table, size_t newRow) { + REALM_ASSERT_DEBUG(!row); + REALM_ASSERT_DEBUG(objectSchema); + row = table[newRow]; + for (auto info : objectSchema->observedObjects) { + if (info->row && info->row.get_index() == row.get_index()) { + prev = info; + next = info->next; + if (next) + next->prev = this; + info->next = this; + return; + } + } + objectSchema->observedObjects.push_back(this); +} + +void RLMObservationInfo::recordObserver(realm::Row& objectRow, RLMClassInfo *objectInfo, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained NSString *const keyPath) { + ++observerCount; + if (row) { + return; + } + + // add ourselves to the list of observed objects if this is the first time + // an observer is being added to a managed object + if (objectRow) { + this->objectSchema = objectInfo; + setRow(*objectRow.get_table(), objectRow.get_index()); + return; + } + + // Arrays need a reference to their containing object to avoid having to + // go through the awful proxy object from mutableArrayValueForKey. + // For managed objects we do this when the object is added or created + // (and have to to support notifications from modifying an object which + // was never observed), but for Swift classes (both RealmSwift and + // RLMObject) we can't do it then because we don't know what the parent + // object is. + + NSUInteger sep = [keyPath rangeOfString:@"."].location; + NSString *key = sep == NSNotFound ? keyPath : [keyPath substringToIndex:sep]; + RLMProperty *prop = objectSchema[key]; + if (prop && prop.type == RLMPropertyTypeArray) { + id value = valueForKey(key); + RLMArray *array = [value isKindOfClass:[RLMListBase class]] ? [value _rlmArray] : value; + array->_key = key; + array->_parentObject = object; + } + else if (auto swiftIvar = prop.swiftIvar) { + if (auto optional = RLMDynamicCast(object_getIvar(object, swiftIvar))) { + optional.property = prop; + optional.object = object; + } + } +} + +void RLMObservationInfo::removeObserver() { + --observerCount; +} + +id RLMObservationInfo::valueForKey(NSString *key) { + if (invalidated) { + if ([key isEqualToString:RLMInvalidatedKey]) { + return @YES; + } + return cachedObjects[key]; + } + + if (key != lastKey) { + lastKey = key; + lastProp = objectSchema ? objectSchema->rlmObjectSchema[key] : nil; + } + + static auto superValueForKey = reinterpret_cast([NSObject methodForSelector:@selector(valueForKey:)]); + if (!lastProp) { + // Not a managed property, so use NSObject's implementation of valueForKey: + return RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key)); + } + + auto getSuper = [&] { + return row ? RLMDynamicGet(object, lastProp) : RLMCoerceToNil(superValueForKey(object, @selector(valueForKey:), key)); + }; + + // We need to return the same object each time for observing over keypaths + // to work, so we store a cache of them here. We can't just cache them on + // the object as that leads to retain cycles. + if (lastProp.type == RLMPropertyTypeArray) { + RLMArray *value = cachedObjects[key]; + if (!value) { + value = getSuper(); + if (!cachedObjects) { + cachedObjects = [NSMutableDictionary new]; + } + cachedObjects[key] = value; + } + return value; + } + + if (lastProp.type == RLMPropertyTypeObject) { + size_t col = row.get_column_index(lastProp.name.UTF8String); + if (row.is_null_link(col)) { + [cachedObjects removeObjectForKey:key]; + return nil; + } + + RLMObjectBase *value = cachedObjects[key]; + if (value && value->_row.get_index() == row.get_link(col)) { + return value; + } + value = getSuper(); + if (!cachedObjects) { + cachedObjects = [NSMutableDictionary new]; + } + cachedObjects[key] = value; + return value; + } + + return getSuper(); +} + +RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, + RLMClassInfo& objectSchema) { + if (info) { + return info; + } + + for (RLMObservationInfo *info : objectSchema.observedObjects) { + if (info->isForRow(row)) { + return info; + } + } + + return nullptr; +} + +void RLMClearTable(RLMClassInfo &objectSchema) { + for (auto info : objectSchema.observedObjects) { + info->willChange(RLMInvalidatedKey); + } + + RLMTrackDeletions(objectSchema.realm, ^{ + objectSchema.table()->clear(); + + for (auto info : objectSchema.observedObjects) { + info->prepareForInvalidation(); + } + }); + + for (auto info : reverse(objectSchema.observedObjects)) { + info->didChange(RLMInvalidatedKey); + } + + objectSchema.observedObjects.clear(); +} + +void RLMTrackDeletions(__unsafe_unretained RLMRealm *const realm, dispatch_block_t block) { + std::vector *> observers; + + // Build up an array of observation info arrays which is indexed by table + // index (the object schemata may be in an entirely different order) + for (auto& info : realm->_info) { + if (info.second.observedObjects.empty()) { + continue; + } + size_t ndx = info.second.table()->get_index_in_group(); + if (ndx >= observers.size()) { + observers.resize(std::max(observers.size() * 2, ndx + 1)); + } + observers[ndx] = &info.second.observedObjects; + } + + // No need for change tracking if no objects are observed + if (observers.empty()) { + block(); + return; + } + + struct change { + RLMObservationInfo *info; + __unsafe_unretained NSString *property; + NSMutableIndexSet *indexes; + }; + + std::vector changes; + std::vector invalidated; + + // This callback is called by core with a list of row deletions and + // resulting link nullifications immediately before things are deleted and nullified + realm.group.set_cascade_notification_handler([&](realm::Group::CascadeNotification const& cs) { + for (auto const& link : cs.links) { + size_t table_ndx = link.origin_table->get_index_in_group(); + if (table_ndx >= observers.size() || !observers[table_ndx]) { + // The modified table has no observers + continue; + } + + for (auto observer : *observers[table_ndx]) { + if (!observer->isForRow(link.origin_row_ndx)) { + continue; + } + + NSString *name = observer->columnName(link.origin_col_ndx); + if (observer->getRow().get_table()->get_column_type(link.origin_col_ndx) != type_LinkList) { + changes.push_back({observer, name}); + continue; + } + + auto c = find_if(begin(changes), end(changes), [&](auto const& c) { + return c.info == observer && c.property == name; + }); + if (c == end(changes)) { + changes.push_back({observer, name, [NSMutableIndexSet new]}); + c = prev(end(changes)); + } + + // We know what row index is being removed from the LinkView, + // but what we actually want is the indexes in the LinkView that + // are going away + auto linkview = observer->getRow().get_linklist(link.origin_col_ndx); + size_t start = 0, index; + while ((index = linkview->find(link.old_target_row_ndx, start)) != realm::not_found) { + [c->indexes addIndex:index]; + start = index + 1; + } + } + } + + for (auto const& row : cs.rows) { + if (row.table_ndx >= observers.size() || !observers[row.table_ndx]) { + // The modified table has no observers + continue; + } + + for (auto observer : *observers[row.table_ndx]) { + if (observer->isForRow(row.row_ndx)) { + invalidated.push_back(observer); + break; + } + } + } + + // The relative order of these loops is very important + for (auto info : invalidated) { + info->willChange(RLMInvalidatedKey); + } + for (auto const& change : changes) { + change.info->willChange(change.property, NSKeyValueChangeRemoval, change.indexes); + } + for (auto info : invalidated) { + info->prepareForInvalidation(); + } + }); + + try { + block(); + } + catch (...) { + realm.group.set_cascade_notification_handler(nullptr); + throw; + } + + for (auto const& change : reverse(changes)) { + change.info->didChange(change.property, NSKeyValueChangeRemoval, change.indexes); + } + for (auto info : reverse(invalidated)) { + info->didChange(RLMInvalidatedKey); + } + + realm.group.set_cascade_notification_handler(nullptr); +} + +namespace { +template +void forEach(realm::BindingContext::ObserverState const& state, Func&& func) { + for (size_t i = 0, size = state.changes.size(); i < size; ++i) { + if (state.changes[i].kind != realm::BindingContext::ColumnInfo::Kind::None) { + func(i, state.changes[i], static_cast(state.info)); + } + } +} +} + +std::vector RLMGetObservedRows(RLMSchemaInfo const& schema) { + std::vector observers; + for (auto& table : schema) { + for (auto info : table.second.observedObjects) { + auto const& row = info->getRow(); + if (!row.is_attached()) + continue; + observers.push_back({ + row.get_table()->get_index_in_group(), + row.get_index(), + info}); + } + } + sort(begin(observers), end(observers)); + return observers; +} + +static NSKeyValueChange convert(realm::BindingContext::ColumnInfo::Kind kind) { + switch (kind) { + case realm::BindingContext::ColumnInfo::Kind::None: + case realm::BindingContext::ColumnInfo::Kind::SetAll: + return NSKeyValueChangeSetting; + case realm::BindingContext::ColumnInfo::Kind::Set: + return NSKeyValueChangeReplacement; + case realm::BindingContext::ColumnInfo::Kind::Insert: + return NSKeyValueChangeInsertion; + case realm::BindingContext::ColumnInfo::Kind::Remove: + return NSKeyValueChangeRemoval; + } +} + +static NSIndexSet *convert(realm::IndexSet const& in, NSMutableIndexSet *out) { + if (in.empty()) { + return nil; + } + + [out removeAllIndexes]; + for (auto range : in) { + [out addIndexesInRange:{range.first, range.second - range.first}]; + } + return out; +} + +void RLMWillChange(std::vector const& observed, + std::vector const& invalidated) { + for (auto info : invalidated) { + static_cast(info)->willChange(RLMInvalidatedKey); + } + if (!observed.empty()) { + NSMutableIndexSet *indexes = [NSMutableIndexSet new]; + for (auto const& o : observed) { + forEach(o, [&](size_t, auto const& change, RLMObservationInfo *info) { + info->willChange(info->columnName(change.initial_column_index), + convert(change.kind), convert(change.indices, indexes)); + }); + } + } + for (auto info : invalidated) { + static_cast(info)->prepareForInvalidation(); + } +} + +void RLMDidChange(std::vector const& observed, + std::vector const& invalidated) { + if (!observed.empty()) { + // Loop in reverse order to avoid O(N^2) behavior in Foundation + NSMutableIndexSet *indexes = [NSMutableIndexSet new]; + for (auto const& o : reverse(observed)) { + forEach(o, [&](size_t i, auto const& change, RLMObservationInfo *info) { + info->didChange(info->columnName(i), convert(change.kind), convert(change.indices, indexes)); + }); + } + } + for (auto const& info : reverse(invalidated)) { + static_cast(info)->didChange(RLMInvalidatedKey); + } +} diff --git a/Pods/Realm/Realm/RLMOptionalBase.mm b/Pods/Realm/Realm/RLMOptionalBase.mm new file mode 100644 index 0000000..8aad1c8 --- /dev/null +++ b/Pods/Realm/Realm/RLMOptionalBase.mm @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMAccessor.h" +#import "RLMOptionalBase.h" +#import "RLMObject_Private.h" +#import "RLMObjectStore.h" +#import "RLMProperty_Private.h" +#import "RLMUtil.hpp" + +#import + +@interface RLMOptionalBase () +@property (nonatomic) id unmanagedValue; +@end + +@implementation RLMOptionalBase + +- (instancetype)init { + return self; +} + +- (id)underlyingValue { + if ((_object && _object->_realm) || _object.isInvalidated) { + return RLMDynamicGet(_object, _property); + } + else { + return _unmanagedValue; + } +} + +- (void)setUnderlyingValue:(id)underlyingValue { + if ((_object && _object->_realm) || _object.isInvalidated) { + if (_property.isPrimary) { + @throw RLMException(@"Primary key can't be changed after an object is inserted."); + } + RLMDynamicSet(_object, _property, underlyingValue, RLMCreationOptionsNone); + } + else { + NSString *propertyName = _property.name; + [_object willChangeValueForKey:propertyName]; + _unmanagedValue = underlyingValue; + [_object didChangeValueForKey:propertyName]; + } +} + +- (BOOL)isKindOfClass:(Class)aClass { + return [self.underlyingValue isKindOfClass:aClass] || RLMIsKindOfClass(object_getClass(self), aClass); +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { + return [self.underlyingValue methodSignatureForSelector:sel]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + [invocation invokeWithTarget:self.underlyingValue]; +} + +- (id)forwardingTargetForSelector:(__unused SEL)sel { + return self.underlyingValue; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + if (id val = self.underlyingValue) { + return [val respondsToSelector:aSelector]; + } + return NO; +} + +- (void)doesNotRecognizeSelector:(SEL)aSelector { + [self.underlyingValue doesNotRecognizeSelector:aSelector]; +} + +@end diff --git a/Pods/Realm/Realm/RLMPredicateUtil.mm b/Pods/Realm/Realm/RLMPredicateUtil.mm new file mode 100644 index 0000000..d2f722b --- /dev/null +++ b/Pods/Realm/Realm/RLMPredicateUtil.mm @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "RLMPredicateUtil.hpp" + +// NSConditionalExpressionType is new in OS X 10.11 and iOS 9.0 +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CONDITIONAL_EXPRESSION_DECLARED (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CONDITIONAL_EXPRESSION_DECLARED (__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) +#else +#define CONDITIONAL_EXPRESSION_DECLARED 0 +#endif + +#if !CONDITIONAL_EXPRESSION_DECLARED + +#define NSConditionalExpressionType 20 + +@interface NSExpression (NewIn1011And90) ++ (NSExpression *)expressionForConditional:(NSPredicate *)predicate trueExpression:(NSExpression *)trueExpression falseExpression:(NSExpression *)falseExpression; +- (NSExpression *)trueExpression; +- (NSExpression *)falseExpression; +@end + +#endif + +namespace { + +struct PredicateExpressionTransformer { + PredicateExpressionTransformer(ExpressionVisitor visitor) : m_visitor(visitor) { } + + NSExpression *visit(NSExpression *expression) const; + NSPredicate *visit(NSPredicate *predicate) const; + + ExpressionVisitor m_visitor; +}; + +NSExpression *PredicateExpressionTransformer::visit(NSExpression *expression) const { + expression = m_visitor(expression); + + switch (expression.expressionType) { + case NSFunctionExpressionType: { + NSMutableArray *arguments = [NSMutableArray array]; + for (NSExpression *argument in expression.arguments) { + [arguments addObject:visit(argument)]; + } + if (expression.operand) { + return [NSExpression expressionForFunction:visit(expression.operand) selectorName:expression.function arguments:arguments]; + } else { + return [NSExpression expressionForFunction:expression.function arguments:arguments]; + } + } + + case NSUnionSetExpressionType: + return [NSExpression expressionForUnionSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + case NSIntersectSetExpressionType: + return [NSExpression expressionForIntersectSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + case NSMinusSetExpressionType: + return [NSExpression expressionForMinusSet:visit(expression.leftExpression) with:visit(expression.rightExpression)]; + + case NSSubqueryExpressionType: + return [NSExpression expressionForSubquery:visit(expression.operand) usingIteratorVariable:expression.variable predicate:visit(expression.predicate)]; + + case NSAggregateExpressionType: { + NSMutableArray *subexpressions = [NSMutableArray array]; + for (NSExpression *subexpression in expression.collection) { + [subexpressions addObject:visit(subexpression)]; + } + return [NSExpression expressionForAggregate:subexpressions]; + } + + case NSConditionalExpressionType: + return [NSExpression expressionForConditional:visit(expression.predicate) trueExpression:visit(expression.trueExpression) falseExpression:visit(expression.falseExpression)]; + + default: + // The remaining expression types do not contain nested expressions or predicates. + return expression; + } +} + +NSPredicate *PredicateExpressionTransformer::visit(NSPredicate *predicate) const { + if ([predicate isKindOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *compoundPredicate = (NSCompoundPredicate *)predicate; + NSMutableArray *subpredicates = [NSMutableArray array]; + for (NSPredicate *subpredicate in compoundPredicate.subpredicates) { + [subpredicates addObject:visit(subpredicate)]; + } + return [[NSCompoundPredicate alloc] initWithType:compoundPredicate.compoundPredicateType subpredicates:subpredicates]; + } + if ([predicate isKindOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *comparisonPredicate = (NSComparisonPredicate *)predicate; + NSExpression *leftExpression = visit(comparisonPredicate.leftExpression); + NSExpression *rightExpression = visit(comparisonPredicate.rightExpression); + return [NSComparisonPredicate predicateWithLeftExpression:leftExpression rightExpression:rightExpression modifier:comparisonPredicate.comparisonPredicateModifier type:comparisonPredicate.predicateOperatorType options:comparisonPredicate.options]; + } + return predicate; +} + +} // anonymous namespace + +NSPredicate *transformPredicate(NSPredicate *predicate, ExpressionVisitor visitor) { + PredicateExpressionTransformer transformer(visitor); + return transformer.visit(predicate); +} diff --git a/Pods/Realm/Realm/RLMProperty.mm b/Pods/Realm/Realm/RLMProperty.mm new file mode 100644 index 0000000..2ce75f4 --- /dev/null +++ b/Pods/Realm/Realm/RLMProperty.mm @@ -0,0 +1,569 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMProperty_Private.hpp" + +#import "RLMArray.h" +#import "RLMListBase.h" +#import "RLMObject.h" +#import "RLMObject_Private.h" +#import "RLMOptionalBase.h" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType) { + return propertyType != RLMPropertyTypeArray && propertyType != RLMPropertyTypeLinkingObjects; +} + +BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType) { + return propertyType == RLMPropertyTypeLinkingObjects; +} + +static bool rawTypeIsComputedProperty(NSString *rawType) { + if ([rawType isEqualToString:@"@\"RLMLinkingObjects\""] || [rawType hasPrefix:@"@\"RLMLinkingObjects<"]) { + return true; + } + + return false; +} + +@implementation RLMProperty + ++ (instancetype)propertyForObjectStoreProperty:(const realm::Property &)prop { + return [[RLMProperty alloc] initWithName:@(prop.name.c_str()) + type:(RLMPropertyType)prop.type + objectClassName:prop.object_type.length() ? @(prop.object_type.c_str()) : nil + linkOriginPropertyName:prop.link_origin_property_name.length() ? @(prop.link_origin_property_name.c_str()) : nil + indexed:prop.is_indexed + optional:prop.is_nullable]; +} + +- (instancetype)initWithName:(NSString *)name + type:(RLMPropertyType)type + objectClassName:(NSString *)objectClassName + linkOriginPropertyName:(NSString *)linkOriginPropertyName + indexed:(BOOL)indexed + optional:(BOOL)optional { + self = [super init]; + if (self) { + _name = name; + _type = type; + _objectClassName = objectClassName; + _linkOriginPropertyName = linkOriginPropertyName; + _indexed = indexed; + _optional = optional; + [self setObjcCodeFromType]; + [self updateAccessors]; + } + + return self; +} + +- (void)setName:(NSString *)name { + _name = name; + [self updateAccessors]; +} + +- (void)updateAccessors { + // populate getter/setter names if generic + if (!_getterName) { + _getterName = _name; + } + if (!_setterName) { + // Objective-C setters only capitalize the first letter of the property name if it falls between 'a' and 'z' + int asciiCode = [_name characterAtIndex:0]; + BOOL shouldUppercase = asciiCode >= 'a' && asciiCode <= 'z'; + NSString *firstChar = [_name substringToIndex:1]; + firstChar = shouldUppercase ? firstChar.uppercaseString : firstChar; + _setterName = [NSString stringWithFormat:@"set%@%@:", firstChar, [_name substringFromIndex:1]]; + } + + _getterSel = NSSelectorFromString(_getterName); + _setterSel = NSSelectorFromString(_setterName); +} + +-(void)setObjcCodeFromType { + if (_optional) { + _objcType = '@'; + return; + } + switch (_type) { + case RLMPropertyTypeInt: + _objcType = 'q'; + break; + case RLMPropertyTypeBool: + _objcType = 'c'; + break; + case RLMPropertyTypeDouble: + _objcType = 'd'; + break; + case RLMPropertyTypeFloat: + _objcType = 'f'; + break; + case RLMPropertyTypeAny: + case RLMPropertyTypeArray: + case RLMPropertyTypeData: + case RLMPropertyTypeDate: + case RLMPropertyTypeObject: + case RLMPropertyTypeString: + case RLMPropertyTypeLinkingObjects: + _objcType = '@'; + break; + } +} + +// determine RLMPropertyType from objc code - returns true if valid type was found/set +- (BOOL)setTypeFromRawType { + const char *code = _objcRawType.UTF8String; + _objcType = *code; // first char of type attr + + // map to RLMPropertyType + switch (self.objcType) { + case 's': // short + case 'i': // int + case 'l': // long + case 'q': // long long + _type = RLMPropertyTypeInt; + return YES; + case 'f': + _type = RLMPropertyTypeFloat; + return YES; + case 'd': + _type = RLMPropertyTypeDouble; + return YES; + case 'c': // BOOL is stored as char - since rlm has no char type this is ok + case 'B': + _type = RLMPropertyTypeBool; + return YES; + case '@': { + _optional = true; + static const char arrayPrefix[] = "@\"RLMArray<"; + static const int arrayPrefixLen = sizeof(arrayPrefix) - 1; + + static const char numberPrefix[] = "@\"NSNumber<"; + static const int numberPrefixLen = sizeof(numberPrefix) - 1; + + static const char linkingObjectsPrefix[] = "@\"RLMLinkingObjects"; + static const int linkingObjectsPrefixLen = sizeof(linkingObjectsPrefix) - 1; + + if (strcmp(code, "@\"NSString\"") == 0) { + _type = RLMPropertyTypeString; + } + else if (strcmp(code, "@\"NSDate\"") == 0) { + _type = RLMPropertyTypeDate; + } + else if (strcmp(code, "@\"NSData\"") == 0) { + _type = RLMPropertyTypeData; + } + else if (strncmp(code, arrayPrefix, arrayPrefixLen) == 0) { + _optional = false; + // get object class from type string - @"RLMArray" + _type = RLMPropertyTypeArray; + _objectClassName = [[NSString alloc] initWithBytes:code + arrayPrefixLen + length:strlen(code + arrayPrefixLen) - 2 // drop trailing >" + encoding:NSUTF8StringEncoding]; + + Class cls = [RLMSchema classForString:_objectClassName]; + if (!cls) { + @throw RLMException(@"Property '%@' is of type 'RLMArray<%@>' which is not a supported RLMArray object type. " + @"RLMArrays can only contain instances of RLMObject subclasses. " + @"See https://realm.io/docs/objc/latest/#to-many for more information.", _name, _objectClassName); + } + } + else if (strncmp(code, numberPrefix, numberPrefixLen) == 0) { + // get number type from type string - @"NSNumber" + NSString *numberType = [[NSString alloc] initWithBytes:code + numberPrefixLen + length:strlen(code + numberPrefixLen) - 2 // drop trailing >" + encoding:NSUTF8StringEncoding]; + + if ([numberType isEqualToString:@"RLMInt"]) { + _type = RLMPropertyTypeInt; + } + else if ([numberType isEqualToString:@"RLMFloat"]) { + _type = RLMPropertyTypeFloat; + } + else if ([numberType isEqualToString:@"RLMDouble"]) { + _type = RLMPropertyTypeDouble; + } + else if ([numberType isEqualToString:@"RLMBool"]) { + _type = RLMPropertyTypeBool; + } + else { + @throw RLMException(@"Property '%@' is of type 'NSNumber<%@>' which is not a supported NSNumber object type. " + @"NSNumbers can only be RLMInt, RLMFloat, RLMDouble, and RLMBool at the moment. " + @"See https://realm.io/docs/objc/latest for more information.", _name, numberType); + } + } + else if (strncmp(code, linkingObjectsPrefix, linkingObjectsPrefixLen) == 0 && + (code[linkingObjectsPrefixLen] == '"' || code[linkingObjectsPrefixLen] == '<')) { + _type = RLMPropertyTypeLinkingObjects; + _optional = false; + + if (!_objectClassName || !_linkOriginPropertyName) { + @throw RLMException(@"Property '%@' is of type RLMLinkingObjects but +linkingObjectsProperties did not specify the class " + "or property that is the origin of the link.", _name); + } + + // If the property was declared with a protocol indicating the contained type, validate that it matches + // the class from the dictionary returned by +linkingObjectsProperties. + if (code[linkingObjectsPrefixLen] == '<') { + NSString *classNameFromProtocol = [[NSString alloc] initWithBytes:code + linkingObjectsPrefixLen + 1 + length:strlen(code + linkingObjectsPrefixLen) - 3 // drop trailing >" + encoding:NSUTF8StringEncoding]; + if (![_objectClassName isEqualToString:classNameFromProtocol]) { + @throw RLMException(@"Property '%@' was declared with type RLMLinkingObjects<%@>, but a conflicting " + "class name of '%@' was returned by +linkingObjectsProperties.", _name, + classNameFromProtocol, _objectClassName); + } + } + } + else if (strcmp(code, "@\"NSNumber\"") == 0) { + @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: NSNumber.", _name); + } + else if (strcmp(code, "@\"RLMArray\"") == 0) { + @throw RLMException(@"Property '%@' requires a protocol defining the contained type - example: RLMArray.", _name); + } + else { + NSString *className; + Class cls = nil; + if (code[1] == '\0') { + className = @"id"; + } + else { + // for objects strip the quotes and @ + className = [_objcRawType substringWithRange:NSMakeRange(2, _objcRawType.length-3)]; + cls = [RLMSchema classForString:className]; + } + + if (!cls) { + @throw RLMException(@"Property '%@' is declared as '%@', which is not a supported RLMObject property type. " + @"All properties must be primitives, NSString, NSDate, NSData, NSNumber, RLMArray, RLMLinkingObjects, or subclasses of RLMObject. " + @"See https://realm.io/docs/objc/latest/api/Classes/RLMObject.html for more information.", _name, className); + } + + _type = RLMPropertyTypeObject; + _optional = true; + _objectClassName = [cls className] ?: className; + } + return YES; + } + default: + return NO; + } +} + +- (bool)parseObjcProperty:(objc_property_t)property { + unsigned int count; + objc_property_attribute_t *attrs = property_copyAttributeList(property, &count); + + bool isReadOnly = false; + for (size_t i = 0; i < count; ++i) { + switch (*attrs[i].name) { + case 'T': + _objcRawType = @(attrs[i].value); + break; + case 'R': + isReadOnly = true; + break; + case 'N': + // nonatomic + break; + case 'D': + // dynamic + break; + case 'G': + _getterName = @(attrs[i].value); + break; + case 'S': + _setterName = @(attrs[i].value); + break; + default: + break; + } + } + free(attrs); + + return isReadOnly; +} + +- (instancetype)initSwiftPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property + instance:(RLMObject *)obj { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _indexed = indexed; + + if (linkPropertyDescriptor) { + _objectClassName = [linkPropertyDescriptor.objectClass className]; + _linkOriginPropertyName = linkPropertyDescriptor.propertyName; + } + + if ([self parseObjcProperty:property]) { + return nil; + } + + id propertyValue = [obj valueForKey:_name]; + + // FIXME: temporarily workaround added since Objective-C generics used in Swift show up as `@` + // * broken starting in Swift 3.0 Xcode 8 b1 + // * tested to still be broken in Swift 3.0 Xcode 8 b6 + // * if the Realm Objective-C Swift tests pass with this removed, it's been fixed + // * once it has been fixed, remove this entire conditional block (contents included) entirely + // * Bug Report: SR-2031 https://bugs.swift.org/browse/SR-2031 + if ([_objcRawType isEqualToString:@"@"]) { + if (propertyValue) { + _objcRawType = [NSString stringWithFormat:@"@\"%@\"", [propertyValue class]]; + } else if (linkPropertyDescriptor) { + // we're going to naively assume that the user used the correct type since we can't check it + _objcRawType = @"@\"RLMLinkingObjects\""; + } + } + + // convert array types to objc variant + if ([_objcRawType isEqualToString:@"@\"RLMArray\""]) { + _objcRawType = [NSString stringWithFormat:@"@\"RLMArray<%@>\"", [propertyValue objectClassName]]; + } + else if ([_objcRawType isEqualToString:@"@\"NSNumber\""]) { + const char *numberType = [propertyValue objCType]; + if (!numberType) { + @throw RLMException(@"Can't persist NSNumber without default value: use a Swift-native number type or provide a default value."); + } + switch (*numberType) { + case 'i': + case 'l': + case 'q': + _objcRawType = @"@\"NSNumber\""; + break; + case 'f': + _objcRawType = @"@\"NSNumber\""; + break; + case 'd': + _objcRawType = @"@\"NSNumber\""; + break; + case 'B': + case 'c': + _objcRawType = @"@\"NSNumber\""; + break; + default: + @throw RLMException(@"Can't persist NSNumber of type '%s': only integers, floats, doubles, and bools are currently supported.", numberType); + } + } + + auto throwForPropertyName = ^(NSString *propertyName){ + @throw RLMException(@"Can't persist property '%@' with incompatible type. " + "Add to Object.ignoredProperties() class method to ignore.", + propertyName); + }; + + if (![self setTypeFromRawType]) { + throwForPropertyName(self.name); + } + + if (_objcType == 'c') { + // Check if it's a BOOL or Int8 by trying to set it to 2 and seeing if + // it actually sets it to 1. + [obj setValue:@2 forKey:name]; + NSNumber *value = [obj valueForKey:name]; + _type = value.intValue == 2 ? RLMPropertyTypeInt : RLMPropertyTypeBool; + } + + // update getter/setter names + [self updateAccessors]; + + return self; +} + +- (instancetype)initWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property +{ + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _indexed = indexed; + + if (linkPropertyDescriptor) { + _objectClassName = [linkPropertyDescriptor.objectClass className]; + _linkOriginPropertyName = linkPropertyDescriptor.propertyName; + } + + bool isReadOnly = [self parseObjcProperty:property]; + bool isComputedProperty = rawTypeIsComputedProperty(_objcRawType); + if (isReadOnly && !isComputedProperty) { + return nil; + } + + if (![self setTypeFromRawType]) { + @throw RLMException(@"Can't persist property '%@' with incompatible type. " + "Add to ignoredPropertyNames: method to ignore.", self.name); + } + + if (!isReadOnly && isComputedProperty) { + @throw RLMException(@"Property '%@' must be declared as readonly as %@ properties cannot be written to.", + self.name, RLMTypeToString(_type)); + } + + // update getter/setter names + [self updateAccessors]; + + return self; +} + +- (instancetype)initSwiftListPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(NSString *)objectClassName { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = RLMPropertyTypeArray; + _objectClassName = objectClassName; + _objcType = 't'; + _swiftIvar = ivar; + + // no obj-c property for generic lists, and thus no getter/setter names + + return self; +} + +- (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + ivar:(Ivar)ivar + propertyType:(RLMPropertyType)propertyType { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = propertyType; + _indexed = indexed; + _objcType = '@'; + _swiftIvar = ivar; + _optional = true; + + // no obj-c property for generic optionals, and thus no getter/setter names + + return self; +} + +- (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(NSString *)objectClassName + linkOriginPropertyName:(NSString *)linkOriginPropertyName { + self = [super init]; + if (!self) { + return nil; + } + + _name = name; + _type = RLMPropertyTypeLinkingObjects; + _objectClassName = objectClassName; + _linkOriginPropertyName = linkOriginPropertyName; + _objcType = '@'; + _swiftIvar = ivar; + + // no obj-c property for generic linking objects properties, and thus no getter/setter names + + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMProperty *prop = [[RLMProperty allocWithZone:zone] init]; + prop->_name = _name; + prop->_type = _type; + prop->_objcType = _objcType; + prop->_objectClassName = _objectClassName; + prop->_indexed = _indexed; + prop->_getterName = _getterName; + prop->_setterName = _setterName; + prop->_getterSel = _getterSel; + prop->_setterSel = _setterSel; + prop->_isPrimary = _isPrimary; + prop->_swiftIvar = _swiftIvar; + prop->_optional = _optional; + prop->_linkOriginPropertyName = _linkOriginPropertyName; + + return prop; +} + +- (RLMProperty *)copyWithNewName:(NSString *)name { + RLMProperty *prop = [self copy]; + prop.name = name; + return prop; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[RLMProperty class]]) { + return NO; + } + + return [self isEqualToProperty:object]; +} + +- (BOOL)isEqualToProperty:(RLMProperty *)property { + return _type == property->_type + && _indexed == property->_indexed + && _isPrimary == property->_isPrimary + && _optional == property->_optional + && [_name isEqualToString:property->_name] + && (_objectClassName == property->_objectClassName || [_objectClassName isEqualToString:property->_objectClassName]) + && (_linkOriginPropertyName == property->_linkOriginPropertyName || [_linkOriginPropertyName isEqualToString:property->_linkOriginPropertyName]); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ {\n\ttype = %@;\n\tobjectClassName = %@;\n\tlinkOriginPropertyName = %@;\n\tindexed = %@;\n\tisPrimary = %@;\n\toptional = %@;\n}", self.name, RLMTypeToString(self.type), self.objectClassName, self.linkOriginPropertyName, self.indexed ? @"YES" : @"NO", self.isPrimary ? @"YES" : @"NO", self.optional ? @"YES" : @"NO"]; +} + +- (realm::Property)objectStoreCopy { + realm::Property p; + p.name = _name.UTF8String; + p.type = (realm::PropertyType)_type; + p.object_type = _objectClassName ? _objectClassName.UTF8String : ""; + p.is_indexed = _indexed; + p.is_nullable = _optional; + p.link_origin_property_name = _linkOriginPropertyName ? _linkOriginPropertyName.UTF8String : ""; + return p; +} + +@end + +@implementation RLMPropertyDescriptor + ++ (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName +{ + RLMPropertyDescriptor *descriptor = [[RLMPropertyDescriptor alloc] init]; + descriptor->_objectClass = objectClass; + descriptor->_propertyName = propertyName; + return descriptor; +} + +@end diff --git a/Pods/Realm/Realm/RLMQueryUtil.mm b/Pods/Realm/Realm/RLMQueryUtil.mm new file mode 100644 index 0000000..cf861e8 --- /dev/null +++ b/Pods/Realm/Realm/RLMQueryUtil.mm @@ -0,0 +1,1378 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMQueryUtil.hpp" + +#import "RLMArray.h" +#import "RLMObjectSchema.h" +#import "RLMObject_Private.hpp" +#import "RLMPredicateUtil.hpp" +#import "RLMProperty.h" +#import "RLMSchema.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "results.hpp" + +#include + +using namespace realm; + +NSString * const RLMPropertiesComparisonTypeMismatchException = @"RLMPropertiesComparisonTypeMismatchException"; +NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException = @"RLMUnsupportedTypesFoundInPropertyComparisonException"; + +NSString * const RLMPropertiesComparisonTypeMismatchReason = @"Property type mismatch between %@ and %@"; +NSString * const RLMUnsupportedTypesFoundInPropertyComparisonReason = @"Comparison between %@ and %@"; + +// small helper to create the many exceptions thrown when parsing predicates +static NSException *RLMPredicateException(NSString *name, NSString *format, ...) { + va_list args; + va_start(args, format); + NSString *reason = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + return [NSException exceptionWithName:name reason:reason userInfo:nil]; +} + +// check a precondition and throw an exception if it is not met +// this should be used iff the condition being false indicates a bug in the caller +// of the function checking its preconditions +static void RLMPrecondition(bool condition, NSString *name, NSString *format, ...) { + if (__builtin_expect(condition, 1)) { + return; + } + + va_list args; + va_start(args, format); + NSString *reason = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + @throw [NSException exceptionWithName:name reason:reason userInfo:nil]; +} + +// return the property for a validated column name +RLMProperty *RLMValidatedProperty(RLMObjectSchema *desc, NSString *columnName) { + RLMProperty *prop = desc[columnName]; + RLMPrecondition(prop, @"Invalid property name", + @"Property '%@' not found in object of type '%@'", columnName, desc.className); + return prop; +} + +namespace { +BOOL RLMPropertyTypeIsNumeric(RLMPropertyType propertyType) { + switch (propertyType) { + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + return YES; + default: + return NO; + } +} + +// FIXME: TrueExpression and FalseExpression should be supported by core in some way + +struct TrueExpression : realm::Expression { + size_t find_first(size_t start, size_t end) const override + { + if (start != end) + return start; + + return realm::not_found; + } + void set_base_table(const Table*) override {} + const Table* get_base_table() const override { return nullptr; } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new TrueExpression(*this)); + } +}; + +struct FalseExpression : realm::Expression { + size_t find_first(size_t, size_t) const override { return realm::not_found; } + void set_base_table(const Table*) override {} + const Table* get_base_table() const override { return nullptr; } + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new FalseExpression(*this)); + } +}; + +NSString *operatorName(NSPredicateOperatorType operatorType) +{ + switch (operatorType) { + case NSLessThanPredicateOperatorType: + return @"<"; + case NSLessThanOrEqualToPredicateOperatorType: + return @"<="; + case NSGreaterThanPredicateOperatorType: + return @">"; + case NSGreaterThanOrEqualToPredicateOperatorType: + return @">="; + case NSEqualToPredicateOperatorType: + return @"=="; + case NSNotEqualToPredicateOperatorType: + return @"!="; + case NSMatchesPredicateOperatorType: + return @"MATCHES"; + case NSLikePredicateOperatorType: + return @"LIKE"; + case NSBeginsWithPredicateOperatorType: + return @"BEGINSWITH"; + case NSEndsWithPredicateOperatorType: + return @"ENDSWITH"; + case NSInPredicateOperatorType: + return @"IN"; + case NSContainsPredicateOperatorType: + return @"CONTAINS"; + case NSBetweenPredicateOperatorType: + return @"BETWEEN"; + case NSCustomSelectorPredicateOperatorType: + return @"custom selector"; + } + + return [NSString stringWithFormat:@"unknown operator %lu", (unsigned long)operatorType]; +} + +Table& get_table(Group& group, RLMObjectSchema *objectSchema) +{ + return *ObjectStore::table_for_object_type(group, objectSchema.className.UTF8String); +} + +// A reference to a column within a query. Can be resolved to a Columns for use in query expressions. +class ColumnReference { +public: + ColumnReference(Query& query, Group& group, RLMSchema *schema, RLMProperty* property, const std::vector& links = {}) + : m_links(links), m_property(property), m_schema(schema), m_group(&group), m_query(&query), m_table(query.get_table().get()) + { + auto& table = walk_link_chain([](Table&, size_t, RLMPropertyType) { }); + m_index = table.get_column_index(m_property.name.UTF8String); + } + + template + auto resolve(SubQuery&&... subquery) const + { + static_assert(sizeof...(SubQuery) < 2, "resolve() takes at most one subquery"); + set_link_chain_on_table(); + if (type() != RLMPropertyTypeLinkingObjects) { + return m_table->template column(index(), std::forward(subquery)...); + } + else { + return resolve_backlink(std::forward(subquery)...); + } + } + + RLMProperty *property() const { return m_property; } + size_t index() const { return m_index; } + RLMPropertyType type() const { return property().type; } + Group& group() const { return *m_group; } + + RLMObjectSchema *link_target_object_schema() const + { + switch (type()) { + case RLMPropertyTypeObject: + case RLMPropertyTypeArray: + case RLMPropertyTypeLinkingObjects: + return m_schema[property().objectClassName]; + default: + REALM_ASSERT(false); + } + } + + bool has_links() const { return m_links.size(); } + + bool has_any_to_many_links() const { + return std::any_of(begin(m_links), end(m_links), [](RLMProperty *property) { + return property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects; + }); + } + + ColumnReference last_link_column() const { + REALM_ASSERT(!m_links.empty()); + return {*m_query, *m_group, m_schema, m_links.back(), {m_links.begin(), m_links.end() - 1}}; + } + + ColumnReference column_ignoring_links(Query& query) const { + return {query, *m_group, m_schema, m_property}; + } + +private: + template + auto resolve_backlink(SubQuery&&... subquery) const + { + // We actually just want `if constexpr (std::is_same::value) { ... }`, + // so fake it by tag-dispatching on the conditional + return do_resolve_backlink(std::is_same(), std::forward(subquery)...); + } + + template + auto do_resolve_backlink(std::true_type, SubQuery&&... subquery) const + { + return with_link_origin(m_property, [&](Table& table, size_t col) { + return m_table->template column(table, col, std::forward(subquery)...); + }); + } + + template + Columns do_resolve_backlink(std::false_type, SubQuery&&...) const + { + // This can't actually happen as we only call resolve_backlink() if + // it's RLMPropertyTypeLinkingObjects + __builtin_unreachable(); + } + + template + Table& walk_link_chain(Func&& func) const + { + auto table = m_query->get_table().get(); + for (const auto& link : m_links) { + if (link.type != RLMPropertyTypeLinkingObjects) { + auto index = table->get_column_index(link.name.UTF8String); + func(*table, index, link.type); + table = table->get_link_target(index).get(); + } + else { + with_link_origin(link, [&](Table& link_origin_table, size_t link_origin_column) { + func(link_origin_table, link_origin_column, link.type); + table = &link_origin_table; + }); + } + } + return *table; + } + + template + auto with_link_origin(RLMProperty *prop, Func&& func) const + { + RLMObjectSchema *link_origin_schema = m_schema[prop.objectClassName]; + Table& link_origin_table = get_table(*m_group, link_origin_schema); + size_t link_origin_column = link_origin_table.get_column_index(prop.linkOriginPropertyName.UTF8String); + return func(link_origin_table, link_origin_column); + } + + void set_link_chain_on_table() const + { + walk_link_chain([&](Table& current_table, size_t column, RLMPropertyType type) { + if (type == RLMPropertyTypeLinkingObjects) { + m_table->backlink(current_table, column); + } + else { + m_table->link(column); + } + }); + } + + std::vector m_links; + RLMProperty *m_property; + RLMSchema *m_schema; + Group *m_group; + Query *m_query; + Table *m_table; + size_t m_index; +}; + +class CollectionOperation { +public: + enum Type { + Count, + Minimum, + Maximum, + Sum, + Average, + }; + + CollectionOperation(Type type, ColumnReference link_column, util::Optional column) + : m_type(type) + , m_link_column(std::move(link_column)) + , m_column(std::move(column)) + { + RLMPrecondition(m_link_column.type() == RLMPropertyTypeArray || m_link_column.type() == RLMPropertyTypeLinkingObjects, + @"Invalid predicate", @"Collection operation can only be applied to a property of type RLMArray."); + + switch (m_type) { + case Count: + RLMPrecondition(!m_column, @"Invalid predicate", @"Result of @count does not have any properties."); + break; + case Minimum: + case Maximum: + case Sum: + case Average: + RLMPrecondition(m_column && RLMPropertyTypeIsNumeric(m_column->type()), @"Invalid predicate", + @"%@ can only be applied to a numeric property.", name_for_type(m_type)); + break; + } + } + + CollectionOperation(NSString *operationName, ColumnReference link_column, util::Optional column = util::none) + : CollectionOperation(type_for_name(operationName), std::move(link_column), std::move(column)) + { + } + + Type type() const { return m_type; } + const ColumnReference& link_column() const { return m_link_column; } + const ColumnReference& column() const { return *m_column; } + + void validate_comparison(id value) const { + switch (m_type) { + case Count: + case Average: + RLMPrecondition([value isKindOfClass:[NSNumber class]], @"Invalid operand", + @"%@ can only be compared with a numeric value.", name_for_type(m_type)); + break; + case Minimum: + case Maximum: + case Sum: + RLMPrecondition(RLMIsObjectValidForProperty(value, m_column->property()), @"Invalid operand", + @"%@ on a property of type %@ cannot be compared with '%@'", + name_for_type(m_type), RLMTypeToString(m_column->type()), value); + break; + } + } + + void validate_comparison(const ColumnReference& column) const { + switch (m_type) { + case Count: + RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand", + @"%@ can only be compared with a numeric value.", name_for_type(m_type)); + break; + case Average: + case Minimum: + case Maximum: + case Sum: + RLMPrecondition(RLMPropertyTypeIsNumeric(column.type()), @"Invalid operand", + @"%@ on a property of type %@ cannot be compared with property of type '%@'", + name_for_type(m_type), RLMTypeToString(m_column->type()), RLMTypeToString(column.type())); + break; + } + } + +private: + static Type type_for_name(NSString *name) { + if ([name isEqualToString:@"@count"]) { + return Count; + } + if ([name isEqualToString:@"@min"]) { + return Minimum; + } + if ([name isEqualToString:@"@max"]) { + return Maximum; + } + if ([name isEqualToString:@"@sum"]) { + return Sum; + } + if ([name isEqualToString:@"@avg"]) { + return Average; + } + @throw RLMPredicateException(@"Invalid predicate", @"Unsupported collection operation '%@'", name); + } + + static NSString *name_for_type(Type type) { + switch (type) { + case Count: return @"@count"; + case Minimum: return @"@min"; + case Maximum: return @"@max"; + case Sum: return @"@sum"; + case Average: return @"@avg"; + } + } + + Type m_type; + ColumnReference m_link_column; + util::Optional m_column; +}; + +class QueryBuilder { +public: + QueryBuilder(Query& query, Group& group, RLMSchema *schema) + : m_query(query), m_group(group), m_schema(schema) { } + + void apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema); + + + void apply_collection_operator_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred); + void apply_value_expression(RLMObjectSchema *desc, NSString *keyPath, id value, NSComparisonPredicate *pred); + void apply_column_expression(RLMObjectSchema *desc, NSString *leftKeyPath, NSString *rightKeyPath, NSComparisonPredicate *predicate); + void apply_subquery_count_expression(RLMObjectSchema *objectSchema, NSExpression *subqueryExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + void apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + void apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right); + + + template + void add_numeric_constraint(RLMPropertyType datatype, + NSPredicateOperatorType operatorType, + A&& lhs, B&& rhs); + + template + void add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs); + + template + void add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + Columns &&column, + T value); + + void add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + StringData value, + Columns&& column); + + template + void add_constraint(RLMPropertyType type, + NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + L lhs, R rhs); + template + void do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, T... values); + void do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null); + + void add_between_constraint(const ColumnReference& column, id value); + + template + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, T value); + void add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value); + void add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column); + void add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&); + + void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, RLMObject *obj); + void add_link_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, realm::null); + template + void add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column); + void add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&); + + template + void add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values); + template + void add_collection_operation_constraint(NSPredicateOperatorType operatorType, + CollectionOperation collectionOperation, T... values); + + + CollectionOperation collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath); + ColumnReference column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPath, bool isAggregate); + +private: + Query& m_query; + Group& m_group; + RLMSchema *m_schema; +}; + +// add a clause for numeric constraints based on operator type +template +void QueryBuilder::add_numeric_constraint(RLMPropertyType datatype, + NSPredicateOperatorType operatorType, + A&& lhs, B&& rhs) +{ + switch (operatorType) { + case NSLessThanPredicateOperatorType: + m_query.and_query(lhs < rhs); + break; + case NSLessThanOrEqualToPredicateOperatorType: + m_query.and_query(lhs <= rhs); + break; + case NSGreaterThanPredicateOperatorType: + m_query.and_query(lhs > rhs); + break; + case NSGreaterThanOrEqualToPredicateOperatorType: + m_query.and_query(lhs >= rhs); + break; + case NSEqualToPredicateOperatorType: + m_query.and_query(lhs == rhs); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(lhs != rhs); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for type %@", operatorName(operatorType), RLMTypeToString(datatype)); + } +} + +template +void QueryBuilder::add_bool_constraint(NSPredicateOperatorType operatorType, A lhs, B rhs) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + m_query.and_query(lhs == rhs); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(lhs != rhs); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for bool type", operatorName(operatorType)); + } +} + +template +void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + Columns &&column, + T value) { + bool caseSensitive = !(predicateOptions & NSCaseInsensitivePredicateOption); + bool diacriticInsensitive = (predicateOptions & NSDiacriticInsensitivePredicateOption); + RLMPrecondition(!diacriticInsensitive, @"Invalid predicate option", + @"NSDiacriticInsensitivePredicateOption not supported for string type"); + + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + m_query.and_query(column.begins_with(value, caseSensitive)); + break; + case NSEndsWithPredicateOperatorType: + m_query.and_query(column.ends_with(value, caseSensitive)); + break; + case NSContainsPredicateOperatorType: + m_query.and_query(column.contains(value, caseSensitive)); + break; + case NSEqualToPredicateOperatorType: + m_query.and_query(column.equal(value, caseSensitive)); + break; + case NSNotEqualToPredicateOperatorType: + m_query.and_query(column.not_equal(value, caseSensitive)); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for string type", operatorName(operatorType)); + } +} + +void QueryBuilder::add_string_constraint(NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, + StringData value, + Columns&& column) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + case NSNotEqualToPredicateOperatorType: + add_string_constraint(operatorType, predicateOptions, std::move(column), value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' is not supported for string type with key path on right side of operator", + operatorName(operatorType)); + } +} + +id value_from_constant_expression_or_value(id value) { + if (NSExpression *exp = RLMDynamicCast(value)) { + RLMPrecondition(exp.expressionType == NSConstantValueExpressionType, + @"Invalid value", + @"Expressions within predicate aggregates must be constant values"); + return exp.constantValue; + } + return value; +} + +void validate_and_extract_between_range(id value, RLMProperty *prop, id *from, id *to) { + NSArray *array = RLMDynamicCast(value); + RLMPrecondition(array, @"Invalid value", @"object must be of type NSArray for BETWEEN operations"); + RLMPrecondition(array.count == 2, @"Invalid value", @"NSArray object must contain exactly two objects for BETWEEN operations"); + + *from = value_from_constant_expression_or_value(array.firstObject); + *to = value_from_constant_expression_or_value(array.lastObject); + RLMPrecondition(RLMIsObjectValidForProperty(*from, prop) && RLMIsObjectValidForProperty(*to, prop), + @"Invalid value", + @"NSArray objects must be of type %@ for BETWEEN operations", RLMTypeToString(prop.type)); +} + +void QueryBuilder::add_between_constraint(const ColumnReference& column, id value) { + if (column.has_any_to_many_links()) { + auto link_column = column.last_link_column(); + Query subquery = get_table(m_group, link_column.link_target_object_schema()).where(); + QueryBuilder(subquery, m_group, m_schema).add_between_constraint(column.column_ignoring_links(subquery), value); + + m_query.and_query(link_column.resolve(std::move(subquery)).count() > 0); + return; + } + + id from, to; + validate_and_extract_between_range(value, column.property(), &from, &to); + + RLMPropertyType type = column.type(); + + m_query.group(); + add_constraint(type, NSGreaterThanOrEqualToPredicateOperatorType, 0, column, from); + add_constraint(type, NSLessThanOrEqualToPredicateOperatorType, 0, column, to); + m_query.end_group(); +} + +template +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, + T value) { + RLMPrecondition(!column.has_links(), @"Unsupported operator", @"NSData properties cannot be queried over an object link."); + + size_t index = column.index(); + switch (operatorType) { + case NSBeginsWithPredicateOperatorType: + m_query.begins_with(index, value); + break; + case NSEndsWithPredicateOperatorType: + m_query.ends_with(index, value); + break; + case NSContainsPredicateOperatorType: + m_query.contains(index, value); + break; + case NSEqualToPredicateOperatorType: + m_query.equal(index, value); + break; + case NSNotEqualToPredicateOperatorType: + m_query.not_equal(index, value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' not supported for binary type", operatorName(operatorType)); + } +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, const ColumnReference& column, id value) { + add_binary_constraint(operatorType, column, RLMBinaryDataForNSData(value)); +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType operatorType, id value, const ColumnReference& column) { + switch (operatorType) { + case NSEqualToPredicateOperatorType: + case NSNotEqualToPredicateOperatorType: + add_binary_constraint(operatorType, column, value); + break; + default: + @throw RLMPredicateException(@"Invalid operator type", + @"Operator '%@' is not supported for binary type with key path on right side of operator", + operatorName(operatorType)); + } +} + +void QueryBuilder::add_binary_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) { + @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two NSData properties are not supported"); +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, RLMObject *obj) { + RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType, + @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison"); + + if (operatorType == NSEqualToPredicateOperatorType) { + m_query.and_query(column.resolve() == obj->_row); + } + else { + m_query.and_query(column.resolve() != obj->_row); + } +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, + const ColumnReference& column, + realm::null) { + RLMPrecondition(!column.has_links(), @"Unsupported operator", @"Multi-level object equality link queries are not supported."); + RLMPrecondition(operatorType == NSEqualToPredicateOperatorType || operatorType == NSNotEqualToPredicateOperatorType, + @"Invalid operator type", @"Only 'Equal' and 'Not Equal' operators supported for object comparison"); + if (operatorType == NSNotEqualToPredicateOperatorType) { + m_query.Not(); + } + + m_query.and_query(column.resolve().is_null()); +} + +template +void QueryBuilder::add_link_constraint(NSPredicateOperatorType operatorType, T obj, const ColumnReference& column) { + // Link constraints only support the equal-to and not-equal-to operators. The order of operands + // is not important for those comparisons so we can delegate to the other implementation. + add_link_constraint(operatorType, column, obj); +} + +void QueryBuilder::add_link_constraint(NSPredicateOperatorType, const ColumnReference&, const ColumnReference&) { + // This is not actually reachable as this case is caught earlier, but this + // overload is needed for the code to compile + @throw RLMPredicateException(@"Invalid predicate", @"Comparisons between two RLMArray properties are not supported"); +} + + +// iterate over an array of subpredicates, using @func to build a query from each +// one and ORing them together +template +void process_or_group(Query &query, id array, Func&& func) { + RLMPrecondition([array conformsToProtocol:@protocol(NSFastEnumeration)], + @"Invalid value", @"IN clause requires an array of items"); + + query.group(); + + bool first = true; + for (id item in array) { + if (!first) { + query.Or(); + } + first = false; + + func(item); + } + + if (first) { + // Queries can't be empty, so if there's zero things in the OR group + // validation will fail. Work around this by adding an expression which + // will never find any rows in a table. + query.and_query(std::unique_ptr(new FalseExpression)); + } + + query.end_group(); +} + +template +RequestedType convert(id value); + +template <> +Timestamp convert(id value) { + return RLMTimestampForNSDate(value); +} + +template <> +bool convert(id value) { + return [value boolValue]; +} + +template <> +Double convert(id value) { + return [value doubleValue]; +} + +template <> +Float convert(id value) { + return [value floatValue]; +} + +template <> +Int convert(id value) { + return [value longLongValue]; +} + +template <> +String convert(id value) { + return RLMStringDataWithNSString(value); +} + +template +realm::null value_of_type(realm::null) { + return realm::null(); +} + +template +auto value_of_type(id value) { + return ::convert(value); +} + +template +auto value_of_type(const ColumnReference& column) { + return column.resolve(); +} + + +template +void QueryBuilder::do_add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, T... values) +{ + static_assert(sizeof...(T) == 2, "do_add_constraint accepts only two values as arguments"); + + switch (type) { + case RLMPropertyTypeBool: + add_bool_constraint(operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeDate: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeDouble: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeFloat: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeInt: + add_numeric_constraint(type, operatorType, value_of_type(values)...); + break; + case RLMPropertyTypeString: + add_string_constraint(operatorType, predicateOptions, value_of_type(values)...); + break; + case RLMPropertyTypeData: + add_binary_constraint(operatorType, values...); + break; + case RLMPropertyTypeObject: + case RLMPropertyTypeArray: + case RLMPropertyTypeLinkingObjects: + add_link_constraint(operatorType, values...); + break; + default: + @throw RLMPredicateException(@"Unsupported predicate value type", + @"Object type %@ not supported", RLMTypeToString(type)); + } +} + +void QueryBuilder::do_add_constraint(RLMPropertyType, NSPredicateOperatorType, NSComparisonPredicateOptions, id, realm::null) +{ + // This is not actually reachable as this case is caught earlier, but this + // overload is needed for the code to compile + @throw RLMPredicateException(@"Invalid predicate expressions", + @"Predicate expressions must compare a keypath and another keypath or a constant value"); +} + +bool is_nsnull(id value) { + return !value || value == NSNull.null; +} + +template +bool is_nsnull(T) { + return false; +} + +template +void QueryBuilder::add_constraint(RLMPropertyType type, NSPredicateOperatorType operatorType, + NSComparisonPredicateOptions predicateOptions, L lhs, R rhs) +{ + // The expression operators are only overloaded for realm::null on the rhs + RLMPrecondition(!is_nsnull(lhs), @"Unsupported operator", + @"Nil is only supported on the right side of operators"); + + if (is_nsnull(rhs)) { + do_add_constraint(type, operatorType, predicateOptions, lhs, realm::null()); + } + else { + do_add_constraint(type, operatorType, predicateOptions, lhs, rhs); + } +} + +ColumnReference QueryBuilder::column_reference_from_key_path(RLMObjectSchema *objectSchema, NSString *keyPath, bool isAggregate) +{ + RLMProperty *property; + std::vector links; + + bool keyPathContainsToManyRelationship = false; + + NSUInteger start = 0, length = keyPath.length, end = NSNotFound; + do { + end = [keyPath rangeOfString:@"." options:0 range:{start, length - start}].location; + NSString *propertyName = [keyPath substringWithRange:{start, end == NSNotFound ? length - start : end - start}]; + property = objectSchema[propertyName]; + RLMPrecondition(property, @"Invalid property name", + @"Property '%@' not found in object of type '%@'", propertyName, objectSchema.className); + + if (property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects) + keyPathContainsToManyRelationship = true; + + if (end != NSNotFound) { + RLMPrecondition(property.type == RLMPropertyTypeObject || property.type == RLMPropertyTypeArray || property.type == RLMPropertyTypeLinkingObjects, + @"Invalid value", @"Property '%@' is not a link in object of type '%@'", propertyName, objectSchema.className); + + links.push_back(property); + REALM_ASSERT(property.objectClassName); + objectSchema = m_schema[property.objectClassName]; + } + + start = end + 1; + } while (end != NSNotFound); + + if (isAggregate && !keyPathContainsToManyRelationship) { + @throw RLMPredicateException(@"Invalid predicate", + @"Aggregate operations can only be used on key paths that include an array property"); + } else if (!isAggregate && keyPathContainsToManyRelationship) { + @throw RLMPredicateException(@"Invalid predicate", + @"Key paths that include an array property must use aggregate operations"); + } + + return ColumnReference(m_query, m_group, m_schema, property, std::move(links)); +} + +void validate_property_value(const ColumnReference& column, + __unsafe_unretained id const value, + __unsafe_unretained NSString *const err, + __unsafe_unretained RLMObjectSchema *const objectSchema, + __unsafe_unretained NSString *const keyPath) { + RLMProperty *prop = column.property(); + if (prop.type == RLMPropertyTypeArray) { + RLMPrecondition([RLMObjectBaseObjectSchema(RLMDynamicCast(value)).className isEqualToString:prop.objectClassName], + @"Invalid value", err, prop.objectClassName, keyPath, objectSchema.className, value); + } + else { + RLMPrecondition(RLMIsObjectValidForProperty(value, prop), + @"Invalid value", err, RLMTypeToString(prop.type), keyPath, objectSchema.className, value); + } + if (RLMObjectBase *obj = RLMDynamicCast(value)) { + RLMPrecondition(!obj->_row.is_attached() || &column.group() == &obj->_realm.group, + @"Invalid value origin", @"Object must be from the Realm being queried"); + } +} + +template +struct ValueOfTypeWithCollectionOperationHelper; + +template <> +struct ValueOfTypeWithCollectionOperationHelper { + static auto convert(const CollectionOperation& operation) + { + assert(operation.type() == CollectionOperation::Count); + return operation.link_column().resolve().count(); + } +}; + +#define VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(OperationType, function) \ +template \ +struct ValueOfTypeWithCollectionOperationHelper { \ + static auto convert(const CollectionOperation& operation) \ + { \ + REALM_ASSERT(operation.type() == OperationType); \ + auto targetColumn = operation.link_column().resolve().template column(operation.column().index()); \ + return targetColumn.function(); \ + } \ +} \ + +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Minimum, min); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Maximum, max); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Sum, sum); +VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER(CollectionOperation::Average, average); +#undef VALUE_OF_TYPE_WITH_COLLECTION_OPERATOR_HELPER + +template +auto value_of_type_with_collection_operation(T&& value) { + return value_of_type(std::forward(value)); +} + +template +auto value_of_type_with_collection_operation(CollectionOperation operation) { + using helper = ValueOfTypeWithCollectionOperationHelper; + return helper::convert(operation); +} + +template +void QueryBuilder::add_collection_operation_constraint(RLMPropertyType propertyType, NSPredicateOperatorType operatorType, T... values) +{ + switch (propertyType) { + case RLMPropertyTypeInt: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + case RLMPropertyTypeFloat: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + case RLMPropertyTypeDouble: + add_numeric_constraint(propertyType, operatorType, value_of_type_with_collection_operation(values)...); + break; + default: + REALM_ASSERT(false && "Only numeric property types should hit this path."); + } +} + +template +void QueryBuilder::add_collection_operation_constraint(NSPredicateOperatorType operatorType, + CollectionOperation collectionOperation, T... values) +{ + static_assert(sizeof...(T) == 2, "add_collection_operation_constraint accepts only two values as arguments"); + + switch (collectionOperation.type()) { + case CollectionOperation::Count: + add_numeric_constraint(RLMPropertyTypeInt, operatorType, + value_of_type_with_collection_operation(values)...); + break; + case CollectionOperation::Minimum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Maximum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Sum: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + case CollectionOperation::Average: + add_collection_operation_constraint(collectionOperation.column().type(), operatorType, values...); + break; + } +} + +bool key_path_contains_collection_operator(NSString *keyPath) { + return [keyPath rangeOfString:@"@"].location != NSNotFound; +} + +NSString *get_collection_operation_name_from_key_path(NSString *keyPath, NSString **leadingKeyPath, NSString **trailingKey) { + NSRange at = [keyPath rangeOfString:@"@"]; + if (at.location == NSNotFound || at.location >= keyPath.length - 1) { + @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath); + } + + if (at.location == 0 || [keyPath characterAtIndex:at.location - 1] != '.') { + @throw RLMPredicateException(@"Invalid key path", @"'%@' is not a valid key path'", keyPath); + } + + NSRange trailingKeyRange = [keyPath rangeOfString:@"." options:0 range:{at.location, keyPath.length - at.location} locale:nil]; + + *leadingKeyPath = [keyPath substringToIndex:at.location - 1]; + if (trailingKeyRange.location == NSNotFound) { + *trailingKey = nil; + return [keyPath substringFromIndex:at.location]; + } else { + *trailingKey = [keyPath substringFromIndex:trailingKeyRange.location + 1]; + return [keyPath substringWithRange:{at.location, trailingKeyRange.location - at.location}]; + } +} + +CollectionOperation QueryBuilder::collection_operation_from_key_path(RLMObjectSchema *desc, NSString *keyPath) { + NSString *leadingKeyPath; + NSString *trailingKey; + NSString *collectionOperationName = get_collection_operation_name_from_key_path(keyPath, &leadingKeyPath, &trailingKey); + + ColumnReference linkColumn = column_reference_from_key_path(desc, leadingKeyPath, true); + util::Optional column; + if (trailingKey) { + RLMPrecondition([trailingKey rangeOfString:@"."].location == NSNotFound, @"Invalid key path", + @"Right side of collection operator may only have a single level key"); + NSString *fullKeyPath = [leadingKeyPath stringByAppendingFormat:@".%@", trailingKey]; + column = column_reference_from_key_path(desc, fullKeyPath, true); + } + + return {collectionOperationName, std::move(linkColumn), std::move(column)}; +} + +void QueryBuilder::apply_collection_operator_expression(RLMObjectSchema *desc, + NSString *keyPath, id value, + NSComparisonPredicate *pred) { + CollectionOperation operation = collection_operation_from_key_path(desc, keyPath); + operation.validate_comparison(value); + + if (pred.leftExpression.expressionType == NSKeyPathExpressionType) { + add_collection_operation_constraint(pred.predicateOperatorType, operation, operation, value); + } else { + add_collection_operation_constraint(pred.predicateOperatorType, operation, value, operation); + } +} + +void QueryBuilder::apply_value_expression(RLMObjectSchema *desc, + NSString *keyPath, id value, + NSComparisonPredicate *pred) +{ + if (key_path_contains_collection_operator(keyPath)) { + apply_collection_operator_expression(desc, keyPath, value, pred); + return; + } + + bool isAny = pred.comparisonPredicateModifier == NSAnyPredicateModifier; + ColumnReference column = column_reference_from_key_path(desc, keyPath, isAny); + + // check to see if this is a between query + if (pred.predicateOperatorType == NSBetweenPredicateOperatorType) { + add_between_constraint(std::move(column), value); + return; + } + + // turn "key.path IN collection" into ored together ==. "collection IN key.path" is handled elsewhere. + if (pred.predicateOperatorType == NSInPredicateOperatorType) { + process_or_group(m_query, value, [&](id item) { + id normalized = value_from_constant_expression_or_value(item); + validate_property_value(column, normalized, + @"Expected object of type %@ in IN clause for property '%@' on object of type '%@', but received: %@", desc, keyPath); + add_constraint(column.type(), NSEqualToPredicateOperatorType, pred.options, column, normalized); + }); + return; + } + + validate_property_value(column, value, @"Expected object of type %@ for property '%@' on object of type '%@', but received: %@", desc, keyPath); + if (pred.leftExpression.expressionType == NSKeyPathExpressionType) { + add_constraint(column.type(), pred.predicateOperatorType, pred.options, std::move(column), value); + } else { + add_constraint(column.type(), pred.predicateOperatorType, pred.options, value, std::move(column)); + } +} + +void QueryBuilder::apply_column_expression(RLMObjectSchema *desc, + NSString *leftKeyPath, NSString *rightKeyPath, + NSComparisonPredicate *predicate) +{ + bool left_key_path_contains_collection_operator = key_path_contains_collection_operator(leftKeyPath); + bool right_key_path_contains_collection_operator = key_path_contains_collection_operator(rightKeyPath); + if (left_key_path_contains_collection_operator && right_key_path_contains_collection_operator) { + @throw RLMPredicateException(@"Unsupported predicate", @"Key paths including aggregate operations cannot be compared with other aggregate operations."); + } + + if (left_key_path_contains_collection_operator) { + CollectionOperation left = collection_operation_from_key_path(desc, leftKeyPath); + ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, false); + left.validate_comparison(right); + add_collection_operation_constraint(predicate.predicateOperatorType, left, left, std::move(right)); + return; + } + if (right_key_path_contains_collection_operator) { + ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, false); + CollectionOperation right = collection_operation_from_key_path(desc, rightKeyPath); + right.validate_comparison(left); + add_collection_operation_constraint(predicate.predicateOperatorType, right, std::move(left), right); + return; + } + + bool isAny = false; + ColumnReference left = column_reference_from_key_path(desc, leftKeyPath, isAny); + ColumnReference right = column_reference_from_key_path(desc, rightKeyPath, isAny); + + // NOTE: It's assumed that column type must match and no automatic type conversion is supported. + RLMPrecondition(left.type() == right.type(), + RLMPropertiesComparisonTypeMismatchException, + RLMPropertiesComparisonTypeMismatchReason, + RLMTypeToString(left.type()), + RLMTypeToString(right.type())); + + // TODO: Should we handle special case where left row is the same as right row (tautology) + add_constraint(left.type(), predicate.predicateOperatorType, predicate.options, + std::move(left), std::move(right)); +} + +// Identify expressions of the form [SELF valueForKeyPath:] +bool is_self_value_for_key_path_function_expression(NSExpression *expression) +{ + if (expression.expressionType != NSFunctionExpressionType) + return false; + + if (expression.operand.expressionType != NSEvaluatedObjectExpressionType) + return false; + + return [expression.function isEqualToString:@"valueForKeyPath:"]; +} + +// -[NSPredicate predicateWithSubtitutionVariables:] results in function expressions of the form [SELF valueForKeyPath:] +// that apply_predicate cannot handle. Replace such expressions with equivalent NSKeyPathExpressionType expressions. +NSExpression *simplify_self_value_for_key_path_function_expression(NSExpression *expression) { + if (is_self_value_for_key_path_function_expression(expression)) { + if (NSString *keyPath = [expression.arguments.firstObject keyPath]) { + return [NSExpression expressionForKeyPath:keyPath]; + } + } + return expression; +} + +void QueryBuilder::apply_subquery_count_expression(RLMObjectSchema *objectSchema, + NSExpression *subqueryExpression, NSPredicateOperatorType operatorType, NSExpression *right) { + if (right.expressionType != NSConstantValueExpressionType || ![right.constantValue isKindOfClass:[NSNumber class]]) { + @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY(…).@count is only supported when compared with a constant number."); + } + int64_t value = [right.constantValue integerValue]; + + ColumnReference collectionColumn = column_reference_from_key_path(objectSchema, [subqueryExpression.collection keyPath], true); + RLMObjectSchema *collectionMemberObjectSchema = m_schema[collectionColumn.property().objectClassName]; + + // Eliminate references to the iteration variable in the subquery. + NSPredicate *subqueryPredicate = [subqueryExpression.predicate predicateWithSubstitutionVariables:@{ subqueryExpression.variable : [NSExpression expressionForEvaluatedObject] }]; + subqueryPredicate = transformPredicate(subqueryPredicate, simplify_self_value_for_key_path_function_expression); + + Query subquery = RLMPredicateToQuery(subqueryPredicate, collectionMemberObjectSchema, m_schema, m_group); + add_numeric_constraint(RLMPropertyTypeInt, operatorType, + collectionColumn.resolve(std::move(subquery)).count(), value); +} + +void QueryBuilder::apply_function_subquery_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right) { + if (![functionExpression.function isEqualToString:@"valueForKeyPath:"] || functionExpression.arguments.count != 1) { + @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported on the result of a SUBQUERY.", functionExpression.function); + } + + NSExpression *keyPathExpression = functionExpression.arguments.firstObject; + if ([keyPathExpression.keyPath isEqualToString:@"@count"]) { + apply_subquery_count_expression(objectSchema, functionExpression.operand, operatorType, right); + } else { + @throw RLMPredicateException(@"Invalid predicate", @"SUBQUERY is only supported when immediately followed by .@count that is compared with a constant number."); + } +} + +void QueryBuilder::apply_function_expression(RLMObjectSchema *objectSchema, NSExpression *functionExpression, + NSPredicateOperatorType operatorType, NSExpression *right) { + if (functionExpression.operand.expressionType == NSSubqueryExpressionType) { + apply_function_subquery_expression(objectSchema, functionExpression, operatorType, right); + } else { + @throw RLMPredicateException(@"Invalid predicate", @"The '%@' function is not supported.", functionExpression.function); + } +} + + +void QueryBuilder::apply_predicate(NSPredicate *predicate, RLMObjectSchema *objectSchema) +{ + // Compound predicates. + if ([predicate isMemberOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *comp = (NSCompoundPredicate *)predicate; + + switch ([comp compoundPredicateType]) { + case NSAndPredicateType: + if (comp.subpredicates.count) { + // Add all of the subpredicates. + m_query.group(); + for (NSPredicate *subp in comp.subpredicates) { + apply_predicate(subp, objectSchema); + } + m_query.end_group(); + } else { + // NSCompoundPredicate's documentation states that an AND predicate with no subpredicates evaluates to TRUE. + m_query.and_query(std::unique_ptr(new TrueExpression)); + } + break; + + case NSOrPredicateType: { + // Add all of the subpredicates with ors inbetween. + process_or_group(m_query, comp.subpredicates, [&](__unsafe_unretained NSPredicate *const subp) { + apply_predicate(subp, objectSchema); + }); + break; + } + + case NSNotPredicateType: + // Add the negated subpredicate + m_query.Not(); + apply_predicate(comp.subpredicates.firstObject, objectSchema); + break; + + default: + @throw RLMPredicateException(@"Invalid compound predicate type", + @"Only support AND, OR and NOT predicate types"); + } + } + else if ([predicate isMemberOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *compp = (NSComparisonPredicate *)predicate; + + // check modifier + RLMPrecondition(compp.comparisonPredicateModifier != NSAllPredicateModifier, + @"Invalid predicate", @"ALL modifier not supported"); + + NSExpressionType exp1Type = compp.leftExpression.expressionType; + NSExpressionType exp2Type = compp.rightExpression.expressionType; + + if (compp.comparisonPredicateModifier == NSAnyPredicateModifier) { + // for ANY queries + RLMPrecondition(exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType, + @"Invalid predicate", + @"Predicate with ANY modifier must compare a KeyPath with RLMArray with a value"); + } + + if (compp.predicateOperatorType == NSBetweenPredicateOperatorType || compp.predicateOperatorType == NSInPredicateOperatorType) { + // Inserting an array via %@ gives NSConstantValueExpressionType, but including it directly gives NSAggregateExpressionType + if (exp1Type == NSKeyPathExpressionType && (exp2Type == NSAggregateExpressionType || exp2Type == NSConstantValueExpressionType)) { + // "key.path IN %@", "key.path IN {…}", "key.path BETWEEN %@", or "key.path BETWEEN {…}". + exp2Type = NSConstantValueExpressionType; + } + else if (compp.predicateOperatorType == NSInPredicateOperatorType && exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) { + // "%@ IN key.path" is equivalent to "ANY key.path IN %@". Rewrite the former into the latter. + compp = [NSComparisonPredicate predicateWithLeftExpression:compp.rightExpression rightExpression:compp.leftExpression + modifier:NSAnyPredicateModifier type:NSEqualToPredicateOperatorType options:0]; + exp1Type = NSKeyPathExpressionType; + exp2Type = NSConstantValueExpressionType; + } + else { + if (compp.predicateOperatorType == NSBetweenPredicateOperatorType) { + @throw RLMPredicateException(@"Invalid predicate", + @"Predicate with BETWEEN operator must compare a KeyPath with an aggregate with two values"); + } + else if (compp.predicateOperatorType == NSInPredicateOperatorType) { + @throw RLMPredicateException(@"Invalid predicate", + @"Predicate with IN operator must compare a KeyPath with an aggregate"); + } + } + } + + if (exp1Type == NSKeyPathExpressionType && exp2Type == NSKeyPathExpressionType) { + // both expression are KeyPaths + apply_column_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.keyPath, compp); + } + else if (exp1Type == NSKeyPathExpressionType && exp2Type == NSConstantValueExpressionType) { + // comparing keypath to value + apply_value_expression(objectSchema, compp.leftExpression.keyPath, compp.rightExpression.constantValue, compp); + } + else if (exp1Type == NSConstantValueExpressionType && exp2Type == NSKeyPathExpressionType) { + // comparing value to keypath + apply_value_expression(objectSchema, compp.rightExpression.keyPath, compp.leftExpression.constantValue, compp); + } + else if (exp1Type == NSFunctionExpressionType) { + apply_function_expression(objectSchema, compp.leftExpression, compp.predicateOperatorType, compp.rightExpression); + } + else if (exp1Type == NSSubqueryExpressionType) { + // The subquery expressions that we support are handled by the NSFunctionExpressionType case above. + @throw RLMPredicateException(@"Invalid predicate expression", @"SUBQUERY is only supported when immediately followed by .@count."); + } + else { + @throw RLMPredicateException(@"Invalid predicate expressions", + @"Predicate expressions must compare a keypath and another keypath or a constant value"); + } + } + else if ([predicate isEqual:[NSPredicate predicateWithValue:YES]]) { + m_query.and_query(std::unique_ptr(new TrueExpression)); + } else if ([predicate isEqual:[NSPredicate predicateWithValue:NO]]) { + m_query.and_query(std::unique_ptr(new FalseExpression)); + } + else { + // invalid predicate type + @throw RLMPredicateException(@"Invalid predicate", + @"Only support compound, comparison, and constant predicates"); + } +} + +size_t RLMValidatedColumnForSort(Table& table, NSString *propName) { + RLMPrecondition([propName rangeOfString:@"."].location == NSNotFound, + @"Invalid sort property", @"Cannot sort on '%@': sorting on key paths is not supported.", propName); + size_t column = table.get_column_index(propName.UTF8String); + RLMPrecondition(column != npos, @"Invalid sort property", + @"Cannot sort on property '%@' on object of type '%s': property not found.", + propName, ObjectStore::object_type_for_table_name(table.get_name()).data()); + + switch (auto type = static_cast(table.get_column_type(column))) { + case RLMPropertyTypeBool: + case RLMPropertyTypeDate: + case RLMPropertyTypeDouble: + case RLMPropertyTypeFloat: + case RLMPropertyTypeInt: + case RLMPropertyTypeString: + break; + + default: + @throw RLMPredicateException(@"Invalid sort property type", + @"Cannot sort on property '%@' on object of type '%s': sorting is only supported on bool, date, double, float, integer, and string properties, but property is of type %@.", + propName, ObjectStore::object_type_for_table_name(table.get_name()).data(), + RLMTypeToString(type)); + } + return column; +} + +} // namespace + +realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, + RLMSchema *schema, Group &group) +{ + auto query = get_table(group, objectSchema).where(); + + // passing a nil predicate is a no-op + if (!predicate) { + return query; + } + + @autoreleasepool { + QueryBuilder(query, group, schema).apply_predicate(predicate, objectSchema); + } + + // Test the constructed query in core + std::string validateMessage = query.validate(); + RLMPrecondition(validateMessage.empty(), @"Invalid query", @"%.*s", + (int)validateMessage.size(), validateMessage.c_str()); + return query; +} + +realm::SortDescriptor RLMSortDescriptorFromDescriptors(realm::Table& table, NSArray *descriptors) { + std::vector> columnIndices; + std::vector ascending; + columnIndices.reserve(descriptors.count); + ascending.reserve(descriptors.count); + + for (RLMSortDescriptor *descriptor in descriptors) { + columnIndices.push_back({RLMValidatedColumnForSort(table, descriptor.property)}); + ascending.push_back(descriptor.ascending); + } + + return {table, std::move(columnIndices), std::move(ascending)}; +} diff --git a/Pods/Realm/Realm/RLMRealm.mm b/Pods/Realm/Realm/RLMRealm.mm new file mode 100644 index 0000000..d5a7a76 --- /dev/null +++ b/Pods/Realm/Realm/RLMRealm.mm @@ -0,0 +1,690 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealm_Private.hpp" + +#import "RLMAnalytics.hpp" +#import "RLMArray_Private.hpp" +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMMigration_Private.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMObjectStore.h" +#import "RLMObject_Private.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealmUtil.hpp" +#import "RLMSchema_Private.hpp" +#import "RLMSyncManager_Private.hpp" +#import "RLMUpdateChecker.hpp" +#import "RLMUtil.hpp" + +#include "impl/realm_coordinator.hpp" +#include "object_store.hpp" +#include "schema.hpp" +#include "shared_realm.hpp" + +#include +#include + +using namespace realm; +using util::File; + +@interface RLMRealm () +@property (nonatomic, strong) NSHashTable *notificationHandlers; +- (void)sendNotifications:(RLMNotification)notification; +@end + +void RLMDisableSyncToDisk() { + realm::disable_sync_to_disk(); +} + +// Notification Token +@interface RLMRealmNotificationToken : RLMNotificationToken +@property (nonatomic, strong) RLMRealm *realm; +@property (nonatomic, copy) RLMNotificationBlock block; +@end + +@implementation RLMRealmNotificationToken +- (void)stop { + [_realm verifyThread]; + [_realm.notificationHandlers removeObject:self]; + _realm = nil; + _block = nil; +} + +- (void)dealloc { + if (_realm || _block) { + NSLog(@"RLMNotificationToken released without unregistering a notification. You must hold " + @"on to the RLMNotificationToken returned from addNotificationBlock and call " + @"-[RLMNotificationToken stop] when you no longer wish to receive RLMRealm notifications."); + } +} +@end + +static bool shouldForciblyDisableEncryption() { + static bool disableEncryption = getenv("REALM_DISABLE_ENCRYPTION"); + return disableEncryption; +} + +NSData *RLMRealmValidatedEncryptionKey(NSData *key) { + if (shouldForciblyDisableEncryption()) { + return nil; + } + + if (key) { + if (key.length != 64) { + @throw RLMException(@"Encryption key must be exactly 64 bytes long"); + } +#if TARGET_OS_WATCH + @throw RLMException(@"Cannot open an encrypted Realm on watchOS."); +#endif + } + + return key; +} + +@implementation RLMRealm { + NSHashTable *_collectionEnumerators; +} + ++ (BOOL)isCoreDebug { + return realm::Version::has_feature(realm::feature_Debug); +} + ++ (void)initialize { + static bool initialized; + if (initialized) { + return; + } + initialized = true; + + RLMCheckForUpdates(); + RLMSendAnalytics(); +} + +- (BOOL)isEmpty { + return realm::ObjectStore::is_empty(self.group); +} + +- (void)verifyThread { + _realm->verify_thread(); +} + +- (BOOL)inWriteTransaction { + return _realm->is_in_transaction(); +} + +- (realm::Group &)group { + return _realm->read_group(); +} + +- (BOOL)autorefresh { + return _realm->auto_refresh(); +} + +- (void)setAutorefresh:(BOOL)autorefresh { + _realm->set_auto_refresh(autorefresh); +} + ++ (NSString *)writeableTemporaryPathForFile:(NSString *)fileName { + return [NSTemporaryDirectory() stringByAppendingPathComponent:fileName]; +} + ++ (instancetype)defaultRealm { + return [RLMRealm realmWithConfiguration:[RLMRealmConfiguration rawDefaultConfiguration] error:nil]; +} + ++ (instancetype)realmWithURL:(NSURL *)fileURL { + RLMRealmConfiguration *configuration = [RLMRealmConfiguration defaultConfiguration]; + configuration.fileURL = fileURL; + return [RLMRealm realmWithConfiguration:configuration error:nil]; +} +// ARC tries to eliminate calls to autorelease when the value is then immediately +// returned, but this results in significantly different semantics between debug +// and release builds for RLMRealm, so force it to always autorelease. +static id RLMAutorelease(id value) { + // +1 __bridge_retained, -1 CFAutorelease + return value ? (__bridge id)CFAutorelease((__bridge_retained CFTypeRef)value) : nil; +} + +static void RLMRealmSetSchemaAndAlign(RLMRealm *realm, RLMSchema *targetSchema) { + realm.schema = targetSchema; + realm->_info = RLMSchemaInfo(realm, targetSchema, realm->_realm->schema()); +} + ++ (instancetype)realmWithSharedRealm:(SharedRealm)sharedRealm schema:(RLMSchema *)schema { + RLMRealm *realm = [RLMRealm new]; + realm->_realm = sharedRealm; + realm->_dynamic = YES; + RLMRealmSetSchemaAndAlign(realm, schema); + return RLMAutorelease(realm); +} + +REALM_NOINLINE void RLMRealmTranslateException(NSError **error) { + try { + throw; + } + catch (RealmFileException const& ex) { + switch (ex.kind()) { + case RealmFileException::Kind::PermissionDenied: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFilePermissionDenied, ex), error); + break; + case RealmFileException::Kind::IncompatibleLockFile: { + NSString *err = @"Realm file is currently open in another process " + "which cannot share access with this process. All " + "processes sharing a single file must be the same " + "architecture. For sharing files between the Realm " + "Browser and an iOS simulator, this means that you " + "must use a 64-bit simulator."; + RLMSetErrorOrThrow(RLMMakeError(RLMErrorIncompatibleLockFile, + File::PermissionDenied(err.UTF8String, ex.path())), error); + break; + } + case RealmFileException::Kind::NotFound: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileNotFound, ex), error); + break; + case RealmFileException::Kind::Exists: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileExists, ex), error); + break; + case RealmFileException::Kind::AccessError: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileAccess, ex), error); + break; + case RealmFileException::Kind::FormatUpgradeRequired: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFileFormatUpgradeRequired, ex), error); + break; + default: + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, ex), error); + break; + } + } + catch (AddressSpaceExhausted const &ex) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorAddressSpaceExhausted, ex), error); + } + catch (SchemaMismatchException const& ex) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorSchemaMismatch, ex), error); + } + catch (std::system_error const& ex) { + RLMSetErrorOrThrow(RLMMakeError(ex), error); + } + catch (const std::exception &exp) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error); + } +} + ++ (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + bool dynamic = configuration.dynamic; + bool readOnly = configuration.readOnly; + + { + Realm::Config& config = configuration.config; + + // try to reuse existing realm first + if (config.cache || dynamic) { + if (RLMRealm *realm = RLMGetThreadLocalCachedRealmForPath(config.path)) { + auto const& old_config = realm->_realm->config(); + if (old_config.read_only() != config.read_only()) { + @throw RLMException(@"Realm at path '%s' already opened with different read permissions", config.path.c_str()); + } + if (old_config.in_memory != config.in_memory) { + @throw RLMException(@"Realm at path '%s' already opened with different inMemory settings", config.path.c_str()); + } + if (realm->_dynamic != dynamic) { + @throw RLMException(@"Realm at path '%s' already opened with different dynamic settings", config.path.c_str()); + } + if (old_config.encryption_key != config.encryption_key) { + @throw RLMException(@"Realm at path '%s' already opened with different encryption key", config.path.c_str()); + } + return RLMAutorelease(realm); + } + } + } + + configuration = [configuration copy]; + Realm::Config& config = configuration.config; + + RLMRealm *realm = [RLMRealm new]; + realm->_dynamic = dynamic; + + // protects the realm cache and accessors cache + static std::mutex initLock; + std::lock_guard lock(initLock); + + try { + realm->_realm = Realm::get_shared_realm(config); + } + catch (...) { + RLMRealmTranslateException(error); + return nil; + } + + // if we have a cached realm on another thread, copy without a transaction + if (RLMRealm *cachedRealm = RLMGetAnyCachedRealmForPath(config.path)) { + RLMRealmSetSchemaAndAlign(realm, cachedRealm.schema); + } + else if (dynamic) { + RLMRealmSetSchemaAndAlign(realm, [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()]); + } + else { + // set/align schema or perform migration if needed + RLMSchema *schema = configuration.customSchema ?: RLMSchema.sharedSchema; + + Realm::MigrationFunction migrationFunction; + auto migrationBlock = configuration.migrationBlock; + if (migrationBlock && configuration.schemaVersion > 0) { + migrationFunction = [=](SharedRealm old_realm, SharedRealm realm, Schema& mutableSchema) { + RLMSchema *oldSchema = [RLMSchema dynamicSchemaFromObjectStoreSchema:old_realm->schema()]; + RLMRealm *oldRealm = [RLMRealm realmWithSharedRealm:old_realm schema:oldSchema]; + + // The destination RLMRealm can't just use the schema from the + // SharedRealm because it doesn't have information about whether or + // not a class was defined in Swift, which effects how new objects + // are created + RLMRealm *newRealm = [RLMRealm realmWithSharedRealm:realm schema:schema.copy]; + + [[[RLMMigration alloc] initWithRealm:newRealm oldRealm:oldRealm schema:mutableSchema] execute:migrationBlock]; + + oldRealm->_realm = nullptr; + newRealm->_realm = nullptr; + }; + } + + try { + realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version, + std::move(migrationFunction)); + } + catch (...) { + RLMRealmTranslateException(error); + return nil; + } + + RLMRealmSetSchemaAndAlign(realm, schema); + RLMRealmCreateAccessors(realm.schema); + + if (!readOnly) { + // initializing the schema started a read transaction, so end it + [realm invalidate]; + } + } + + if (config.cache) { + RLMCacheRealm(config.path, realm); + } + + if (!readOnly) { + realm->_realm->m_binding_context = RLMCreateBindingContext(realm); + } + + return RLMAutorelease(realm); +} + ++ (void)resetRealmState { + [RLMSyncManager _resetStateForTesting]; + RLMClearRealmCache(); + realm::_impl::RealmCoordinator::clear_cache(); + [RLMRealmConfiguration resetRealmConfigurationState]; +} + +- (void)verifyNotificationsAreSupported { + [self verifyThread]; + if (_realm->config().read_only()) { + @throw RLMException(@"Read-only Realms do not change and do not have change notifications"); + } + if (!_realm->can_deliver_notifications()) { + @throw RLMException(@"Can only add notification blocks from within runloops."); + } +} + +- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block { + if (!block) { + @throw RLMException(@"The notification block should not be nil"); + } + [self verifyNotificationsAreSupported]; + + _realm->read_group(); + + if (!_notificationHandlers) { + _notificationHandlers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; + } + + RLMRealmNotificationToken *token = [[RLMRealmNotificationToken alloc] init]; + token.realm = self; + token.block = block; + [_notificationHandlers addObject:token]; + return token; +} + +- (void)sendNotifications:(RLMNotification)notification { + NSAssert(!_realm->config().read_only(), @"Read-only realms do not have notifications"); + + NSUInteger count = _notificationHandlers.count; + if (count == 0) { + return; + } + // call this realms notification blocks + if (count == 1) { + if (auto block = [_notificationHandlers.anyObject block]) { + block(notification, self); + } + } + else { + for (RLMRealmNotificationToken *token in _notificationHandlers.allObjects) { + if (auto block = token.block) { + block(notification, self); + } + } + } +} + +- (RLMRealmConfiguration *)configuration { + RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init]; + configuration.config = _realm->config(); + configuration.dynamic = _dynamic; + configuration.customSchema = _schema; + return configuration; +} + +- (void)beginWriteTransaction { + try { + _realm->begin_transaction(); + } + catch (std::exception &ex) { + @throw RLMException(ex); + } +} + +- (void)commitWriteTransaction { + [self commitWriteTransaction:nil]; +} + +- (BOOL)commitWriteTransaction:(NSError **)outError { + try { + _realm->commit_transaction(); + return YES; + } + catch (...) { + RLMRealmTranslateException(outError); + return NO; + } +} + +- (void)transactionWithBlock:(void(^)(void))block { + [self transactionWithBlock:block error:nil]; +} + +- (BOOL)transactionWithBlock:(void(^)(void))block error:(NSError **)outError { + [self beginWriteTransaction]; + block(); + if (_realm->is_in_transaction()) { + return [self commitWriteTransaction:outError]; + } + return YES; +} + +- (void)cancelWriteTransaction { + try { + _realm->cancel_transaction(); + } + catch (std::exception &ex) { + @throw RLMException(ex); + } +} + +- (void)invalidate { + if (_realm->is_in_transaction()) { + NSLog(@"WARNING: An RLMRealm instance was invalidated during a write " + "transaction and all pending changes have been rolled back."); + } + + [self detachAllEnumerators]; + + for (auto& objectInfo : _info) { + for (RLMObservationInfo *info : objectInfo.second.observedObjects) { + info->willChange(RLMInvalidatedKey); + } + } + + _realm->invalidate(); + + for (auto& objectInfo : _info) { + for (RLMObservationInfo *info : objectInfo.second.observedObjects) { + info->didChange(RLMInvalidatedKey); + } + objectInfo.second.releaseTable(); + } +} + +/** + Replaces all string columns in this Realm with a string enumeration column and compacts the + database file. + + Cannot be called from a write transaction. + + Compaction will not occur if other `RLMRealm` instances exist. + + While compaction is in progress, attempts by other threads or processes to open the database will + wait. + + Be warned that resource requirements for compaction is proportional to the amount of live data in + the database. + + Compaction works by writing the database contents to a temporary database file and then replacing + the database with the temporary one. The name of the temporary file is formed by appending + `.tmp_compaction_space` to the name of the database. + + @return YES if the compaction succeeded. + */ +- (BOOL)compact { + // compact() automatically ends the read transaction, but we need to clean + // up cached state and send invalidated notifications when that happens, so + // explicitly end it first unless we're in a write transaction (in which + // case compact() will throw an exception) + if (!_realm->is_in_transaction()) { + [self invalidate]; + } + + try { + return _realm->compact(); + } + catch (std::exception const& ex) { + @throw RLMException(ex); + } +} + +- (void)dealloc { + if (_realm) { + if (_realm->is_in_transaction()) { + [self cancelWriteTransaction]; + NSLog(@"WARNING: An RLMRealm instance was deallocated during a write transaction and all " + "pending changes have been rolled back. Make sure to retain a reference to the " + "RLMRealm for the duration of the write transaction."); + } + } +} + +- (BOOL)refresh { + return _realm->refresh(); +} + +- (void)addObject:(__unsafe_unretained RLMObject *const)object { + RLMAddObjectToRealm(object, self, false); +} + +- (void)addObjects:(id)array { + for (RLMObject *obj in array) { + if (![obj isKindOfClass:[RLMObject class]]) { + @throw RLMException(@"Cannot insert objects of type %@ with addObjects:. Only RLMObjects are supported.", + NSStringFromClass(obj.class)); + } + [self addObject:obj]; + } +} + +- (void)addOrUpdateObject:(RLMObject *)object { + // verify primary key + if (!object.objectSchema.primaryKeyProperty) { + @throw RLMException(@"'%@' does not have a primary key and can not be updated", object.objectSchema.className); + } + + RLMAddObjectToRealm(object, self, true); +} + +- (void)addOrUpdateObjectsFromArray:(id)array { + for (RLMObject *obj in array) { + [self addOrUpdateObject:obj]; + } +} + +- (void)deleteObject:(RLMObject *)object { + RLMDeleteObjectFromRealm(object, self); +} + +- (void)deleteObjects:(id)array { + if ([array respondsToSelector:@selector(realm)] && [array respondsToSelector:@selector(deleteObjectsFromRealm)]) { + if (self != (RLMRealm *)[array realm]) { + @throw RLMException(@"Can only delete objects from the Realm they belong to."); + } + [array deleteObjectsFromRealm]; + } + else if ([array conformsToProtocol:@protocol(NSFastEnumeration)]) { + for (id obj in array) { + if ([obj isKindOfClass:RLMObjectBase.class]) { + RLMDeleteObjectFromRealm(obj, self); + } + } + } + else { + @throw RLMException(@"Invalid array type - container must be an RLMArray, RLMArray, or NSArray of RLMObjects"); + } +} + +- (void)deleteAllObjects { + RLMDeleteAllObjectsFromRealm(self); +} + +- (RLMResults *)allObjects:(NSString *)objectClassName { + return RLMGetObjects(self, objectClassName, nil); +} + +- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objects:objectClassName where:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objects:(NSString *)objectClassName where:(NSString *)predicateFormat args:(va_list)args { + return [self objects:objectClassName withPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (RLMResults *)objects:(NSString *)objectClassName withPredicate:(NSPredicate *)predicate { + return RLMGetObjects(self, objectClassName, predicate); +} + +- (RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey { + return RLMGetObject(self, className, primaryKey); +} + ++ (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error { + try { + RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init]; + config.fileURL = fileURL; + config.encryptionKey = RLMRealmValidatedEncryptionKey(key); + + uint64_t version = Realm::get_schema_version(config.config); + if (version == realm::ObjectStore::NotVersioned) { + RLMSetErrorOrThrow([NSError errorWithDomain:RLMErrorDomain code:RLMErrorFail userInfo:@{NSLocalizedDescriptionKey:@"Cannot open an uninitialized realm in read-only mode"}], error); + } + return version; + } + catch (std::exception &exp) { + RLMSetErrorOrThrow(RLMMakeError(RLMErrorFail, exp), error); + return RLMNotVersioned; + } +} + ++ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration { + // Preserves backwards compatibility + NSError *error; + [self performMigrationForConfiguration:configuration error:&error]; + return error; +} + ++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error { + if (RLMGetAnyCachedRealmForPath(configuration.config.path)) { + @throw RLMException(@"Cannot migrate Realms that are already open."); + } + + NSError *localError; // Prevents autorelease + BOOL success; + @autoreleasepool { + success = [RLMRealm realmWithConfiguration:configuration error:&localError] != nil; + } + if (!success && error) { + *error = localError; // Must set outside pool otherwise will free anyway + } + return success; +} + +- (RLMObject *)createObject:(NSString *)className withValue:(id)value { + return (RLMObject *)RLMCreateObjectInRealmWithValue(self, className, value, false); +} + +- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(NSData *)key error:(NSError **)error { + key = RLMRealmValidatedEncryptionKey(key); + NSString *path = fileURL.path; + + try { + _realm->write_copy(path.UTF8String, {static_cast(key.bytes), key.length}); + return YES; + } + catch (...) { + __autoreleasing NSError *dummyError; + if (!error) { + error = &dummyError; + } + RLMRealmTranslateException(error); + return NO; + } + + return NO; +} + +- (void)registerEnumerator:(RLMFastEnumerator *)enumerator { + if (!_collectionEnumerators) { + _collectionEnumerators = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory]; + } + [_collectionEnumerators addObject:enumerator]; + +} + +- (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator { + [_collectionEnumerators removeObject:enumerator]; +} + +- (void)detachAllEnumerators { + for (RLMFastEnumerator *enumerator in _collectionEnumerators) { + [enumerator detach]; + } + _collectionEnumerators = nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm b/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm new file mode 100644 index 0000000..c5f5e34 --- /dev/null +++ b/Pods/Realm/Realm/RLMRealmConfiguration+Sync.mm @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealmConfiguration+Sync.h" + +#import "RLMRealmConfiguration_Private.hpp" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMSyncFileManager.h" +#import "RLMSyncManager_Private.hpp" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync_config.hpp" +#import "sync_manager.hpp" + +@implementation RLMRealmConfiguration (Sync) + +#pragma mark - API + +- (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration { + RLMSyncUser *user = syncConfiguration.user; + if (user.state == RLMSyncUserStateError) { + @throw RLMException(@"Cannot set a sync configuration which has an errored-out user."); + } + + NSURL *realmURL = syncConfiguration.realmURL; + // Ensure sync manager is initialized, if it hasn't already been. + [RLMSyncManager sharedManager]; + NSAssert(user.identity, @"Cannot call this method on a user that doesn't have an identity."); + NSURL *localFileURL = [RLMSyncFileManager fileURLForRawRealmURL:realmURL user:user]; + if (syncConfiguration.customFileURL) { + localFileURL = syncConfiguration.customFileURL; + } + self.config.path = [[localFileURL path] UTF8String]; + self.config.in_memory = false; + self.config.sync_config = std::make_shared([syncConfiguration rawConfiguration]); + self.config.schema_mode = realm::SchemaMode::Additive; +} + +- (RLMSyncConfiguration *)syncConfiguration { + if (!self.config.sync_config) { + return nil; + } + realm::SyncConfig& sync_config = *self.config.sync_config; + // Try to get the user + RLMSyncUser *thisUser = [[RLMSyncManager sharedManager] _userForIdentity:@(sync_config.user_tag.c_str())]; + if (!thisUser) { + @throw RLMException(@"Could not find the user this configuration refers to."); + } + NSURL *realmURL = [NSURL URLWithString:@(sync_config.realm_url.c_str())]; + RLMSyncConfiguration *c = [[RLMSyncConfiguration alloc] initWithUser:thisUser + realmURL:realmURL]; + c.stopPolicy = realm::translateStopPolicy(sync_config.stop_policy); + return c; +} + +@end diff --git a/Pods/Realm/Realm/RLMRealmConfiguration.mm b/Pods/Realm/Realm/RLMRealmConfiguration.mm new file mode 100644 index 0000000..b6ea49e --- /dev/null +++ b/Pods/Realm/Realm/RLMRealmConfiguration.mm @@ -0,0 +1,266 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealmConfiguration_Private.h" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMRealm_Private.h" +#import "RLMSchema_Private.hpp" +#import "RLMUtil.hpp" + +#import "schema.hpp" +#import "shared_realm.hpp" + +static NSString *const c_RLMRealmConfigurationProperties[] = { + @"fileURL", + @"inMemoryIdentifier", + @"encryptionKey", + @"readOnly", + @"schemaVersion", + @"migrationBlock", + @"deleteRealmIfMigrationNeeded", + @"dynamic", + @"customSchema", +}; + +static NSString *const c_defaultRealmFileName = @"default.realm"; +RLMRealmConfiguration *s_defaultConfiguration; + +NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *bundleIdentifier) { + return [RLMDefaultDirectoryForBundleIdentifier(bundleIdentifier) + stringByAppendingPathComponent:fileName]; +} + +NSString *RLMRealmPathForFile(NSString *fileName) { + static NSString *directory = RLMDefaultDirectoryForBundleIdentifier(nil); + return [directory stringByAppendingPathComponent:fileName]; +} + +@implementation RLMRealmConfiguration { + realm::Realm::Config _config; +} + +- (realm::Realm::Config&)config { + return _config; +} + ++ (instancetype)defaultConfiguration { + return [[self rawDefaultConfiguration] copy]; +} + ++ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration { + if (!configuration) { + @throw RLMException(@"Cannot set the default configuration to nil."); + } + @synchronized(c_defaultRealmFileName) { + s_defaultConfiguration = [configuration copy]; + } +} + ++ (RLMRealmConfiguration *)rawDefaultConfiguration { + @synchronized(c_defaultRealmFileName) { + if (!s_defaultConfiguration) { + s_defaultConfiguration = [[RLMRealmConfiguration alloc] init]; + } + } + return s_defaultConfiguration; +} + ++ (void)resetRealmConfigurationState { + @synchronized(c_defaultRealmFileName) { + s_defaultConfiguration = nil; + } +} + +- (instancetype)init { + self = [super init]; + if (self) { + static NSURL *defaultRealmURL = [NSURL fileURLWithPath:RLMRealmPathForFile(c_defaultRealmFileName)]; + self.fileURL = defaultRealmURL; + self.schemaVersion = 0; + } + + return self; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + RLMRealmConfiguration *configuration = [[[self class] allocWithZone:zone] init]; + configuration->_config = _config; + configuration->_dynamic = _dynamic; + configuration->_migrationBlock = _migrationBlock; + configuration->_customSchema = _customSchema; + return configuration; +} + +- (NSString *)description { + NSMutableString *string = [NSMutableString stringWithFormat:@"%@ {\n", self.class]; + for (NSString *key : c_RLMRealmConfigurationProperties) { + NSString *description = [[self valueForKey:key] description]; + description = [description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]; + + [string appendFormat:@"\t%@ = %@;\n", key, description]; + } + return [string stringByAppendingString:@"}"]; +} + +static void RLMNSStringToStdString(std::string &out, NSString *in) { + out.resize([in maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]); + if (out.empty()) { + return; + } + + NSUInteger size = out.size(); + [in getBytes:&out[0] + maxLength:size + usedLength:&size + encoding:NSUTF8StringEncoding + options:0 range:{0, in.length} remainingRange:nullptr]; + out.resize(size); +} + +- (NSURL *)fileURL { + if (_config.in_memory || _config.sync_config) { + return nil; + } + return [NSURL fileURLWithPath:@(_config.path.c_str())]; +} + +- (void)setFileURL:(NSURL *)fileURL { + NSString *path = fileURL.path; + if (path.length == 0) { + @throw RLMException(@"Realm path must not be empty"); + } + _config.sync_config = nullptr; + + RLMNSStringToStdString(_config.path, path); + _config.in_memory = false; +} + +- (NSString *)inMemoryIdentifier { + if (!_config.in_memory) { + return nil; + } + return [@(_config.path.c_str()) lastPathComponent]; +} + +- (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier { + if (inMemoryIdentifier.length == 0) { + @throw RLMException(@"In-memory identifier must not be empty"); + } + _config.sync_config = nullptr; + + RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]); + _config.in_memory = true; +} + +- (NSData *)encryptionKey { + return _config.encryption_key.empty() ? nil : [NSData dataWithBytes:_config.encryption_key.data() length:_config.encryption_key.size()]; +} + +- (void)setEncryptionKey:(NSData * __nullable)encryptionKey { + if (NSData *key = RLMRealmValidatedEncryptionKey(encryptionKey)) { + auto bytes = static_cast(key.bytes); + _config.encryption_key.assign(bytes, bytes + key.length); + } + else { + _config.encryption_key.clear(); + } +} + +- (BOOL)readOnly { + return _config.read_only(); +} + +- (void)setReadOnly:(BOOL)readOnly { + if (readOnly) { + if (self.deleteRealmIfMigrationNeeded) { + @throw RLMException(@"Cannot set `readOnly` when `deleteRealmIfMigrationNeeded` is set."); + } + _config.schema_mode = realm::SchemaMode::ReadOnly; + } + else if (self.readOnly) { + _config.schema_mode = realm::SchemaMode::Automatic; + } +} + +- (uint64_t)schemaVersion { + return _config.schema_version; +} + +- (void)setSchemaVersion:(uint64_t)schemaVersion { + if (schemaVersion == RLMNotVersioned) { + @throw RLMException(@"Cannot set schema version to %llu (RLMNotVersioned)", RLMNotVersioned); + } + _config.schema_version = schemaVersion; +} + +- (BOOL)deleteRealmIfMigrationNeeded { + return _config.schema_mode == realm::SchemaMode::ResetFile; +} + +- (void)setDeleteRealmIfMigrationNeeded:(BOOL)deleteRealmIfMigrationNeeded { + if (deleteRealmIfMigrationNeeded) { + if (self.readOnly) { + @throw RLMException(@"Cannot set `deleteRealmIfMigrationNeeded` when `readOnly` is set."); + } + _config.schema_mode = realm::SchemaMode::ResetFile; + } + else if (self.deleteRealmIfMigrationNeeded) { + _config.schema_mode = realm::SchemaMode::Automatic; + } +} + +- (NSArray *)objectClasses { + return [_customSchema.objectSchema valueForKeyPath:@"objectClass"]; +} + +- (void)setObjectClasses:(NSArray *)objectClasses { + self.customSchema = [RLMSchema schemaWithObjectClasses:objectClasses]; +} + +- (void)setDynamic:(bool)dynamic { + _dynamic = dynamic; + _config.cache = !dynamic; +} + +- (bool)cache { + return _config.cache; +} + +- (void)setCache:(bool)cache { + _config.cache = cache; +} + +- (bool)disableFormatUpgrade { + return _config.disable_format_upgrade; +} + +- (void)setDisableFormatUpgrade:(bool)disableFormatUpgrade { + _config.disable_format_upgrade = disableFormatUpgrade; +} + +- (realm::SchemaMode)schemaMode { + return _config.schema_mode; +} + +- (void)setSchemaMode:(realm::SchemaMode)mode { + _config.schema_mode = mode; +} + +@end + diff --git a/Pods/Realm/Realm/RLMRealmUtil.mm b/Pods/Realm/Realm/RLMRealmUtil.mm new file mode 100644 index 0000000..72d1051 --- /dev/null +++ b/Pods/Realm/Realm/RLMRealmUtil.mm @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealmUtil.hpp" + +#import "RLMObjectSchema_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMUtil.hpp" + +#import +#import + +#import "binding_context.hpp" + +#import +#import +#import +#import +#import +#import + +// Global realm state +static std::mutex s_realmCacheMutex; +static std::map s_realmsPerPath; + +void RLMCacheRealm(std::string const& path, RLMRealm *realm) { + std::lock_guard lock(s_realmCacheMutex); + NSMapTable *realms = s_realmsPerPath[path]; + if (!realms) { + s_realmsPerPath[path] = realms = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsObjectPersonality + valueOptions:NSPointerFunctionsWeakMemory]; + } + [realms setObject:realm forKey:@(pthread_mach_thread_np(pthread_self()))]; +} + +RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path) { + std::lock_guard lock(s_realmCacheMutex); + return [s_realmsPerPath[path] objectEnumerator].nextObject; +} + +RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path) { + mach_port_t threadID = pthread_mach_thread_np(pthread_self()); + std::lock_guard lock(s_realmCacheMutex); + return [s_realmsPerPath[path] objectForKey:@(threadID)]; +} + +void RLMClearRealmCache() { + std::lock_guard lock(s_realmCacheMutex); + s_realmsPerPath.clear(); +} + +namespace { +class RLMNotificationHelper : public realm::BindingContext { +public: + RLMNotificationHelper(RLMRealm *realm) : _realm(realm) { } + + bool can_deliver_notifications() const noexcept override { + // The main thread may not be in a run loop yet if we're called from + // something like `applicationDidFinishLaunching:`, but it presumably will + // be in the future + if ([NSThread isMainThread]) { + return true; + } + // Current mode indicates why the current callout from the runloop was made, + // and is null if a runloop callout isn't currently being processed + if (auto mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent())) { + CFRelease(mode); + return true; + } + return false; + } + + void changes_available() override { + @autoreleasepool { + auto realm = _realm; + if (realm && !realm.autorefresh) { + [realm sendNotifications:RLMRealmRefreshRequiredNotification]; + } + } + } + + std::vector get_observed_rows() override { + @autoreleasepool { + if (auto realm = _realm) { + [realm detachAllEnumerators]; + return RLMGetObservedRows(realm->_info); + } + return {}; + } + } + + void will_change(std::vector const& observed, std::vector const& invalidated) override { + @autoreleasepool { + RLMWillChange(observed, invalidated); + } + } + + void did_change(std::vector const& observed, std::vector const& invalidated) override { + try { + @autoreleasepool { + RLMDidChange(observed, invalidated); + [_realm sendNotifications:RLMRealmDidChangeNotification]; + } + } + catch (...) { + // This can only be called during a write transaction if it was + // called due to the transaction beginning, so cancel it to ensure + // exceptions thrown here behave the same as exceptions thrown when + // actually beginning the write + if (_realm.inWriteTransaction) { + [_realm cancelWriteTransaction]; + } + throw; + } + } + +private: + // This is owned by the realm, so it needs to not retain the realm + __weak RLMRealm *const _realm; +}; +} // anonymous namespace + + +std::unique_ptr RLMCreateBindingContext(RLMRealm *realm) { + return std::unique_ptr(new RLMNotificationHelper(realm)); +} diff --git a/Pods/Realm/Realm/RLMResults.mm b/Pods/Realm/Realm/RLMResults.mm new file mode 100644 index 0000000..3b4fc64 --- /dev/null +++ b/Pods/Realm/Realm/RLMResults.mm @@ -0,0 +1,447 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMResults_Private.h" + +#import "RLMArray_Private.hpp" +#import "RLMCollection_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMObservation.hpp" +#import "RLMProperty_Private.h" +#import "RLMQueryUtil.hpp" +#import "RLMRealm_Private.hpp" +#import "RLMSchema_Private.h" +#import "RLMUtil.hpp" + +#import "results.hpp" + +#import +#import +#import + +using namespace realm; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincomplete-implementation" +@implementation RLMNotificationToken +@end +#pragma clang diagnostic pop + +// +// RLMResults implementation +// +@implementation RLMResults { + realm::Results _results; + RLMRealm *_realm; + RLMClassInfo *_info; +} + +- (instancetype)initPrivate { + self = [super init]; + return self; +} + +static void assertKeyPathIsNotNested(NSString *keyPath) { + if ([keyPath rangeOfString:@"."].location != NSNotFound) { + @throw RLMException(@"Nested key paths are not supported yet for KVC collection operators."); + } +} + +[[gnu::noinline]] +[[noreturn]] +static void throwError(NSString *aggregateMethod) { + try { + throw; + } + catch (realm::InvalidTransactionException const&) { + @throw RLMException(@"Cannot modify Results outside of a write transaction"); + } + catch (realm::IncorrectThreadException const&) { + @throw RLMException(@"Realm accessed from incorrect thread"); + } + catch (realm::Results::InvalidatedException const&) { + @throw RLMException(@"RLMResults has been invalidated"); + } + catch (realm::Results::DetatchedAccessorException const&) { + @throw RLMException(@"Object has been invalidated"); + } + catch (realm::Results::IncorrectTableException const& e) { + @throw RLMException(@"Object type '%s' does not match RLMResults type '%s'.", + e.actual.data(), e.expected.data()); + } + catch (realm::Results::OutOfBoundsIndexException const& e) { + @throw RLMException(@"Index %zu is out of bounds (must be less than %zu)", + e.requested, e.valid_count); + } + catch (realm::Results::UnsupportedColumnTypeException const& e) { + @throw RLMException(@"%@ is not supported for %@ property '%s'", + aggregateMethod, + RLMTypeToString((RLMPropertyType)e.column_type), + e.column_name.data()); + } +} + +template +static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) { + try { + return f(); + } + catch (...) { + throwError(aggregateMethod); + } +} + ++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info + results:(realm::Results)results { + RLMResults *ar = [[self alloc] initPrivate]; + ar->_results = std::move(results); + ar->_realm = info.realm; + ar->_info = &info; + return ar; +} + ++ (instancetype)emptyDetachedResults { + return [[self alloc] initPrivate]; +} + +static inline void RLMResultsValidateInWriteTransaction(__unsafe_unretained RLMResults *const ar) { + ar->_realm->_realm->verify_thread(); + ar->_realm->_realm->verify_in_write(); +} + +- (BOOL)isInvalidated { + return translateErrors([&] { return !_results.is_valid(); }); +} + +- (NSUInteger)count { + return translateErrors([&] { return _results.size(); }); +} + +- (NSString *)objectClassName { + return RLMStringDataToNSString(_results.get_object_type()); +} + +- (RLMObjectSchema *)objectSchema { + return _info->rlmObjectSchema; +} + +- (RLMClassInfo *)objectInfo { + return _info; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unused __unsafe_unretained id [])buffer + count:(NSUInteger)len { + __autoreleasing RLMFastEnumerator *enumerator; + if (state->state == 0) { + enumerator = [[RLMFastEnumerator alloc] initWithCollection:self objectSchema:*_info]; + state->extra[0] = (long)enumerator; + state->extra[1] = self.count; + } + else { + enumerator = (__bridge id)(void *)state->extra[0]; + } + + return [enumerator countByEnumeratingWithState:state count:len]; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + NSUInteger index = [self indexOfObjectWhere:predicateFormat args:args]; + va_end(args); + return index; +} + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args { + return [self indexOfObjectWithPredicate:[NSPredicate predicateWithFormat:predicateFormat + arguments:args]]; +} + +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { + if (_results.get_mode() == Results::Mode::Empty) { + return NSNotFound; + } + + Query query = translateErrors([&] { return _results.get_query(); }); + query.and_query(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)); + query.sync_view_if_needed(); + +#if REALM_VER_MAJOR >= 2 + size_t indexInTable; + if (const auto& sort = _results.get_sort()) { + // A sort order is specified so we need to return the first match given that ordering. + TableView table_view = query.find_all(); + table_view.sort(sort); + if (!table_view.size()) { + return NSNotFound; + } + indexInTable = table_view.get_source_ndx(0); + } else { + indexInTable = query.find(); + } + if (indexInTable == realm::not_found) { + return NSNotFound; + } + return RLMConvertNotFound(_results.index_of(indexInTable)); +#else + TableView table_view; + if (const auto& sort = _results.get_sort()) { + // A sort order is specified so we need to return the first match given that ordering. + table_view = query.find_all(); + table_view.sort(sort); + } else { + table_view = query.find_all(0, -1, 1); + } + if (!table_view.size()) { + return NSNotFound; + } + return _results.index_of(table_view.get_source_ndx(0)); +#endif +} + +- (id)objectAtIndex:(NSUInteger)index { + return translateErrors([&] { + return RLMCreateObjectAccessor(_realm, *_info, _results.get(index)); + }); +} + +- (id)firstObject { + auto row = translateErrors([&] { return _results.first(); }); + return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; +} + +- (id)lastObject { + auto row = translateErrors([&] { return _results.last(); }); + return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; +} + +- (NSUInteger)indexOfObject:(RLMObject *)object { + if (!object || (!object->_realm && !object.invalidated)) { + return NSNotFound; + } + + return translateErrors([&] { + return RLMConvertNotFound(_results.index_of(object->_row)); + }); +} + +- (id)valueForKeyPath:(NSString *)keyPath { + if ([keyPath characterAtIndex:0] == '@') { + if ([keyPath isEqualToString:@"@count"]) { + return @(self.count); + } + NSRange operatorRange = [keyPath rangeOfString:@"." options:NSLiteralSearch]; + NSUInteger keyPathLength = keyPath.length; + NSUInteger separatorIndex = operatorRange.location != NSNotFound ? operatorRange.location : keyPathLength; + NSString *operatorName = [keyPath substringWithRange:NSMakeRange(1, separatorIndex - 1)]; + SEL opSelector = NSSelectorFromString([NSString stringWithFormat:@"_%@ForKeyPath:", operatorName]); + BOOL isValidOperator = [self respondsToSelector:opSelector]; + if (!isValidOperator) { + @throw RLMException(@"Unsupported KVC collection operator found in key path '%@'", keyPath); + } + else if (separatorIndex >= keyPathLength - 1) { + @throw RLMException(@"Missing key path for KVC collection operator %@ in key path '%@'", operatorName, keyPath); + } + NSString *operatorKeyPath = [keyPath substringFromIndex:separatorIndex + 1]; + if (isValidOperator) { + return ((id(*)(id, SEL, id))objc_msgSend)(self, opSelector, operatorKeyPath); + } + } + return [super valueForKeyPath:keyPath]; +} + +- (id)valueForKey:(NSString *)key { + return translateErrors([&] { + return RLMCollectionValueForKey(self, key); + }); +} + +- (void)setValue:(id)value forKey:(NSString *)key { + translateErrors([&] { RLMResultsValidateInWriteTransaction(self); }); + RLMCollectionSetValueForKey(self, key, value); +} + +- (NSNumber *)_aggregateForKeyPath:(NSString *)keyPath method:(util::Optional (Results::*)(size_t))method methodName:(NSString *)methodName { + assertKeyPathIsNotNested(keyPath); + return [self aggregate:keyPath method:method methodName:methodName]; +} + +- (NSNumber *)_minForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::min methodName:@"@min"]; +} + +- (NSNumber *)_maxForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::max methodName:@"@max"]; +} + +- (NSNumber *)_sumForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::sum methodName:@"@sum"]; +} + +- (NSNumber *)_avgForKeyPath:(NSString *)keyPath { + return [self _aggregateForKeyPath:keyPath method:&Results::average methodName:@"@avg"]; +} + +- (NSArray *)_unionOfObjectsForKeyPath:(NSString *)keyPath { + assertKeyPathIsNotNested(keyPath); + return translateErrors([&] { + return RLMCollectionValueForKey(self, keyPath); + }); +} + +- (NSArray *)_distinctUnionOfObjectsForKeyPath:(NSString *)keyPath { + return [NSSet setWithArray:[self _unionOfObjectsForKeyPath:keyPath]].allObjects; +} + +- (NSArray *)_unionOfArraysForKeyPath:(NSString *)keyPath { + assertKeyPathIsNotNested(keyPath); + if ([keyPath isEqualToString:@"self"]) { + @throw RLMException(@"self is not a valid key-path for a KVC array collection operator as 'unionOfArrays'."); + } + + return translateErrors([&] { + NSArray *nestedResults = RLMCollectionValueForKey(self, keyPath); + NSMutableArray *flatArray = [NSMutableArray arrayWithCapacity:nestedResults.count]; + for (id array in nestedResults) { + NSArray *nsArray = RLMCollectionValueForKey(array, @"self"); + [flatArray addObjectsFromArray:nsArray]; + } + return flatArray; + }); +} + +- (NSArray *)_distinctUnionOfArraysForKeyPath:(__unused NSString *)keyPath { + return [NSSet setWithArray:[self _unionOfArraysForKeyPath:keyPath]].allObjects; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ... { + va_list args; + va_start(args, predicateFormat); + RLMResults *results = [self objectsWhere:predicateFormat args:args]; + va_end(args); + return results; +} + +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args { + return [self objectsWithPredicate:[NSPredicate predicateWithFormat:predicateFormat arguments:args]]; +} + +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Empty) { + return self; + } + auto query = RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group); + return [RLMResults resultsWithObjectInfo:*_info results:_results.filter(std::move(query))]; + }); +} + +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending { + return [self sortedResultsUsingDescriptors:@[[RLMSortDescriptor sortDescriptorWithProperty:property ascending:ascending]]]; +} + +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties { + if (properties.count == 0) { + return self; + } + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Empty) { + return self; + } + + return [RLMResults resultsWithObjectInfo:*_info results:_results.sort(RLMSortDescriptorFromDescriptors(*_info->table(), properties))]; + }); +} + +- (id)objectAtIndexedSubscript:(NSUInteger)index { + return [self objectAtIndex:index]; +} + +- (id)aggregate:(NSString *)property method:(util::Optional (Results::*)(size_t))method methodName:(NSString *)methodName { + size_t column = _info->tableColumn(property); + auto value = translateErrors([&] { return (_results.*method)(column); }, methodName); + if (!value) { + return nil; + } + return RLMMixedToObjc(*value); +} + +- (id)minOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::min methodName:@"minOfProperty"]; +} + +- (id)maxOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::max methodName:@"maxOfProperty"]; +} + +- (id)sumOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::sum methodName:@"sumOfProperty"]; +} + +- (id)averageOfProperty:(NSString *)property { + return [self aggregate:property method:&Results::average methodName:@"averageOfProperty"]; +} + +- (void)deleteObjectsFromRealm { + return translateErrors([&] { + if (_results.get_mode() == Results::Mode::Table) { + RLMResultsValidateInWriteTransaction(self); + RLMClearTable(*self.objectInfo); + } + else { + RLMTrackDeletions(_realm, ^{ _results.clear(); }); + } + }); +} + +- (NSString *)description { + return RLMDescriptionWithMaxDepth(@"RLMResults", self, RLMDescriptionMaxDepth); +} + +- (NSUInteger)indexInSource:(NSUInteger)index { + return translateErrors([&] { return _results.get(index).get_index(); }); +} + +- (realm::TableView)tableView { + return translateErrors([&] { return _results.get_tableview(); }); +} + +// The compiler complains about the method's argument type not matching due to +// it not having the generic type attached, but it doesn't seem to be possible +// to actually include the generic type +// http://www.openradar.me/radar?id=6135653276319744 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-parameter-types" +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *, RLMCollectionChange *, NSError *))block { + [_realm verifyNotificationsAreSupported]; + return RLMAddNotificationBlock(self, _results, block, true); +} +#pragma clang diagnostic pop + +- (BOOL)isAttached +{ + return !!_realm; +} + +@end + +@implementation RLMLinkingObjects +@end diff --git a/Pods/Realm/Realm/RLMSchema.mm b/Pods/Realm/Realm/RLMSchema.mm new file mode 100644 index 0000000..a3899bd --- /dev/null +++ b/Pods/Realm/Realm/RLMSchema.mm @@ -0,0 +1,338 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSchema_Private.h" + +#import "RLMAccessor.h" +#import "RLMObject_Private.hpp" +#import "RLMObjectSchema_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMRealm_Private.hpp" +#import "RLMSwiftSupport.h" +#import "RLMUtil.hpp" + +#import "object_store.hpp" +#import "schema.hpp" + +#import + +#import +#include + +using namespace realm; + +const uint64_t RLMNotVersioned = realm::ObjectStore::NotVersioned; + +// RLMSchema private properties +@interface RLMSchema () +@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName; +@end + +static RLMSchema *s_sharedSchema = [[RLMSchema alloc] init]; +static NSMutableDictionary *s_localNameToClass = [[NSMutableDictionary alloc] init]; +static NSMutableDictionary *s_privateObjectSubclasses = [[NSMutableDictionary alloc] init]; + +static enum class SharedSchemaState { + Uninitialized, + Initializing, + Initialized +} s_sharedSchemaState = SharedSchemaState::Uninitialized; + +@implementation RLMSchema { + NSArray *_objectSchema; + realm::Schema _objectStoreSchema; +} + +// Caller must @synchronize on s_localNameToClass +static RLMObjectSchema *RLMRegisterClass(Class cls) { + if (RLMObjectSchema *schema = s_privateObjectSubclasses[[cls className]]) { + return schema; + } + + auto prevState = s_sharedSchemaState; + s_sharedSchemaState = SharedSchemaState::Initializing; + RLMObjectSchema *schema = [RLMObjectSchema schemaForObjectClass:cls]; + s_sharedSchemaState = prevState; + + // set unmanaged class on shared shema for unmanaged object creation + schema.unmanagedClass = RLMUnmanagedAccessorClassForObjectClass(schema.objectClass, schema); + + // override sharedSchema class methods for performance + RLMReplaceSharedSchemaMethod(cls, schema); + + s_privateObjectSubclasses[schema.className] = schema; + if ([cls shouldIncludeInDefaultSchema] && prevState != SharedSchemaState::Initialized) { + s_sharedSchema.objectSchemaByName[schema.className] = schema; + } + + return schema; +} + +// Caller must @synchronize on s_localNameToClass +static void RLMRegisterClassLocalNames(Class *classes, NSUInteger count) { + for (NSUInteger i = 0; i < count; i++) { + Class cls = classes[i]; + + if (!RLMIsObjectSubclass(cls) || RLMIsGeneratedClass(cls)) { + continue; + } + + NSString *className = NSStringFromClass(cls); + if ([RLMSwiftSupport isSwiftClassName:className]) { + className = [RLMSwiftSupport demangleClassName:className]; + } + // NSStringFromClass demangles the names for top-level Swift classes + // but not for nested classes. _T indicates it's a Swift symbol, t + // indicates it's a type, and C indicates it's a class. + else if ([className hasPrefix:@"_TtC"]) { + @throw RLMException(@"RLMObject subclasses cannot be nested within other declarations. Please move %@ to global scope.", className); + } + + if (Class existingClass = s_localNameToClass[className]) { + if (existingClass != cls) { + @throw RLMException(@"RLMObject subclasses with the same name cannot be included twice in the same target. " + @"Please make sure '%@' is only linked once to your current target.", className); + } + continue; + } + + s_localNameToClass[className] = cls; + RLMReplaceClassNameMethod(cls, className); + } +} + +- (instancetype)init { + self = [super init]; + if (self) { + _objectSchemaByName = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSArray *)objectSchema { + if (!_objectSchema) { + _objectSchema = [_objectSchemaByName allValues]; + } + return _objectSchema; +} + +- (void)setObjectSchema:(NSArray *)objectSchema { + _objectSchema = objectSchema; + _objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:objectSchema.count]; + for (RLMObjectSchema *object in objectSchema) { + [_objectSchemaByName setObject:object forKey:object.className]; + } +} + +- (RLMObjectSchema *)schemaForClassName:(NSString *)className { + if (RLMObjectSchema *schema = _objectSchemaByName[className]) { + return schema; // fast path for already-initialized schemas + } else if (Class cls = [RLMSchema classForString:className]) { + [cls sharedSchema]; // initialize the schema + return _objectSchemaByName[className]; // try again + } else { + return nil; + } +} + +- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)className { + RLMObjectSchema *schema = [self schemaForClassName:className]; + if (!schema) { + @throw RLMException(@"Object type '%@' not managed by the Realm", className); + } + return schema; +} + ++ (instancetype)schemaWithObjectClasses:(NSArray *)classes { + NSUInteger count = classes.count; + auto classArray = std::make_unique<__unsafe_unretained Class[]>(count); + [classes getObjects:classArray.get() range:NSMakeRange(0, count)]; + + RLMSchema *schema = [[self alloc] init]; + @synchronized(s_localNameToClass) { + RLMRegisterClassLocalNames(classArray.get(), count); + + schema->_objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:count]; + for (Class cls in classes) { + if (!RLMIsObjectSubclass(cls)) { + @throw RLMException(@"Can't add non-Object type '%@' to a schema.", cls); + } + schema->_objectSchemaByName[[cls className]] = RLMRegisterClass(cls); + } + } + + NSMutableArray *errors = [NSMutableArray new]; + // Verify that all of the targets of links are included in the class list + [schema->_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(id, RLMObjectSchema *objectSchema, BOOL *) { + for (RLMProperty *prop in objectSchema.properties) { + if (prop.type != RLMPropertyTypeObject && prop.type != RLMPropertyTypeArray) { + continue; + } + if (!schema->_objectSchemaByName[prop.objectClassName]) { + [errors addObject:[NSString stringWithFormat:@"- '%@.%@' links to class '%@', which is missing from the list of classes managed by the Realm", objectSchema.className, prop.name, prop.objectClassName]]; + } + } + }]; + if (errors.count) { + @throw RLMException(@"Invalid class subset list:\n%@", [errors componentsJoinedByString:@"\n"]); + } + + return schema; +} + ++ (RLMObjectSchema *)sharedSchemaForClass:(Class)cls { + @synchronized(s_localNameToClass) { + // We create instances of Swift objects during schema init, and they + // obviously need to not also try to initialize the schema + if (s_sharedSchemaState == SharedSchemaState::Initializing) { + return nil; + } + + RLMRegisterClassLocalNames(&cls, 1); + return RLMRegisterClass(cls); + } +} + ++ (instancetype)partialSharedSchema { + return s_sharedSchema; +} + +// schema based on runtime objects ++ (instancetype)sharedSchema { + @synchronized(s_localNameToClass) { + // We replace this method with one which just returns s_sharedSchema + // once initialization is complete, but we still need to check if it's + // already complete because it may have been done by another thread + // while we were waiting for the lock + if (s_sharedSchemaState == SharedSchemaState::Initialized) { + return s_sharedSchema; + } + + if (s_sharedSchemaState == SharedSchemaState::Initializing) { + @throw RLMException(@"Illegal recursive call of +[%@ %@]. Note: Properties of Swift `Object` classes must not be prepopulated with queried results from a Realm.", self, NSStringFromSelector(_cmd)); + } + + s_sharedSchemaState = SharedSchemaState::Initializing; + try { + // Make sure we've discovered all classes + { + unsigned int numClasses; + using malloc_ptr = std::unique_ptr<__unsafe_unretained Class[], decltype(&free)>; + malloc_ptr classes(objc_copyClassList(&numClasses), &free); + RLMRegisterClassLocalNames(classes.get(), numClasses); + } + + [s_localNameToClass enumerateKeysAndObjectsUsingBlock:^(NSString *, Class cls, BOOL *) { + RLMRegisterClass(cls); + }]; + } + catch (...) { + s_sharedSchemaState = SharedSchemaState::Uninitialized; + throw; + } + + // Replace this method with one that doesn't need to acquire a lock + Class metaClass = objc_getMetaClass(class_getName(self)); + IMP imp = imp_implementationWithBlock(^{ return s_sharedSchema; }); + class_replaceMethod(metaClass, @selector(sharedSchema), imp, "@@:"); + + s_sharedSchemaState = SharedSchemaState::Initialized; + } + + return s_sharedSchema; +} + +// schema based on tables in a realm ++ (instancetype)dynamicSchemaFromObjectStoreSchema:(Schema const&)objectStoreSchema { + // cache descriptors for all subclasses of RLMObject + NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:objectStoreSchema.size()]; + for (auto &objectSchema : objectStoreSchema) { + RLMObjectSchema *schema = [RLMObjectSchema objectSchemaForObjectStoreSchema:objectSchema]; + [schemaArray addObject:schema]; + } + + // set class array and mapping + RLMSchema *schema = [RLMSchema new]; + schema.objectSchema = schemaArray; + return schema; +} + ++ (Class)classForString:(NSString *)className { + if (Class cls = s_localNameToClass[className]) { + return cls; + } + + if (Class cls = NSClassFromString(className)) { + return RLMIsObjectSubclass(cls) ? cls : nil; + } + + // className might be the local name of a Swift class we haven't registered + // yet, so scan them all then recheck + { + unsigned int numClasses; + std::unique_ptr<__unsafe_unretained Class[], decltype(&free)> classes(objc_copyClassList(&numClasses), &free); + RLMRegisterClassLocalNames(classes.get(), numClasses); + } + + return s_localNameToClass[className]; +} + +- (id)copyWithZone:(NSZone *)zone { + RLMSchema *schema = [[RLMSchema allocWithZone:zone] init]; + schema->_objectSchemaByName = [[NSMutableDictionary allocWithZone:zone] + initWithDictionary:_objectSchemaByName copyItems:YES]; + return schema; +} + +- (BOOL)isEqualToSchema:(RLMSchema *)schema { + if (_objectSchemaByName.count != schema->_objectSchemaByName.count) { + return NO; + } + __block BOOL matches = YES; + [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RLMObjectSchema *objectSchema, BOOL *stop) { + if (![schema->_objectSchemaByName[name] isEqualToObjectSchema:objectSchema]) { + *stop = YES; + matches = NO; + } + }]; + return matches; +} + +- (NSString *)description { + NSMutableString *objectSchemaString = [NSMutableString string]; + NSArray *sort = @[[NSSortDescriptor sortDescriptorWithKey:@"className" ascending:YES]]; + for (RLMObjectSchema *objectSchema in [self.objectSchema sortedArrayUsingDescriptors:sort]) { + [objectSchemaString appendFormat:@"\t%@\n", + [objectSchema.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]]; + } + return [NSString stringWithFormat:@"Schema {\n%@}", objectSchemaString]; +} + +- (Schema)objectStoreCopy { + if (_objectStoreSchema.size() == 0) { + std::vector schema; + schema.reserve(_objectSchemaByName.count); + [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:[&](NSString *, RLMObjectSchema *objectSchema, BOOL *) { + schema.push_back(objectSchema.objectStoreCopy); + }]; + _objectStoreSchema = std::move(schema); + } + return _objectStoreSchema; +} + +@end diff --git a/Pods/Realm/Realm/RLMSwiftSupport.m b/Pods/Realm/Realm/RLMSwiftSupport.m new file mode 100644 index 0000000..e16c79e --- /dev/null +++ b/Pods/Realm/Realm/RLMSwiftSupport.m @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSwiftSupport.h" + +@implementation RLMSwiftSupport + ++ (BOOL)isSwiftClassName:(NSString *)className { + return [className rangeOfString:@"."].location != NSNotFound; +} + ++ (NSString *)demangleClassName:(NSString *)className { + return [className substringFromIndex:[className rangeOfString:@"."].location + 1]; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncConfiguration.mm b/Pods/Realm/Realm/RLMSyncConfiguration.mm new file mode 100644 index 0000000..d42b09c --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncConfiguration.mm @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncConfiguration_Private.hpp" + +#import "RLMSyncManager_Private.hpp" +#import "RLMSyncUser.h" +#import "RLMSyncUtil_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync_manager.hpp" +#import "sync_config.hpp" + +static BOOL isValidRealmURL(NSURL *url) { + NSString *scheme = [url scheme]; + if (![scheme isEqualToString:@"realm"] && ![scheme isEqualToString:@"realms"]) { + return NO; + } + return YES; +} + +@interface RLMSyncConfiguration () { + std::function _error_handler; +} + +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + customFileURL:(nullable NSURL *)customFileURL + stopPolicy:(RLMSyncStopPolicy)stopPolicy + errorHandler:(std::function)errorHandler NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readwrite) RLMSyncUser *user; +@property (nonatomic, readwrite) NSURL *realmURL; + +@end + +@implementation RLMSyncConfiguration + +- (instancetype)initWithRawConfig:(realm::SyncConfig)config { + RLMSyncUser *user = [[RLMSyncManager sharedManager] _userForIdentity:@(config.user_tag.c_str())]; + // Note that `user` is allowed to be nil. Any code which uses this private API must ensure that a sync configuration + // with a nil user is destroyed or gets a valid user before the configuration is exposed to application code. + NSURL *realmURL = [NSURL URLWithString:@(config.realm_url.c_str())]; + RLMSyncStopPolicy stopPolicy = realm::translateStopPolicy(config.stop_policy); + self = [self initWithUser:user + realmURL:realmURL + customFileURL:nil + stopPolicy:stopPolicy + errorHandler:config.error_handler]; + return self; +} + +- (realm::SyncConfig)rawConfiguration { + std::string user_tag = [self.user.identity UTF8String]; + std::string realm_url = [[self.realmURL absoluteString] UTF8String]; + auto stop_policy = realm::translateStopPolicy(self.stopPolicy); + + return realm::SyncConfig(std::move(user_tag), std::move(realm_url), _error_handler, std::move(stop_policy)); +} + +- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url { + return [self initWithUser:user + realmURL:url + customFileURL:nil + stopPolicy:RLMSyncStopPolicyAfterChangesUploaded + errorHandler:nullptr]; +} + +- (instancetype)initWithUser:(RLMSyncUser *)user + realmURL:(NSURL *)url + customFileURL:(nullable NSURL *)customFileURL + stopPolicy:(RLMSyncStopPolicy)stopPolicy + errorHandler:(std::function)errorHandler { + if (self = [super init]) { + self.user = user; + if (!isValidRealmURL(url)) { + @throw RLMException(@"The provided URL (%@) was not a valid Realm URL.", [url absoluteString]); + } + self.customFileURL = customFileURL; + self.stopPolicy = stopPolicy; + self.realmURL = url; + + if (errorHandler) { + _error_handler = std::move(errorHandler); + } else { + // Automatically configure the per-Realm error handler. + _error_handler = [=](int error_code, std::string message, realm::SyncSessionError error_type) { + RLMSyncSession *session = [user sessionForURL:url]; + [[RLMSyncManager sharedManager] _fireErrorWithCode:error_code + message:@(message.c_str()) + session:session + errorClass:error_type]; + }; + } + + return self; + } + return nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncCredential.m b/Pods/Realm/Realm/RLMSyncCredential.m new file mode 100644 index 0000000..15e87c7 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncCredential.m @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncCredential.h" +#import "RLMSyncUtil_Private.h" + +/// A Twitter account as an identity provider. +//extern RLMIdentityProvider const RLMIdentityProviderTwitter; + +RLMIdentityProvider const RLMIdentityProviderDebug = @"debug"; +RLMIdentityProvider const RLMIdentityProviderRealm = @"realm"; +RLMIdentityProvider const RLMIdentityProviderUsernamePassword = @"password"; +RLMIdentityProvider const RLMIdentityProviderFacebook = @"facebook"; +RLMIdentityProvider const RLMIdentityProviderTwitter = @"twitter"; +RLMIdentityProvider const RLMIdentityProviderGoogle = @"google"; +RLMIdentityProvider const RLMIdentityProviderICloud = @"icloud"; + +@interface RLMSyncCredential () + +- (instancetype)initWithCustomToken:(RLMCredentialToken)token + provider:(RLMIdentityProvider)provider + userInfo:(NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readwrite) RLMCredentialToken token; +@property (nonatomic, readwrite) RLMIdentityProvider provider; +@property (nonatomic, readwrite) NSDictionary *userInfo; + +@end + +@implementation RLMSyncCredential + ++ (instancetype)credentialWithFacebookToken:(RLMCredentialToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderFacebook userInfo:nil]; +} + ++ (instancetype)credentialWithGoogleToken:(RLMCredentialToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderGoogle userInfo:nil]; +} + ++ (instancetype)credentialWithICloudToken:(RLMCredentialToken)token { + return [[self alloc] initWithCustomToken:token provider:RLMIdentityProviderICloud userInfo:nil]; +} + ++ (instancetype)credentialWithUsername:(NSString *)username + password:(NSString *)password + actions:(RLMAuthenticationActions)actions { + return [[self alloc] initWithCustomToken:username + provider:RLMIdentityProviderUsernamePassword + userInfo:@{kRLMSyncPasswordKey: password, + kRLMSyncActionsKey: @(actions)}]; +} + ++ (instancetype)credentialWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity { + return [[self alloc] initWithCustomToken:accessToken + provider:RLMIdentityProviderAccessToken + userInfo:@{kRLMSyncIdentityKey: identity}]; +} + +- (instancetype)initWithCustomToken:(RLMCredentialToken)token + provider:(RLMIdentityProvider)provider + userInfo:(NSDictionary *)userInfo { + if (self = [super init]) { + self.token = token; + self.provider = provider; + self.userInfo = userInfo; + return self; + } + return nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncFileManager.mm b/Pods/Realm/Realm/RLMSyncFileManager.mm new file mode 100644 index 0000000..02a6d92 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncFileManager.mm @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncFileManager.h" + +#import "RLMSyncUser.h" +#import "RLMUtil.hpp" + +static NSString *const RLMSyncUtilityFolderName = @"io.realm.object-server-metadata"; +static NSString *const RLMSyncMetadataRealmName = @"sync_metadata.realm"; + +@implementation RLMSyncFileManager + +/** + The directory within which all Realm Object Server related Realm database and support files are stored. This directory + is a subdirectory within the default directory within which normal on-disk Realms are stored. + + The directory will be created if it does not already exist, and then verified. If there was an error setting it up an + exception will be thrown. + */ ++ (NSURL *)_baseDirectory { + static NSURL *s_baseDirectory; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Create the path. + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *base = [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)]; + s_baseDirectory = [base URLByAppendingPathComponent:@"realm-object-server" isDirectory:YES]; + + // If the directory does not already exist, create it. + [manager createDirectoryAtURL:s_baseDirectory + withIntermediateDirectories:YES + attributes:nil + error:nil]; + BOOL isDirectory = YES; + BOOL fileExists = [manager fileExistsAtPath:[s_baseDirectory path] isDirectory:&isDirectory]; + if (!fileExists || !isDirectory) { + @throw RLMException(@"Could not prepare the directory for storing synchronized Realm files."); + } + }); + return s_baseDirectory; +} + +/** + Return the file URL for a directory contained within the sync base directory. If the diretory does not already exist, + it will automatically be created. + */ ++ (NSURL *)_folderPathForString:(nonnull NSString *)folderName { + NSFileManager *manager = [NSFileManager defaultManager]; + NSURL *userDir = [[self _baseDirectory] URLByAppendingPathComponent:folderName]; + [manager createDirectoryAtURL:userDir withIntermediateDirectories:YES attributes:nil error:nil]; + BOOL isDirectory = YES; + BOOL fileExists = [manager fileExistsAtPath:[userDir path] isDirectory:&isDirectory]; + if (!fileExists || !isDirectory) { + @throw RLMException(@"Could not make a directory; a non-directory file already exists."); + } + return userDir; +} + +/** + Return the file URL for the directory storing a given Realm Sync user's state. + */ ++ (NSURL *)_folderPathForUserIdentity:(nonnull NSString *)identity { + NSCharacterSet *alpha = [NSCharacterSet alphanumericCharacterSet]; + NSString *escapedName = [identity stringByAddingPercentEncodingWithAllowedCharacters:alpha]; + if ([escapedName isEqualToString:RLMSyncUtilityFolderName]) { + @throw RLMException(@"Invalid user identity: cannot be a reserved term."); + } + return [self _folderPathForString:escapedName]; +} + +/** + Return the file URL for the sync metadata Realm. + */ ++ (NSURL *)fileURLForMetadata { + NSURL *utilityFolder = [self _folderPathForString:RLMSyncUtilityFolderName]; + return [utilityFolder URLByAppendingPathComponent:RLMSyncMetadataRealmName]; +} + +/** + Return the file URL for a given combination of a Realm Object Server URL and Realm Sync user. + */ ++ (NSURL *)fileURLForRawRealmURL:(NSURL *)url user:(RLMSyncUser *)user { + NSAssert(user.identity, @"Cannot call this method on a user that doesn't yet have an identity..."); + + NSCharacterSet *alpha = [NSCharacterSet alphanumericCharacterSet]; + NSString *filename = [[url absoluteString] stringByAddingPercentEncodingWithAllowedCharacters:alpha]; + + // Create and validate the user directory. + NSURL *userDir = [self _folderPathForUserIdentity:user.identity]; + return [userDir URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.realm", filename]]; +} + +/** + Remove all Realm state for a user. + */ ++ (BOOL)removeFilesForUserIdentity:(NSString *)identity error:(NSError **)error { + NSURL *userDir = [self _folderPathForUserIdentity:identity]; + NSFileManager *manager = [NSFileManager defaultManager]; + return [manager removeItemAtURL:userDir error:error]; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncManager.mm b/Pods/Realm/Realm/RLMSyncManager.mm new file mode 100644 index 0000000..c1cf091 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncManager.mm @@ -0,0 +1,333 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncManager_Private.hpp" + +#import "RLMRealmConfiguration+Sync.h" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncFileManager.h" +#import "RLMSyncSession_Private.h" +#import "RLMSyncUser_Private.hpp" +#import "RLMUtil.hpp" + +#import "sync_config.hpp" +#import "sync_manager.hpp" +#import "sync_metadata.hpp" +#import "sync_session.hpp" + +using namespace realm; +using Level = realm::util::Logger::Level; + +namespace { + +Level levelForSyncLogLevel(RLMSyncLogLevel logLevel) { + switch (logLevel) { + case RLMSyncLogLevelOff: return Level::off; + case RLMSyncLogLevelFatal: return Level::fatal; + case RLMSyncLogLevelError: return Level::error; + case RLMSyncLogLevelWarn: return Level::warn; + case RLMSyncLogLevelInfo: return Level::info; + case RLMSyncLogLevelDetail: return Level::detail; + case RLMSyncLogLevelDebug: return Level::debug; + case RLMSyncLogLevelTrace: return Level::trace; + case RLMSyncLogLevelAll: return Level::all; + } + REALM_UNREACHABLE(); // Unrecognized log level. +} + +struct CocoaSyncLogger : public realm::util::RootLogger { + void do_log(Level, std::string message) override { + NSLog(@"Sync: %@", RLMStringDataToNSString(message)); + } +}; + +struct CocoaSyncLoggerFactory : public realm::SyncLoggerFactory { + std::unique_ptr make_logger(realm::util::Logger::Level level) override { + auto logger = std::make_unique(); + logger->set_level_threshold(level); + return std::move(logger); + } +} s_syncLoggerFactory; + +} // anonymous namespace + +@interface RLMSyncManager () + +- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER; + +@property (nonnull, nonatomic) NSMutableDictionary *activeUsers; +@property (nonnull, nonatomic) NSMutableDictionary *loggedOutUsers; + +@end + +@implementation RLMSyncManager + +static RLMSyncManager *s_sharedManager = nil; +static dispatch_once_t s_onceToken; + ++ (instancetype)sharedManager { + dispatch_once(&s_onceToken, ^{ + s_sharedManager = [[RLMSyncManager alloc] initPrivate]; + }); + return s_sharedManager; +} + +- (RLMSyncSession *)sessionForSyncConfiguration:(RLMSyncConfiguration *)config { + NSURL *fileURL = [RLMSyncFileManager fileURLForRawRealmURL:config.realmURL user:config.user]; + return [config.user _registerSessionForBindingWithFileURL:fileURL + syncConfig:config + standaloneSession:YES + onCompletion:nil]; +} + +- (instancetype)initPrivate { + if (self = [super init]) { + // Create the global error handler. + auto errorLambda = [=](int error_code, std::string message) { + NSError *error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:@{@"description": @(message.c_str()), + @"error": @(error_code)}]; + [self _fireError:error]; + }; + + // Create the static login callback. This is called whenever any Realm wishes to BIND to the Realm Object Server + // for the first time. + SyncLoginFunction loginLambda = [=](const std::string& path, const SyncConfig& config) { + NSString *localFilePath = @(path.c_str()); + RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithRawConfig:config]; + [self _handleBindRequestForSyncConfig:syncConfig + localFilePath:localFilePath]; + }; + + _disableSSLValidation = NO; + self.logLevel = RLMSyncLogLevelWarn; + realm::SyncManager::shared().set_logger_factory(s_syncLoggerFactory); + + self.activeUsers = [NSMutableDictionary dictionary]; + self.loggedOutUsers = [NSMutableDictionary dictionary]; + + // Initialize the sync engine. + SyncManager::shared().set_error_handler(errorLambda); + SyncManager::shared().set_login_function(loginLambda); + NSString *metadataDirectory = [[RLMSyncFileManager fileURLForMetadata] path]; + bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION"); + _metadata_manager = std::make_unique([metadataDirectory UTF8String], should_encrypt); + [self _cleanUpMarkedUsers]; + [self _loadPersistedUsers]; + return self; + } + return nil; +} + +- (void)setLogLevel:(RLMSyncLogLevel)logLevel { + _logLevel = logLevel; + realm::SyncManager::shared().set_log_level(levelForSyncLogLevel(logLevel)); +} + +- (NSString *)appID { + if (!_appID) { + _appID = [[NSBundle mainBundle] bundleIdentifier] ?: @"(none)"; + } + return _appID; +} + +- (void)setDisableSSLValidation:(BOOL)disableSSLValidation { + _disableSSLValidation = disableSSLValidation; + realm::SyncManager::shared().set_client_should_validate_ssl(!disableSSLValidation); +} + + +#pragma mark - Private API + ++ (void)_resetStateForTesting { + // Log out all the logged-in users. This will immediately kill any open sessions. + NSMutableArray *buffer = [NSMutableArray array]; + if (s_sharedManager) { + for (id key in s_sharedManager.activeUsers) { + [buffer addObject:s_sharedManager.activeUsers[key]]; + } + } + [[s_sharedManager.activeUsers allValues] makeObjectsPerformSelector:@selector(logOut)]; + + // Reset the singleton. + s_onceToken = 0; + s_sharedManager = nil; + + // Destroy the metadata Realm. + NSURL *metadataURL = [RLMSyncFileManager fileURLForMetadata]; + // FIXME: replace this with the appropriate call to `[RLMSyncFileManager deleteRealmAtPath:]` once that code is in. + NSFileManager *manager = [NSFileManager defaultManager]; + [manager removeItemAtURL:metadataURL error:nil]; + [manager removeItemAtURL:[metadataURL URLByAppendingPathExtension:@"lock"] error:nil]; + [manager removeItemAtURL:[metadataURL URLByAppendingPathExtension:@"management"] error:nil]; +} + +- (void)_fireError:(NSError *)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.errorHandler) { + self.errorHandler(error, nil); + } + }); +} + +- (void)_fireErrorWithCode:(int)errorCode + message:(NSString *)message + session:(RLMSyncSession *)session + errorClass:(realm::SyncSessionError)errorClass { + NSError *error; + + switch (errorClass) { + case realm::SyncSessionError::UserFatal: + // Kill the user. + [[session parentUser] setState:RLMSyncUserStateError]; + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientUserError + userInfo:@{@"description": message, + @"error": @(errorCode)}]; + break; + case realm::SyncSessionError::SessionFatal: + // Kill the session. + [session _invalidate]; + case realm::SyncSessionError::AccessDenied: + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:@{@"description": message, + @"error": @(errorCode)}]; + break; + case realm::SyncSessionError::Debug: + // Report the error. There's nothing the user can do about it, though. + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientInternalError + userInfo:@{@"description": message, + @"error": @(errorCode)}]; + break; + } + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self.errorHandler + || (errorClass == realm::SyncSessionError::Debug && self.logLevel >= RLMSyncLogLevelDebug)) { + return; + } + self.errorHandler(error, nil); + }); +} + +- (SyncMetadataManager&)_metadataManager { + return *_metadata_manager; +} + +/// Load persisted users from the object store, and then turn them into actual users. +- (void)_loadPersistedUsers { + @synchronized(_activeUsers) { + SyncUserMetadataResults users = _metadata_manager->all_unmarked_users(); + for (size_t i = 0; i < users.size(); i++) { + RLMSyncUser *user = [[RLMSyncUser alloc] initWithMetadata:users.get(i)]; + self.activeUsers[user.identity] = user; + } + } +} + +/// Clean up marked users and destroy them. +- (void)_cleanUpMarkedUsers { + @synchronized(_activeUsers) { + std::vector dead_users; + SyncUserMetadataResults users_to_remove = _metadata_manager->all_users_marked_for_removal(); + for (size_t i = 0; i < users_to_remove.size(); i++) { + auto user = users_to_remove.get(i); + // FIXME: delete user data in a different way? (This deletes a logged-out user's data as soon as the app + // launches again, which might not be how some apps want to treat their data.) + [RLMSyncFileManager removeFilesForUserIdentity:@(user.identity().c_str()) error:nil]; + dead_users.emplace_back(std::move(user)); + } + for (auto user : dead_users) { + user.remove(); + } + } +} + +- (void)_handleBindRequestForSyncConfig:(RLMSyncConfiguration *)syncConfig + localFilePath:(NSString *)filePathString { + @synchronized(self) { + RLMSyncUser *user = syncConfig.user; + if (!user || (user.state == RLMSyncUserStateError)) { + // Can't do anything, the configuration is malformed. + // FIXME: report an error via the global error handler. + return; + } else { + // FIXME: should the completion block actually do anything? + [user _registerSessionForBindingWithFileURL:[NSURL fileURLWithPath:filePathString] + syncConfig:syncConfig + standaloneSession:NO + onCompletion:self.sessionCompletionNotifier]; + } + } +} + +- (void)_removeInvalidUsers { + NSMutableArray *keyBuffer = [NSMutableArray array]; + for (NSString *key in self.activeUsers) { + if (self.activeUsers[key].state == RLMSyncUserStateError) { + [keyBuffer addObject:key]; + } + } + [self.activeUsers removeObjectsForKeys:keyBuffer]; +} + +- (NSArray *)_allUsers { + @synchronized(_activeUsers) { + [self _removeInvalidUsers]; + return [self.activeUsers allValues]; + } +} + +- (RLMSyncUser *)_registerUser:(RLMSyncUser *)user { + @synchronized(_activeUsers) { + [self _removeInvalidUsers]; + NSString *identity = user.identity; + if (RLMSyncUser *user = [self.activeUsers objectForKey:identity]) { + return user; + } else if (RLMSyncUser *user = [self.loggedOutUsers objectForKey:identity]) { + [user setState:RLMSyncUserStateActive]; + [self.loggedOutUsers removeObjectForKey:identity]; + [self.activeUsers setObject:user forKey:identity]; + return user; + } + [self.activeUsers setObject:user forKey:identity]; + return nil; + } +} + +- (void)_deregisterLoggedOutUser:(RLMSyncUser *)user { + @synchronized(_activeUsers) { + NSString *identity = user.identity; + RLMSyncUser *user = [self.activeUsers objectForKey:identity]; + if (!user) { + @throw RLMException(@"Cannot unregister a user that isn't registered."); + } + [self.activeUsers removeObjectForKey:identity]; + self.loggedOutUsers[identity] = user; + } +} + +- (RLMSyncUser *)_userForIdentity:(NSString *)identity { + @synchronized (_activeUsers) { + return [self.activeUsers objectForKey:identity] ?: [self.loggedOutUsers objectForKey:identity]; + } +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncSession.mm b/Pods/Realm/Realm/RLMSyncSession.mm new file mode 100644 index 0000000..5e03a9c --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncSession.mm @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncSession_Private.h" + +#import "RLMAuthResponseModel.h" +#import "RLMNetworkClient.h" +#import "RLMRealmConfiguration+Sync.h" +#import "RLMSyncConfiguration.h" +#import "RLMSyncManager_Private.hpp" +#import "RLMSyncSessionHandle.hpp" +#import "RLMSyncUser_Private.hpp" +#import "RLMSyncUtil.h" +#import "RLMTokenModels.h" +#import "RLMUtil.hpp" + +#import "sync_manager.hpp" + +@implementation RLMSessionBindingPackage + +- (instancetype)initWithFileURL:(NSURL *)fileURL + syncConfig:(RLMSyncConfiguration *)syncConfig + standalone:(BOOL)isStandalone + block:(RLMSyncBasicErrorReportingBlock)block { + if (self = [super init]) { + self.fileURL = fileURL; + self.syncConfig = syncConfig; + self.isStandalone = isStandalone; + self.block = block; + return self; + } + return nil; +} + +@end + +@interface RLMSyncSession () + +@property (nonatomic, readwrite) RLMSyncSessionState state; +@property (nonatomic, readwrite) RLMSyncUser *parentUser; +@property (nonatomic, readwrite) NSURL *realmURL; + +@property (nullable, nonatomic) RLMSyncSessionHandle *sessionHandle; + +@end + +@implementation RLMSyncSession + +- (instancetype)initWithFileURL:(NSURL *)fileURL realmURL:(NSURL *)realmURL { + if (self = [super init]) { + self.fileURL = fileURL; + self.realmURL = realmURL; + self.resolvedPath = nil; + self.deferredBindingPackage = nil; + _state = RLMSyncSessionStateUnbound; + return self; + } + return nil; +} + +- (nullable RLMSyncConfiguration *)configuration { + RLMSyncUser *user = self.parentUser; + if (user && self.state != RLMSyncSessionStateInvalid) { + return [[RLMSyncConfiguration alloc] initWithUser:user realmURL:self.realmURL]; + } + return nil; +} + +- (void)_logOut { + [self.sessionHandle logOut]; + [self.refreshTimer invalidate]; + _state = RLMSyncSessionStateLoggedOut; +} + +- (void)_invalidate { + [self.refreshTimer invalidate]; + _state = RLMSyncSessionStateInvalid; + self.sessionHandle = nil; + [self.parentUser _deregisterSessionWithRealmURL:self.realmURL]; + self.parentUser = nil; +} + +#pragma mark - per-Realm access token API + +- (void)configureWithAccessToken:(RLMServerToken)token + expiry:(NSTimeInterval)expiry + user:(RLMSyncUser *)user + handle:(RLMSyncSessionHandle *)handle { + self.parentUser = user; + self.accessToken = token; + self.accessTokenExpiry = expiry; + self.sessionHandle = handle; + [self _scheduleRefreshTimer]; +} + +- (void)_scheduleRefreshTimer { + static NSTimeInterval const refreshBuffer = 10; + + [self.refreshTimer invalidate]; + NSTimeInterval refreshTime = self.accessTokenExpiry - refreshBuffer; + NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSince1970:refreshTime] + interval:1 + target:self + selector:@selector(_refreshForTimer:) + userInfo:nil + repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; + self.refreshTimer = timer; +} + +- (void)_refreshForTimer:(__unused NSTimer *)timer { + [self _refresh]; +} + +- (void)_refresh { + RLMSyncUser *user = self.parentUser; + if (!user || !self.resolvedPath) { + return; + } + RLMServerToken refreshToken = user.refreshToken; + + NSDictionary *json = @{ + kRLMSyncProviderKey: @"realm", + kRLMSyncPathKey: self.resolvedPath, + kRLMSyncDataKey: refreshToken, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + }; + + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:YES + requireRefreshToken:NO]; + if (!model) { + // Malformed JSON + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + [[RLMSyncManager sharedManager] _fireError:error]; + return; + } else { + // Success + // For now, assume just one access token. + RLMTokenModel *tokenModel = model.accessToken; + self.accessToken = model.accessToken.token; + self.accessTokenExpiry = tokenModel.tokenData.expires; + [self _scheduleRefreshTimer]; + + [self refreshAccessToken:tokenModel.token serverURL:nil]; + } + } else { + // Something else went wrong + NSError *syncError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncUnderlyingErrorKey: error}]; + [[RLMSyncManager sharedManager] _fireError:syncError]; + // Certain errors should trigger a retry. + if (error.domain == NSURLErrorDomain) { + BOOL shouldRetry = NO; + switch (error.code) { + case NSURLErrorCannotConnectToHost: + shouldRetry = YES; + // FIXME: 120 seconds is an arbitrarily chosen value, consider rationalizing it. + self.accessTokenExpiry = [[NSDate dateWithTimeIntervalSinceNow:120] timeIntervalSince1970]; + break; + case NSURLErrorNotConnectedToInternet: + case NSURLErrorNetworkConnectionLost: + case NSURLErrorTimedOut: + case NSURLErrorDNSLookupFailed: + case NSURLErrorCannotFindHost: + shouldRetry = YES; + // FIXME: 30 seconds is an arbitrarily chosen value, consider rationalizing it. + self.accessTokenExpiry = [[NSDate dateWithTimeIntervalSinceNow:30] timeIntervalSince1970]; + break; + default: + break; + } + if (shouldRetry) { + [self _scheduleRefreshTimer]; + } + } + } + }; + [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + server:user.authenticationServer + JSON:json + completion:handler]; +} + +- (void)refreshAccessToken:(NSString *)token serverURL:(NSURL *)serverURL +{ + if ([self.sessionHandle refreshAccessToken:token serverURL:serverURL]) { + self.state = RLMSyncSessionStateActive; + } + else { + [self _invalidate]; + } +} + +- (void)setState:(RLMSyncSessionState)state { + // At all state transitions, check to see if the session should be invalidated. + if ([self.sessionHandle sessionIsInErrorState]) { + [self _invalidate]; + return; + } + _state = state; + if (state == RLMSyncSessionStateActive) { + self.deferredBindingPackage = nil; + } +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncSessionHandle.mm b/Pods/Realm/Realm/RLMSyncSessionHandle.mm new file mode 100644 index 0000000..d301d24 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncSessionHandle.mm @@ -0,0 +1,206 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncSessionHandle.hpp" + +#import "sync_session.hpp" + +using namespace realm; + +@interface RLMSyncWeakSessionHandle : RLMSyncSessionHandle { +@public + std::weak_ptr _ptr; +} +@end + +@interface RLMSyncStrongSessionHandle : RLMSyncSessionHandle { +@public + std::shared_ptr _ptr; +} +@end + +#pragma mark - Weak session handle + +@implementation RLMSyncWeakSessionHandle + +- (BOOL)sessionIsInErrorState { + if (auto pointer = _ptr.lock()) { + return !(pointer->is_valid()); + } + return NO; +} + +- (BOOL)sessionStillExists { + return bool(_ptr.lock()) == true; +} + +- (BOOL)refreshAccessToken:(NSString *)accessToken serverURL:(NSURL *)serverURL { + if (auto pointer = _ptr.lock()) { + pointer->refresh_access_token(accessToken.UTF8String, + serverURL ? util::make_optional(serverURL.absoluteString.UTF8String) + : util::none); + return YES; + } + return NO; +} + +- (void)logOut { + if (auto pointer = _ptr.lock()) { + pointer->log_out(); + } +} + +- (void)revive { + if (auto pointer = _ptr.lock()) { + pointer->revive_if_needed(); + } +} + +- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue + callback:(void(^)(void))callback { + if (auto pointer = _ptr.lock()) { + queue = queue ?: dispatch_get_main_queue(); + pointer->wait_for_upload_completion([=](){ + dispatch_async(queue, ^{ + callback(); + }); + }); + return YES; + } + return NO; +} + +- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue + callback:(void(^)(void))callback { + if (auto pointer = _ptr.lock()) { + queue = queue ?: dispatch_get_main_queue(); + pointer->wait_for_download_completion([=](){ + dispatch_async(queue, ^{ + callback(); + }); + }); + return YES; + } + return NO; +} + +@end + +#pragma mark - Strong session handle + +@implementation RLMSyncStrongSessionHandle + +- (BOOL)sessionIsInErrorState { + return !(_ptr->is_valid()); +} + +- (BOOL)sessionStillExists { + return YES; +} + +- (BOOL)refreshAccessToken:(NSString *)accessToken serverURL:(NSURL *)serverURL { + _ptr->refresh_access_token(accessToken.UTF8String, + serverURL ? util::make_optional(serverURL.absoluteString.UTF8String) + : util::none); + return YES; +} + +- (void)logOut { + _ptr->log_out(); +} + +- (void)revive { + _ptr->revive_if_needed(); +} + +- (BOOL)waitForUploadCompletionOnQueue:(dispatch_queue_t)queue + callback:(void(^)(void))callback { + queue = queue ?: dispatch_get_main_queue(); + _ptr->wait_for_upload_completion([=](){ + dispatch_async(queue, ^{ + callback(); + }); + }); + return YES; +} + +- (BOOL)waitForDownloadCompletionOnQueue:(dispatch_queue_t)queue + callback:(void(^)(void))callback { + queue = queue ?: dispatch_get_main_queue(); + _ptr->wait_for_download_completion([=](){ + dispatch_async(queue, ^{ + callback(); + }); + }); + return YES; +} + +@end + +#pragma mark - Abstract base class + +@implementation RLMSyncSessionHandle + ++ (instancetype)syncSessionHandleForWeakPointer:(std::shared_ptr)session { + RLMSyncWeakSessionHandle *h = [[RLMSyncWeakSessionHandle alloc] init]; + h->_ptr = std::move(session); + return h; +} + ++ (instancetype)syncSessionHandleForPointer:(std::shared_ptr)session { + RLMSyncStrongSessionHandle *h = [[RLMSyncStrongSessionHandle alloc] init]; + h->_ptr = std::move(session); + return h; +} + +- (void)logOut { + NSAssert(NO, @"Subclasses must override..."); +} + +- (BOOL)sessionIsInErrorState { + NSAssert(NO, @"Subclasses must override..."); + return NO; +} + +- (BOOL)sessionStillExists { + NSAssert(NO, @"Subclasses must override..."); + return NO; +} + +- (BOOL)refreshAccessToken:(__unused NSString *)accessToken serverURL:(__unused NSURL *)serverURL { + NSAssert(NO, @"Subclasses must override..."); + return NO; +} + +- (void)revive { + NSAssert(NO, @"Subclasses must override..."); +} + +- (BOOL)waitForUploadCompletionOnQueue:(__unused dispatch_queue_t)queue + callback:(__unused void(^)(void))callback { + NSAssert(NO, @"Subclasses must override..."); + return NO; +} + +- (BOOL)waitForDownloadCompletionOnQueue:(__unused dispatch_queue_t)queue + callback:(__unused void(^)(void))callback { + NSAssert(NO, @"Subclasses must override..."); + return NO; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncUser.mm b/Pods/Realm/Realm/RLMSyncUser.mm new file mode 100644 index 0000000..0642501 --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncUser.mm @@ -0,0 +1,507 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncUser_Private.hpp" + +#import "RLMAuthResponseModel.h" +#import "RLMNetworkClient.h" +#import "RLMSyncConfiguration_Private.hpp" +#import "RLMSyncManager_Private.hpp" +#import "RLMSyncSession_Private.h" +#import "RLMSyncSessionHandle.hpp" +#import "RLMTokenModels.h" +#import "RLMUtil.hpp" + +#import "sync_manager.hpp" +#import "sync_metadata.hpp" +#import "sync_session.hpp" + +using namespace realm; + +@interface RLMSyncUser () + +- (instancetype)initWithAuthServer:(nullable NSURL *)authServer NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readwrite) RLMSyncUserState state; + +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) NSURL *authenticationServer; + +@property (nonatomic) NSMutableDictionary *sessionsStorage; +@property (nonatomic) NSDictionary *loggedOutSessions; + +@property (nonatomic) RLMServerToken directAccessToken; + +@end + +@implementation RLMSyncUser + +#pragma mark - static API + ++ (NSArray *)all { + return [[RLMSyncManager sharedManager] _allUsers]; +} + +#pragma mark - API + +- (instancetype)initWithAuthServer:(nullable NSURL *)authServer { + if (self = [super init]) { + self.state = RLMSyncUserStateLoggedOut; + self.directAccessToken = nil; + self.authenticationServer = authServer; + self.sessionsStorage = [NSMutableDictionary dictionary]; + // NOTE: If we add support for anonymous users, we will need to register the user to the global user store + // when the user is first created, versus when the user logs in. + return self; + } + return nil; +} + ++ (void)authenticateWithCredential:(RLMSyncCredential *)credential + authServerURL:(NSURL *)authServerURL + onCompletion:(RLMUserCompletionBlock)completion { + [self authenticateWithCredential:credential + authServerURL:authServerURL + timeout:30 + onCompletion:completion]; +} + ++ (void)authenticateWithCredential:(RLMSyncCredential *)credential + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + onCompletion:(RLMUserCompletionBlock)completion { + RLMSyncUser *user = [[RLMSyncUser alloc] initWithAuthServer:authServerURL]; + [RLMSyncUser _performLogInForUser:user + credential:credential + authServerURL:authServerURL + timeout:timeout + completionBlock:completion]; +} + +- (void)logOut { + if (self.state != RLMSyncUserStateActive || !self.identity) { + // FIXME: report a warning to the global error handler? + return; + } + self.state = RLMSyncUserStateLoggedOut; + for (NSURL *url in self.sessionsStorage) { + [self.sessionsStorage[url] _logOut]; + } + self.loggedOutSessions = [self.sessionsStorage copy]; + self.sessionsStorage = [NSMutableDictionary dictionary]; + [[RLMSyncManager sharedManager] _deregisterLoggedOutUser:self]; + auto metadata = SyncUserMetadata([[RLMSyncManager sharedManager] _metadataManager], + [self.identity UTF8String], + false); + metadata.mark_for_removal(); +} + +- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url { + RLMSyncSession *session = [self.sessionsStorage objectForKey:url]; + RLMSyncSessionHandle *handle = [session sessionHandle]; + if (handle && ![handle sessionStillExists]) { + [self.sessionsStorage removeObjectForKey:url]; + return nil; + } else if ([handle sessionIsInErrorState]) { + [session _invalidate]; + return nil; + } + return session; +} + +- (NSArray *)allSessions { + NSMutableArray *buffer = [NSMutableArray arrayWithCapacity:self.sessionsStorage.count]; + NSArray *allURLs = [self.sessionsStorage allKeys]; + for (NSURL *url in allURLs) { + RLMSyncSession *session = [self sessionForURL:url]; + if (session) { + [buffer addObject:session]; + } + } + return [buffer copy]; +} + + +#pragma mark - Private API + +- (void)_enterErrorState { + self.state = RLMSyncUserStateError; +} + +- (void)_enterActiveState { + self.state = RLMSyncUserStateActive; +} + +- (void)_deregisterSessionWithRealmURL:(NSURL *)realmURL { + [self.sessionsStorage removeObjectForKey:realmURL]; +} + +- (instancetype)initWithMetadata:(SyncUserMetadata)metadata { + NSURL *url = nil; + if (metadata.server_url()) { + url = [NSURL URLWithString:@(metadata.server_url()->c_str())]; + } + self = [self initWithAuthServer:url]; + self.identity = @(metadata.identity().c_str()); + if (auto user_token = metadata.user_token()) { + // FIXME: Once the new auth system is enabled, rename "refreshToken" to "userToken" to reflect its new role. + self.refreshToken = @(user_token->c_str()); + self.state = RLMSyncUserStateActive; + } else { + // For now, throw an exception. In the future we may want to allow for "anonymous" style users. + @throw RLMException(@"Invalid persisted user: there must be a valid access token."); + } + return self; +} + +- (void)_updatePersistedMetadata { + if (!self.refreshToken) { + // For now, throw an exception. In the future we may want to allow for "anonymous" style users. + @throw RLMException(@"Invalid persisted user: there must be a valid access token."); + } + + NSURL *authServer = self.authenticationServer; + NSString *refreshToken = self.refreshToken; + auto server = authServer ? util::Optional([[authServer absoluteString] UTF8String]) : none; + auto token = refreshToken ? util::Optional([refreshToken UTF8String]) : none; + auto metadata = SyncUserMetadata([[RLMSyncManager sharedManager] _metadataManager], [self.identity UTF8String]); + metadata.set_state(server, token); +} + ++ (void)_performLogInForUser:(RLMSyncUser *)user + credential:(RLMSyncCredential *)credential + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + completionBlock:(RLMUserCompletionBlock)completion { + // Wrap the completion block. + RLMUserCompletionBlock theBlock = ^(RLMSyncUser *user, NSError *error){ + if (!completion) { return; } + dispatch_async(dispatch_get_main_queue(), ^{ + completion(user, error); + }); + }; + + // Special credential login should be treated differently. + if (credential.provider == RLMIdentityProviderAccessToken) { + [self _performLoginForDirectAccessTokenCredential:credential user:user completionBlock:theBlock]; + return; + } + + // Prepare login network request + NSMutableDictionary *json = [@{ + kRLMSyncProviderKey: credential.provider, + kRLMSyncDataKey: credential.token, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + } mutableCopy]; + NSMutableDictionary *info = [(credential.userInfo ?: @{}) mutableCopy]; + + if (credential.provider == RLMIdentityProviderUsernamePassword) { + RLMAuthenticationActions actions = [info[kRLMSyncActionsKey] integerValue]; + if (actions & RLMAuthenticationActionsCreateAccount) { + info[kRLMSyncRegisterKey] = @(YES); + } + } + + if ([info count] > 0) { + // Munge user info into the JSON request. + json[@"user_info"] = info; + } + + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:NO + requireRefreshToken:YES]; + if (!model) { + // Malformed JSON + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + theBlock(nil, error); + return; + } else { + // Success: store the tokens. + user.identity = model.refreshToken.tokenData.identity; + user.refreshToken = model.refreshToken.token; + RLMSyncUser *existingUser = [[RLMSyncManager sharedManager] _registerUser:user]; + RLMSyncUser *actualUser = existingUser ?: user; + if (existingUser) { + [actualUser _mergeDataFromDuplicateUser:user]; + } + actualUser.state = RLMSyncUserStateActive; + [actualUser _updatePersistedMetadata]; + [actualUser _bindAllDeferredRealms]; + theBlock(actualUser, nil); + } + } else { + // Something else went wrong + theBlock(nil, error); + } + }; + [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + server:authServerURL + JSON:json + timeout:timeout + completion:handler]; +} + ++ (void)_performLoginForDirectAccessTokenCredential:(RLMSyncCredential *)credential + user:(RLMSyncUser *)user + completionBlock:(nonnull RLMUserCompletionBlock)completion { + user.directAccessToken = credential.token; + NSString *identity = credential.userInfo[kRLMSyncIdentityKey]; + NSAssert(identity != nil, @"Improperly created direct access token credential."); + user.identity = identity; + RLMSyncUser *existingUser = [[RLMSyncManager sharedManager] _registerUser:user]; + RLMSyncUser *actualUser = existingUser ?: user; + if (existingUser) { + [actualUser _mergeDataFromDuplicateUser:user]; + } + actualUser.state = RLMSyncUserStateActive; + [actualUser _bindAllDeferredRealms]; + completion(actualUser, nil); +} + +/** + The argument to this method represents a duplicate, not-yet-active user; the receiver is an existing user. Merge the + state of the duplicate user into the existing user, including the most up-to-date tokens and any sessions that are + waiting to be activated. + */ +- (void)_mergeDataFromDuplicateUser:(RLMSyncUser *)user { + NSAssert(user.state != RLMSyncUserStateActive, @"Erroneous user-to-be-merged: user can't be active."); + NSAssert([self.identity isEqualToString:user.identity], @"Logic error: can only merge two equivalent users."); + + self.directAccessToken = user.directAccessToken; + self.refreshToken = user.refreshToken; + // Move over any sessions that are waiting to be started up. + for (NSURL *url in user.sessionsStorage) { + RLMSyncSession *session = [user.sessionsStorage objectForKey:url]; + NSAssert(session.state != RLMSyncSessionStateActive, + @"Logic error: a newly-created user can't have active sessions."); + if (session.state == RLMSyncSessionStateUnbound) { + [self.sessionsStorage setObject:session forKey:url]; + } + } +} + +// Upon successfully logging in, bind any Realm which was opened and registered to the user previously. +- (void)_bindAllDeferredRealms { + NSAssert(self.state == RLMSyncUserStateActive, + @"_bindAllDeferredRealms can't be called unless the user is logged in."); + for (NSURL *key in self.sessionsStorage) { + RLMSyncSession *session = self.sessionsStorage[key]; + RLMSessionBindingPackage *package = session.deferredBindingPackage; + if (session.state == RLMSyncSessionStateUnbound && package) { + [self _bindSessionWithLocalFileURL:package.fileURL + syncConfig:package.syncConfig + standalone:package.isStandalone + onCompletion:package.block]; + } + } + for (NSURL *key in self.loggedOutSessions) { + RLMSyncSession *session = self.loggedOutSessions[key]; + RLMSyncSessionHandle *handle = session.sessionHandle; + if ([handle sessionStillExists] && ![handle sessionIsInErrorState]) { + // If the session still exists, there's at least one strong reference to it somewhere. Revive it. + // This will start the login handshake if necessary. + [handle revive]; + } + self.sessionsStorage[key] = session; + } + self.loggedOutSessions = nil; +} + +- (void)_bindSessionWithDirectAccessToken:(RLMServerToken)accessToken + syncConfig:(RLMSyncConfiguration *)syncConfig + localFileURL:(NSURL *)fileURL + standalone:(BOOL)isStandalone + onCompletion:(RLMSyncBasicErrorReportingBlock)completion { + NSURL *realmURL = syncConfig.realmURL; + RLMSyncSession *session = self.sessionsStorage[realmURL]; + std::string file_path = [[fileURL path] UTF8String]; + std::string realm_url = [[realmURL absoluteString] UTF8String]; + + RLMSyncSessionHandle *sessionHandle; + auto underlyingSession = SyncManager::shared().get_session(file_path, syncConfig.rawConfiguration); + if (isStandalone) { + sessionHandle = [RLMSyncSessionHandle syncSessionHandleForPointer:underlyingSession]; + } else { + sessionHandle = [RLMSyncSessionHandle syncSessionHandleForWeakPointer:underlyingSession]; + } + [session configureWithAccessToken:accessToken + expiry:[[NSDate distantFuture] timeIntervalSince1970] + user:self + handle:sessionHandle]; + [session refreshAccessToken:accessToken serverURL:realmURL]; + + if (completion) { + bool success = session.state != RLMSyncSessionStateInvalid; + completion(success ? nil : [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + } +} + +// Immediately begin the handshake to get the resolved remote path and the access token. +- (void)_bindSessionWithLocalFileURL:(NSURL *)fileURL + syncConfig:(RLMSyncConfiguration *)syncConfig + standalone:(BOOL)isStandalone + onCompletion:(RLMSyncBasicErrorReportingBlock)completion { + if (self.directAccessToken) { + /// Like with the normal authentication methods below, make binding asynchronous so we don't recursively + /// try to acquire the session lock. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self _bindSessionWithDirectAccessToken:self.directAccessToken + syncConfig:syncConfig + localFileURL:fileURL + standalone:isStandalone + onCompletion:completion]; + }); + return; + } + + NSURL *realmURL = syncConfig.realmURL; + RLMServerPath unresolvedPath = [realmURL path]; + NSDictionary *json = @{ + kRLMSyncPathKey: unresolvedPath, + kRLMSyncProviderKey: @"realm", + kRLMSyncDataKey: self.refreshToken, + kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID, + }; + + RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) { + if (json && !error) { + RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json + requireAccessToken:YES + requireRefreshToken:NO]; + if (!model) { + // Malformed JSON + error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncErrorJSONKey: json}]; + if (completion) { + completion(error); + } + [[RLMSyncManager sharedManager] _fireError:error]; + return; + } else { + // Success + // For now, assume just one access token. + std::string file_path = [[fileURL path] UTF8String]; + RLMTokenModel *tokenModel = model.accessToken; + NSString *accessToken = tokenModel.token; + + // Register the Realm as being linked to this User. + RLMServerPath resolvedPath = tokenModel.tokenData.path; + RLMSyncSession *session = [self.sessionsStorage objectForKey:realmURL]; + session.resolvedPath = resolvedPath; + NSAssert(session, + @"Could not get a sync session object for the path '%@', this is an error", + unresolvedPath); + RLMSyncSessionHandle *sessionHandle; + auto underlyingSession = SyncManager::shared().get_session(file_path, syncConfig.rawConfiguration); + if (isStandalone) { + sessionHandle = [RLMSyncSessionHandle syncSessionHandleForPointer:underlyingSession]; + } else { + sessionHandle = [RLMSyncSessionHandle syncSessionHandleForWeakPointer:underlyingSession]; + } + [session configureWithAccessToken:accessToken + expiry:tokenModel.tokenData.expires + user:self + handle:sessionHandle]; + + // Bind the Realm + NSURLComponents *urlBuffer = [NSURLComponents componentsWithURL:realmURL resolvingAgainstBaseURL:YES]; + urlBuffer.path = resolvedPath; + NSURL *resolvedURL = [urlBuffer URL]; + if (!resolvedURL) { + @throw RLMException(@"Resolved path returned from the server was invalid (%@).", resolvedPath); + } + [session refreshAccessToken:accessToken serverURL:resolvedURL]; + + if (completion) { + bool success = session.state != RLMSyncSessionStateInvalid; + completion(success ? nil : [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]); + } + } + } else { + // Something else went wrong + NSError *syncError = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorBadResponse + userInfo:@{kRLMSyncUnderlyingErrorKey: error}]; + if (completion) { + completion(syncError); + } + [[RLMSyncManager sharedManager] _fireError:syncError]; + } + }; + [RLMNetworkClient postRequestToEndpoint:RLMServerEndpointAuth + server:self.authenticationServer + JSON:json + completion:handler]; +} + +// A callback handler for a Realm, used to get an updated access token which can then be used to bind the Realm. +- (RLMSyncSession *)_registerSessionForBindingWithFileURL:(NSURL *)fileURL + syncConfig:(RLMSyncConfiguration *)syncConfig + standaloneSession:(BOOL)isStandalone + onCompletion:(nullable RLMSyncBasicErrorReportingBlock)completion { + NSURL *realmURL = syncConfig.realmURL; + if (RLMSyncSession *session = [self.sessionsStorage objectForKey:realmURL]) { + RLMSyncSessionHandle *handle = [session sessionHandle]; + // The Realm at this particular path has already been registered to this user. + if ([handle sessionStillExists]) { + [session _refresh]; + if (completion) { + completion(nil); + } + return session; + } else if ([handle sessionIsInErrorState]) { + // Prohibit registering an invalid session. + if (completion) { + NSError *error = [NSError errorWithDomain:RLMSyncErrorDomain + code:RLMSyncErrorClientSessionError + userInfo:nil]; + completion(error); + } + return nil; + } + } + + RLMSyncSession *session = [[RLMSyncSession alloc] initWithFileURL:fileURL realmURL:realmURL]; + self.sessionsStorage[realmURL] = session; + + if (self.state == RLMSyncUserStateLoggedOut) { + // We will delay the path resolution/access token handshake until the user logs in. + session.deferredBindingPackage = [[RLMSessionBindingPackage alloc] initWithFileURL:fileURL + syncConfig:syncConfig + standalone:isStandalone + block:completion]; + } else { + // User is logged in, start the handshake immediately. + [self _bindSessionWithLocalFileURL:fileURL + syncConfig:syncConfig + standalone:isStandalone + onCompletion:completion]; + } + return session; +} + +@end diff --git a/Pods/Realm/Realm/RLMSyncUtil.mm b/Pods/Realm/Realm/RLMSyncUtil.mm new file mode 100644 index 0000000..879c9ba --- /dev/null +++ b/Pods/Realm/Realm/RLMSyncUtil.mm @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil_Private.hpp" + +RLMIdentityProvider const RLMIdentityProviderAccessToken = @"_access_token"; + +NSString *const RLMSyncErrorDomain = @"io.realm.sync"; + +NSString *const kRLMSyncAppIDKey = @"app_id"; +NSString *const kRLMSyncDataKey = @"data"; +NSString *const kRLMSyncErrorJSONKey = @"json"; +NSString *const kRLMSyncIdentityKey = @"identity"; +NSString *const kRLMSyncPasswordKey = @"password"; +NSString *const kRLMSyncPathKey = @"path"; +NSString *const kRLMSyncProviderKey = @"provider"; +NSString *const kRLMSyncRegisterKey = @"register"; +NSString *const kRLMSyncUnderlyingErrorKey = @"underlying_error"; +NSString *const kRLMSyncActionsKey = @"actions"; + +namespace realm { + +SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy) { + switch (stopPolicy) { + case RLMSyncStopPolicyImmediately: return SyncSessionStopPolicy::Immediately; + case RLMSyncStopPolicyLiveIndefinitely: return SyncSessionStopPolicy::LiveIndefinitely; + case RLMSyncStopPolicyAfterChangesUploaded: return SyncSessionStopPolicy::AfterChangesUploaded; + } + REALM_UNREACHABLE(); // Unrecognized stop policy. +} + +RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy) +{ + switch (stop_policy) { + case SyncSessionStopPolicy::Immediately: return RLMSyncStopPolicyImmediately; + case SyncSessionStopPolicy::LiveIndefinitely: return RLMSyncStopPolicyLiveIndefinitely; + case SyncSessionStopPolicy::AfterChangesUploaded: return RLMSyncStopPolicyAfterChangesUploaded; + } + REALM_UNREACHABLE(); +} + +} diff --git a/Pods/Realm/Realm/RLMTokenModels.m b/Pods/Realm/Realm/RLMTokenModels.m new file mode 100644 index 0000000..e85e70e --- /dev/null +++ b/Pods/Realm/Realm/RLMTokenModels.m @@ -0,0 +1,71 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMTokenModels.h" +#import "RLMSyncUtil_Private.h" + +static const NSString *const kRLMSyncTokenDataKey = @"token_data"; +static const NSString *const kRLMSyncTokenKey = @"token"; +static const NSString *const kRLMSyncExpiresKey = @"expires"; + +@interface RLMTokenDataModel () + +@property (nonatomic, readwrite) NSString *identity; +@property (nonatomic, readwrite) NSString *appID; +@property (nonatomic, readwrite) NSString *path; +@property (nonatomic, readwrite) NSTimeInterval expires; +//@property (nonatomic, readwrite) NSArray *access; + +@end + +@implementation RLMTokenDataModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncIdentityKey, identity); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncAppIDKey, appID); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_DOUBLE_OR_ABORT(jsonDictionary, kRLMSyncExpiresKey, expires); + return self; + } + return nil; +} + +@end + +@interface RLMTokenModel () + +@property (nonatomic, readwrite) NSString *token; +@property (nonatomic, nullable, readwrite) NSString *path; +@property (nonatomic, readwrite) RLMTokenDataModel *tokenData; + +@end + +@implementation RLMTokenModel + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary { + if (self = [super init]) { + RLM_SYNC_PARSE_STRING_OR_ABORT(jsonDictionary, kRLMSyncTokenKey, token); + RLM_SYNC_PARSE_OPTIONAL_STRING(jsonDictionary, kRLMSyncPathKey, path); + RLM_SYNC_PARSE_MODEL_OR_ABORT(jsonDictionary, kRLMSyncTokenDataKey, RLMTokenDataModel, tokenData); + return self; + } + return nil; +} + +@end diff --git a/Pods/Realm/Realm/RLMUpdateChecker.mm b/Pods/Realm/Realm/RLMUpdateChecker.mm new file mode 100644 index 0000000..e282ee8 --- /dev/null +++ b/Pods/Realm/Realm/RLMUpdateChecker.mm @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMUpdateChecker.hpp" + +#import "RLMRealm.h" +#import "RLMUtil.hpp" + +#if TARGET_IPHONE_SIMULATOR && !defined(REALM_COCOA_VERSION) +#import "RLMVersion.h" +#endif + +void RLMCheckForUpdates() { +#if TARGET_IPHONE_SIMULATOR + if (getenv("REALM_DISABLE_UPDATE_CHECKER") || RLMIsRunningInPlayground()) { + return; + } + + auto handler = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error || ((NSHTTPURLResponse *)response).statusCode != 200) { + return; + } + + NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (![REALM_COCOA_VERSION isEqualToString:latestVersion]) { + NSLog(@"Version %@ of Realm is now available: https://github.com/realm/realm-cocoa/blob/v%@/CHANGELOG.md", latestVersion, latestVersion); + } + }; + + NSString *url = [NSString stringWithFormat:@"https://static.realm.io/update/cocoa?%@", REALM_COCOA_VERSION]; + [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:url] completionHandler:handler] resume]; +#endif +} diff --git a/Pods/Realm/Realm/RLMUtil.mm b/Pods/Realm/Realm/RLMUtil.mm new file mode 100644 index 0000000..fa77a9d --- /dev/null +++ b/Pods/Realm/Realm/RLMUtil.mm @@ -0,0 +1,387 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMUtil.hpp" + +#import "RLMArray_Private.hpp" +#import "RLMListBase.h" +#import "RLMObjectSchema_Private.hpp" +#import "RLMObjectStore.h" +#import "RLMObject_Private.hpp" +#import "RLMProperty_Private.h" +#import "RLMSchema_Private.h" +#import "RLMSwiftSupport.h" + +#import "shared_realm.hpp" + +#import +#import + +#include +#include + +#if !defined(REALM_COCOA_VERSION) +#import "RLMVersion.h" +#endif + +static inline bool nsnumber_is_like_integer(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(bool) || + data_type == *@encode(char) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long); +} + +static inline bool nsnumber_is_like_bool(__unsafe_unretained NSNumber *const obj) +{ + // @encode(BOOL) is 'B' on iOS 64 and 'c' + // objcType is always 'c'. Therefore compare to "c". + if ([obj objCType][0] == 'c') { + return true; + } + + if (nsnumber_is_like_integer(obj)) { + int value = [obj intValue]; + return value == 0 || value == 1; + } + + return false; +} + +static inline bool nsnumber_is_like_float(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(float) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long) || + // A double is like float if it fits within float bounds + (data_type == *@encode(double) && ABS([obj doubleValue]) <= FLT_MAX); +} + +static inline bool nsnumber_is_like_double(__unsafe_unretained NSNumber *const obj) +{ + char data_type = [obj objCType][0]; + return data_type == *@encode(double) || + data_type == *@encode(float) || + data_type == *@encode(short) || + data_type == *@encode(int) || + data_type == *@encode(long) || + data_type == *@encode(long long) || + data_type == *@encode(unsigned short) || + data_type == *@encode(unsigned int) || + data_type == *@encode(unsigned long) || + data_type == *@encode(unsigned long long); +} + +BOOL RLMIsObjectValidForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const property) { + if (property.optional && !RLMCoerceToNil(obj)) { + return YES; + } + + switch (property.type) { + case RLMPropertyTypeString: + return [obj isKindOfClass:[NSString class]]; + case RLMPropertyTypeBool: + if ([obj isKindOfClass:[NSNumber class]]) { + return nsnumber_is_like_bool(obj); + } + return NO; + case RLMPropertyTypeDate: + return [obj isKindOfClass:[NSDate class]]; + case RLMPropertyTypeInt: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_integer(number); + } + return NO; + case RLMPropertyTypeFloat: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_float(number); + } + return NO; + case RLMPropertyTypeDouble: + if (NSNumber *number = RLMDynamicCast(obj)) { + return nsnumber_is_like_double(number); + } + return NO; + case RLMPropertyTypeData: + return [obj isKindOfClass:[NSData class]]; + case RLMPropertyTypeAny: + return NO; + case RLMPropertyTypeObject: + case RLMPropertyTypeLinkingObjects: { + // only NSNull, nil, or objects which derive from RLMObject and match the given + // object class are valid + RLMObjectBase *objBase = RLMDynamicCast(obj); + return objBase && [objBase->_objectSchema.className isEqualToString:property.objectClassName]; + } + case RLMPropertyTypeArray: { + if (RLMArray *array = RLMDynamicCast(obj)) { + return [array.objectClassName isEqualToString:property.objectClassName]; + } + if (RLMListBase *list = RLMDynamicCast(obj)) { + return [list._rlmArray.objectClassName isEqualToString:property.objectClassName]; + } + if ([obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + // check each element for compliance + for (id el in (id)obj) { + RLMObjectBase *obj = RLMDynamicCast(el); + if (!obj || ![obj->_objectSchema.className isEqualToString:property.objectClassName]) { + return NO; + } + } + return YES; + } + if (!obj || obj == NSNull.null) { + return YES; + } + return NO; + } + } + @throw RLMException(@"Invalid RLMPropertyType specified"); +} + +NSDictionary *RLMDefaultValuesForObjectSchema(__unsafe_unretained RLMObjectSchema *const objectSchema) { + if (!objectSchema.isSwiftClass) { + return [objectSchema.objectClass defaultPropertyValues]; + } + + NSMutableDictionary *defaults = nil; + if ([objectSchema.objectClass isSubclassOfClass:RLMObject.class]) { + defaults = [NSMutableDictionary dictionaryWithDictionary:[objectSchema.objectClass defaultPropertyValues]]; + } + else { + defaults = [NSMutableDictionary dictionary]; + } + RLMObject *defaultObject = [[objectSchema.objectClass alloc] init]; + for (RLMProperty *prop in objectSchema.properties) { + if (!defaults[prop.name] && defaultObject[prop.name]) { + defaults[prop.name] = defaultObject[prop.name]; + } + } + return defaults; +} + +static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) { + NSMutableDictionary *userInfo = @{RLMRealmVersionKey: REALM_COCOA_VERSION, + RLMRealmCoreVersionKey: @REALM_VERSION}.mutableCopy; + if (additionalUserInfo != nil) { + [userInfo addEntriesFromDictionary:additionalUserInfo]; + } + NSException *e = [NSException exceptionWithName:RLMExceptionName + reason:reason + userInfo:userInfo]; + return e; +} + +NSException *RLMException(NSString *fmt, ...) { + va_list args; + va_start(args, fmt); + NSException *e = RLMException([[NSString alloc] initWithFormat:fmt arguments:args], @{}); + va_end(args); + return e; +} + +NSException *RLMException(std::exception const& exception) { + return RLMException(@"%@", @(exception.what())); +} + +NSError *RLMMakeError(RLMError code, std::exception const& exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + @"Error Code": @(code)}]; +} + +NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError& exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + NSFilePathErrorKey: @(exception.get_path().c_str()), + @"Error Code": @(code)}]; +} + +NSError *RLMMakeError(RLMError code, const realm::RealmFileException& exception) { + NSString *underlying = @(exception.underlying().c_str()); + return [NSError errorWithDomain:RLMErrorDomain + code:code + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + NSFilePathErrorKey: @(exception.path().c_str()), + @"Error Code": @(code), + @"Underlying": underlying.length == 0 ? @"n/a" : underlying}]; +} + +NSError *RLMMakeError(std::system_error const& exception) { + BOOL isGenericCategoryError = (exception.code().category() == std::generic_category()); + NSString *category = @(exception.code().category().name()); + NSString *errorDomain = isGenericCategoryError ? NSPOSIXErrorDomain : RLMUnknownSystemErrorDomain; + + return [NSError errorWithDomain:errorDomain + code:exception.code().value() + userInfo:@{NSLocalizedDescriptionKey: @(exception.what()), + @"Error Code": @(exception.code().value()), + @"Category": category}]; +} + +NSError *RLMMakeError(NSException *exception) { + return [NSError errorWithDomain:RLMErrorDomain + code:0 + userInfo:@{NSLocalizedDescriptionKey: exception.reason}]; +} + +void RLMSetErrorOrThrow(NSError *error, NSError **outError) { + if (outError) { + *outError = error; + } + else { + NSString *msg = error.localizedDescription; + if (error.userInfo[NSFilePathErrorKey]) { + msg = [NSString stringWithFormat:@"%@: %@", error.userInfo[NSFilePathErrorKey], error.localizedDescription]; + } + @throw RLMException(msg, @{NSUnderlyingErrorKey: error}); + } +} + +// Determines if class1 descends from class2 +static inline BOOL RLMIsSubclass(Class class1, Class class2) { + class1 = class_getSuperclass(class1); + return RLMIsKindOfClass(class1, class2); +} + +static bool treatFakeObjectAsRLMObject = false; + +void RLMSetTreatFakeObjectAsRLMObject(BOOL flag) { + treatFakeObjectAsRLMObject = flag; +} + +BOOL RLMIsObjectOrSubclass(Class klass) { + if (RLMIsKindOfClass(klass, RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return RLMIsKindOfClass(klass, FakeObjectClass); + } + return NO; +} + +BOOL RLMIsObjectSubclass(Class klass) { + if (RLMIsSubclass(class_getSuperclass(klass), RLMObjectBase.class)) { + return YES; + } + + if (treatFakeObjectAsRLMObject) { + static Class FakeObjectClass = NSClassFromString(@"FakeObject"); + return RLMIsSubclass(klass, FakeObjectClass); + } + return NO; +} + +BOOL RLMIsDebuggerAttached() +{ + int name[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid() + }; + + struct kinfo_proc info; + size_t info_size = sizeof(info); + if (sysctl(name, sizeof(name)/sizeof(name[0]), &info, &info_size, NULL, 0) == -1) { + NSLog(@"sysctl() failed: %s", strerror(errno)); + return false; + } + + return (info.kp_proc.p_flag & P_TRACED) != 0; +} + +BOOL RLMIsRunningInPlayground() { + return [[NSBundle mainBundle].bundleIdentifier hasPrefix:@"com.apple.dt.playground."]; +} + +id RLMMixedToObjc(realm::Mixed const& mixed) { + switch (mixed.get_type()) { + case realm::type_String: + return RLMStringDataToNSString(mixed.get_string()); + case realm::type_Int: + return @(mixed.get_int()); + case realm::type_Float: + return @(mixed.get_float()); + case realm::type_Double: + return @(mixed.get_double()); + case realm::type_Bool: + return @(mixed.get_bool()); + case realm::type_Timestamp: + return RLMTimestampToNSDate(mixed.get_timestamp()); + case realm::type_Binary: + return RLMBinaryDataToNSData(mixed.get_binary()); + case realm::type_Link: + case realm::type_LinkList: + default: + @throw RLMException(@"Invalid data type for RLMPropertyTypeAny property."); + } +} + +NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier) { +#if TARGET_OS_TV + (void)bundleIdentifier; + // tvOS prohibits writing to the Documents directory, so we use the Library/Caches directory instead. + return NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; +#elif TARGET_OS_IPHONE + (void)bundleIdentifier; + // On iOS the Documents directory isn't user-visible, so put files there + return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; +#else + // On OS X it is, so put files in Application Support. If we aren't running + // in a sandbox, put it in a subdirectory based on the bundle identifier + // to avoid accidentally sharing files between applications + NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0]; + if (![[NSProcessInfo processInfo] environment][@"APP_SANDBOX_CONTAINER_ID"]) { + if (!bundleIdentifier) { + bundleIdentifier = [NSBundle mainBundle].bundleIdentifier; + } + if (!bundleIdentifier) { + bundleIdentifier = [NSBundle mainBundle].executablePath.lastPathComponent; + } + + path = [path stringByAppendingPathComponent:bundleIdentifier]; + + // create directory + [[NSFileManager defaultManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } + return path; +#endif +} diff --git a/Pods/Realm/Realm/module.modulemap b/Pods/Realm/Realm/module.modulemap new file mode 100644 index 0000000..3d6c640 --- /dev/null +++ b/Pods/Realm/Realm/module.modulemap @@ -0,0 +1,28 @@ +framework module Realm { + umbrella header "Realm.h" + + export * + module * { export * } + + explicit module Private { + header "RLMAccessor.h" + header "RLMArray_Private.h" + header "RLMListBase.h" + header "RLMObjectBase_Dynamic.h" + header "RLMObjectSchema_Private.h" + header "RLMObjectStore.h" + header "RLMObject_Private.h" + header "RLMOptionalBase.h" + header "RLMProperty_Private.h" + header "RLMRealmConfiguration_Private.h" + header "RLMRealm_Private.h" + header "RLMResults_Private.h" + header "RLMSchema_Private.h" + header "RLMSyncConfiguration_Private.h" + } + + explicit module Dynamic { + header "RLMRealm_Dynamic.h" + header "RLMObjectBase_Dynamic.h" + } +} diff --git a/Pods/Realm/build.sh b/Pods/Realm/build.sh new file mode 100755 index 0000000..d95a867 --- /dev/null +++ b/Pods/Realm/build.sh @@ -0,0 +1,1382 @@ +#!/bin/sh + +################################################################################## +# Custom build tool for Realm Objective-C binding. +# +# (C) Copyright 2011-2015 by realm.io. +################################################################################## + +# Warning: pipefail is not a POSIX compatible option, but on OS X it works just fine. +# OS X uses a POSIX complain version of bash as /bin/sh, but apparently it does +# not strip away this feature. Also, this will fail if somebody forces the script +# to be run with zsh. +set -o pipefail +set -e + +source_root="$(dirname "$0")" + +# You can override the version of the core library +: ${REALM_CORE_VERSION:=$(sed -n 's/^REALM_CORE_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} # set to "current" to always use the current build + +: ${REALM_SYNC_VERSION:=$(sed -n 's/^REALM_SYNC_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} + +: ${REALM_OBJECT_SERVER_VERSION:=$(sed -n 's/^REALM_OBJECT_SERVER_VERSION=\(.*\)$/\1/p' ${source_root}/dependencies.list)} + +# You can override the xcmode used +: ${XCMODE:=xcodebuild} # must be one of: xcodebuild (default), xcpretty, xctool + +# Provide a fallback value for TMPDIR, relevant for Xcode Bots +: ${TMPDIR:=$(getconf DARWIN_USER_TEMP_DIR)} + +PATH=/usr/libexec:$PATH + +if ! [ -z "${JENKINS_HOME}" ]; then + XCPRETTY_PARAMS="--no-utf --report junit --output build/reports/junit.xml" + CODESIGN_PARAMS="CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO" +fi + +export REALM_SKIP_DEBUGGER_CHECKS=YES + +usage() { +cat <> "sync/object-server/configuration.yml" + sed -i '' -e "s/ listen_address: '0\.0\.0\.0'/ listen_address: '::'/" "sync/object-server/configuration.yml" + touch "sync/object-server/do_not_open_browser" +} + +download_core() { + echo "Downloading dependency: core ${REALM_CORE_VERSION}" + TMP_DIR="$TMPDIR/core_bin" + mkdir -p "${TMP_DIR}" + CORE_TMP_TAR="${TMP_DIR}/core-${REALM_CORE_VERSION}.tar.xz.tmp" + CORE_TAR="${TMP_DIR}/core-${REALM_CORE_VERSION}.tar.xz" + if [ ! -f "${CORE_TAR}" ]; then + local CORE_URL="https://static.realm.io/downloads/core/realm-core-${REALM_CORE_VERSION}.tar.xz" + set +e # temporarily disable immediate exit + local ERROR # sweeps the exit code unless declared separately + ERROR=$(curl --fail --silent --show-error --location "$CORE_URL" --output "${CORE_TMP_TAR}" 2>&1 >/dev/null) + if [[ $? -ne 0 ]]; then + echo "Downloading core failed:\n${ERROR}" + exit 1 + fi + set -e # re-enable flag + mv "${CORE_TMP_TAR}" "${CORE_TAR}" + fi + + ( + cd "${TMP_DIR}" + rm -rf core + tar xf "${CORE_TAR}" --xz + mv core core-${REALM_CORE_VERSION} + ) + + rm -rf core-${REALM_CORE_VERSION} core + mv ${TMP_DIR}/core-${REALM_CORE_VERSION} . + ln -s core-${REALM_CORE_VERSION} core +} + +download_sync() { + echo "Downloading dependency: sync ${REALM_SYNC_VERSION}" + TMP_DIR="$TMPDIR/sync_bin" + mkdir -p "${TMP_DIR}" + SYNC_TMP_TAR="${TMP_DIR}/sync-${REALM_SYNC_VERSION}.tar.xz.tmp" + SYNC_TAR="${TMP_DIR}/sync-${REALM_SYNC_VERSION}.tar.xz" + if [ ! -f "${SYNC_TAR}" ]; then + local SYNC_URL="https://static.realm.io/downloads/sync/realm-sync-cocoa-${REALM_SYNC_VERSION}.tar.xz" + set +e # temporarily disable immediate exit + local ERROR # sweeps the exit code unless declared separately + ERROR=$(curl --fail --silent --show-error --location "$SYNC_URL" --output "${SYNC_TMP_TAR}" 2>&1 >/dev/null) + if [[ $? -ne 0 ]]; then + echo "Downloading sync failed:\n${ERROR}" + exit 1 + fi + set -e # re-enable flag + mv "${SYNC_TMP_TAR}" "${SYNC_TAR}" + fi + + ( + cd "${TMP_DIR}" + rm -rf sync + tar xf "${SYNC_TAR}" --xz + mv core sync-${REALM_SYNC_VERSION} + ) + + rm -rf sync-${REALM_SYNC_VERSION} core + mv ${TMP_DIR}/sync-${REALM_SYNC_VERSION} . + ln -s sync-${REALM_SYNC_VERSION} core +} + +###################################### +# Variables +###################################### + +COMMAND="$1" + +# Use Debug config if command ends with -debug, otherwise default to Release +case "$COMMAND" in + *-debug) + COMMAND="${COMMAND%-debug}" + CONFIGURATION="Debug" + ;; + *) CONFIGURATION=${CONFIGURATION:-Release} +esac +export CONFIGURATION + +source "${source_root}/scripts/swift-version.sh" + +case "$COMMAND" in + + ###################################### + # Clean + ###################################### + "clean") + find . -type d -name build -exec rm -r "{}" +\; + exit 0 + ;; + + ###################################### + # Object Server + ###################################### + "download-object-server") + download_object_server + exit 0 + ;; + + "start-object-server") + # kill any object servers that are still running + (pgrep -f realm-object-server || true) | while read pid; do + kill $pid + done + ./sync/start-object-server.command + exit 0 + ;; + + "reset-object-server") + package="${source_root}/sync" + for file in "$package"/realm-object-server-*; do + if [ -d "$file" ]; then + package="$file" + break + fi + done + rm -rf "$package/object-server/root_dir/" + rm -rf "$package/object-server/temp_dir/" + exit 0 + ;; + + ###################################### + # Core + ###################################### + "download-core") + if [ "$REALM_CORE_VERSION" = "current" ]; then + echo "Using version of core already in core/ directory" + exit 0 + fi + if [ -d core -a -d ../realm-core -a ! -L core ]; then + # Allow newer versions than expected for local builds as testing + # with unreleased versions is one of the reasons to use a local build + if ! $(grep -i "${REALM_CORE_VERSION} Release notes" core/release_notes.txt >/dev/null); then + echo "Local build of core is out of date." + exit 1 + else + echo "The core library seems to be up to date." + fi + elif ! [ -L core ]; then + echo "core is not a symlink. Deleting..." + rm -rf core + download_core + # With a prebuilt version we only want to check the first non-empty + # line so that checking out an older commit will download the + # appropriate version of core if the already-present version is too new + elif ! $(grep -m 1 . core/release_notes.txt | grep -i "${REALM_CORE_VERSION} RELEASE NOTES" >/dev/null); then + download_core + else + echo "The core library seems to be up to date." + fi + exit 0 + ;; + + ###################################### + # Sync + ###################################### + "download-sync") + if [ "$REALM_SYNC_VERSION" = "current" ]; then + echo "Using version of core already in core/ directory" + exit 0 + fi + if [ -d core -a -d ../realm-core -a -d ../realm-sync -a ! -L core ]; then + echo "Using version of core already in core/ directory" + elif ! [ -L core ]; then + echo "core is not a symlink. Deleting..." + rm -rf core + download_sync + elif [[ "$(cat core/version.txt)" != "$REALM_SYNC_VERSION" ]]; then + download_sync + else + echo "The core library seems to be up to date." + fi + exit 0 + ;; + + ###################################### + # Swift versioning + ###################################### + "set-swift-version") + version="$2" + if [[ -z "$version" ]]; then + version="$REALM_SWIFT_VERSION" + fi + + SWIFT_VERSION_FILE="RealmSwift/SwiftVersion.swift" + CONTENTS="let swiftLanguageVersion = \"$version\"" + if [ ! -f "$SWIFT_VERSION_FILE" ] || ! grep -q "$CONTENTS" "$SWIFT_VERSION_FILE"; then + echo "$CONTENTS" > "$SWIFT_VERSION_FILE" + fi + + exit 0 + ;; + + "prelaunch-simulator") + sh ${source_root}/scripts/reset-simulators.sh + ;; + + ###################################### + # Building + ###################################### + "build") + sh build.sh ios-static + sh build.sh ios-dynamic + sh build.sh ios-swift + sh build.sh watchos + sh build.sh watchos-swift + sh build.sh tvos + sh build.sh tvos-swift + sh build.sh osx + sh build.sh osx-swift + exit 0 + ;; + + "ios-static") + build_combined 'Realm iOS static' Realm iphoneos iphonesimulator "-static" + exit 0 + ;; + + "ios-dynamic") + build_combined Realm Realm iphoneos iphonesimulator + exit 0 + ;; + + "ios-swift") + sh build.sh ios-dynamic + build_combined RealmSwift RealmSwift iphoneos iphonesimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/ios/Realm.framework build/ios/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "watchos") + build_combined Realm Realm watchos watchsimulator + exit 0 + ;; + + "watchos-swift") + sh build.sh watchos + build_combined RealmSwift RealmSwift watchos watchsimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/watchos/Realm.framework build/watchos/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "tvos") + build_combined Realm Realm appletvos appletvsimulator + exit 0 + ;; + + "tvos-swift") + sh build.sh tvos + build_combined RealmSwift RealmSwift appletvos appletvsimulator '' "/swift-$REALM_SWIFT_VERSION" + cp -R build/tvos/Realm.framework build/tvos/swift-$REALM_SWIFT_VERSION + exit 0 + ;; + + "osx") + xc "-scheme Realm -configuration $CONFIGURATION" + clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/Realm.framework" "build/osx" "Realm.framework" + exit 0 + ;; + + "osx-swift") + sh build.sh osx + xc "-scheme 'RealmSwift' -configuration $CONFIGURATION build" + destination="build/osx/swift-$REALM_SWIFT_VERSION" + clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/RealmSwift.framework" "$destination" "RealmSwift.framework" + cp -R build/osx/Realm.framework "$destination" + exit 0 + ;; + + ###################################### + # Analysis + ###################################### + + "analyze-osx") + xc "-scheme Realm -configuration $CONFIGURATION analyze" + exit 0 + ;; + + ###################################### + # Testing + ###################################### + "test") + set +e # Run both sets of tests even if the first fails + failed=0 + sh build.sh test-ios-static || failed=1 + sh build.sh test-ios-dynamic || failed=1 + sh build.sh test-ios-swift || failed=1 + sh build.sh test-ios-devices || failed=1 + sh build.sh test-tvos-devices || failed=1 + sh build.sh test-osx || failed=1 + sh build.sh test-osx-swift || failed=1 + exit $failed + ;; + + "test-all") + set +e + failed=0 + sh build.sh test || failed=1 + sh build.sh test-debug || failed=1 + exit $failed + ;; + + "test-ios-static") + test_ios_static "name=iPhone 6" + exit 0 + ;; + + "test-ios7-static") + test_ios_static "name=iPhone 5S,OS=7.1" + exit 0 + ;; + + "test-ios-dynamic") + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + xc "-scheme Realm -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" + exit 0 + ;; + + "test-ios-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' build" + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test 'ARCHS=\$(ARCHS_STANDARD_32_BIT)'" + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk iphonesimulator -destination 'name=iPhone 6' test" + exit 0 + ;; + + "test-ios-devices") + failed=0 + trap "failed=1" ERR + sh build.sh test-ios-devices-objc + sh build.sh test-ios-devices-swift + exit $failed + ;; + + "test-ios-devices-objc") + test_devices iphoneos "Realm iOS static" "$CONFIGURATION" + exit $? + ;; + + "test-ios-devices-swift") + test_devices iphoneos "RealmSwift" "$CONFIGURATION" + exit $? + ;; + + "test-tvos") + xc "-scheme Realm -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + exit $? + ;; + + "test-tvos-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION -sdk appletvsimulator -destination 'name=Apple TV 1080p' test" + exit $? + ;; + + "test-tvos-devices") + test_devices appletvos TestHost "$CONFIGURATION" + ;; + + "test-osx") + COVERAGE_PARAMS="" + if [[ "$CONFIGURATION" == "Debug" ]]; then + COVERAGE_PARAMS="GCC_GENERATE_TEST_COVERAGE_FILES=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES" + fi + xc "-scheme Realm -configuration $CONFIGURATION test $COVERAGE_PARAMS" + exit 0 + ;; + + "test-osx-swift") + xc "-scheme RealmSwift -configuration $CONFIGURATION test" + exit 0 + ;; + + "test-osx-object-server") + xc "-scheme 'Object Server Tests' -configuration $CONFIGURATION -sdk macosx test" + exit 0 + ;; + + ###################################### + # Full verification + ###################################### + "verify") + sh build.sh verify-cocoapods + sh build.sh verify-docs + sh build.sh verify-osx + sh build.sh verify-osx-debug + sh build.sh verify-osx-swift + sh build.sh verify-osx-swift-debug + sh build.sh verify-ios-static + sh build.sh verify-ios-static-debug + sh build.sh verify-ios7-static + sh build.sh verify-ios7-static-debug + sh build.sh verify-ios-dynamic + sh build.sh verify-ios-dynamic-debug + sh build.sh verify-ios-swift + sh build.sh verify-ios-swift-debug + sh build.sh verify-ios-device-objc + sh build.sh verify-ios-device-swift + sh build.sh verify-watchos + sh build.sh verify-tvos + sh build.sh verify-tvos-debug + sh build.sh verify-tvos-device + sh build.sh verify-swiftlint + sh build.sh verify-osx-object-server + ;; + + "verify-cocoapods") + if [[ -d .git ]]; then + # Verify the current branch, unless one was already specified in the sha environment variable. + if [[ -z $sha ]]; then + export sha=$(git rev-parse --abbrev-ref HEAD) + fi + + if [[ $(git log -1 @{push}..) != "" ]] || ! git diff-index --quiet HEAD; then + echo "WARNING: verify-cocoapods will test the latest revision of $sha found on GitHub." + echo " Any unpushed local changes will not be tested." + echo "" + sleep 1 + fi + fi + + cd examples/installation + sh build.sh test-ios-objc-cocoapods + sh build.sh test-ios-objc-cocoapods-dynamic + sh build.sh test-ios-swift-cocoapods + sh build.sh test-osx-objc-cocoapods + sh build.sh test-osx-swift-cocoapods + sh build.sh test-watchos-objc-cocoapods + sh build.sh test-watchos-swift-cocoapods + ;; + + "verify-osx-encryption") + REALM_ENCRYPT_ALL=YES sh build.sh test-osx + exit 0 + ;; + + "verify-osx") + sh build.sh test-osx + sh build.sh analyze-osx + sh build.sh examples-osx + + ( + cd examples/osx/objc/build/DerivedData/RealmExamples/Build/Products/$CONFIGURATION + DYLD_FRAMEWORK_PATH=. ./JSONImport >/dev/null + ) + exit 0 + ;; + + "verify-osx-swift") + sh build.sh test-osx-swift + exit 0 + ;; + + "verify-ios-static") + sh build.sh test-ios-static + sh build.sh examples-ios + ;; + + "verify-ios7-static") + sh build.sh test-ios7-static + ;; + + "verify-ios-dynamic") + sh build.sh test-ios-dynamic + ;; + + "verify-ios-swift") + sh build.sh test-ios-swift + sh build.sh examples-ios-swift + ;; + + "verify-ios-device-objc") + sh build.sh test-ios-devices-objc + exit 0 + ;; + + "verify-ios-device-swift") + sh build.sh test-ios-devices-swift + exit 0 + ;; + + "verify-docs") + sh build.sh docs + for lang in swift objc; do + undocumented="docs/${lang}_output/undocumented.json" + if ruby -rjson -e "j = JSON.parse(File.read('docs/${lang}_output/undocumented.json')); exit j['warnings'].length != 0"; then + echo "Undocumented Realm $lang declarations:" + cat "$undocumented" + exit 1 + fi + done + exit 0 + ;; + + "verify-watchos") + sh build.sh watchos-swift + exit 0 + ;; + + "verify-tvos") + sh build.sh test-tvos + sh build.sh test-tvos-swift + sh build.sh examples-tvos + sh build.sh examples-tvos-swift + exit 0 + ;; + + "verify-tvos-device") + sh build.sh test-tvos-devices + exit 0 + ;; + + "verify-swiftlint") + swiftlint lint --strict + exit 0 + ;; + + "verify-osx-object-server") + sh build.sh download-object-server + sh build.sh test-osx-object-server + exit 0 + ;; + + ###################################### + # Docs + ###################################### + "docs") + build_docs objc + build_docs swift + exit 0 + ;; + + ###################################### + # Examples + ###################################### + "examples") + sh build.sh clean + sh build.sh examples-ios + sh build.sh examples-ios-swift + sh build.sh examples-osx + sh build.sh examples-tvos + sh build.sh examples-tvos-swift + exit 0 + ;; + + "examples-ios") + sh build.sh prelaunch-simulator + workspace="examples/ios/objc/RealmExamples.xcworkspace" + pod install --project-directory="$workspace/.." --no-repo-update + xc "-workspace $workspace -scheme Simple -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme TableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Migration -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Backlink -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme GroupedTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme RACTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Encryption -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + + if [ ! -z "${JENKINS_HOME}" ]; then + xc "-workspace $workspace -scheme Extension -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + fi + + exit 0 + ;; + + "examples-ios-swift") + if [ "$REALM_SWIFT_VERSION" == "2.3" ]; then # Skip Swift 2.3 examples for now. + exit 0 + fi + sh build.sh prelaunch-simulator + workspace="examples/ios/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme Simple -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme TableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Migration -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Encryption -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme Backlink -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme GroupedTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + if [ "$REALM_SWIFT_VERSION" == "2.2" ]; then # Only Swift 2.2 has the ReactKitTableView example + pod install --project-directory="$workspace/.." --no-repo-update + xc "-workspace $workspace -scheme ReactKitTableView -configuration $CONFIGURATION -destination 'name=iPhone 6' build ${CODESIGN_PARAMS}" + fi + exit 0 + ;; + + "examples-osx") + xc "-workspace examples/osx/objc/RealmExamples.xcworkspace -scheme JSONImport -configuration ${CONFIGURATION} build ${CODESIGN_PARAMS}" + ;; + + "examples-tvos") + workspace="examples/tvos/objc/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + exit 0 + ;; + + "examples-tvos-swift") + if [ "$REALM_SWIFT_VERSION" == "2.3" ]; then # Skip Swift 2.3 examples for now. + exit 0 + fi + workspace="examples/tvos/swift-$REALM_SWIFT_VERSION/RealmExamples.xcworkspace" + xc "-workspace $workspace -scheme DownloadCache -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + xc "-workspace $workspace -scheme PreloadedData -configuration $CONFIGURATION -destination 'name=Apple TV 1080p' build ${CODESIGN_PARAMS}" + exit 0 + ;; + + ###################################### + # Versioning + ###################################### + "get-version") + version_file="Realm/Realm-Info.plist" + echo "$(PlistBuddy -c "Print :CFBundleVersion" "$version_file")" + exit 0 + ;; + + "set-version") + realm_version="$2" + version_files="Realm/Realm-Info.plist" + + if [ -z "$realm_version" ]; then + echo "You must specify a version." + exit 1 + fi + for version_file in $version_files; do + PlistBuddy -c "Set :CFBundleVersion $realm_version" "$version_file" + PlistBuddy -c "Set :CFBundleShortVersionString $realm_version" "$version_file" + done + sed -i '' "s/^VERSION=.*/VERSION=$realm_version/" dependencies.list + exit 0 + ;; + + ###################################### + # Bitcode Detection + ###################################### + + "binary-has-bitcode") + BINARY="$2" + # Although grep has a '-q' flag to prevent logging to stdout, grep + # behaves differently when used, so redirect stdout to /dev/null. + if otool -l "$BINARY" | grep "segname __LLVM" > /dev/null 2>&1; then + exit 0 + fi + # Work around rdar://21826157 by checking for bitcode in thin binaries + + # Get architectures for binary + archs="$(lipo -info "$BINARY" | rev | cut -d ':' -f1 | rev)" + + archs_array=( $archs ) + if [[ ${#archs_array[@]} < 2 ]]; then + exit 1 # Early exit if not a fat binary + fi + + TEMPDIR=$(mktemp -d $TMPDIR/realm-bitcode-check.XXXX) + + for arch in $archs; do + lipo -thin "$arch" "$BINARY" -output "$TEMPDIR/$arch" + if otool -l "$TEMPDIR/$arch" | grep -q "segname __LLVM"; then + exit 0 + fi + done + exit 1 + ;; + + ###################################### + # CocoaPods + ###################################### + "cocoapods-setup") + if [ ! -d core ]; then + sh build.sh download-sync + rm core + mv sync-* core + fi + + if [[ "$2" != "swift" ]]; then + if [ ! -d Realm/ObjectStore/src ]; then + cat >&2 <&1 | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + if [ "$failed" = "1" ] && cat build/build.log | grep -E 'DTXProxyChannel|DTXChannel|out of date and needs to be rebuilt'; then + echo "Known Xcode error detected. Running job again." + failed=0 + sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + elif [ "$failed" = "1" ] && tail ~/Library/Logs/CoreSimulator/CoreSimulator.log | grep -E "Operation not supported|Failed to lookup com.apple.coreservices.lsuseractivity.simulatorsupport"; then + echo "Known Xcode error detected. Running job again." + failed=0 + sh build.sh verify-$target | tee build/build.log | xcpretty -r junit -o build/reports/junit.xml || failed=1 + fi + if [ "$failed" = "1" ]; then + echo "\n\n***\nbuild/build.log\n***\n\n" && cat build/build.log + echo "\n\n***\nCoreSimulator.log\n***\n\n" && tail -n2000 ~/Library/Logs/CoreSimulator/CoreSimulator.log + exit 1 + fi + fi + + if [ "$target" = "osx" ] && [ "$configuration" = "Debug" ]; then + gcovr -r . -f ".*Realm.*" -e ".*Tests.*" -e ".*core.*" --xml > build/reports/coverage-report.xml + WS=$(pwd | sed "s/\//\\\\\//g") + sed -i ".bak" "s/\./${WS}/" build/reports/coverage-report.xml + fi + ;; + + ###################################### + # Release packaging + ###################################### + + "package-examples") + cd tightdb_objc + ./scripts/package_examples.rb + zip --symlinks -r realm-examples.zip examples -x "examples/installation/*" + ;; + + "package-test-examples") + if ! VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*-[a-z]*'); then + VERSION=$(echo realm-objc-*.zip | grep -o '\d*\.\d*\.\d*') + fi + OBJC="realm-objc-${VERSION}" + SWIFT="realm-swift-${VERSION}" + unzip ${OBJC}.zip + + cp $0 ${OBJC} + cp -r ${source_root}/scripts ${OBJC} + cd ${OBJC} + sh build.sh examples-ios + sh build.sh examples-tvos + sh build.sh examples-osx + cd .. + rm -rf ${OBJC} + + unzip ${SWIFT}.zip + + cp $0 ${SWIFT} + cp -r ${source_root}/scripts ${SWIFT} + cd ${SWIFT} + sh build.sh examples-ios-swift + sh build.sh examples-tvos-swift + cd .. + rm -rf ${SWIFT} + ;; + + "package-ios-static") + cd tightdb_objc + + sh build.sh prelaunch-simulator + sh build.sh test-ios-static + sh build.sh ios-static + + cd build/ios-static + zip --symlinks -r realm-framework-ios.zip Realm.framework + ;; + + "package-ios-dynamic") + cd tightdb_objc + + sh build.sh prelaunch-simulator + sh build.sh ios-dynamic + cd build/ios + zip --symlinks -r realm-dynamic-framework-ios.zip Realm.framework + ;; + + "package-osx") + cd tightdb_objc + sh build.sh test-osx + + cd build/DerivedData/Realm/Build/Products/Release + zip --symlinks -r realm-framework-osx.zip Realm.framework + ;; + + "package-ios-swift") + cd tightdb_objc + for version in 2.2 2.3 3.0; do + REALM_SWIFT_VERSION="$version" sh build.sh prelaunch-simulator + REALM_SWIFT_VERSION="$version" sh build.sh ios-swift + done + + cd build/ios + zip --symlinks -r realm-swift-framework-ios.zip swift-2.2 swift-2.3 swift-3.0 + ;; + + "package-osx-swift") + cd tightdb_objc + for version in 2.2 2.3 3.0; do + REALM_SWIFT_VERSION="$version" sh build.sh prelaunch-simulator + REALM_SWIFT_VERSION="$version" sh build.sh osx-swift + done + + cd build/osx + zip --symlinks -r realm-swift-framework-osx.zip swift-2.2 swift-2.3 swift-3.0 + ;; + + "package-watchos") + cd tightdb_objc + sh build.sh watchos + + cd build/watchos + zip --symlinks -r realm-framework-watchos.zip Realm.framework + ;; + + "package-watchos-swift") + cd tightdb_objc + for version in 2.2 2.3 3.0; do + REALM_SWIFT_VERSION="$version" sh build.sh prelaunch-simulator + REALM_SWIFT_VERSION="$version" sh build.sh watchos-swift + done + + cd build/watchos + zip --symlinks -r realm-swift-framework-watchos.zip swift-2.2 swift-2.3 swift-3.0 + ;; + + "package-tvos") + cd tightdb_objc + sh build.sh tvos + + cd build/tvos + zip --symlinks -r realm-framework-tvos.zip Realm.framework + ;; + + "package-tvos-swift") + cd tightdb_objc + for version in 2.2 2.3 3.0; do + REALM_SWIFT_VERSION="$version" sh build.sh prelaunch-simulator + REALM_SWIFT_VERSION="$version" sh build.sh tvos-swift + done + + cd build/tvos + zip --symlinks -r realm-swift-framework-tvos.zip swift-2.2 swift-2.3 swift-3.0 + ;; + + "package-release") + LANG="$2" + TEMPDIR=$(mktemp -d $TMPDIR/realm-release-package-${LANG}.XXXX) + + cd tightdb_objc + VERSION=$(sh build.sh get-version) + cd .. + + FOLDER=${TEMPDIR}/realm-${LANG}-${VERSION} + + mkdir -p ${FOLDER}/osx ${FOLDER}/ios ${FOLDER}/watchos ${FOLDER}/tvos + + if [[ "${LANG}" == "objc" ]]; then + mkdir -p ${FOLDER}/ios/static + mkdir -p ${FOLDER}/ios/dynamic + mkdir -p ${FOLDER}/Swift + + ( + cd ${FOLDER}/osx + unzip ${WORKSPACE}/realm-framework-osx.zip + ) + + ( + cd ${FOLDER}/ios/static + unzip ${WORKSPACE}/realm-framework-ios.zip + ) + + ( + cd ${FOLDER}/ios/dynamic + unzip ${WORKSPACE}/realm-dynamic-framework-ios.zip + ) + + ( + cd ${FOLDER}/watchos + unzip ${WORKSPACE}/realm-framework-watchos.zip + ) + + ( + cd ${FOLDER}/tvos + unzip ${WORKSPACE}/realm-framework-tvos.zip + ) + else + ( + cd ${FOLDER}/osx + unzip ${WORKSPACE}/realm-swift-framework-osx.zip + ) + + ( + cd ${FOLDER}/ios + unzip ${WORKSPACE}/realm-swift-framework-ios.zip + ) + + ( + cd ${FOLDER}/watchos + unzip ${WORKSPACE}/realm-swift-framework-watchos.zip + ) + + ( + cd ${FOLDER}/tvos + unzip ${WORKSPACE}/realm-swift-framework-tvos.zip + ) + fi + + ( + cd ${WORKSPACE}/tightdb_objc + cp -R plugin ${FOLDER} + cp LICENSE ${FOLDER}/LICENSE.txt + if [[ "${LANG}" == "objc" ]]; then + cp Realm/Swift/RLMSupport.swift ${FOLDER}/Swift/ + fi + ) + + ( + cd ${FOLDER} + unzip ${WORKSPACE}/realm-examples.zip + cd examples + if [[ "${LANG}" == "objc" ]]; then + rm -rf ios/swift-* tvos/swift-* + else + rm -rf ios/objc ios/rubymotion osx tvos/objc + fi + ) + + cat > ${FOLDER}/docs.webloc < + + + + URL + https://realm.io/docs/${LANG}/${VERSION} + + +EOF + + ( + cd ${TEMPDIR} + zip --symlinks -r realm-${LANG}-${VERSION}.zip realm-${LANG}-${VERSION} + mv realm-${LANG}-${VERSION}.zip ${WORKSPACE} + ) + ;; + + "test-package-release") + # Generate a release package locally for testing purposes + # Real releases should always be done via Jenkins + if [ -z "${WORKSPACE}" ]; then + echo 'WORKSPACE must be set to a directory to assemble the release in' + exit 1 + fi + if [ -d "${WORKSPACE}" ]; then + echo 'WORKSPACE directory should not already exist' + exit 1 + fi + + REALM_SOURCE="$(pwd)" + mkdir -p "$WORKSPACE" + WORKSPACE="$(cd "$WORKSPACE" && pwd)" + export WORKSPACE + cd $WORKSPACE + git clone --recursive $REALM_SOURCE tightdb_objc + + echo 'Packaging iOS' + sh tightdb_objc/build.sh package-ios-static + cp tightdb_objc/build/ios-static/realm-framework-ios.zip . + sh tightdb_objc/build.sh package-ios-dynamic + cp tightdb_objc/build/ios/realm-dynamic-framework-ios.zip . + sh tightdb_objc/build.sh package-ios-swift + cp tightdb_objc/build/ios/realm-swift-framework-ios.zip . + + echo 'Packaging OS X' + sh tightdb_objc/build.sh package-osx + cp tightdb_objc/build/DerivedData/Realm/Build/Products/Release/realm-framework-osx.zip . + sh tightdb_objc/build.sh package-osx-swift + cp tightdb_objc/build/osx/realm-swift-framework-osx.zip . + + echo 'Packaging watchOS' + sh tightdb_objc/build.sh package-watchos + cp tightdb_objc/build/watchos/realm-framework-watchos.zip . + sh tightdb_objc/build.sh package-watchos-swift + cp tightdb_objc/build/watchos/realm-swift-framework-watchos.zip . + + echo 'Packaging tvOS' + sh tightdb_objc/build.sh package-tvos + cp tightdb_objc/build/tvos/realm-framework-tvos.zip . + sh tightdb_objc/build.sh package-tvos-swift + cp tightdb_objc/build/tvos/realm-swift-framework-tvos.zip . + + echo 'Packaging examples' + sh tightdb_objc/build.sh package-examples + cp tightdb_objc/realm-examples.zip . + + echo 'Building final release packages' + sh tightdb_objc/build.sh package-release objc + sh tightdb_objc/build.sh package-release swift + + echo 'Testing packaged examples' + sh tightdb_objc/build.sh package-test-examples + ;; + + "github-release") + if [ -z "${GITHUB_ACCESS_TOKEN}" ]; then + echo 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' + exit 1 + fi + ./scripts/github_release.rb + ;; + + "add-empty-changelog") + empty_section=$(cat < CHANGELOG.md + echo >> CHANGELOG.md + echo "$changelog" >> CHANGELOG.md + ;; + + *) + echo "Unknown command '$COMMAND'" + usage + exit 1 + ;; +esac diff --git a/Pods/Realm/core/librealm-ios.a b/Pods/Realm/core/librealm-ios.a new file mode 100644 index 0000000..b0aca58 Binary files /dev/null and b/Pods/Realm/core/librealm-ios.a differ diff --git a/Pods/Realm/include/RLMAccessor.h b/Pods/Realm/include/RLMAccessor.h new file mode 100644 index 0000000..f70039b --- /dev/null +++ b/Pods/Realm/include/RLMAccessor.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + + +@class RLMObjectSchema, RLMProperty, RLMObjectBase, RLMProperty; + +#ifdef __cplusplus +typedef NSUInteger RLMCreationOptions; +#else +typedef NS_OPTIONS(NSUInteger, RLMCreationOptions); +#endif + +NS_ASSUME_NONNULL_BEGIN + +// +// Accessors Class Creation/Caching +// + +// get accessor classes for an object class - generates classes if not cached +Class RLMAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, NSString *prefix); +Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema); + +// Check if a given class is a generated accessor class +bool RLMIsGeneratedClass(Class cls); + +// +// Dynamic getters/setters +// +FOUNDATION_EXTERN void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id __nullable val); +FOUNDATION_EXTERN id __nullable RLMDynamicGet(RLMObjectBase *obj, RLMProperty *prop); +FOUNDATION_EXTERN id __nullable RLMDynamicGetByName(RLMObjectBase *obj, NSString *propName, bool asList); + +// by property/column +void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val, RLMCreationOptions options); + +// +// Class modification +// + +// Replace className method for the given class +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className); + +// Replace sharedSchema method for the given class +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema * __nullable schema); + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMAnalytics.hpp b/Pods/Realm/include/RLMAnalytics.hpp new file mode 100644 index 0000000..76bc429 --- /dev/null +++ b/Pods/Realm/include/RLMAnalytics.hpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously submits build information to Realm if running in an iOS +// simulator or on OS X if a debugger is attached. Does nothing if running on an +// iOS / watchOS device or if a debugger is *not* attached. +// +// To be clear: this does *not* run when your app is in production or on +// your end-user’s devices; it will only run in the simulator or when a debugger +// is attached. +// +// Why are we doing this? In short, because it helps us build a better product +// for you. None of the data personally identifies you, your employer or your +// app, but it *will* help us understand what language you use, what iOS +// versions you target, etc. Having this info will help prioritizing our time, +// adding new features and deprecating old features. Collecting an anonymized +// bundle & anonymized MAC is the only way for us to count actual usage of the +// other metrics accurately. If we don’t have a way to deduplicate the info +// reported, it will be useless, as a single developer building their Swift app +// 10 times would report 10 times more than a single Objective-C developer that +// only builds once, making the data all but useless. +// No one likes sharing data unless it’s necessary, we get it, and we’ve +// debated adding this for a long long time. Since Realm is a free product +// without an email signup, we feel this is a necessary step so we can collect +// relevant data to build a better product for you. If you truly, absolutely +// feel compelled to not send this data back to Realm, then you can set an env +// variable named REALM_DISABLE_ANALYTICS. Since Realm is free we believe +// letting these analytics run is a small price to pay for the product & support +// we give you. +// +// Currently the following information is reported: +// - What version of Realm is being used, and from which language (obj-c or Swift). +// - What version of OS X it's running on (in case Xcode aggressively drops +// support for older versions again, we need to know what we need to support). +// - The minimum iOS/OS X version that the application is targeting (again, to +// help us decide what versions we need to support). +// - An anonymous MAC address and bundle ID to aggregate the other information on. +// - What version of Swift is being used (if applicable). + +void RLMSendAnalytics(); diff --git a/Pods/Realm/include/RLMArray.h b/Pods/Realm/include/RLMArray.h new file mode 100644 index 0000000..fc2ef9f --- /dev/null +++ b/Pods/Realm/include/RLMArray.h @@ -0,0 +1,369 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObject, RLMRealm, RLMResults, RLMNotificationToken; + +/** + `RLMArray` is the container type in Realm used to define to-many relationships. + + Unlike an `NSArray`, `RLMArray`s hold a single type, specified by the `objectClassName` property. + This is referred to in these docs as the “type” of the array. + + When declaring an `RLMArray` property, the type must be marked as conforming to a + protocol by the same name as the objects it should contain (see the + `RLM_ARRAY_TYPE` macro). In addition, the property can be declared using Objective-C + generics for better compile-time type safety. + + RLM_ARRAY_TYPE(ObjectType) + ... + @property RLMArray *arrayOfObjectTypes; + + `RLMArray`s can be queried with the same predicates as `RLMObject` and `RLMResult`s. + + `RLMArray`s cannot be created directly. `RLMArray` properties on `RLMObject`s are + lazily created when accessed, or can be obtained by querying a Realm. + + ### Key-Value Observing + + `RLMArray` supports array key-value observing on `RLMArray` properties on `RLMObject` + subclasses, and the `invalidated` property on `RLMArray` instances themselves is + key-value observing compliant when the `RLMArray` is attached to a managed + `RLMObject` (`RLMArray`s on unmanaged `RLMObject`s will never become invalidated). + + Because `RLMArray`s are attached to the object which they are a property of, they + do not require using the mutable collection proxy objects from + `-mutableArrayValueForKey:` or KVC-compatible mutation methods on the containing + object. Instead, you can call the mutation methods on the `RLMArray` directly. + */ + +@interface RLMArray : NSObject + +#pragma mark - Properties + +/** + The number of objects in the array. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the array. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages the array. Returns `nil` for unmanaged arrays. + */ +@property (nonatomic, readonly, nullable) RLMRealm *realm; + +/** + Indicates if the array can no longer be accessed. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +#pragma mark - Accessing Objects from an Array + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the array. + */ +- (RLMObjectType)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the array. + + Returns `nil` if called on an empty array. + + @return An `RLMObject` of the type contained in the array. + */ +- (nullable RLMObjectType)firstObject; + +/** + Returns the last object in the array. + + Returns `nil` if called on an empty array. + + @return An `RLMObject` of the type contained in the array. + */ +- (nullable RLMObjectType)lastObject; + + + +#pragma mark - Adding, Removing, and Replacing Objects in an Array + +/** + Adds an object to the end of the array. + + @warning This method may only be called during a write transaction. + + @param object An `RLMObject` of the type contained in the array. + */ +- (void)addObject:(RLMObjectType)object; + +/** + Adds an array of objects to the end of the array. + + @warning This method may only be called during a write transaction. + + @param objects An enumerable object such as `NSArray` or `RLMResults` which contains objects of the + same class as the array. + */ +- (void)addObjects:(id)objects; + +/** + Inserts an object at the given index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param anObject An `RLMObject` of the type contained in the array. + @param index The index at which to insert the object. + */ +- (void)insertObject:(RLMObjectType)anObject atIndex:(NSUInteger)index; + +/** + Removes an object at the given index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index The array index identifying the object to be removed. + */ +- (void)removeObjectAtIndex:(NSUInteger)index; + +/** + Removes the last object in the array. + + @warning This method may only be called during a write transaction. +*/ +- (void)removeLastObject; + +/** + Removes all objects from the array. + + @warning This method may only be called during a write transaction. + */ +- (void)removeAllObjects; + +/** + Replaces an object at the given index with a new object. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index The index of the object to be replaced. + @param anObject An object (of the same type as returned from the `objectClassName` selector). + */ +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObjectType)anObject; + +/** + Moves the object at the given source index to the given destination index. + + Throws an exception if the index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param sourceIndex The index of the object to be moved. + @param destinationIndex The index to which the object at `sourceIndex` should be moved. + */ +- (void)moveObjectAtIndex:(NSUInteger)sourceIndex toIndex:(NSUInteger)destinationIndex; + +/** + Exchanges the objects in the array at given indices. + + Throws an exception if either index exceeds the bounds of the array. + + @warning This method may only be called during a write transaction. + + @param index1 The index of the object which should replace the object at index `index2`. + @param index2 The index of the object which should replace the object at index `index1`. + */ +- (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2; + +#pragma mark - Querying an Array + +/** + Returns the index of an object in the array. + + Returns `NSNotFound` if the object is not found in the array. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObjectType)object; + +/** + Returns the index of the first object in the array matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the array. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the array matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the array. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all the objects matching the given predicate in the array. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all the objects matching the given predicate in the array. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` of objects that match the given predicate + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from the array. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from the array. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +/// :nodoc: +- (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index; + +/// :nodoc: +- (void)setObject:(RLMObjectType)newValue atIndexedSubscript:(NSUInteger)index; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the array changes. + + The block will be asynchronously called with the initial array, and then + called again after each write transaction which changes any of the objects in + the array, which objects are in the results, or the order of the objects in the + array. + + The `changes` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the array were added, removed or modified. If a write transaction + did not modify any objects in the array, the block is not called at all. + See the `RLMCollectionChange` documentation for information on how the changes + are reported and an example of updating a `UITableView`. + + If an error occurs the block will be called with `nil` for the results + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial results. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + Person *person = [[Person allObjectsInRealm:realm] firstObject]; + NSLog(@"person.dogs.count: %zu", person.dogs.count); // => 0 + self.token = [person.dogs addNotificationBlock(RLMArray *dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count) // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [person.dogs addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + @warning This method may only be called on a managed array. + + @param block The block to be called each time the array changes. + @return A token which must be held for as long as you want updates to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *__nullable array, + RLMCollectionChange *__nullable changes, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +#pragma mark - Unavailable Methods + +/** + `-[RLMArray init]` is not available because `RLMArray`s cannot be created directly. + `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + */ +- (instancetype)init __attribute__((unavailable("RLMArrays cannot be created directly"))); + +/** + `+[RLMArray new]` is not available because `RLMArray`s cannot be created directly. + `RLMArray` properties on `RLMObject`s are lazily created when accessed, or can be obtained by querying a Realm. + */ ++ (instancetype)new __attribute__((unavailable("RLMArrays cannot be created directly"))); + +@end + +/// :nodoc: +@interface RLMArray (Swift) +// for use only in Swift class definitions +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMArray_Private.h b/Pods/Realm/include/RLMArray_Private.h new file mode 100644 index 0000000..5e15e2d --- /dev/null +++ b/Pods/Realm/include/RLMArray_Private.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMArray () +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; +- (NSString *)descriptionWithMaxDepth:(NSUInteger)depth; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMArray_Private.hpp b/Pods/Realm/include/RLMArray_Private.hpp new file mode 100644 index 0000000..5db9abe --- /dev/null +++ b/Pods/Realm/include/RLMArray_Private.hpp @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMArray_Private.h" + +#import "RLMCollection_Private.hpp" + +#import + +#import + +namespace realm { + class Results; +} + +@class RLMObjectBase, RLMObjectSchema, RLMProperty; +class RLMClassInfo; +class RLMObservationInfo; + +@interface RLMArray () { +@protected + NSString *_objectClassName; +@public + // The name of the property which this RLMArray represents + NSString *_key; + __weak RLMObjectBase *_parentObject; +} +@end + +// +// LinkView backed RLMArray subclass +// +@interface RLMArrayLinkView : RLMArray +- (instancetype)initWithParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property; + +// deletes all objects in the RLMArray from their containing realms +- (void)deleteObjectsFromRealm; +@end + +void RLMValidateArrayObservationKey(NSString *keyPath, RLMArray *array); + +// Initialize the observation info for an array if needed +void RLMEnsureArrayObservationInfo(std::unique_ptr& info, + NSString *keyPath, RLMArray *array, id observed); + + +// +// RLMResults private methods +// +@interface RLMResults () ++ (instancetype)resultsWithObjectInfo:(RLMClassInfo&)info + results:(realm::Results)results; + +- (void)deleteObjectsFromRealm; +@end diff --git a/Pods/Realm/include/RLMAuthResponseModel.h b/Pods/Realm/include/RLMAuthResponseModel.h new file mode 100644 index 0000000..0cbf68e --- /dev/null +++ b/Pods/Realm/include/RLMAuthResponseModel.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +/** + An internal class representing a valid JSON response to an auth request. + + ``` + { + "access_token": { ... } // (optional), + "refresh_token": { ... } // (optional) + } + ``` + */ +@class RLMTokenModel; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMAuthResponseModel : NSObject + +@property (nonatomic, readonly, nullable) RLMTokenModel *accessToken; +@property (nonatomic, readonly, nullable) RLMTokenModel *refreshToken; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary + requireAccessToken:(BOOL)requireAccessToken + requireRefreshToken:(BOOL)requireRefreshToken; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMTokenModel cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMTokenModel cannot be created directly"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMClassInfo.hpp b/Pods/Realm/include/RLMClassInfo.hpp new file mode 100644 index 0000000..ea4610a --- /dev/null +++ b/Pods/Realm/include/RLMClassInfo.hpp @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import +#import + +namespace realm { + class ObjectSchema; + class Schema; + class Table; + struct Property; +} + +class RLMObservationInfo; +@class RLMRealm, RLMSchema, RLMObjectSchema, RLMProperty; + +NS_ASSUME_NONNULL_BEGIN + +namespace std { +// Add specializations so that NSString can be used as the key for hash containers +template<> struct hash { + size_t operator()(__unsafe_unretained NSString *const str) const { + return [str hash]; + } +}; +template<> struct equal_to { + bool operator()(__unsafe_unretained NSString * lhs, __unsafe_unretained NSString *rhs) const { + return [lhs isEqualToString:rhs]; + } +}; +} + +// The per-RLMRealm object schema information which stores the cached table +// reference, handles table column lookups, and tracks observed objects +class RLMClassInfo { +public: + RLMClassInfo(RLMRealm *, RLMObjectSchema *, const realm::ObjectSchema *); + + __unsafe_unretained RLMRealm *const realm; + __unsafe_unretained RLMObjectSchema *const rlmObjectSchema; + const realm::ObjectSchema *const objectSchema; + + // Storage for the functionality in RLMObservation for handling indirect + // changes to KVO-observed things + std::vector observedObjects; + + // Get the table for this object type. Will return nullptr only if it's a + // read-only Realm that is missing the table entirely. + realm::Table *_Nullable table() const; + + // Get the RLMProperty for a given table column, or `nil` if it is a column + // not used by the current schema + RLMProperty *_Nullable propertyForTableColumn(NSUInteger) const noexcept; + + // Get the RLMProperty that's used as the primary key, or `nil` if there is + // no primary key for the current schema + RLMProperty *_Nullable propertyForPrimaryKey() const noexcept; + + // Get the table column for the given property. The property must be a valid + // persisted property. + NSUInteger tableColumn(NSString *propertyName) const; + NSUInteger tableColumn(RLMProperty *property) const; + + RLMClassInfo &linkTargetType(size_t index); + + void releaseTable() { m_table = nullptr; } + +private: + mutable realm::Table *_Nullable m_table = nullptr; + std::vector m_linkTargets; +}; + +// A per-RLMRealm object schema map which stores RLMClassInfo keyed on the name +class RLMSchemaInfo { + using impl = std::unordered_map; +public: + RLMSchemaInfo() = default; + RLMSchemaInfo(RLMRealm *realm, RLMSchema *rlmSchema, realm::Schema const& schema); + + // Look up by name, throwing if it's not present + RLMClassInfo& operator[](NSString *name); + + impl::iterator begin() noexcept; + impl::iterator end() noexcept; + impl::const_iterator begin() const noexcept; + impl::const_iterator end() const noexcept; +private: + std::unordered_map m_objects; +}; + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMCollection.h b/Pods/Realm/include/RLMCollection.h new file mode 100644 index 0000000..030fcc7 --- /dev/null +++ b/Pods/Realm/include/RLMCollection.h @@ -0,0 +1,325 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm, RLMResults, RLMObject, RLMSortDescriptor, RLMNotificationToken, RLMCollectionChange; + +/** + A homogenous collection of `RLMObject` instances. Examples of conforming types include `RLMArray`, + `RLMResults`, and `RLMLinkingObjects`. + */ +@protocol RLMCollection + +@required + +#pragma mark - Properties + +/** + The number of objects in the collection. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the collection. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages the collection, or `nil` for unmanaged collections. + */ +@property (nonatomic, readonly) RLMRealm *realm; + +#pragma mark - Accessing Objects from a Collection + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the collection. + */ +- (id)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the collection. + + Returns `nil` if called on an empty collection. + + @return An `RLMObject` of the type contained in the collection. + */ +- (nullable id)firstObject; + +/** + Returns the last object in the collection. + + Returns `nil` if called on an empty collection. + + @return An `RLMObject` of the type contained in the collection. + */ +- (nullable id)lastObject; + +#pragma mark - Querying a Collection + +/** + Returns the index of an object in the collection. + + Returns `NSNotFound` if the object is not found in the collection. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObject *)object; + +/** + Returns the index of the first object in the collection matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the collection. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the collection matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the collection. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all objects matching the given predicate in the collection. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all objects matching the given predicate in the collection. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing objects that match the given predicate. + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from the collection. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from the collection. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +/// :nodoc: +- (id)objectAtIndexedSubscript:(NSUInteger)index; + +/** + Returns an `NSArray` containing the results of invoking `valueForKey:` using `key` on each of the collection's objects. + + @param key The name of the property. + + @return An `NSArray` containing results. + */ +- (nullable id)valueForKey:(NSString *)key; + +/** + Invokes `setValue:forKey:` on each of the collection's objects using the specified `value` and `key`. + + @warning This method may only be called during a write transaction. + + @param value The object value. + @param key The name of the property. + */ +- (void)setValue:(nullable id)value forKey:(NSString *)key; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the collection changes. + + The block will be asynchronously called with the initial collection, and then + called again after each write transaction which changes either any of the + objects in the collection, or which objects are in the collection. + + The `change` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the collection were added, removed or modified. If a write transaction + did not modify any objects in this collection, the block is not called at all. + See the `RLMCollectionChange` documentation for information on how the changes + are reported and an example of updating a `UITableView`. + + If an error occurs the block will be called with `nil` for the collection + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + At the time when the block is called, the collection object will be fully + evaluated and up-to-date, and as long as you do not perform a write transaction + on the same thread or explicitly call `-[RLMRealm refresh]`, accessing it will + never perform blocking work. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial collection. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + id collection = [Dog allObjects]; + NSLog(@"dogs.count: %zu", dogs.count); // => 0 + self.token = [collection addNotificationBlock:^(id dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count); // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [realm addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + + @param block The block to be called each time the collection changes. + @return A token which must be held for as long as you want collection notifications to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(id __nullable collection, + RLMCollectionChange *__nullable change, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +@end + +/** + An `RLMSortDescriptor` stores a property name and a sort order for use with + `sortedResultsUsingDescriptors:`. It is similar to `NSSortDescriptor`, but supports + only the subset of functionality which can be efficiently run by Realm's query + engine. + + `RLMSortDescriptor` instances are immutable. + */ +@interface RLMSortDescriptor : NSObject + +#pragma mark - Properties + +/** + The name of the property which the sort descriptor orders results by. + */ +@property (nonatomic, readonly) NSString *property; + +/** + Whether the descriptor sorts in ascending or descending order. + */ +@property (nonatomic, readonly) BOOL ascending; + +#pragma mark - Methods + +/** + Returns a new sort descriptor for the given property name and sort direction. + */ ++ (instancetype)sortDescriptorWithProperty:(NSString *)propertyName ascending:(BOOL)ascending; + +/** + Returns a copy of the receiver with the sort direction reversed. + */ +- (instancetype)reversedSortDescriptor; + +@end + +/** + A `RLMCollectionChange` object encapsulates information about changes to collections + that are reported by Realm notifications. + + `RLMCollectionChange` is passed to the notification blocks registered with + `-addNotificationBlock` on `RLMArray` and `RLMResults`, and reports what rows in the + collection changed since the last time the notification block was called. + + The change information is available in two formats: a simple array of row + indices in the collection for each type of change, and an array of index paths + in a requested section suitable for passing directly to `UITableView`'s batch + update methods. A complete example of updating a `UITableView` named `tv`: + + [tv beginUpdates]; + [tv deleteRowsAtIndexPaths:[changes deletionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv insertRowsAtIndexPaths:[changes insertionsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv reloadRowsAtIndexPaths:[changes modificationsInSection:0] withRowAnimation:UITableViewRowAnimationAutomatic]; + [tv endUpdates]; + + All of the arrays in an `RLMCollectionChange` are always sorted in ascending order. + */ +@interface RLMCollectionChange : NSObject +/// The indices of objects in the previous version of the collection which have +/// been removed from this one. +@property (nonatomic, readonly) NSArray *deletions; + +/// The indices in the new version of the collection which were newly inserted. +@property (nonatomic, readonly) NSArray *insertions; + +/** + The indices in the new version of the collection which were modified. + + For `RLMResults`, this means that one or more of the properties of the object at + that index were modified (or an object linked to by that object was + modified). + + For `RLMArray`, the array itself being modified to contain a + different object at that index will also be reported as a modification. + */ +@property (nonatomic, readonly) NSArray *modifications; + +/// Returns the index paths of the deletion indices in the given section. +- (NSArray *)deletionsInSection:(NSUInteger)section; + +/// Returns the index paths of the insertion indices in the given section. +- (NSArray *)insertionsInSection:(NSUInteger)section; + +/// Returns the index paths of the modification indices in the given section. +- (NSArray *)modificationsInSection:(NSUInteger)section; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMCollection_Private.hpp b/Pods/Realm/include/RLMCollection_Private.hpp new file mode 100644 index 0000000..3fd2691 --- /dev/null +++ b/Pods/Realm/include/RLMCollection_Private.hpp @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +namespace realm { + class List; + class Results; + class TableView; + struct CollectionChangeSet; + struct NotificationToken; +} +class RLMClassInfo; + +@protocol RLMFastEnumerable +@property (nonatomic, readonly) RLMRealm *realm; +@property (nonatomic, readonly) RLMClassInfo *objectInfo; +@property (nonatomic, readonly) NSUInteger count; + +- (NSUInteger)indexInSource:(NSUInteger)index; +- (realm::TableView)tableView; +@end + +// An object which encapulates the shared logic for fast-enumerating RLMArray +// and RLMResults, and has a buffer to store strong references to the current +// set of enumerated items +@interface RLMFastEnumerator : NSObject +- (instancetype)initWithCollection:(id)collection + objectSchema:(RLMClassInfo&)objectSchema; + +// Detach this enumerator from the source collection. Must be called before the +// source collection is changed. +- (void)detach; + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + count:(NSUInteger)len; +@end + +@interface RLMCancellationToken : RLMNotificationToken +- (instancetype)initWithToken:(realm::NotificationToken)token; +@end + +@interface RLMCollectionChange () +- (instancetype)initWithChanges:(realm::CollectionChangeSet)indices; +@end + +template +RLMNotificationToken *RLMAddNotificationBlock(id objcCollection, + Collection& collection, + void (^block)(id, RLMCollectionChange *, NSError *), + bool suppressInitialChange=false); + +NSArray *RLMCollectionValueForKey(id collection, NSString *key); +void RLMCollectionSetValueForKey(id collection, NSString *key, id value); +NSString *RLMDescriptionWithMaxDepth(NSString *name, id collection, NSUInteger depth); diff --git a/Pods/Realm/include/RLMConstants.h b/Pods/Realm/include/RLMConstants.h new file mode 100644 index 0000000..5b7899d --- /dev/null +++ b/Pods/Realm/include/RLMConstants.h @@ -0,0 +1,204 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +// For compatibility with Xcode 7, before extensible string enums were introduced, +#ifdef NS_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM NS_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(_, extensible_string_enum) NS_SWIFT_NAME(extensible_string_enum) +#else +#define RLM_EXTENSIBLE_STRING_ENUM +#define RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(fully_qualified, _) NS_SWIFT_NAME(fully_qualified) +#endif + +#if __has_attribute(ns_error_domain) && (!__cplusplus || __cplusplus >= 201103L) +#define RLM_ERROR_ENUM(type, name, domain) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wignored-attributes\"") \ + NS_ENUM(type, __attribute__((ns_error_domain(domain))) name) \ + _Pragma("clang diagnostic pop") +#else +#define RLM_ERROR_ENUM(type, name, domain) NS_ENUM(type, name) +#endif + + +#pragma mark - Enums + +/** + `RLMPropertyType` is an enumeration describing all property types supported in Realm models. + + For more information, see [Realm Models](https://realm.io/docs/objc/latest/#models). + */ +// Make sure numbers match those in +typedef NS_ENUM(int32_t, RLMPropertyType) { + +#pragma mark - Primitive types + + /** Integers: `NSInteger`, `int`, `long`, `Int` (Swift) */ + RLMPropertyTypeInt = 0, + /** Booleans: `BOOL`, `bool`, `Bool` (Swift) */ + RLMPropertyTypeBool = 1, + /** Floating-point numbers: `float`, `Float` (Swift) */ + RLMPropertyTypeFloat = 9, + /** Double-precision floating-point numbers: `double`, `Double` (Swift) */ + RLMPropertyTypeDouble = 10, + +#pragma mark - Object types + + /** Strings: `NSString`, `String` (Swift) */ + RLMPropertyTypeString = 2, + /** Binary data: `NSData` */ + RLMPropertyTypeData = 4, + /** + Any object: `id`. + + This property type is no longer supported for new models. However, old models with any-typed properties are still + supported for migration purposes. + */ + RLMPropertyTypeAny = 6, + /** Dates: `NSDate` */ + RLMPropertyTypeDate = 8, + +#pragma mark - Array/Linked object types + + /** Realm model objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeObject = 12, + /** Realm arrays. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeArray = 13, + /** Realm linking objects. See [Realm Models](https://realm.io/docs/objc/latest/#models) for more information. */ + RLMPropertyTypeLinkingObjects = 14, +}; + +/** An error domain identifying Realm-specific errors. */ +extern NSString * const RLMErrorDomain; + +/** An error domain identifying non-specific system errors. */ +extern NSString * const RLMUnknownSystemErrorDomain; + +/** + `RLMError` is an enumeration representing all recoverable errors. It is associated with the + Realm error domain specified in `RLMErrorDomain`. + */ +typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) { + /** Denotes a general error that occurred when trying to open a Realm. */ + RLMErrorFail = 1, + + /** Denotes a file I/O error that occurred when trying to open a Realm. */ + RLMErrorFileAccess = 2, + + /** + Denotes a file permission error that ocurred when trying to open a Realm. + + This error can occur if the user does not have permission to open or create + the specified file in the specified access mode when opening a Realm. + */ + RLMErrorFilePermissionDenied = 3, + + /** Denotes an error where a file was to be written to disk, but another file with the same name already exists. */ + RLMErrorFileExists = 4, + + /** + Denotes an error that occurs if a file could not be found. + + This error may occur if a Realm file could not be found on disk when trying to open a + Realm as read-only, or if the directory part of the specified path was not found when + trying to write a copy. + */ + RLMErrorFileNotFound = 5, + + /** + Denotes an error that occurs if a file format upgrade is required to open the file, + but upgrades were explicitly disabled. + */ + RLMErrorFileFormatUpgradeRequired = 6, + + /** + Denotes an error that occurs if the database file is currently open in another + process which cannot share with the current process due to an + architecture mismatch. + + This error may occur if trying to share a Realm file between an i386 (32-bit) iOS + Simulator and the Realm Browser application. In this case, please use the 64-bit + version of the iOS Simulator. + */ + RLMErrorIncompatibleLockFile = 8, + + /** Denotes an error that occurs when there is insufficient available address space. */ + RLMErrorAddressSpaceExhausted = 9, + + /** Denotes an error that occurs if there is a schema version mismatch, so that a migration is required. */ + RLMErrorSchemaMismatch = 10, +}; + +#pragma mark - Constants + +#pragma mark - Notification Constants + +/** + A notification indicating that changes were made to a Realm. +*/ +typedef NSString * RLMNotification RLM_EXTENSIBLE_STRING_ENUM; + +/** + This notification is posted by a Realm when the data in that Realm has changed. + + More specifically, this notification is posted after a Realm has been refreshed to + reflect a write transaction. This can happen when an autorefresh occurs, when + `-[RLMRealm refresh]` is called, after an implicit refresh from `-[RLMRealm beginWriteTransaction]`, + or after a local write transaction is completed. + */ +extern RLMNotification const RLMRealmRefreshRequiredNotification +RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmRefreshRequiredNotification, RefreshRequired); + +/** + This notification is posted by a Realm when a write transaction has been + committed to a Realm on a different thread for the same file. + + It is not posted if `-[RLMRealm autorefresh]` is enabled, or if the Realm is + refreshed before the notification has a chance to run. + + Realms with autorefresh disabled should normally install a handler for this + notification which calls `-[RLMRealm refresh]` after doing some work. Refreshing + the Realm is optional, but not refreshing the Realm may lead to large Realm + files. This is because Realm must keep an extra copy of the data for the stale + Realm. + */ +extern RLMNotification const RLMRealmDidChangeNotification +RLM_EXTENSIBLE_STRING_ENUM_CASE_SWIFT_NAME(RLMRealmDidChangeNotification, DidChange); + +#pragma mark - Other Constants + +/** The schema version used for uninitialized Realms */ +extern const uint64_t RLMNotVersioned; + +/** The corresponding value is the name of an exception thrown by Realm. */ +extern NSString * const RLMExceptionName; + +/** The corresponding value is a Realm file version. */ +extern NSString * const RLMRealmVersionKey; + +/** The corresponding key is the version of the underlying database engine. */ +extern NSString * const RLMRealmCoreVersionKey; + +/** The corresponding key is the Realm invalidated property name. */ +extern NSString * const RLMInvalidatedKey; + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMListBase.h b/Pods/Realm/include/RLMListBase.h new file mode 100644 index 0000000..0151cfb --- /dev/null +++ b/Pods/Realm/include/RLMListBase.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMArray; + +NS_ASSUME_NONNULL_BEGIN + +// A base class for Swift generic Lists to make it possible to interact with +// them from obj-c +@interface RLMListBase : NSObject +@property (nonatomic, strong) RLMArray *_rlmArray; + +- (instancetype)initWithArray:(RLMArray *)array; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMMigration.h b/Pods/Realm/include/RLMMigration.h new file mode 100644 index 0000000..d3da9ff --- /dev/null +++ b/Pods/Realm/include/RLMMigration.h @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMSchema; +@class RLMArray; +@class RLMObject; + +/** + A block type which provides both the old and new versions of an object in the Realm. Object + properties can only be accessed using keyed subscripting. + + @see `-[RLMMigration enumerateObjects:block:]` + + @param oldObject The object from the original Realm (read-only). + @param newObject The object from the migrated Realm (read-write). +*/ +typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObject * __nullable newObject); + +/** + `RLMMigration` instances encapsulate information intended to facilitate a schema migration. + + A `RLMMigration` instance is passed into a user-defined `RLMMigrationBlock` block when updating + the version of a Realm. This instance provides access to the old and new database schemas, the + objects in the Realm, and provides functionality for modifying the Realm during the migration. + */ +@interface RLMMigration : NSObject + +#pragma mark - Properties + +/** + Returns the old `RLMSchema`. This is the schema which describes the Realm before the + migration is applied. + */ +@property (nonatomic, readonly) RLMSchema *oldSchema; + +/** + Returns the new `RLMSchema`. This is the schema which describes the Realm after the + migration is applied. + */ +@property (nonatomic, readonly) RLMSchema *newSchema; + + +#pragma mark - Altering Objects during a Migration + +/** + Enumerates all the objects of a given type in the Realm, providing both the old and new versions + of each object. Within the block, object properties can only be accessed using keyed subscripting. + + @param className The name of the `RLMObject` class to enumerate. + + @warning All objects returned are of a type specific to the current migration and should not be cast + to `className`. Instead, treat them as `RLMObject`s and use keyed subscripting to access + properties. + */ +- (void)enumerateObjects:(NSString *)className block:(__attribute__((noescape)) RLMObjectMigrationBlock)block; + +/** + Creates and returns an `RLMObject` instance of type `className` in the Realm being migrated. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an `NSArray` as the `value` argument, all properties must be present, valid and in the same order as + the properties defined in the model. + + @param className The name of the `RLMObject` class to create. + @param value The value used to populate the object. + */ +- (RLMObject *)createObject:(NSString *)className withValue:(id)value; + +/** + Deletes an object from a Realm during a migration. + + It is permitted to call this method from within the block passed to `-[enumerateObjects:block:]`. + + @param object Object to be deleted from the Realm being migrated. + */ +- (void)deleteObject:(RLMObject *)object; + +/** + Deletes the data for the class with the given name. + + All objects of the given class will be deleted. If the `RLMObject` subclass no longer exists in your program, + any remaining metadata for the class will be removed from the Realm file. + + @param name The name of the `RLMObject` class to delete. + + @return A Boolean value indicating whether there was any data to delete. + */ +- (BOOL)deleteDataForClassName:(NSString *)name; + +/** + Renames a property of the given class from `oldName` to `newName`. + + @param className The name of the class whose property should be renamed. This class must be present + in both the old and new Realm schemas. + @param oldName The old name for the property to be renamed. There must not be a property with this name in the + class as defined by the new Realm schema. + @param newName The new name for the property to be renamed. There must not be a property with this name in the + class as defined by the old Realm schema. + */ +- (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMMigration_Private.h b/Pods/Realm/include/RLMMigration_Private.h new file mode 100644 index 0000000..99699e5 --- /dev/null +++ b/Pods/Realm/include/RLMMigration_Private.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import +#import + +namespace realm { + class Schema; +} + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMMigration () + +@property (nonatomic, strong) RLMRealm *oldRealm; +@property (nonatomic, strong) RLMRealm *realm; + +- (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema; + +- (void)execute:(RLMMigrationBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMNetworkClient.h b/Pods/Realm/include/RLMNetworkClient.h new file mode 100644 index 0000000..c72afed --- /dev/null +++ b/Pods/Realm/include/RLMNetworkClient.h @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil_Private.h" + +/** + An enum describing all possible endpoints on the Realm Object Server. + */ +typedef NS_ENUM(NSUInteger, RLMServerEndpoint) { + RLMServerEndpointAuth, + RLMServerEndpointLogout, + RLMServerEndpointAddCredential, + RLMServerEndpointRemoveCredential, +}; + +/** + A simple Realm Object Server network client that wraps `NSURLSession`. + */ +@interface RLMNetworkClient : NSObject + +NS_ASSUME_NONNULL_BEGIN + ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + completion:(RLMSyncCompletionBlock)completionBlock; + +/** + Post some JSON data to the authentication server, and asynchronously call a completion block with a JSON response + and/or error. + */ ++ (void)postRequestToEndpoint:(RLMServerEndpoint)endpoint + server:(NSURL *)serverURL + JSON:(NSDictionary *)jsonDictionary + timeout:(NSTimeInterval)timeout + completion:(RLMSyncCompletionBlock)completionBlock; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMObject.h b/Pods/Realm/include/RLMObject.h new file mode 100644 index 0000000..407a258 --- /dev/null +++ b/Pods/Realm/include/RLMObject.h @@ -0,0 +1,445 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMPropertyDescriptor; +@class RLMRealm; +@class RLMResults; +@class RLMObjectSchema; + +/** + `RLMObject` is a base class for model objects representing data stored in Realms. + + Define your model classes by subclassing `RLMObject` and adding properties to be managed. + Then instantiate and use your custom subclasses instead of using the `RLMObject` class directly. + + // Dog.h + @interface Dog : RLMObject + @property NSString *name; + @property BOOL adopted; + @end + + // Dog.m + @implementation Dog + @end //none needed + + ### Supported property types + + - `NSString` + - `NSInteger`, `int`, `long`, `float`, and `double` + - `BOOL` or `bool` + - `NSDate` + - `NSData` + - `NSNumber`, where `X` is one of `RLMInt`, `RLMFloat`, `RLMDouble` or `RLMBool`, for optional number properties + - `RLMObject` subclasses, to model many-to-one relationships. + - `RLMArray`, where `X` is an `RLMObject` subclass, to model many-to-many relationships. + + ### Querying + + You can initiate queries directly via the class methods: `allObjects`, `objectsWhere:`, and `objectsWithPredicate:`. + These methods allow you to easily query a custom subclass for instances of that class in the default Realm. + + To search in a Realm other than the default Realm, use the `allObjectsInRealm:`, `objectsInRealm:where:`, + and `objectsInRealm:withPredicate:` class methods. + + @see `RLMRealm` + + ### Relationships + + See our [Cocoa guide](https://realm.io/docs/objc/latest#relationships) for more details. + + ### Key-Value Observing + + All `RLMObject` properties (including properties you create in subclasses) are + [Key-Value Observing compliant](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html), + except for `realm` and `objectSchema`. + + Keep the following tips in mind when observing Realm objects: + + 1. Unlike `NSMutableArray` properties, `RLMArray` properties do not require + using the proxy object returned from `-mutableArrayValueForKey:`, or defining + KVC mutation methods on the containing class. You can simply call methods on + the `RLMArray` directly; any changes will be automatically observed by the containing + object. + 2. Unmanaged `RLMObject` instances cannot be added to a Realm while they have any + observed properties. + 3. Modifying managed `RLMObject`s within `-observeValueForKeyPath:ofObject:change:context:` + is not recommended. Properties may change even when the Realm is not in a write + transaction (for example, when `-[RLMRealm refresh]` is called after changes + are made on a different thread), and notifications sent prior to the change + being applied (when `NSKeyValueObservingOptionPrior` is used) may be sent at + times when you *cannot* begin a write transaction. + */ + +@interface RLMObject : RLMObjectBase + +#pragma mark - Creating & Initializing Objects + +/** + Creates an unmanaged instance of a Realm object. + + Call `addObject:` on an `RLMRealm` instance to add an unmanaged object into that Realm. + + @see `[RLMRealm addObject:]` + */ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + + +/** + Creates an unmanaged instance of a Realm object. + + Pass in an `NSArray` or `NSDictionary` instance to set the values of the object's properties. + + Call `addObject:` on an `RLMRealm` instance to add an unmanaged object into that Realm. + + @see `[RLMRealm addObject:]` + */ +- (instancetype)initWithValue:(id)value NS_DESIGNATED_INITIALIZER; + + +/** + Returns the class name for a Realm object subclass. + + @warning Do not override. Realm relies on this method returning the exact class + name. + + @return The class name for the model class. + */ ++ (NSString *)className; + +/** + Creates an instance of a Realm object with a given value, and adds it to the default Realm. + + If nested objects are included in the argument, `createInDefaultRealmWithValue:` will be recursively called + on them. + + The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in + `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if + any required properties are not present and those properties were not defined with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param value The value used to populate the object. + + @see `defaultPropertyValues` + */ ++ (instancetype)createInDefaultRealmWithValue:(id)value; + +/** + Creates an instance of a Realm object with a given value, and adds it to the specified Realm. + + If nested objects are included in the argument, `createInRealm:withValue:` will be recursively called + on them. + + The `value` argument can be a key-value coding compliant object, an array or dictionary returned from the methods in + `NSJSONSerialization`, or an array containing one element for each managed property. An exception will be thrown if any + required properties are not present and those properties were not defined with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param realm The Realm which should manage the newly-created object. + @param value The value used to populate the object. + + @see `defaultPropertyValues` + */ ++ (instancetype)createInRealm:(RLMRealm *)realm withValue:(id)value; + +/** + Creates or updates a Realm object within the default Realm. + + This method may only be called on Realm object types with a primary key defined. If there is already + an object with the same primary key value in the default Realm, its values are updated and the object + is returned. Otherwise, this method creates and populates a new instance of the object in the default Realm. + + If nested objects are included in the argument, `createOrUpdateInDefaultRealmWithValue:` will be + recursively called on them if they have primary keys, `createInDefaultRealmWithValue:` if they do not. + + If the argument is a Realm object already managed by the default Realm, the argument's type is the same + as the receiver, and the objects have identical values for their managed properties, this method does nothing. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param value The value used to populate the object. + + @see `defaultPropertyValues`, `primaryKey` + */ ++ (instancetype)createOrUpdateInDefaultRealmWithValue:(id)value; + +/** + Creates or updates an Realm object within a specified Realm. + + This method may only be called on Realm object types with a primary key defined. If there is already + an object with the same primary key value in the given Realm, its values are updated and the object + is returned. Otherwise this method creates and populates a new instance of this object in the given Realm. + + If nested objects are included in the argument, `createOrUpdateInRealm:withValue:` will be + recursively called on them if they have primary keys, `createInRealm:withValue:` if they do not. + + If the argument is a Realm object already managed by the given Realm, the argument's type is the same + as the receiver, and the objects have identical values for their managed properties, this method does nothing. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @param realm The Realm which should own the object. + @param value The value used to populate the object. + + @see `defaultPropertyValues`, `primaryKey` + */ ++ (instancetype)createOrUpdateInRealm:(RLMRealm *)realm withValue:(id)value; + +#pragma mark - Properties + +/** + The Realm which manages the object, or `nil` if the object is unmanaged. + */ +@property (nonatomic, readonly, nullable) RLMRealm *realm; + +/** + The object schema which lists the managed properties for the object. + */ +@property (nonatomic, readonly) RLMObjectSchema *objectSchema; + +/** + Indicates if the object can no longer be accessed because it is now invalid. + + An object can no longer be accessed if the object has been deleted from the Realm that manages it, or + if `invalidate` is called on that Realm. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + + +#pragma mark - Customizing your Objects + +/** + Returns an array of property names for properties which should be indexed. + + Only string, integer, boolean, and `NSDate` properties are supported. + + @return An array of property names. + */ ++ (NSArray *)indexedProperties; + +/** + Override this method to specify the default values to be used for each property. + + @return A dictionary mapping property names to their default values. + */ ++ (nullable NSDictionary *)defaultPropertyValues; + +/** + Override this method to specify the name of a property to be used as the primary key. + + Only properties of types `RLMPropertyTypeString` and `RLMPropertyTypeInt` can be designated as the primary key. + Primary key properties enforce uniqueness for each value whenever the property is set, which incurs minor overhead. + Indexes are created automatically for primary key properties. + + @return The name of the property designated as the primary key. + */ ++ (nullable NSString *)primaryKey; + +/** + Override this method to specify the names of properties to ignore. These properties will not be managed by the Realm + that manages the object. + + @return An array of property names to ignore. + */ ++ (nullable NSArray *)ignoredProperties; + +/** + Override this method to specify the names of properties that are non-optional (i.e. cannot be assigned a `nil` value). + + By default, all properties of a type whose values can be set to `nil` are considered optional properties. + To require that an object in a Realm always store a non-`nil` value for a property, + add the name of the property to the array returned from this method. + + Properties of `RLMObject` type cannot be non-optional. Array and `NSNumber` properties + can be non-optional, but there is no reason to do so: arrays do not support storing nil, and + if you want a non-optional number you should instead use the primitive type. + + @return An array of property names that are required. + */ ++ (NSArray *)requiredProperties; + +/** + Override this method to provide information related to properties containing linking objects. + + Each property of type `RLMLinkingObjects` must have a key in the dictionary returned by this method consisting + of the property name. The corresponding value must be an instance of `RLMPropertyDescriptor` that describes the class + and property that the property is linked to. + + return @{ @"owners": [RLMPropertyDescriptor descriptorWithClass:Owner.class propertyName:@"dogs"] }; + + @return A dictionary mapping property names to `RLMPropertyDescriptor` instances. + */ ++ (NSDictionary *)linkingObjectsProperties; + + +#pragma mark - Getting & Querying Objects from the Default Realm + +/** + Returns all objects of this object type from the default Realm. + + @return An `RLMResults` containing all objects of this type in the default Realm. + */ ++ (RLMResults *)allObjects; + +/** + Returns all objects of this object type matching the given predicate from the default Realm. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing all objects of this type in the default Realm that match the given predicate. + */ ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + + +/** + Returns all objects of this object type matching the given predicate from the default Realm. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing all objects of this type in the default Realm that match the given predicate. + */ ++ (RLMResults *)objectsWithPredicate:(nullable NSPredicate *)predicate; + +/** + Retrieves the single instance of this object type with the given primary key from the default Realm. + + Returns the object from the default Realm which has the given primary key, or + `nil` if the object does not exist. This is slightly faster than the otherwise + equivalent `[[SubclassName objectsWhere:@"primaryKeyPropertyName = %@", key] firstObject]`. + + This method requires that `primaryKey` be overridden on the receiving subclass. + + @return An object of this object type, or `nil` if an object with the given primary key does not exist. + @see `-primaryKey` + */ ++ (nullable instancetype)objectForPrimaryKey:(nullable id)primaryKey; + + +#pragma mark - Querying Specific Realms + +/** + Returns all objects of this object type from the specified Realm. + + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm. + */ ++ (RLMResults *)allObjectsInRealm:(RLMRealm *)realm; + +/** + Returns all objects of this object type matching the given predicate from the specified Realm. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm that match the given predicate. + */ ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat, ...; + +/// :nodoc: ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all objects of this object type matching the given predicate from the specified Realm. + + @param predicate A predicate to use to filter the elements. + @param realm The Realm to query. + + @return An `RLMResults` containing all objects of this type in the specified Realm that match the given predicate. + */ ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm withPredicate:(nullable NSPredicate *)predicate; + +/** + Retrieves the single instance of this object type with the given primary key from the specified Realm. + + Returns the object from the specified Realm which has the given primary key, or + `nil` if the object does not exist. This is slightly faster than the otherwise + equivalent `[[SubclassName objectsInRealm:realm where:@"primaryKeyPropertyName = %@", key] firstObject]`. + + This method requires that `primaryKey` be overridden on the receiving subclass. + + @return An object of this object type, or `nil` if an object with the given primary key does not exist. + @see `-primaryKey` + */ ++ (nullable instancetype)objectInRealm:(RLMRealm *)realm forPrimaryKey:(nullable id)primaryKey; + +#pragma mark - Other Instance Methods + +/** + Returns YES if another Realm object instance points to the same object as the receiver in the Realm managing + the receiver. + + For object types with a primary, key, `isEqual:` is overridden to use this method (along with a corresponding + implementation for `hash`). + + @param object The object to compare the receiver to. + + @return Whether the object represents the same object as the receiver. + */ +- (BOOL)isEqualToObject:(RLMObject *)object; + +#pragma mark - Dynamic Accessors + +/// :nodoc: +- (nullable id)objectForKeyedSubscript:(NSString *)key; + +/// :nodoc: +- (void)setObject:(nullable id)obj forKeyedSubscript:(NSString *)key; + +@end + +#pragma mark - RLMArray Property Declaration + +/** + Properties on `RLMObject`s of type `RLMArray` must have an associated type. A type is associated + with an `RLMArray` property by defining a protocol for the object type that the array should contain. + To define the protocol for an object, you can use the macro RLM_ARRAY_TYPE: + + RLM_ARRAY_TYPE(ObjectType) + ... + @property RLMArray *arrayOfObjectTypes; + */ +#define RLM_ARRAY_TYPE(RLM_OBJECT_SUBCLASS)\ +@protocol RLM_OBJECT_SUBCLASS \ +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectBase.h b/Pods/Realm/include/RLMObjectBase.h new file mode 100644 index 0000000..1ad1234 --- /dev/null +++ b/Pods/Realm/include/RLMObjectBase.h @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm; +@class RLMSchema; +@class RLMObjectSchema; + +/// :nodoc: +@interface RLMObjectBase : NSObject + +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + ++ (NSString *)className; + +// Returns whether the class is included in the default set of classes managed by a Realm. ++ (BOOL)shouldIncludeInDefaultSchema; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectBase_Dynamic.h b/Pods/Realm/include/RLMObjectBase_Dynamic.h new file mode 100644 index 0000000..08f25d5 --- /dev/null +++ b/Pods/Realm/include/RLMObjectBase_Dynamic.h @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMObjectSchema, RLMRealm; + +NS_ASSUME_NONNULL_BEGIN + +/** + Returns the Realm that manages the object, if one exists. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve the Realm that manages the object via `RLMObject`. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + + @return The Realm which manages this object. Returns `nil `for unmanaged objects. + */ +FOUNDATION_EXTERN RLMRealm * _Nullable RLMObjectBaseRealm(RLMObjectBase * _Nullable object); + +/** + Returns an `RLMObjectSchema` which describes the managed properties of the object. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve `objectSchema` via `RLMObject`. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + + @return The object schema which lists the managed properties for the object. + */ +FOUNDATION_EXTERN RLMObjectSchema * _Nullable RLMObjectBaseObjectSchema(RLMObjectBase * _Nullable object); + +/** + Returns the object corresponding to a key value. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to retrieve key values via `RLMObject`. + + @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + @param key The name of the property. + + @return The object for the property requested. + */ +FOUNDATION_EXTERN id _Nullable RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase * _Nullable object, NSString *key); + +/** + Sets a value for a key on the object. + + @warning This function is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is + recommended to set key values via `RLMObject`. + + @warning Will throw an `NSUndefinedKeyException` if `key` is not present on the object. + + @param object An `RLMObjectBase` obtained via a Swift `Object` or `RLMObject`. + @param key The name of the property. + @param obj The object to set as the value of the key. + */ +FOUNDATION_EXTERN void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase * _Nullable object, NSString *key, id _Nullable obj); + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectSchema.h b/Pods/Realm/include/RLMObjectSchema.h new file mode 100644 index 0000000..808cdfa --- /dev/null +++ b/Pods/Realm/include/RLMObjectSchema.h @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMProperty; + +/** + This class represents Realm model object schemas. + + When using Realm, `RLMObjectSchema` instances allow performing migrations and + introspecting the database's schema. + + Object schemas map to tables in the core database. + */ +@interface RLMObjectSchema : NSObject + +#pragma mark - Properties + +/** + An array of `RLMProperty` instances representing the managed properties of a class described by the schema. + + @see `RLMProperty` + */ +@property (nonatomic, readonly, copy) NSArray *properties; + +/** + The name of the class the schema describes. + */ +@property (nonatomic, readonly) NSString *className; + +/** + The property which serves as the primary key for the class the schema describes, if any. + */ +@property (nonatomic, readonly, nullable) RLMProperty *primaryKeyProperty; + +#pragma mark - Methods + +/** + Retrieves an `RLMProperty` object by the property name. + + @param propertyName The property's name. + + @return An `RLMProperty` object, or `nil` if there is no property with the given name. + */ +- (nullable RLMProperty *)objectForKeyedSubscript:(NSString *)propertyName; + +/** + Returns whether two `RLMObjectSchema` instances are equal. + */ +- (BOOL)isEqualToObjectSchema:(RLMObjectSchema *)objectSchema; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectSchema_Private.h b/Pods/Realm/include/RLMObjectSchema_Private.h new file mode 100644 index 0000000..45f3018 --- /dev/null +++ b/Pods/Realm/include/RLMObjectSchema_Private.h @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +// RLMObjectSchema private +@interface RLMObjectSchema () { +@public + bool _isSwiftClass; +} + +// writable redecleration +@property (nonatomic, readwrite, copy) NSArray *properties; +@property (nonatomic, readwrite, assign) bool isSwiftClass; + +// class used for this object schema +@property (nonatomic, readwrite, assign) Class objectClass; +@property (nonatomic, readwrite, assign) Class accessorClass; +@property (nonatomic, readwrite, assign) Class unmanagedClass; + +@property (nonatomic, readwrite, nullable) RLMProperty *primaryKeyProperty; + +@property (nonatomic, copy) NSArray *computedProperties; +@property (nonatomic, readonly) NSArray *swiftGenericProperties; + +// returns a cached or new schema for a given object class ++ (instancetype)schemaForObjectClass:(Class)objectClass; +@end + +@interface RLMObjectSchema (Dynamic) +/** + This method is useful only in specialized circumstances, for example, when accessing objects + in a Realm produced externally. If you are simply building an app on Realm, it is not recommended + to use this method as an [RLMObjectSchema](RLMObjectSchema) is generated automatically for every [RLMObject](RLMObject) subclass. + + Initialize an RLMObjectSchema with classname, objectClass, and an array of properties + + @warning This method is useful only in specialized circumstances. + + @param objectClassName The name of the class used to refer to objects of this type. + @param objectClass The Objective-C class used when creating instances of this type. + @param properties An array of RLMProperty instances describing the managed properties for this type. + + @return An initialized instance of RLMObjectSchema. + */ +- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObjectSchema_Private.hpp b/Pods/Realm/include/RLMObjectSchema_Private.hpp new file mode 100644 index 0000000..3d93be2 --- /dev/null +++ b/Pods/Realm/include/RLMObjectSchema_Private.hpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObjectSchema_Private.h" + +#import "object_schema.hpp" + +@interface RLMObjectSchema () +// create realm::ObjectSchema copy +- (realm::ObjectSchema)objectStoreCopy; + +// initialize with realm::ObjectSchema ++ (instancetype)objectSchemaForObjectStoreSchema:(realm::ObjectSchema const&)objectSchema; +@end diff --git a/Pods/Realm/include/RLMObjectStore.h b/Pods/Realm/include/RLMObjectStore.h new file mode 100644 index 0000000..3d7f194 --- /dev/null +++ b/Pods/Realm/include/RLMObjectStore.h @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#ifdef __cplusplus +extern "C" { +#endif + +@class RLMRealm, RLMSchema, RLMObjectBase, RLMResults, RLMProperty; + +NS_ASSUME_NONNULL_BEGIN + +// +// Accessor Creation +// + +// create or get cached accessors for the given schema +void RLMRealmCreateAccessors(RLMSchema *schema); + + +// +// Options for object creation +// +typedef NS_OPTIONS(NSUInteger, RLMCreationOptions) { + // Normal object creation + RLMCreationOptionsNone = 0, + // If the property is a link or array property, upsert the linked objects + // if they have a primary key, and insert them otherwise. + RLMCreationOptionsCreateOrUpdate = 1 << 0, + // Allow unmanaged objects to be promoted to managed objects + // if false objects are copied during object creation + RLMCreationOptionsPromoteUnmanaged = 1 << 1, + // Use the SetDefault instruction. + RLMCreationOptionsSetDefault = 1 << 2, +}; + + +// +// Adding, Removing, Getting Objects +// + +// add an object to the given realm +void RLMAddObjectToRealm(RLMObjectBase *object, RLMRealm *realm, bool createOrUpdate); + +// delete an object from its realm +void RLMDeleteObjectFromRealm(RLMObjectBase *object, RLMRealm *realm); + +// deletes all objects from a realm +void RLMDeleteAllObjectsFromRealm(RLMRealm *realm); + +// get objects of a given class +RLMResults *RLMGetObjects(RLMRealm *realm, NSString *objectClassName, NSPredicate * _Nullable predicate) +NS_RETURNS_RETAINED; + +// get an object with the given primary key +id _Nullable RLMGetObject(RLMRealm *realm, NSString *objectClassName, id _Nullable key) NS_RETURNS_RETAINED; + +// create object from array or dictionary +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id _Nullable value, bool createOrUpdate) +NS_RETURNS_RETAINED; + + +// +// Accessor Creation +// + + +// switch List<> properties from being backed by unmanaged RLMArrays to RLMArrayLinkView +void RLMInitializeSwiftAccessorGenerics(RLMObjectBase *object); + +#ifdef __cplusplus +} + +namespace realm { + class Table; + template class BasicRowExpr; + using RowExpr = BasicRowExpr; +} +class RLMClassInfo; + +// Create accessors +RLMObjectBase *RLMCreateObjectAccessor(RLMRealm *realm, RLMClassInfo& info, + NSUInteger index) NS_RETURNS_RETAINED; +RLMObjectBase *RLMCreateObjectAccessor(RLMRealm *realm, RLMClassInfo& info, + realm::RowExpr row) NS_RETURNS_RETAINED; +#endif + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObject_Private.h b/Pods/Realm/include/RLMObject_Private.h new file mode 100644 index 0000000..400f587 --- /dev/null +++ b/Pods/Realm/include/RLMObject_Private.h @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +// RLMObject accessor and read/write realm +@interface RLMObjectBase () { +@public + RLMRealm *_realm; + __unsafe_unretained RLMObjectSchema *_objectSchema; +} + +// unmanaged initializer +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema NS_DESIGNATED_INITIALIZER; + +// live accessor initializer +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema NS_DESIGNATED_INITIALIZER; + +// shared schema for this class ++ (nullable RLMObjectSchema *)sharedSchema; + +// provide injection point for alternative Swift object util class ++ (Class)objectUtilClass:(BOOL)isSwift; + +@end + +@interface RLMObject () + +// unmanaged initializer +- (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema NS_DESIGNATED_INITIALIZER; + +// live accessor initializer +- (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm + schema:(RLMObjectSchema *)schema NS_DESIGNATED_INITIALIZER; + +@end + +@interface RLMDynamicObject : RLMObject + +@end + +// A reference to an object's row that doesn't keep the object accessor alive. +// Used by some Swift property types, such as LinkingObjects, to avoid retain cycles +// with their containing object. +@interface RLMWeakObjectHandle : NSObject + +- (instancetype)initWithObject:(RLMObjectBase *)object; + +// Consumes the row, so can only usefully be called once. +@property (nonatomic, readonly) RLMObjectBase *object; + +@end + +// Calls valueForKey: and re-raises NSUndefinedKeyExceptions +FOUNDATION_EXTERN id _Nullable RLMValidatedValueForProperty(id object, NSString *key, NSString *className); + +// Compare two RLObjectBases +FOUNDATION_EXTERN BOOL RLMObjectBaseAreEqual(RLMObjectBase * _Nullable o1, RLMObjectBase * _Nullable o2); + +// Get ObjectUil class for objc or swift +FOUNDATION_EXTERN Class RLMObjectUtilClass(BOOL isSwift); + +FOUNDATION_EXTERN const NSUInteger RLMDescriptionMaxDepth; + +@class RLMProperty, RLMArray; +@interface RLMObjectUtil : NSObject + ++ (nullable NSArray *)ignoredPropertiesForClass:(Class)cls; ++ (nullable NSArray *)indexedPropertiesForClass:(Class)cls; ++ (nullable NSDictionary *> *)linkingObjectsPropertiesForClass:(Class)cls; + ++ (nullable NSArray *)getGenericListPropertyNames:(id)obj; ++ (nullable NSDictionary *)getLinkingObjectsProperties:(id)object; + ++ (nullable NSDictionary *)getOptionalProperties:(id)obj; ++ (nullable NSArray *)requiredPropertiesForClass:(Class)cls; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMObject_Private.hpp b/Pods/Realm/include/RLMObject_Private.hpp new file mode 100644 index 0000000..7f79dd6 --- /dev/null +++ b/Pods/Realm/include/RLMObject_Private.hpp @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMObject_Private.h" + +#import "RLMRealm_Private.hpp" +#import "RLMUtil.hpp" + +#import // required by row.hpp +#import + +class RLMObservationInfo; + +// RLMObject accessor and read/write realm +@interface RLMObjectBase () { + @public + realm::Row _row; + RLMObservationInfo *_observationInfo; + RLMClassInfo *_info; +} +@end + +// FIXME-2.0: This should be folded into initWithRealm:schema:, but changing the +// signature of that is a breaking change for Swift +id RLMCreateManagedAccessor(Class cls, RLMRealm *realm, RLMClassInfo *info) NS_RETURNS_RETAINED; + +// throw an exception if the object is invalidated or on the wrong thread +static inline void RLMVerifyAttached(__unsafe_unretained RLMObjectBase *const obj) { + if (!obj->_row.is_attached()) { + @throw RLMException(@"Object has been deleted or invalidated."); + } + [obj->_realm verifyThread]; +} + +// throw an exception if the object can't be modified for any reason +static inline void RLMVerifyInWriteTransaction(__unsafe_unretained RLMObjectBase *const obj) { + // first verify is attached + RLMVerifyAttached(obj); + + if (!obj->_realm.inWriteTransaction) { + @throw RLMException(@"Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first."); + } +} diff --git a/Pods/Realm/include/RLMObservation.hpp b/Pods/Realm/include/RLMObservation.hpp new file mode 100644 index 0000000..0f5b215 --- /dev/null +++ b/Pods/Realm/include/RLMObservation.hpp @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "binding_context.hpp" + +#import +#import + +#import + +@class RLMObjectBase, RLMRealm, RLMSchema, RLMProperty, RLMObjectSchema; +class RLMClassInfo; +class RLMSchemaInfo; + +namespace realm { + class History; + class SharedGroup; +} + +// RLMObservationInfo stores all of the KVO-related data for RLMObjectBase and +// RLMArray. There is a one-to-one relationship between observed objects and +// RLMObservationInfo instances, so it could be folded into RLMObjectBase, and +// is a separate class mostly to avoid making all accessor objects far larger. +// +// RLMClassInfo stores a vector of pointers to the first observation info +// created for each row. If there are multiple observation infos for a single +// row (such as if there are multiple observed objects backed by a single row, +// or if both an object and an array property of that object are observed), +// they're stored in an intrusive doubly-linked-list in the `next` and `prev` +// members. This is done primarily to make it simpler and faster to loop over +// all of the observed objects for a single row, as that needs to be done for +// every change. +class RLMObservationInfo { +public: + RLMObservationInfo(id object); + RLMObservationInfo(RLMClassInfo &objectSchema, std::size_t row, id object); + ~RLMObservationInfo(); + + realm::Row const& getRow() const { + return row; + } + + NSString *columnName(size_t col) const noexcept; + + // Send willChange/didChange notifications to all observers for this object/row + // Sends the array versions if indexes is non-nil, normal versions otherwise + void willChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const; + void didChange(NSString *key, NSKeyValueChange kind=NSKeyValueChangeSetting, NSIndexSet *indexes=nil) const; + + bool isForRow(size_t ndx) const { + return row && row.get_index() == ndx; + } + + void recordObserver(realm::Row& row, RLMClassInfo *objectInfo, RLMObjectSchema *objectSchema, NSString *keyPath); + void removeObserver(); + bool hasObservers() const { return observerCount > 0; } + + // valueForKey: on observed object and array properties needs to return the + // same object each time for KVO to work at all. Doing this all the time + // requires some odd semantics to avoid reference cycles, so instead we do + // it only to the extent specifically required by KVO. In addition, we + // need to continue to return the same object even if this row is deleted, + // or deleting an object with active observers will explode horribly. + // Once prepareForInvalidation() is called, valueForKey() will always return + // the cached value for object and array properties without checking the + // backing row to verify it's up-to-date. + // + // prepareForInvalidation() must be called on the head of the linked list + // (i.e. on the object pointed to directly by the object schema) + id valueForKey(NSString *key); + + void prepareForInvalidation(); + +private: + // Doubly-linked-list of observed objects for the same row as this + RLMObservationInfo *next = nullptr; + RLMObservationInfo *prev = nullptr; + + // Row being observed + realm::Row row; + RLMClassInfo *objectSchema = nullptr; + + // Object doing the observing + __unsafe_unretained id object = nil; + + // valueForKey: hack + bool invalidated = false; + size_t observerCount = 0; + NSString *lastKey = nil; + __unsafe_unretained RLMProperty *lastProp = nil; + + // objects returned from valueForKey() to keep them alive in case observers + // are added and so that they can still be accessed after row is detached + NSMutableDictionary *cachedObjects; + + void setRow(realm::Table &table, size_t newRow); + + template + void forEach(F&& f) const { + // The user's observation handler may release their last reference to + // the object being observed, which will result in the RLMObservationInfo + // being destroyed. As a result, we need to retain the object which owns + // both `this` and the current info we're looking at. + __attribute__((objc_precise_lifetime)) id self = object, current; + for (auto info = prev; info; info = info->prev) { + current = info->object; + f(info->object); + } + for (auto info = this; info; info = info->next) { + current = info->object; + f(info->object); + } + } + + // Default move/copy constructors don't work due to the intrusive linked + // list and we don't need them + RLMObservationInfo(RLMObservationInfo const&) = delete; + RLMObservationInfo(RLMObservationInfo&&) = delete; + RLMObservationInfo& operator=(RLMObservationInfo const&) = delete; + RLMObservationInfo& operator=(RLMObservationInfo&&) = delete; +}; + +// Get the the observation info chain for the given row +// Will simply return info if it's non-null, and will search ojectSchema's array +// for a matching one otherwise, and return null if there are none +RLMObservationInfo *RLMGetObservationInfo(RLMObservationInfo *info, size_t row, RLMClassInfo& objectSchema); + +// delete all objects from a single table with change notifications +void RLMClearTable(RLMClassInfo &realm); + +// invoke the block, sending notifications for cascading deletes/link nullifications +void RLMTrackDeletions(RLMRealm *realm, dispatch_block_t block); + +std::vector RLMGetObservedRows(RLMSchemaInfo const& schema); +void RLMWillChange(std::vector const& observed, std::vector const& invalidated); +void RLMDidChange(std::vector const& observed, std::vector const& invalidated); diff --git a/Pods/Realm/include/RLMOptionalBase.h b/Pods/Realm/include/RLMOptionalBase.h new file mode 100644 index 0000000..5ee261e --- /dev/null +++ b/Pods/Realm/include/RLMOptionalBase.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObjectBase, RLMProperty; + +@interface RLMOptionalBase : NSProxy + +- (instancetype)init; + +@property (nonatomic, weak) RLMObjectBase *object; + +@property (nonatomic, unsafe_unretained) RLMProperty *property; + +@property (nonatomic, strong, nullable) id underlyingValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMPlatform.h b/Pods/Realm/include/RLMPlatform.h new file mode 100644 index 0000000..e69de29 diff --git a/Pods/Realm/include/RLMPredicateUtil.hpp b/Pods/Realm/include/RLMPredicateUtil.hpp new file mode 100644 index 0000000..11b8e15 --- /dev/null +++ b/Pods/Realm/include/RLMPredicateUtil.hpp @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +using ExpressionVisitor = NSExpression *(*)(NSExpression *); +NSPredicate *transformPredicate(NSPredicate *, ExpressionVisitor); diff --git a/Pods/Realm/include/RLMPrefix.h b/Pods/Realm/include/RLMPrefix.h new file mode 100644 index 0000000..df08ce9 --- /dev/null +++ b/Pods/Realm/include/RLMPrefix.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifdef __OBJC__ +#import +#endif + +#ifdef __cplusplus +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#endif diff --git a/Pods/Realm/include/RLMProperty.h b/Pods/Realm/include/RLMProperty.h new file mode 100644 index 0000000..f19c592 --- /dev/null +++ b/Pods/Realm/include/RLMProperty.h @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/// :nodoc: +@protocol RLMInt +@end + +/// :nodoc: +@protocol RLMBool +@end + +/// :nodoc: +@protocol RLMDouble +@end + +/// :nodoc: +@protocol RLMFloat +@end + +/// :nodoc: +@interface NSNumber () +@end + +/** + `RLMProperty` instances represent properties managed by a Realm in the context of an object schema. Such properties may + be persisted to a Realm file or computed from other data from the Realm. + + When using Realm, `RLMProperty` instances allow performing migrations and introspecting the database's schema. + + These property instances map to columns in the core database. + */ +@interface RLMProperty : NSObject + +#pragma mark - Properties + +/** + The name of the property. + */ +@property (nonatomic, readonly) NSString *name; + +/** + The type of the property. + + @see `RLMPropertyType` + */ +@property (nonatomic, readonly) RLMPropertyType type; + +/** + Indicates whether this property is indexed. + + @see `RLMObject` + */ +@property (nonatomic, readonly) BOOL indexed; + +/** + For `RLMObject` and `RLMArray` properties, the name of the class of object stored in the property. + */ +@property (nonatomic, readonly, copy, nullable) NSString *objectClassName; + +/** + For linking objects properties, the property name of the property the linking objects property is linked to. + */ +@property (nonatomic, readonly, copy, nullable) NSString *linkOriginPropertyName; + +/** + Indicates whether this property is optional. + */ +@property (nonatomic, readonly) BOOL optional; + +#pragma mark - Methods + +/** + Returns whether a given property object is equal to the receiver. + */ +- (BOOL)isEqualToProperty:(RLMProperty *)property; + +@end + + +/** + An `RLMPropertyDescriptor` instance represents a specific property on a given class. + */ +@interface RLMPropertyDescriptor : NSObject + +/** + Creates and returns a property descriptor. + + @param objectClass The class of this property descriptor. + @param propertyName The name of this property descriptor. + */ ++ (instancetype)descriptorWithClass:(Class)objectClass propertyName:(NSString *)propertyName; + +/// The class of the property. +@property (nonatomic, readonly) Class objectClass; + +/// The name of the property. +@property (nonatomic, readonly) NSString *propertyName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMProperty_Private.h b/Pods/Realm/include/RLMProperty_Private.h new file mode 100644 index 0000000..d36f978 --- /dev/null +++ b/Pods/Realm/include/RLMProperty_Private.h @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +@class RLMObjectBase; + +NS_ASSUME_NONNULL_BEGIN + +BOOL RLMPropertyTypeIsNullable(RLMPropertyType propertyType); +BOOL RLMPropertyTypeIsComputed(RLMPropertyType propertyType); + +// private property interface +@interface RLMProperty () { +@public + RLMPropertyType _type; + Ivar _swiftIvar; +} + +- (instancetype)initWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(nullable RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property; + +- (instancetype)initSwiftPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + linkPropertyDescriptor:(nullable RLMPropertyDescriptor *)linkPropertyDescriptor + property:(objc_property_t)property + instance:(RLMObjectBase *)objectInstance; + +- (instancetype)initSwiftListPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(nullable NSString *)objectClassName; + +- (instancetype)initSwiftOptionalPropertyWithName:(NSString *)name + indexed:(BOOL)indexed + ivar:(Ivar)ivar + propertyType:(RLMPropertyType)propertyType; + +- (instancetype)initSwiftLinkingObjectsPropertyWithName:(NSString *)name + ivar:(Ivar)ivar + objectClassName:(nullable NSString *)objectClassName + linkOriginPropertyName:(nullable NSString *)linkOriginPropertyName; + +// private setters +@property (nonatomic, readwrite) NSString *name; +@property (nonatomic, readwrite, assign) RLMPropertyType type; +@property (nonatomic, readwrite) BOOL indexed; +@property (nonatomic, readwrite) BOOL optional; +@property (nonatomic, copy, nullable) NSString *objectClassName; + +// private properties +@property (nonatomic, assign) NSUInteger index; +@property (nonatomic, assign) char objcType; +@property (nonatomic, copy) NSString *objcRawType; +@property (nonatomic, assign) BOOL isPrimary; +@property (nonatomic, assign) Ivar swiftIvar; + +// getter and setter names +@property (nonatomic, copy) NSString *getterName; +@property (nonatomic, copy) NSString *setterName; +@property (nonatomic) SEL getterSel; +@property (nonatomic) SEL setterSel; + +- (RLMProperty *)copyWithNewName:(NSString *)name; + +@end + +@interface RLMProperty (Dynamic) +/** + This method is useful only in specialized circumstances, for example, in conjunction with + +[RLMObjectSchema initWithClassName:objectClass:properties:]. If you are simply building an + app on Realm, it is not recommened to use this method. + + Initialize an RLMProperty + + @warning This method is useful only in specialized circumstances. + + @param name The property name. + @param type The property type. + @param objectClassName The object type used for Object and Array types. + @param linkOriginPropertyName The property name of the origin of a link. Used for linking objects properties. + + @return An initialized instance of RLMProperty. + */ +- (instancetype)initWithName:(NSString *)name + type:(RLMPropertyType)type + objectClassName:(nullable NSString *)objectClassName + linkOriginPropertyName:(nullable NSString *)linkOriginPropertyName + indexed:(BOOL)indexed + optional:(BOOL)optional; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMProperty_Private.hpp b/Pods/Realm/include/RLMProperty_Private.hpp new file mode 100644 index 0000000..0e214d5 --- /dev/null +++ b/Pods/Realm/include/RLMProperty_Private.hpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "property.hpp" + +@interface RLMProperty () + ++ (instancetype)propertyForObjectStoreProperty:(const realm::Property&)property; + +- (realm::Property)objectStoreCopy; + +@end diff --git a/Pods/Realm/include/RLMQueryUtil.hpp b/Pods/Realm/include/RLMQueryUtil.hpp new file mode 100644 index 0000000..ce31c70 --- /dev/null +++ b/Pods/Realm/include/RLMQueryUtil.hpp @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import + +namespace realm { + class Group; + class Query; + class SortDescriptor; + class Table; +} + +@class RLMObjectSchema, RLMProperty, RLMSchema, RLMSortDescriptor; + +extern NSString * const RLMPropertiesComparisonTypeMismatchException; +extern NSString * const RLMUnsupportedTypesFoundInPropertyComparisonException; + +realm::Query RLMPredicateToQuery(NSPredicate *predicate, RLMObjectSchema *objectSchema, + RLMSchema *schema, realm::Group &group); + +// return property - throw for invalid column name +RLMProperty *RLMValidatedProperty(RLMObjectSchema *objectSchema, NSString *columnName); + +// validate the array of RLMSortDescriptors and convert it to a realm::SortDescriptor +realm::SortDescriptor RLMSortDescriptorFromDescriptors(realm::Table& table, NSArray *descriptors); diff --git a/Pods/Realm/include/RLMRealm.h b/Pods/Realm/include/RLMRealm.h new file mode 100644 index 0000000..d6fc933 --- /dev/null +++ b/Pods/Realm/include/RLMRealm.h @@ -0,0 +1,514 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import "RLMConstants.h" + +@class RLMRealmConfiguration, RLMObject, RLMSchema, RLMMigration, RLMNotificationToken; + +NS_ASSUME_NONNULL_BEGIN + +/** + An `RLMRealm` instance (also referred to as "a Realm") represents a Realm + database. + + Realms can either be stored on disk (see `+[RLMRealm realmWithURL:]`) or in + memory (see `RLMRealmConfiguration`). + + `RLMRealm` instances are cached internally, and constructing equivalent `RLMRealm` + objects (for example, by using the same path or identifier) multiple times on a single thread + within a single iteration of the run loop will normally return the same + `RLMRealm` object. + + If you specifically want to ensure an `RLMRealm` instance is + destroyed (for example, if you wish to open a Realm, check some property, and + then possibly delete the Realm file and re-open it), place the code which uses + the Realm within an `@autoreleasepool {}` and ensure you have no other + strong references to it. + + @warning `RLMRealm` instances are not thread safe and cannot be shared across + threads or dispatch queues. Trying to do so will cause an exception to be thrown. + You must call this method on each thread you want + to interact with the Realm on. For dispatch queues, this means that you must + call it in each block which is dispatched, as a queue is not guaranteed to run + all of its blocks on the same thread. + */ + +@interface RLMRealm : NSObject + +#pragma mark - Creating & Initializing a Realm + +/** + Obtains an instance of the default Realm. + + The default Realm is used by the `RLMObject` class methods + which do not take an `RLMRealm` parameter, but is otherwise not special. The + default Realm is persisted as *default.realm* under the *Documents* directory of + your Application on iOS, and in your application's *Application Support* + directory on OS X. + + The default Realm is created using the default `RLMRealmConfiguration`, which + can be changed via `+[RLMRealmConfiguration setDefaultConfiguration:]`. + + @return The default `RLMRealm` instance for the current thread. + */ ++ (instancetype)defaultRealm; + +/** + Obtains an `RLMRealm` instance with the given configuration. + + @param configuration A configuration object to use when creating the Realm. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return An `RLMRealm` instance. + */ ++ (nullable instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; + +/** + Obtains an `RLMRealm` instance persisted at a specified file URL. + + @param fileURL The local URL of the file the Realm should be saved at. + + @return An `RLMRealm` instance. + */ ++ (instancetype)realmWithURL:(NSURL *)fileURL; + +/** + The `RLMSchema` used by the Realm. + */ +@property (nonatomic, readonly) RLMSchema *schema; + +/** + Indicates if the Realm is currently engaged in a write transaction. + + @warning Do not simply check this property and then start a write transaction whenever an object needs to be + created, updated, or removed. Doing so might cause a large number of write transactions to be created, + degrading performance. Instead, always prefer performing multiple updates during a single transaction. + */ +@property (nonatomic, readonly) BOOL inWriteTransaction; + +/** + The `RLMRealmConfiguration` object that was used to create this `RLMRealm` instance. + */ +@property (nonatomic, readonly) RLMRealmConfiguration *configuration; + +/** + Indicates if this Realm contains any objects. + */ +@property (nonatomic, readonly) BOOL isEmpty; + +#pragma mark - Notifications + +/** + The type of a block to run whenever the data within the Realm is modified. + + @see `-[RLMRealm addNotificationBlock:]` + */ +typedef void (^RLMNotificationBlock)(RLMNotification notification, RLMRealm *realm); + +#pragma mark - Receiving Notification when a Realm Changes + +/** + Adds a notification handler for changes in this Realm, and returns a notification token. + + Notification handlers are called after each write transaction is committed, + either on the current thread or other threads. + + Handler blocks are called on the same thread that they were added on, and may only be added on threads which are + currently within a run loop. Unless you are specifically creating and running a run loop on a background thread, this + will normally only be the main thread. + + The block has the following definition: + + typedef void(^RLMNotificationBlock)(RLMNotification notification, RLMRealm *realm); + + It receives the following parameters: + + - `NSString` \***notification**: The name of the incoming notification. See + `RLMRealmNotification` for information on what + notifications are sent. + - `RLMRealm` \***realm**: The Realm for which this notification occurred. + + @param block A block which is called to process Realm notifications. + + @return A token object which must be retained as long as you wish to continue + receiving change notifications. + */ +- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block __attribute__((warn_unused_result)); + +#pragma mark - Transactions + + +#pragma mark - Writing to a Realm + +/** + Begins a write transaction on the Realm. + + Only one write transaction can be open at a time. Write transactions cannot be + nested, and trying to begin a write transaction on a Realm which is + already in a write transaction will throw an exception. Calls to + `beginWriteTransaction` from `RLMRealm` instances in other threads will block + until the current write transaction completes. + + Before beginning the write transaction, `beginWriteTransaction` updates the + `RLMRealm` instance to the latest Realm version, as if `refresh` had been called, and + generates notifications if applicable. This has no effect if the Realm + was already up to date. + + It is rarely a good idea to have write transactions span multiple cycles of + the run loop, but if you do wish to do so you will need to ensure that the + Realm participating in the write transaction is kept alive until the write transaction + is committed. + */ +- (void)beginWriteTransaction; + +/** + Commits all write operations in the current write transaction, and ends the + transaction. + + @warning This method may only be called during a write transaction. + */ +- (void)commitWriteTransaction NS_SWIFT_UNAVAILABLE(""); + +/** + Commits all write operations in the current write transaction, and ends the + transaction. + + @warning This method may only be called during a write transaction. + + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return Whether the transaction succeeded. + */ +- (BOOL)commitWriteTransaction:(NSError **)error; + +/** + Reverts all writes made during the current write transaction and ends the transaction. + + This rolls back all objects in the Realm to the state they were in at the + beginning of the write transaction, and then ends the transaction. + + This restores the data for deleted objects, but does not revive invalidated + object instances. Any `RLMObject`s which were added to the Realm will be + invalidated rather than becoming unmanaged. + Given the following code: + + ObjectType *oldObject = [[ObjectType objectsWhere:@"..."] firstObject]; + ObjectType *newObject = [[ObjectType alloc] init]; + + [realm beginWriteTransaction]; + [realm addObject:newObject]; + [realm deleteObject:oldObject]; + [realm cancelWriteTransaction]; + + Both `oldObject` and `newObject` will return `YES` for `isInvalidated`, + but re-running the query which provided `oldObject` will once again return + the valid object. + + @warning This method may only be called during a write transaction. + */ +- (void)cancelWriteTransaction; + +/** + Performs actions contained within the given block inside a write transaction. + + @see `[RLMRealm transactionWithBlock:error:]` + */ +- (void)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block NS_SWIFT_UNAVAILABLE(""); + +/** + Performs actions contained within the given block inside a write transaction. + + Write transactions cannot be nested, and trying to execute a write transaction + on a Realm which is already participating in a write transaction will throw an + exception. Calls to `transactionWithBlock:` from `RLMRealm` instances in other + threads will block until the current write transaction completes. + + Before beginning the write transaction, `transactionWithBlock:` updates the + `RLMRealm` instance to the latest Realm version, as if `refresh` had been called, and + generates notifications if applicable. This has no effect if the Realm + was already up to date. + + @param block The block containing actions to perform. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return Whether the transaction succeeded. + */ +- (BOOL)transactionWithBlock:(__attribute__((noescape)) void(^)(void))block error:(NSError **)error; + +/** + Updates the Realm and outstanding objects managed by the Realm to point to the most recent data. + + @return Whether there were any updates for the Realm. Note that `YES` may be returned even if no data actually + changed. + */ +- (BOOL)refresh; + +/** + Set this property to `YES` to automatically update this Realm when changes happen in other threads. + + If set to `YES` (the default), changes made on other threads will be reflected + in this Realm on the next cycle of the run loop after the changes are + committed. If set to `NO`, you must manually call `-refresh` on the Realm to + update it to get the latest data. + + Note that by default, background threads do not have an active run loop and you + will need to manually call `-refresh` in order to update to the latest version, + even if `autorefresh` is set to `YES`. + + Even with this property enabled, you can still call `-refresh` at any time to update the + Realm before the automatic refresh would occur. + + Notifications are sent when a write transaction is committed whether or not + automatic refreshing is enabled. + + Disabling `autorefresh` on a Realm without any strong references to it will not + have any effect, and `autorefresh` will revert back to `YES` the next time the Realm is created. + This is normally irrelevant as it means that there is + nothing to refresh (as managed `RLMObject`s, `RLMArray`s, and `RLMResults` have strong + references to the Realm that manages them), but it means that setting + `RLMRealm.defaultRealm.autorefresh = NO` in + `application:didFinishLaunchingWithOptions:` and only later storing Realm + objects will not work. + + Defaults to `YES`. + */ +@property (nonatomic) BOOL autorefresh; + +/** + Writes a compacted and optionally encrypted copy of the Realm to the given local URL. + + The destination file cannot already exist. + + Note that if this method is called from within a write transaction, the *current* data is written, not the data from + the point when the previous write transaction was committed. + + @param fileURL Local URL to save the Realm to. + @param key Optional 64-byte encryption key to encrypt the new file with. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return `YES` if the Realm was successfully written to disk, `NO` if an error occurred. +*/ +- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error; + +/** + Invalidates all `RLMObject`s, `RLMResults`, `RLMLinkingObjects`, and `RLMArray`s managed by the Realm. + + A Realm holds a read lock on the version of the data accessed by it, so + that changes made to the Realm on different threads do not modify or delete the + data seen by this Realm. Calling this method releases the read lock, + allowing the space used on disk to be reused by later write transactions rather + than growing the file. This method should be called before performing long + blocking operations on a background thread on which you previously read data + from the Realm which you no longer need. + + All `RLMObject`, `RLMResults` and `RLMArray` instances obtained from this + `RLMRealm` instance on the current thread are invalidated. `RLMObject`s and `RLMArray`s + cannot be used. `RLMResults` will become empty. The Realm itself remains valid, + and a new read transaction is implicitly begun the next time data is read from the Realm. + + Calling this method multiple times in a row without reading any data from the + Realm, or before ever reading any data from the Realm, is a no-op. This method + may not be called on a read-only Realm. + */ +- (void)invalidate; + +#pragma mark - Accessing Objects + + +#pragma mark - Adding and Removing Objects from a Realm + +/** + Adds an object to the Realm. + + Once added, this object is considered to be managed by the Realm. It can be retrieved + using the `objectsWhere:` selectors on `RLMRealm` and on subclasses of `RLMObject`. + + When added, all child relationships referenced by this object will also be added to + the Realm if they are not already in it. + + If the object or any related objects are already being managed by a different Realm + an exception will be thrown. Use `-[RLMObject createInRealm:withObject:]` to insert a copy of a managed object + into a different Realm. + + The object to be added must be valid and cannot have been previously deleted + from a Realm (i.e. `isInvalidated` must be `NO`). + + @warning This method may only be called during a write transaction. + + @param object The object to be added to this Realm. + */ +- (void)addObject:(RLMObject *)object; + +/** + Adds all the objects in a collection to the Realm. + + This is the equivalent of calling `addObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An enumerable object such as `NSArray` or `RLMResults` which contains objects to be added to + the Realm. + + @see `addObject:` + */ +- (void)addObjects:(id)array; + +/** + Adds or updates an existing object into the Realm. + + The object provided must have a designated primary key. If no objects exist in the Realm + with the same primary key value, the object is inserted. Otherwise, the existing object is + updated with any changed values. + + As with `addObject:`, the object cannot already be managed by a different + Realm. Use `-[RLMObject createOrUpdateInRealm:withValue:]` to copy values to + a different Realm. + + @warning This method may only be called during a write transaction. + + @param object The object to be added or updated. + */ +- (void)addOrUpdateObject:(RLMObject *)object; + +/** + Adds or updates all the objects in a collection into the Realm. + + This is the equivalent of calling `addOrUpdateObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An `NSArray`, `RLMArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be added to the Realm. + + @see `addOrUpdateObject:` + */ +- (void)addOrUpdateObjectsFromArray:(id)array; + +/** + Deletes an object from the Realm. Once the object is deleted it is considered invalidated. + + @warning This method may only be called during a write transaction. + + @param object The object to be deleted. + */ +- (void)deleteObject:(RLMObject *)object; + +/** + Deletes one or more objects from the Realm. + + This is the equivalent of calling `deleteObject:` for every object in a collection. + + @warning This method may only be called during a write transaction. + + @param array An `RLMArray`, `NSArray`, or `RLMResults` of `RLMObject`s (or subclasses) to be deleted. + + @see `deleteObject:` + */ +- (void)deleteObjects:(id)array; + +/** + Deletes all objects from the Realm. + + @warning This method may only be called during a write transaction. + + @see `deleteObject:` + */ +- (void)deleteAllObjects; + + +#pragma mark - Migrations + +/** + The type of a migration block used to migrate a Realm. + + @param migration A `RLMMigration` object used to perform the migration. The + migration object allows you to enumerate and alter any + existing objects which require migration. + + @param oldSchemaVersion The schema version of the Realm being migrated. + */ +typedef void (^RLMMigrationBlock)(RLMMigration *migration, uint64_t oldSchemaVersion); + +/** + Returns the schema version for a Realm at a given local URL. + + @param fileURL Local URL to a Realm file. + @param key 64-byte key used to encrypt the file, or `nil` if it is unencrypted. + @param error If an error occurs, upon return contains an `NSError` object + that describes the problem. If you are not interested in + possible errors, pass in `NULL`. + + @return The version of the Realm at `fileURL`, or `RLMNotVersioned` if the version cannot be read. + */ ++ (uint64_t)schemaVersionAtURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error +NS_REFINED_FOR_SWIFT; + +/** + Performs the given Realm configuration's migration block on a Realm at the given path. + + This method is called automatically when opening a Realm for the first time and does + not need to be called explicitly. You can choose to call this method to control + exactly when and how migrations are performed. + + @param configuration The Realm configuration used to open and migrate the Realm. + @return The error that occurred while applying the migration, if any. + + @see RLMMigration + */ ++ (nullable NSError *)migrateRealm:(RLMRealmConfiguration *)configuration +__deprecated_msg("Use `performMigrationForConfiguration:error:`") NS_REFINED_FOR_SWIFT; + +/** + Performs the given Realm configuration's migration block on a Realm at the given path. + + This method is called automatically when opening a Realm for the first time and does + not need to be called explicitly. You can choose to call this method to control + exactly when and how migrations are performed. + + @param configuration The Realm configuration used to open and migrate the Realm. + @return The error that occurred while applying the migration, if any. + + @see RLMMigration + */ ++ (BOOL)performMigrationForConfiguration:(RLMRealmConfiguration *)configuration error:(NSError **)error; + +@end + +/** + A token which is returned from methods which subscribe to changes to a Realm. + + Change subscriptions in Realm return an `RLMNotificationToken` instance, + which can be used to unsubscribe from the changes. You must store a strong + reference to the token for as long as you want to continue to receive notifications. + When you wish to stop, call the `-stop` method. Notifications are also stopped if + the token is deallocated. + */ +@interface RLMNotificationToken : NSObject +/// Stops notifications for the change subscription that returned this token. +- (void)stop; +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealmConfiguration+Sync.h b/Pods/Realm/include/RLMRealmConfiguration+Sync.h new file mode 100644 index 0000000..2a7aca5 --- /dev/null +++ b/Pods/Realm/include/RLMRealmConfiguration+Sync.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil.h" + +@class RLMSyncConfiguration; + +/// :nodoc: +@interface RLMRealmConfiguration (Sync) + +NS_ASSUME_NONNULL_BEGIN + +/** + A configuration object representing configuration state for Realms intended to sync with a Realm Object Server. + + This property is mutually exclusive with both `inMemoryIdentifier` and `fileURL`; setting one will nil out the other + two. + + @see `RLMSyncConfiguration` + */ +@property (nullable, nonatomic) RLMSyncConfiguration *syncConfiguration; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMRealmConfiguration.h b/Pods/Realm/include/RLMRealmConfiguration.h new file mode 100644 index 0000000..21e163b --- /dev/null +++ b/Pods/Realm/include/RLMRealmConfiguration.h @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + An `RLMRealmConfiguration` instance describes the different options used to + create an instance of a Realm. + + `RLMRealmConfiguration` instances are just plain `NSObject`s. Unlike `RLMRealm`s + and `RLMObject`s, they can be freely shared between threads as long as you do not + mutate them. + + Creating configuration objects for class subsets (by setting the + `objectClasses` property) can be expensive. Because of this, you will normally want to + cache and reuse a single configuration object for each distinct configuration rather than + creating a new object each time you open a Realm. + */ +@interface RLMRealmConfiguration : NSObject + +#pragma mark - Default Configuration + +/** + Returns the default configuration used to create Realms when no other + configuration is explicitly specified (i.e. `+[RLMRealm defaultRealm]`). + + @return The default Realm configuration. + */ ++ (instancetype)defaultConfiguration; + +/** + Sets the default configuration to the given `RLMRealmConfiguration`. + + @param configuration The new default Realm configuration. + */ ++ (void)setDefaultConfiguration:(RLMRealmConfiguration *)configuration; + +#pragma mark - Properties + +/// The local URL of the Realm file. Mutually exclusive with `inMemoryIdentifier`. +@property (nonatomic, copy, nullable) NSURL *fileURL; + +/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL`. +@property (nonatomic, copy, nullable) NSString *inMemoryIdentifier; + +/// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled. +@property (nonatomic, copy, nullable) NSData *encryptionKey; + +/// Whether to open the Realm in read-only mode. +/// +/// This is required to be able to open Realm files which are not writeable or +/// are in a directory which is not writeable. This should only be used on files +/// which will not be modified by anyone while they are open, and not just to +/// get a read-only view of a file which may be written to by another thread or +/// process. Opening in read-only mode requires disabling Realm's reader/writer +/// coordination, so committing a write transaction from another process will +/// result in crashes. +@property (nonatomic) BOOL readOnly; + +/// The current schema version. +@property (nonatomic) uint64_t schemaVersion; + +/// The block which migrates the Realm to the current version. +@property (nonatomic, copy, nullable) RLMMigrationBlock migrationBlock; + +/** + Whether to recreate the Realm file with the provided schema if a migration is required. + This is the case when the stored schema differs from the provided schema or + the stored schema version differs from the version on this configuration. + Setting this property to `YES` deletes the file if a migration would otherwise be required or executed. + + @note Setting this property to `YES` doesn't disable file format migrations. + */ +@property (nonatomic) BOOL deleteRealmIfMigrationNeeded; + +/// The classes managed by the Realm. +@property (nonatomic, copy, nullable) NSArray *objectClasses; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealmConfiguration_Private.h b/Pods/Realm/include/RLMRealmConfiguration_Private.h new file mode 100644 index 0000000..7581af1 --- /dev/null +++ b/Pods/Realm/include/RLMRealmConfiguration_Private.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMSchema; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMRealmConfiguration () + +@property (nonatomic, readwrite) bool cache; +@property (nonatomic, readwrite) bool dynamic; +@property (nonatomic, readwrite) bool disableFormatUpgrade; +@property (nonatomic, copy, nullable) RLMSchema *customSchema; + +// Get the default confiugration without copying it ++ (RLMRealmConfiguration *)rawDefaultConfiguration; + ++ (void)resetRealmConfigurationState; +@end + +// Get a path in the platform-appropriate documents directory with the given filename +FOUNDATION_EXTERN NSString *RLMRealmPathForFile(NSString *fileName); +FOUNDATION_EXTERN NSString *RLMRealmPathForFileAndBundleIdentifier(NSString *fileName, NSString *mainBundleIdentifier); + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealmConfiguration_Private.hpp b/Pods/Realm/include/RLMRealmConfiguration_Private.hpp new file mode 100644 index 0000000..a89fb0f --- /dev/null +++ b/Pods/Realm/include/RLMRealmConfiguration_Private.hpp @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealmConfiguration_Private.h" +#import "shared_realm.hpp" + +@interface RLMRealmConfiguration () +- (realm::Realm::Config&)config; + +@property (nonatomic) realm::SchemaMode schemaMode; +@end diff --git a/Pods/Realm/include/RLMRealmUtil.hpp b/Pods/Realm/include/RLMRealmUtil.hpp new file mode 100644 index 0000000..36dbc84 --- /dev/null +++ b/Pods/Realm/include/RLMRealmUtil.hpp @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import +#import + +@class RLMRealm; + +namespace realm { + class BindingContext; +} + +// Add a Realm to the weak cache +void RLMCacheRealm(std::string const& path, RLMRealm *realm); +// Get a Realm for the given path which can be used on the current thread +RLMRealm *RLMGetThreadLocalCachedRealmForPath(std::string const& path); +// Get a Realm for the given path +RLMRealm *RLMGetAnyCachedRealmForPath(std::string const& path); +// Clear the weak cache of Realms +void RLMClearRealmCache(); + +std::unique_ptr RLMCreateBindingContext(RLMRealm *realm); diff --git a/Pods/Realm/include/RLMRealm_Dynamic.h b/Pods/Realm/include/RLMRealm_Dynamic.h new file mode 100644 index 0000000..3a26f49 --- /dev/null +++ b/Pods/Realm/include/RLMRealm_Dynamic.h @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import +#import + +@class RLMResults; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMRealm (Dynamic) + +#pragma mark - Getting Objects from a Realm + +/** + Returns all objects of a given type from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The name of the `RLMObject` subclass to retrieve on (e.g. `MyClass.className`). + + @return An `RLMResults` containing all objects in the Realm of the given type. + + @see `+[RLMObject allObjects]` + */ +- (RLMResults *)allObjects:(NSString *)className; + +/** + Returns all objects matching the given predicate from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The type of objects you are looking for (name of the class). + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` containing results matching the given predicate. + + @see `+[RLMObject objectsWhere:]` + */ +- (RLMResults *)objects:(NSString *)className where:(NSString *)predicateFormat, ...; + +/** + Returns all objects matching the given predicate from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get objects of a single class is to use the class + methods on `RLMObject`. + + @param className The type of objects you are looking for (name of the class). + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` containing results matching the given predicate. + + @see `+[RLMObject objectsWhere:]` + */ +- (RLMResults *)objects:(NSString *)className withPredicate:(NSPredicate *)predicate; + +/** + Returns the object of the given type with the given primary key from the Realm. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. The preferred way to get an object of a single class is to use the class + methods on `RLMObject`. + + @param className The class name for the object you are looking for. + @param primaryKey The primary key value for the object you are looking for. + + @return An object, or `nil` if an object with the given primary key does not exist. + + @see `+[RLMObject objectForPrimaryKey:]` + */ +- (nullable RLMObject *)objectWithClassName:(NSString *)className forPrimaryKey:(id)primaryKey; + +/** + Creates an `RLMObject` instance of type `className` in the Realm, and populates it using a given object. + + The `value` argument is used to populate the object. It can be a key-value coding compliant object, an array or + dictionary returned from the methods in `NSJSONSerialization`, or an array containing one element for each managed + property. An exception will be thrown if any required properties are not present and those properties were not defined + with default values. + + When passing in an array as the `value` argument, all properties must be present, valid and in the same order as the + properties defined in the model. + + @warning This method is useful only in specialized circumstances, for example, when building components + that integrate with Realm. If you are simply building an app on Realm, it is recommended to + use `[RLMObject createInDefaultRealmWithValue:]`. + + @param value The value used to populate the object. + + @return An `RLMObject` instance of type `className`. + */ +-(RLMObject *)createObject:(NSString *)className withValue:(id)value; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealm_Private.h b/Pods/Realm/include/RLMRealm_Private.h new file mode 100644 index 0000000..dbfe320 --- /dev/null +++ b/Pods/Realm/include/RLMRealm_Private.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMFastEnumerator; + +NS_ASSUME_NONNULL_BEGIN + +// Disable syncing files to disk. Cannot be re-enabled. Use only for tests. +FOUNDATION_EXTERN void RLMDisableSyncToDisk(); + +FOUNDATION_EXTERN NSData * _Nullable RLMRealmValidatedEncryptionKey(NSData *key); + +// Translate an in-flight exception resulting from opening a SharedGroup to +// an NSError or NSException (if error is nil) +void RLMRealmTranslateException(NSError **error); + +// RLMRealm private members +@interface RLMRealm () + +@property (nonatomic, readonly) BOOL dynamic; +@property (nonatomic, readwrite) RLMSchema *schema; + ++ (void)resetRealmState; + +- (void)registerEnumerator:(RLMFastEnumerator *)enumerator; +- (void)unregisterEnumerator:(RLMFastEnumerator *)enumerator; +- (void)detachAllEnumerators; + +- (void)sendNotifications:(RLMNotification)notification; +- (void)verifyThread; +- (void)verifyNotificationsAreSupported; + ++ (NSString *)writeableTemporaryPathForFile:(NSString *)fileName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMRealm_Private.hpp b/Pods/Realm/include/RLMRealm_Private.hpp new file mode 100644 index 0000000..a1bb294 --- /dev/null +++ b/Pods/Realm/include/RLMRealm_Private.hpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMRealm_Private.h" + +#import "RLMClassInfo.hpp" + +namespace realm { + class Group; + class Realm; +} + +@interface RLMRealm () { + @public + std::shared_ptr _realm; + RLMSchemaInfo _info; +} + +// FIXME - group should not be exposed +@property (nonatomic, readonly) realm::Group &group; +@end diff --git a/Pods/Realm/include/RLMResults.h b/Pods/Realm/include/RLMResults.h new file mode 100644 index 0000000..d1226b0 --- /dev/null +++ b/Pods/Realm/include/RLMResults.h @@ -0,0 +1,332 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObject, RLMRealm, RLMNotificationToken; + +/** + `RLMResults` is an auto-updating container type in Realm returned from object + queries. It represents the results of the query in the form of a collection of objects. + + `RLMResults` can be queried using the same predicates as `RLMObject` and `RLMArray`, + and you can chain queries to further filter results. + + `RLMResults` always reflect the current state of the Realm on the current thread, + including during write transactions on the current thread. The one exception to + this is when using `for...in` fast enumeration, which will always enumerate + over the objects which matched the query when the enumeration is begun, even if + some of them are deleted or modified to be excluded by the filter during the + enumeration. + + `RLMResults` are lazily evaluated the first time they are accessed; they only + run queries when the result of the query is requested. This means that + chaining several temporary `RLMResults` to sort and filter your data does not + perform any extra work processing the intermediate state. + + Once the results have been evaluated or a notification block has been added, + the results are eagerly kept up-to-date, with the work done to keep them + up-to-date done on a background thread whenever possible. + + `RLMResults` cannot be directly instantiated. + */ +@interface RLMResults : NSObject + +#pragma mark - Properties + +/** + The number of objects in the results collection. + */ +@property (nonatomic, readonly, assign) NSUInteger count; + +/** + The class name (i.e. type) of the `RLMObject`s contained in the results collection. + */ +@property (nonatomic, readonly, copy) NSString *objectClassName; + +/** + The Realm which manages this results collection. + */ +@property (nonatomic, readonly) RLMRealm *realm; + +/** + Indicates if the results collection is no longer valid. + + The results collection becomes invalid if `invalidate` is called on the containing `realm`. + An invalidated results collection can be accessed, but will always be empty. + */ +@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated; + +#pragma mark - Accessing Objects from an RLMResults + +/** + Returns the object at the index specified. + + @param index The index to look up. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (RLMObjectType)objectAtIndex:(NSUInteger)index; + +/** + Returns the first object in the results collection. + + Returns `nil` if called on an empty results collection. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (nullable RLMObjectType)firstObject; + +/** + Returns the last object in the results collection. + + Returns `nil` if called on an empty results collection. + + @return An `RLMObject` of the type contained in the results collection. + */ +- (nullable RLMObjectType)lastObject; + +#pragma mark - Querying Results + +/** + Returns the index of an object in the results collection. + + Returns `NSNotFound` if the object is not found in the results collection. + + @param object An object (of the same type as returned from the `objectClassName` selector). + */ +- (NSUInteger)indexOfObject:(RLMObjectType)object; + +/** + Returns the index of the first object in the results collection matching the predicate. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return The index of the object, or `NSNotFound` if the object is not found in the results collection. + */ +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns the index of the first object in the results collection matching the predicate. + + @param predicate The predicate with which to filter the objects. + + @return The index of the object, or `NSNotFound` if the object is not found in the results collection. + */ +- (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate; + +/** + Returns all the objects matching the given predicate in the results collection. + + @param predicateFormat A predicate format string, optionally followed by a variable number of arguments. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWhere:(NSString *)predicateFormat, ...; + +/// :nodoc: +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +/** + Returns all the objects matching the given predicate in the results collection. + + @param predicate The predicate with which to filter the objects. + + @return An `RLMResults` of objects that match the given predicate. + */ +- (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate; + +/** + Returns a sorted `RLMResults` from an existing results collection. + + @param property The property name to sort by. + @param ascending The direction to sort in. + + @return An `RLMResults` sorted by the specified property. + */ +- (RLMResults *)sortedResultsUsingProperty:(NSString *)property ascending:(BOOL)ascending; + +/** + Returns a sorted `RLMResults` from an existing results collection. + + @param properties An array of `RLMSortDescriptor`s to sort by. + + @return An `RLMResults` sorted by the specified properties. + */ +- (RLMResults *)sortedResultsUsingDescriptors:(NSArray *)properties; + +#pragma mark - Notifications + +/** + Registers a block to be called each time the results collection changes. + + The block will be asynchronously called with the initial results collection, + and then called again after each write transaction which changes either any + of the objects in the results, or which objects are in the results. + + The `change` parameter will be `nil` the first time the block is called. + For each call after that, it will contain information about + which rows in the results collection were added, removed or modified. If a + write transaction did not modify any objects in the results collection, + the block is not called at all. See the `RLMCollectionChange` documentation for + information on how the changes are reported and an example of updating a + `UITableView`. + + If an error occurs the block will be called with `nil` for the results + parameter and a non-`nil` error. Currently the only errors that can occur are + when opening the Realm on the background worker thread. + + At the time when the block is called, the `RLMResults` object will be fully + evaluated and up-to-date, and as long as you do not perform a write transaction + on the same thread or explicitly call `-[RLMRealm refresh]`, accessing it will + never perform blocking work. + + Notifications are delivered via the standard run loop, and so can't be + delivered while the run loop is blocked by other activity. When + notifications can't be delivered instantly, multiple notifications may be + coalesced into a single notification. This can include the notification + with the initial results. For example, the following code performs a write + transaction immediately after adding the notification block, so there is no + opportunity for the initial notification to be delivered first. As a + result, the initial notification will reflect the state of the Realm after + the write transaction. + + RLMResults *results = [Dog allObjects]; + NSLog(@"dogs.count: %zu", dogs.count); // => 0 + self.token = [results addNotificationBlock:^(RLMResults *dogs, + RLMCollectionChange *changes, + NSError *error) { + // Only fired once for the example + NSLog(@"dogs.count: %zu", dogs.count); // => 1 + }]; + [realm transactionWithBlock:^{ + Dog *dog = [[Dog alloc] init]; + dog.name = @"Rex"; + [realm addObject:dog]; + }]; + // end of run loop execution context + + You must retain the returned token for as long as you want updates to continue + to be sent to the block. To stop receiving updates, call `-stop` on the token. + + @warning This method cannot be called during a write transaction, or when the + containing Realm is read-only. + + @param block The block to be called whenever a change occurs. + @return A token which must be held for as long as you want updates to be delivered. + */ +- (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMResults *__nullable results, + RLMCollectionChange *__nullable change, + NSError *__nullable error))block __attribute__((warn_unused_result)); + +#pragma mark - Aggregating Property Values + +/** + Returns the minimum (lowest) value of the given property among all the objects + represented by the results collection. + + NSNumber *min = [results minOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose minimum value is desired. Only properties of types `int`, `float`, `double`, and + `NSDate` are supported. + + @return The minimum value of the property. + */ +- (nullable id)minOfProperty:(NSString *)property; + +/** + Returns the maximum (highest) value of the given property among all the objects represented by the results collection. + + NSNumber *max = [results maxOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose maximum value is desired. Only properties of types `int`, `float`, `double`, and + `NSDate` are supported. + + @return The maximum value of the property. + */ +- (nullable id)maxOfProperty:(NSString *)property; + +/** + Returns the sum of the values of a given property over all the objects represented by the results collection. + + NSNumber *sum = [results sumOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose values should be summed. Only properties of types `int`, `float`, and `double` are + supported. + + @return The sum of the given property. + */ +- (NSNumber *)sumOfProperty:(NSString *)property; + +/** + Returns the average value of a given property over the objects represented by the results collection. + + NSNumber *average = [results averageOfProperty:@"age"]; + + @warning You cannot use this method on `RLMObject`, `RLMArray`, and `NSData` properties. + + @param property The property whose average value should be calculated. Only properties of types `int`, `float`, and + `double` are supported. + + @return The average value of the given property. This will be of type `double` for both `float` and `double` + properties. + */ +- (nullable NSNumber *)averageOfProperty:(NSString *)property; + +/// :nodoc: +- (RLMObjectType)objectAtIndexedSubscript:(NSUInteger)index; + +#pragma mark - Unavailable Methods + +/** + `-[RLMResults init]` is not available because `RLMResults` cannot be created directly. + `RLMResults` can be obtained by querying a Realm. + */ +- (instancetype)init __attribute__((unavailable("RLMResults cannot be created directly"))); + +/** + `+[RLMResults new]` is not available because `RLMResults` cannot be created directly. + `RLMResults` can be obtained by querying a Realm. + */ ++ (instancetype)new __attribute__((unavailable("RLMResults cannot be created directly"))); + +@end + +/** + `RLMLinkingObjects` is an auto-updating container type. It represents a collection of objects that link to its + parent object. + + For more information, please see the "Inverse Relationships" section in the + [documentation](https://realm.io/docs/objc/latest/#relationships). + */ +@interface RLMLinkingObjects : RLMResults +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMResults_Private.h b/Pods/Realm/include/RLMResults_Private.h new file mode 100644 index 0000000..f74b4fd --- /dev/null +++ b/Pods/Realm/include/RLMResults_Private.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMObjectSchema; + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMResults () +@property (nonatomic, readonly, getter=isAttached) BOOL attached; + ++ (instancetype)emptyDetachedResults; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSchema.h b/Pods/Realm/include/RLMSchema.h new file mode 100644 index 0000000..2b11792 --- /dev/null +++ b/Pods/Realm/include/RLMSchema.h @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMObjectSchema; + +/** + `RLMSchema` instances represent collections of model object schemas managed by a Realm. + + When using Realm, `RLMSchema` instances allow performing migrations and + introspecting the database's schema. + + Schemas map to collections of tables in the core database. + */ +@interface RLMSchema : NSObject + +#pragma mark - Properties + +/** + An `NSArray` containing `RLMObjectSchema`s for all object types in the Realm. + + This property is intended to be used during migrations for dynamic introspection. + + @see `RLMObjectSchema` + */ +@property (nonatomic, readonly, copy) NSArray *objectSchema; + +#pragma mark - Methods + +/** + Returns an `RLMObjectSchema` for the given class name in the schema. + + @param className The object class name. + @return An `RLMObjectSchema` for the given class in the schema. + + @see `RLMObjectSchema` + */ +- (nullable RLMObjectSchema *)schemaForClassName:(NSString *)className; + +/** + Looks up and returns an `RLMObjectSchema` for the given class name in the Realm. + + If there is no object of type `className` in the schema, an exception will be thrown. + + @param className The object class name. + @return An `RLMObjectSchema` for the given class in this Realm. + + @see `RLMObjectSchema` + */ +- (RLMObjectSchema *)objectForKeyedSubscript:(NSString *)className; + +/** + Returns whether two `RLMSchema` instances are equivalent. + */ +- (BOOL)isEqualToSchema:(RLMSchema *)schema; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSchema_Private.h b/Pods/Realm/include/RLMSchema_Private.h new file mode 100644 index 0000000..f5823d1 --- /dev/null +++ b/Pods/Realm/include/RLMSchema_Private.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class RLMRealm; + +// +// RLMSchema private interface +// +@interface RLMSchema () + +/** + Returns an `RLMSchema` containing only the given `RLMObject` subclasses. + + @param classes The classes to be included in the schema. + + @return An `RLMSchema` containing only the given classes. + */ ++ (instancetype)schemaWithObjectClasses:(NSArray *)classes; + +@property (nonatomic, readwrite, copy) NSArray *objectSchema; + +// schema based on runtime objects ++ (instancetype)sharedSchema; + +// schema based upon all currently registered object classes ++ (instancetype)partialSharedSchema; + +// class for string ++ (nullable Class)classForString:(NSString *)className; + ++ (nullable RLMObjectSchema *)sharedSchemaForClass:(Class)cls; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSchema_Private.hpp b/Pods/Realm/include/RLMSchema_Private.hpp new file mode 100644 index 0000000..197ddee --- /dev/null +++ b/Pods/Realm/include/RLMSchema_Private.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSchema_Private.h" + +#import + +namespace realm { + class Schema; + class ObjectSchema; +} + +@interface RLMSchema () ++ (instancetype)dynamicSchemaFromObjectStoreSchema:(realm::Schema const&)objectStoreSchema; +- (realm::Schema)objectStoreCopy; +@end diff --git a/Pods/Realm/include/RLMSwiftBridgingHeader.h b/Pods/Realm/include/RLMSwiftBridgingHeader.h new file mode 100644 index 0000000..4758043 --- /dev/null +++ b/Pods/Realm/include/RLMSwiftBridgingHeader.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +@interface RLMRealm (Swift) ++ (void)resetRealmState; +@end + +@interface RLMArray (Swift) + +- (instancetype)initWithObjectClassName:(NSString *)objectClassName; + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +@end + +@interface RLMResults (Swift) + +- (NSUInteger)indexOfObjectWhere:(NSString *)predicateFormat args:(va_list)args; +- (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; + +@end + +@interface RLMObjectBase (Swift) + +- (instancetype)initWithRealm:(RLMRealm *)realm schema:(RLMObjectSchema *)schema defaultValues:(BOOL)useDefaults; + ++ (RLMResults *)objectsWhere:(NSString *)predicateFormat args:(va_list)args; ++ (RLMResults *)objectsInRealm:(RLMRealm *)realm where:(NSString *)predicateFormat args:(va_list)args; + +@end diff --git a/Pods/Realm/include/RLMSwiftSupport.h b/Pods/Realm/include/RLMSwiftSupport.h new file mode 100644 index 0000000..6e45b65 --- /dev/null +++ b/Pods/Realm/include/RLMSwiftSupport.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSwiftSupport : NSObject + ++ (BOOL)isSwiftClassName:(NSString *)className; ++ (NSString *)demangleClassName:(NSString *)className; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncConfiguration.h b/Pods/Realm/include/RLMSyncConfiguration.h new file mode 100644 index 0000000..6181d91 --- /dev/null +++ b/Pods/Realm/include/RLMSyncConfiguration.h @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMSyncUser; + +NS_ASSUME_NONNULL_BEGIN + +/** + A configuration object representing configuration state for a Realm which is intended to sync with a Realm Object + Server. + */ +@interface RLMSyncConfiguration : NSObject + +/// The user to which the remote Realm belongs. +@property (nonatomic, readonly) RLMSyncUser *user; + +/** + The URL of the remote Realm upon the Realm Object Server. + + @warning The URL cannot end with `.realm`, `.realm.lock` or `.realm.management`. + */ +@property (nonatomic, readonly) NSURL *realmURL; + +/** + Create a sync configuration instance. + + @param user A `RLMSyncUser` that owns the Realm at the given URL. + @param url The unresolved absolute URL to the Realm on the Realm Object Server, e.g. + `realm://example.org/~/path/to/realm`. "Unresolved" means the path should + contain the wildcard marker `~`, which will automatically be filled in with + the user identity by the Realm Object Server. + */ +- (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("This type cannot be created directly"))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncConfiguration_Private.h b/Pods/Realm/include/RLMSyncConfiguration_Private.h new file mode 100644 index 0000000..b55fa11 --- /dev/null +++ b/Pods/Realm/include/RLMSyncConfiguration_Private.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, RLMSyncStopPolicy) { + RLMSyncStopPolicyImmediately, + RLMSyncStopPolicyLiveIndefinitely, + RLMSyncStopPolicyAfterChangesUploaded, +}; + +@interface RLMSyncConfiguration () + +@property (nonatomic) RLMSyncStopPolicy stopPolicy; + +// Internal-only APIs +@property (nullable, nonatomic) NSURL *customFileURL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncConfiguration_Private.hpp b/Pods/Realm/include/RLMSyncConfiguration_Private.hpp new file mode 100644 index 0000000..1a80e7f --- /dev/null +++ b/Pods/Realm/include/RLMSyncConfiguration_Private.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncConfiguration_Private.h" + +namespace realm { +struct SyncConfig; +} + +@interface RLMSyncConfiguration () + +- (instancetype)initWithRawConfig:(realm::SyncConfig)config; + +- (realm::SyncConfig)rawConfiguration; + +@end diff --git a/Pods/Realm/include/RLMSyncCredential.h b/Pods/Realm/include/RLMSyncCredential.h new file mode 100644 index 0000000..2537544 --- /dev/null +++ b/Pods/Realm/include/RLMSyncCredential.h @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil.h" + +NS_ASSUME_NONNULL_BEGIN + +/// A token representing an identity provider's credential. +typedef NSString* RLMCredentialToken; + +/// An options type representing different account actions which can be associated with certain types of credentials. +typedef NS_OPTIONS(NSUInteger, RLMAuthenticationActions) { + /// Create a new Realm Object Server account. + RLMAuthenticationActionsCreateAccount = 1 << 0, + /// Use an existing Realm Object Server account. + RLMAuthenticationActionsUseExistingAccount = 1 << 1, +}; + +/// A type representing the unique identifier of a Realm Object Server identity provider. +typedef NSString *RLMIdentityProvider RLM_EXTENSIBLE_STRING_ENUM; + +/// The debug identity provider, which accepts any token string and creates a user associated with that token if one +/// does not yet exist. Not enabled for Realm Object Server configured for production. +extern RLMIdentityProvider const RLMIdentityProviderDebug; + +/// The username/password identity provider. User accounts are handled by the Realm Object Server directly without the +/// involvement of a third-party identity provider. +extern RLMIdentityProvider const RLMIdentityProviderUsernamePassword; + +/// A Facebook account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderFacebook; + +/// A Google account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderGoogle; + +/// An iCloud account as an identity provider. +extern RLMIdentityProvider const RLMIdentityProviderICloud; + +/** + An opaque credential representing a specific Realm Object Server user. + */ +@interface RLMSyncCredential : NSObject + +/// An opaque credential token containing information that uniquely identifies a Realm Object Server user. +@property (nonatomic, readonly) RLMCredentialToken token; + +/// The name of the identity provider which generated the credential token. +@property (nonatomic, readonly) RLMIdentityProvider provider; + +/// A dictionary containing additional pertinent information. In most cases this is automatically configured. +@property (nonatomic, readonly) NSDictionary *userInfo; + +/** + Construct and return a credential from a Facebook account token. + */ ++ (instancetype)credentialWithFacebookToken:(RLMCredentialToken)token; + +/** + Construct and return a credential from a Google account token. + */ ++ (instancetype)credentialWithGoogleToken:(RLMCredentialToken)token; + +/** + Construct and return a credential from an iCloud account token. + */ ++ (instancetype)credentialWithICloudToken:(RLMCredentialToken)token; + +/** + Construct and return a credential from a Realm Object Server username and password. + */ ++ (instancetype)credentialWithUsername:(NSString *)username + password:(NSString *)password + actions:(RLMAuthenticationActions)actions; + +/** + Construct and return a special credential representing a token that can be directly used to open a Realm. The identity + is used to uniquely identify the user across application launches. + */ ++ (instancetype)credentialWithAccessToken:(RLMServerToken)accessToken identity:(NSString *)identity; + +/** + Construct and return a credential with a custom token string, identity provider string, and optional user info. In most + cases, the convenience initializers should be used instead. + */ +- (instancetype)initWithCustomToken:(RLMCredentialToken)token + provider:(RLMIdentityProvider)provider + userInfo:(nullable NSDictionary *)userInfo NS_DESIGNATED_INITIALIZER; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncCredential cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncCredential cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncFileManager.h b/Pods/Realm/include/RLMSyncFileManager.h new file mode 100644 index 0000000..c7cba42 --- /dev/null +++ b/Pods/Realm/include/RLMSyncFileManager.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMSyncUser; + +@interface RLMSyncFileManager : NSObject + +NS_ASSUME_NONNULL_BEGIN + ++ (NSURL *)fileURLForRawRealmURL:(NSURL *)url user:(RLMSyncUser *)user; ++ (NSURL *)fileURLForMetadata; ++ (BOOL)removeFilesForUserIdentity:(NSString *)identity error:(NSError * _Nullable* _Nullable)error; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncManager.h b/Pods/Realm/include/RLMSyncManager.h new file mode 100644 index 0000000..a8b99a5 --- /dev/null +++ b/Pods/Realm/include/RLMSyncManager.h @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil.h" + +@class RLMSyncSession, RLMSyncConfiguration; + +/// An enum representing different levels of sync-related logging that can be configured. +typedef NS_ENUM(NSUInteger, RLMSyncLogLevel) { + /// Nothing will ever be logged. + RLMSyncLogLevelOff, + /// Only fatal errors will be logged. + RLMSyncLogLevelFatal, + /// Only errors will be logged. + RLMSyncLogLevelError, + /// Warnings and errors will be logged. + RLMSyncLogLevelWarn, + /// Information about sync events will be logged. Fewer events will be logged in order to avoid overhead. + RLMSyncLogLevelInfo, + /// Information about sync events will be logged. More events will be logged than with `RLMSyncLogLevelInfo`. + RLMSyncLogLevelDetail, + /// Log information that can aid in debugging. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelDebug, + /// Log information that can aid in debugging. More events will be logged than with `RLMSyncLogLevelDebug`. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelTrace, + /// Log information that can aid in debugging. More events will be logged than with `RLMSyncLogLevelTrace`. + /// + /// - warning: Will incur a measurable performance impact. + RLMSyncLogLevelAll +}; + +NS_ASSUME_NONNULL_BEGIN + +/// A block type representing a block which can be used to report a sync-related error to the application. If the error +/// pertains to a specific session, that session will also be passed into the block. +typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable); + +/** + A singleton manager which serves as a central point for sync-related configuration. + */ +@interface RLMSyncManager : NSObject + +/** + An optional block which can be used to report sync-related errors to your application. Errors reported through this + mechanism are always fatal; they represent attempts to open sessions which are invalid (for example, using malformed + URLs). + */ +@property (nullable, nonatomic, copy) RLMSyncErrorReportingBlock errorHandler; + +/** + A reverse-DNS string uniquely identifying this application. In most cases this is automatically set by the SDK, and + does not have to be explicitly configured. + */ +@property (nonatomic) NSString *appID; + +/** + Whether SSL certificate validation should be disabled. SSL certificate validation is ON by default. Setting this + property after at least one synced Realm or standalone Session has been opened is a no-op. + + @warning NEVER disable certificate validation for clients and servers in production. + */ +@property (nonatomic) BOOL disableSSLValidation; + +/** + The logging threshold which newly opened synced Realms will use. Defaults to `RLMSyncLogLevelInfo`. Set this before + any synced Realms are opened. Logging strings are output to ASL. + */ +@property (nonatomic) RLMSyncLogLevel logLevel; + +/// The sole instance of the singleton. ++ (instancetype)sharedManager; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncManager cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncManager cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncManager_Private.hpp b/Pods/Realm/include/RLMSyncManager_Private.hpp new file mode 100644 index 0000000..f1b6ad9 --- /dev/null +++ b/Pods/Realm/include/RLMSyncManager_Private.hpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncManager.h" + +#import "RLMSyncUtil_Private.h" + +#import "sync_config.hpp" +#import "sync_metadata.hpp" + +@class RLMSyncUser; + +// All private API methods are threadsafe and synchronized, unless denoted otherwise. Since they are expected to be +// called very infrequently, this should pose no issues. + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncManager () { + std::unique_ptr _metadata_manager; +} + +@property (nullable, nonatomic, copy) RLMSyncBasicErrorReportingBlock sessionCompletionNotifier; + +/** + Given a sync configuration, open and return a standalone session. + + If a standalone session was previously opened but encountered a fatal error, attempting to open an equivalent session + (by using the same configuration) will return `nil`. + */ +- (nullable RLMSyncSession *)sessionForSyncConfiguration:(RLMSyncConfiguration *)config NS_UNAVAILABLE; + +/// Reset the singleton instance, and any saved state. Only for use with Realm Object Store tests. ++ (void)_resetStateForTesting; + +- (void)_fireError:(NSError *)error; + +- (void)_fireErrorWithCode:(int)errorCode + message:(NSString *)message + session:(nullable RLMSyncSession *)session + errorClass:(realm::SyncSessionError)errorClass; + +// Note that this method doesn't need to be threadsafe, since all locking is coordinated internally. +- (realm::SyncMetadataManager&)_metadataManager; + +- (NSArray *)_allUsers; + +/** + Register a user. If an equivalent user has already been registered, the argument is not added to the store, and the + existing user is returned. Otherwise, the argument is added to the store and `nil` is returned. + + Precondition: registered user is in the `active` state (is valid). + */ +- (nullable RLMSyncUser *)_registerUser:(RLMSyncUser *)user; + +- (void)_deregisterLoggedOutUser:(RLMSyncUser *)user; + +- (nullable RLMSyncUser *)_userForIdentity:(NSString *)identity; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncSession.h b/Pods/Realm/include/RLMSyncSession.h new file mode 100644 index 0000000..1232785 --- /dev/null +++ b/Pods/Realm/include/RLMSyncSession.h @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +/** + The current state of a sync session object. + */ +typedef NS_ENUM(NSUInteger, RLMSyncSessionState) { + /// The sync session is valid, but has not yet been bound to the Realm Object Server. + RLMSyncSessionStateUnbound, + /// The sync session is bound to the Realm Object Server and communicating with it. + RLMSyncSessionStateActive, + /// The sync session is logged out, but could be rebound to the Realm Object Server. + RLMSyncSessionStateLoggedOut, + /// The sync session encountered an error and is invalid; it should be discarded. + RLMSyncSessionStateInvalid +}; + +@class RLMSyncUser, RLMSyncConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +/** + An object encapsulating a Realm Object Server "session". Sessions represent the communication between the client (and a + local Realm file on disk), and the server (and a remote Realm at a given URL stored on a Realm Object Server). + + Sessions are always created by the SDK and vended out through various APIs. The lifespans of sessions associated with + Realms are managed automatically. + */ +@interface RLMSyncSession : NSObject + +/// The session's current state. +@property (nonatomic, readonly) RLMSyncSessionState state; + +/// The Realm Object Server URL of the remote Realm this session corresponds to. +@property (nonatomic, readonly) NSURL *realmURL; + +/// A reference to the user object owning the Realm this session corresponds to. If the session object is invalid, this +/// property may automatically be set to `nil`. +@property (nonatomic, weak, nullable, readonly) RLMSyncUser *parentUser; + +/// If the session is valid, return a sync configuration that can be used to open the Realm associated with this +/// session. +- (nullable RLMSyncConfiguration *)configuration; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncSessionHandle.hpp b/Pods/Realm/include/RLMSyncSessionHandle.hpp new file mode 100644 index 0000000..445cbae --- /dev/null +++ b/Pods/Realm/include/RLMSyncSessionHandle.hpp @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import + +namespace realm { +struct SyncSession; +} + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncSessionHandle : NSObject + ++ (instancetype)syncSessionHandleForWeakPointer:(std::shared_ptr)pointer; ++ (instancetype)syncSessionHandleForPointer:(std::shared_ptr)pointer; + +/// Whether the underlying session is in an unrecoverable error state. +- (BOOL)sessionIsInErrorState; + +/// Whether the underlying session still exists, if the session reference is weak. +- (BOOL)sessionStillExists; + +/// Inform the session that the user that owns it has logged out. +- (void)logOut; + +/// Refresh the access token for the session. +- (BOOL)refreshAccessToken:(NSString *)accessToken serverURL:(nullable NSURL *)serverURL; + +/// Revive the session. +- (void)revive; + +/// Wait for pending uploads to complete or the session to expire, and dispatch the callback onto the specified queue. +- (BOOL)waitForUploadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; + +/// Wait for pending downloads to complete or the session to expire, and dispatch the callback onto the specified queue. +- (BOOL)waitForDownloadCompletionOnQueue:(nullable dispatch_queue_t)queue callback:(void(^)(void))callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncSession_Private.h b/Pods/Realm/include/RLMSyncSession_Private.h new file mode 100644 index 0000000..63c1da8 --- /dev/null +++ b/Pods/Realm/include/RLMSyncSession_Private.h @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncSession.h" + +#import "RLMSyncConfiguration.h" +#import "RLMSyncUtil_Private.h" + +@class RLMSyncUser, RLMSyncSessionHandle; + +@interface RLMSessionBindingPackage : NSObject + +NS_ASSUME_NONNULL_BEGIN + +@property (nullable, nonatomic, copy) RLMSyncBasicErrorReportingBlock block; +@property (nonatomic) NSURL *fileURL; +@property (nonatomic) RLMSyncConfiguration *syncConfig; +@property (nonatomic) BOOL isStandalone; + +- (instancetype)initWithFileURL:(NSURL *)fileURL + syncConfig:(RLMSyncConfiguration *)syncConfig + standalone:(BOOL)isStandalone + block:(nullable RLMSyncBasicErrorReportingBlock)block; + +@end + +@interface RLMSyncSession () RLM_SYNC_UNINITIALIZABLE + +- (void)_refresh; +- (void)_logOut; +- (void)_invalidate; + +- (void)setState:(RLMSyncSessionState)state; + +/// The path on disk where the Realm file backing this synced Realm is stored. +@property (nonatomic) NSURL *fileURL; + +@property (nullable, nonatomic) RLMSessionBindingPackage *deferredBindingPackage; +@property (nullable, nonatomic) RLMServerPath resolvedPath; + +- (instancetype)initWithFileURL:(NSURL *)fileURL realmURL:(NSURL *)realmURL; + +#pragma mark - per-Realm access token API + +@property (nullable, nonatomic) RLMServerToken accessToken; +@property (nonatomic) NSTimeInterval accessTokenExpiry; + +@property (nonatomic) NSTimer *refreshTimer; + +- (nullable RLMSyncSessionHandle *)sessionHandle; + +- (void)configureWithAccessToken:(RLMServerToken)token + expiry:(NSTimeInterval)expiry + user:(RLMSyncUser *)user + handle:(RLMSyncSessionHandle *)session; + +- (void)refreshAccessToken:(NSString *)token serverURL:(nullable NSURL *)serverURL; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncUser.h b/Pods/Realm/include/RLMSyncUser.h new file mode 100644 index 0000000..b9083b9 --- /dev/null +++ b/Pods/Realm/include/RLMSyncUser.h @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +@class RLMSyncUser, RLMSyncCredential, RLMSyncSession, RLMRealm; + +/** + The state of the user object. + */ +typedef NS_ENUM(NSUInteger, RLMSyncUserState) { + /// The user is logged out. Call `authenticateWithCredential:...` with a valid credential to log the user back in. + RLMSyncUserStateLoggedOut, + /// The user is logged in, and any Realms associated with it are syncing with the Realm Object Server. + RLMSyncUserStateActive, + /// The user has encountered a fatal error state, and cannot be used. + RLMSyncUserStateError, +}; + +/// A block type used for APIs which asynchronously vend a `RLMSyncUser`. +typedef void(^RLMUserCompletionBlock)(RLMSyncUser * _Nullable, NSError * _Nullable); + +NS_ASSUME_NONNULL_BEGIN + +/** + A `RLMSyncUser` instance represents a single Realm Object Server user account (or just user). + + A user may have one or more credentials associated with it. These credentials uniquely identify the user to a + third-party auth provider, and are used to sign into a Realm Object Server user account. + + Note that users are only vended out via SDK APIs, and only one user instance ever exists for a given user account. + */ +@interface RLMSyncUser : NSObject + +/** + An array of all valid, logged-in users. + */ ++ (NSArray *)all; + +/** + The unique Realm Object Server user ID string identifying this user. + */ +@property (nonatomic, readonly) NSString *identity; + +/** + The URL of the authentication server this user will communicate with. + */ +@property (nullable, nonatomic, readonly) NSURL *authenticationServer; + +/** + The current state of the user. + */ +@property (nonatomic, readonly) RLMSyncUserState state; + +/** + Create, log in, and asynchronously return a new user object, specifying a custom timeout for the network request. A + credential identifying the user must be passed in. The user becomes available in the completion block, at which point + it is ready for use. + */ ++ (void)authenticateWithCredential:(RLMSyncCredential *)credential + authServerURL:(NSURL *)authServerURL + timeout:(NSTimeInterval)timeout + onCompletion:(RLMUserCompletionBlock)completion NS_REFINED_FOR_SWIFT; + +/** + Create, log in, and asynchronously return a new user object. A credential identifying the user must be passed in. The + user becomes available in the completion block, at which point it is ready for use. + */ ++ (void)authenticateWithCredential:(RLMSyncCredential *)credential + authServerURL:(NSURL *)authServerURL + onCompletion:(RLMUserCompletionBlock)completion +NS_SWIFT_UNAVAILABLE("Use the full version of this API."); + +/** + Log a user out, destroying their server state, deregistering them from the SDK, and removing any synced Realms + associated with them from on-disk storage. If the user is already logged out or in an error state, this is a no-op. + + This method should be called whenever the application is committed to not using a user again unless they are recreated. + Failing to call this method may result in unused files and metadata needlessly taking up space. + */ +- (void)logOut; + +/** + Retrieve a valid session object belonging to this user for a given URL, or `nil` if no such object exists. + */ +- (nullable RLMSyncSession *)sessionForURL:(NSURL *)url; + +/** + Retrieve all the valid sessions belonging to this user. + */ +- (NSArray *)allSessions; + +/// :nodoc: +- (instancetype)init __attribute__((unavailable("RLMSyncUser cannot be created directly"))); + +/// :nodoc: ++ (instancetype)new __attribute__((unavailable("RLMSyncUser cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncUser_Private.hpp b/Pods/Realm/include/RLMSyncUser_Private.hpp new file mode 100644 index 0000000..879e8c8 --- /dev/null +++ b/Pods/Realm/include/RLMSyncUser_Private.hpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncUser.h" + +#import "RLMSyncConfiguration.h" +#import "RLMSyncUtil_Private.h" + +#include "sync_config.hpp" +#include "sync_metadata.hpp" + +@class RLMSyncConfiguration; + +typedef void(^RLMFetchedRealmCompletionBlock)(NSError * _Nullable, RLMRealm * _Nullable, BOOL * _Nonnull); + +NS_ASSUME_NONNULL_BEGIN + +@interface RLMSyncUser () + +@property (nullable, nonatomic) RLMServerToken refreshToken; + +/// Create a user based on a `SyncUserMetadata` object. This method does NOT register the user to the sync manager's +/// user store. +- (instancetype)initWithMetadata:(realm::SyncUserMetadata)metadata; + +/** + Register a Realm to a user. + + @param fileURL The location of the file on disk where the local copy of the Realm will be saved. + @param completion An optional completion block. + */ +- (nullable RLMSyncSession *)_registerSessionForBindingWithFileURL:(NSURL *)fileURL + syncConfig:(RLMSyncConfiguration *)syncConfig + standaloneSession:(BOOL)isStandalone + onCompletion:(nullable RLMSyncBasicErrorReportingBlock)completion; + +- (void)setState:(RLMSyncUserState)state; +- (void)_deregisterSessionWithRealmURL:(NSURL *)realmURL; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Pods/Realm/include/RLMSyncUtil.h b/Pods/Realm/include/RLMSyncUtil.h new file mode 100644 index 0000000..080db25 --- /dev/null +++ b/Pods/Realm/include/RLMSyncUtil.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMConstants.h" + +/// A token originating from the Realm Object Server. +typedef NSString* RLMServerToken; + +NS_ASSUME_NONNULL_BEGIN + +/// The error domain string for all SDK errors related to synchronization functionality. +extern NSString *const RLMSyncErrorDomain; + +/// An error which is related to synchronization with a Realm Object Server. +typedef RLM_ERROR_ENUM(NSInteger, RLMSyncError, RLMSyncErrorDomain) { + /// An error that indicates that the response received from the authentication server was malformed. + RLMSyncErrorBadResponse = 1, + + /// An error that indicates that the supplied Realm path was invalid, or could not be resolved by the authentication + /// server. + RLMSyncErrorBadRemoteRealmPath = 2, + + /// An error that indicates that the response received from the authentication server was an HTTP error code. The + /// `userInfo` dictionary contains the actual error code value. + RLMSyncErrorHTTPStatusCodeError = 3, + + /// An error that indicates a problem with the session (a specific Realm opened for sync). + RLMSyncErrorClientSessionError = 4, + + /// An error that indicates a problem with a specific user. + RLMSyncErrorClientUserError = 5, + + /// An error that indicates an internal error with the underlying synchronization engine. Only for information. + RLMSyncErrorClientInternalError = 6, +}; + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMSyncUtil_Private.h b/Pods/Realm/include/RLMSyncUtil_Private.h new file mode 100644 index 0000000..5a049c7 --- /dev/null +++ b/Pods/Realm/include/RLMSyncUtil_Private.h @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncUtil.h" + +#import "RLMSyncCredential.h" + +typedef void(^RLMSyncCompletionBlock)(NSError * _Nullable, NSDictionary * _Nullable); +typedef void(^RLMSyncBasicErrorReportingBlock)(NSError * _Nullable); + +typedef NSString* RLMServerPath; + +NS_ASSUME_NONNULL_BEGIN + +extern RLMIdentityProvider const RLMIdentityProviderAccessToken; +extern RLMIdentityProvider const RLMIdentityProviderRealm; + +extern NSString *const kRLMSyncAppIDKey; +extern NSString *const kRLMSyncDataKey; +extern NSString *const kRLMSyncErrorJSONKey; +extern NSString *const kRLMSyncIdentityKey; +extern NSString *const kRLMSyncPasswordKey; +extern NSString *const kRLMSyncPathKey; +extern NSString *const kRLMSyncProviderKey; +extern NSString *const kRLMSyncRegisterKey; +extern NSString *const kRLMSyncUnderlyingErrorKey; +extern NSString *const kRLMSyncActionsKey; + +#define RLM_SYNC_UNINITIALIZABLE \ +- (instancetype)init __attribute__((unavailable("This type cannot be created directly"))); \ ++ (instancetype)new __attribute__((unavailable("This type cannot be created directly"))); + +NS_ASSUME_NONNULL_END + +/// A macro to parse a string out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_STRING_OR_ABORT(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSString class]]) { return nil; } \ +self.prop_macro_val = data; \ +} \ + +#define RLM_SYNC_PARSE_OPTIONAL_STRING(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSString class]]) { data = nil; } \ +self.prop_macro_val = data; \ +} \ + +/// A macro to parse a double out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_DOUBLE_OR_ABORT(json_macro_val, key_macro_val, prop_macro_val) \ +{ \ +id data = json_macro_val[key_macro_val]; \ +if (![data isKindOfClass:[NSNumber class]]) { return nil; } \ +self.prop_macro_val = [data doubleValue]; \ +} \ + +/// A macro to build a sub-model out of a JSON dictionary, or return nil. +#define RLM_SYNC_PARSE_MODEL_OR_ABORT(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ +{ \ +id raw = json_macro_val[key_macro_val]; \ +if (![raw isKindOfClass:[NSDictionary class]]) { return nil; } \ +id model = [[class_macro_val alloc] initWithDictionary:raw]; \ +if (!model) { return nil; } \ +self.prop_macro_val = model; \ +} \ + +#define RLM_SYNC_PARSE_OPTIONAL_MODEL(json_macro_val, key_macro_val, class_macro_val, prop_macro_val) \ +{ \ +id model; \ +id raw = json_macro_val[key_macro_val]; \ +if (![raw isKindOfClass:[NSDictionary class]]) { model = nil; } \ +else { model = [[class_macro_val alloc] initWithDictionary:raw]; } \ +self.prop_macro_val = model; \ +} \ diff --git a/Pods/Realm/include/RLMSyncUtil_Private.hpp b/Pods/Realm/include/RLMSyncUtil_Private.hpp new file mode 100644 index 0000000..3ad03f6 --- /dev/null +++ b/Pods/Realm/include/RLMSyncUtil_Private.hpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import "RLMSyncUtil_Private.h" + +#import "RLMSyncConfiguration_Private.h" + +#import "sync_manager.hpp" + +namespace realm { + +SyncSessionStopPolicy translateStopPolicy(RLMSyncStopPolicy stopPolicy); +RLMSyncStopPolicy translateStopPolicy(SyncSessionStopPolicy stop_policy); + +} diff --git a/Pods/Realm/include/RLMTokenModels.h b/Pods/Realm/include/RLMTokenModels.h new file mode 100644 index 0000000..bd97092 --- /dev/null +++ b/Pods/Realm/include/RLMTokenModels.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import "RLMSyncUtil_Private.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RLMTokenDataModel; + +@interface RLMTokenModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *token; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) RLMTokenDataModel *tokenData; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +@interface RLMTokenDataModel : NSObject RLM_SYNC_UNINITIALIZABLE + +@property (nonatomic, readonly) NSString *identity; +@property (nonatomic, nullable, readonly) NSString *appID; +@property (nonatomic, nullable, readonly) NSString *path; +@property (nonatomic, readonly) NSTimeInterval expires; +//@property (nonatomic, readonly) NSArray *access; + +- (instancetype)initWithDictionary:(NSDictionary *)jsonDictionary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pods/Realm/include/RLMUpdateChecker.hpp b/Pods/Realm/include/RLMUpdateChecker.hpp new file mode 100644 index 0000000..7f01ac7 --- /dev/null +++ b/Pods/Realm/include/RLMUpdateChecker.hpp @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +// Asynchronously check for updates to Realm if running on a simulator +void RLMCheckForUpdates(); diff --git a/Pods/Realm/include/RLMUtil.hpp b/Pods/Realm/include/RLMUtil.hpp new file mode 100644 index 0000000..e0161b9 --- /dev/null +++ b/Pods/Realm/include/RLMUtil.hpp @@ -0,0 +1,204 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import +#import +#import + +#import +#import +#import +#import +#import + +namespace realm { + class Mixed; +} + +@class RLMObjectSchema; +@class RLMProperty; + +namespace realm { + class RealmFileException; +} + +__attribute__((format(NSString, 1, 2))) +NSException *RLMException(NSString *fmt, ...); +NSException *RLMException(std::exception const& exception); + +NSError *RLMMakeError(RLMError code, std::exception const& exception); +NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError&); +NSError *RLMMakeError(RLMError code, const realm::RealmFileException&); +NSError *RLMMakeError(std::system_error const& exception); +NSError *RLMMakeError(NSException *exception); + +void RLMSetErrorOrThrow(NSError *error, NSError **outError); + +// returns if the object can be inserted as the given type +BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop); + +// gets default values for the given schema (+defaultPropertyValues) +// merges with native property defaults if Swift class +NSDictionary *RLMDefaultValuesForObjectSchema(RLMObjectSchema *objectSchema); + +BOOL RLMIsDebuggerAttached(); +BOOL RLMIsRunningInPlayground(); + +// C version of isKindOfClass +static inline BOOL RLMIsKindOfClass(Class class1, Class class2) { + while (class1) { + if (class1 == class2) return YES; + class1 = class_getSuperclass(class1); + } + return NO; +} + +// Returns whether the class is a descendent of RLMObjectBase +BOOL RLMIsObjectOrSubclass(Class klass); + +// Returns whether the class is an indirect descendant of RLMObjectBase +BOOL RLMIsObjectSubclass(Class klass); + +template +static inline T *RLMDynamicCast(__unsafe_unretained id obj) { + if ([obj isKindOfClass:[T class]]) { + return obj; + } + return nil; +} + +template +static inline T RLMCoerceToNil(__unsafe_unretained T obj) { + if (static_cast(obj) == NSNull.null) { + return nil; + } + else if (__unsafe_unretained auto optional = RLMDynamicCast(obj)) { + return RLMCoerceToNil(optional.underlyingValue); + } + return obj; +} + +// Translate an rlmtype to a string representation +static inline NSString *RLMTypeToString(RLMPropertyType type) { + switch (type) { + case RLMPropertyTypeString: + return @"string"; + case RLMPropertyTypeInt: + return @"int"; + case RLMPropertyTypeBool: + return @"bool"; + case RLMPropertyTypeDate: + return @"date"; + case RLMPropertyTypeData: + return @"data"; + case RLMPropertyTypeDouble: + return @"double"; + case RLMPropertyTypeFloat: + return @"float"; + case RLMPropertyTypeAny: + return @"any"; + case RLMPropertyTypeObject: + return @"object"; + case RLMPropertyTypeArray: + return @"array"; + case RLMPropertyTypeLinkingObjects: + return @"linking objects"; + } + return @"Unknown"; +} + +// String conversion utilities +static inline NSString * RLMStringDataToNSString(realm::StringData stringData) { + static_assert(sizeof(NSUInteger) >= sizeof(size_t), + "Need runtime overflow check for size_t to NSUInteger conversion"); + if (stringData.is_null()) { + return nil; + } + else { + return [[NSString alloc] initWithBytes:stringData.data() + length:stringData.size() + encoding:NSUTF8StringEncoding]; + } +} + +static inline realm::StringData RLMStringDataWithNSString(__unsafe_unretained NSString *const string) { + static_assert(sizeof(size_t) >= sizeof(NSUInteger), + "Need runtime overflow check for NSUInteger to size_t conversion"); + return realm::StringData(string.UTF8String, + [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); +} + +// Binary conversion utilities +static inline NSData *RLMBinaryDataToNSData(realm::BinaryData binaryData) { + return binaryData ? [NSData dataWithBytes:binaryData.data() length:binaryData.size()] : nil; +} + +static inline realm::BinaryData RLMBinaryDataForNSData(__unsafe_unretained NSData *const data) { + // this is necessary to ensure that the empty NSData isn't treated by core as the null realm::BinaryData + // because data.bytes == 0 when data.length == 0 + // the casting bit ensures that we create a data with a non-null pointer + auto bytes = static_cast(data.bytes) ?: static_cast((__bridge void *)data); + return realm::BinaryData(bytes, data.length); +} + +// Date conversion utilities +// These use the reference date and shift the seconds rather than just getting +// the time interval since the epoch directly to avoid losing sub-second precision +static inline NSDate *RLMTimestampToNSDate(realm::Timestamp ts) NS_RETURNS_RETAINED { + if (ts.is_null()) + return nil; + auto timeInterval = ts.get_seconds() - NSTimeIntervalSince1970 + ts.get_nanoseconds() / 1'000'000'000.0; + return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:timeInterval]; +} + +static inline realm::Timestamp RLMTimestampForNSDate(__unsafe_unretained NSDate *const date) { + auto timeInterval = date.timeIntervalSinceReferenceDate; + if (isnan(timeInterval)) + return {0, 0}; // Arbitrary choice + + // Clamp dates that we can't represent as a Timestamp to the maximum value + if (timeInterval >= std::numeric_limits::max() - NSTimeIntervalSince1970) + return {std::numeric_limits::max(), 1'000'000'000 - 1}; + if (timeInterval - NSTimeIntervalSince1970 < std::numeric_limits::min()) + return {std::numeric_limits::min(), -1'000'000'000 + 1}; + + auto seconds = static_cast(timeInterval); + auto nanoseconds = static_cast((timeInterval - seconds) * 1'000'000'000.0); + seconds += static_cast(NSTimeIntervalSince1970); + + // Seconds and nanoseconds have to have the same sign + if (nanoseconds < 0 && seconds > 0) { + nanoseconds += 1'000'000'000; + --seconds; + } + return {seconds, nanoseconds}; +} + +static inline NSUInteger RLMConvertNotFound(size_t index) { + return index == realm::not_found ? NSNotFound : index; +} + +id RLMMixedToObjc(realm::Mixed const& value); + +// For unit testing purposes, allow an Objective-C class named FakeObject to also be used +// as the base class of managed objects. This allows for testing invalid schemas. +void RLMSetTreatFakeObjectAsRLMObject(BOOL flag); + +// Given a bundle identifier, return the base directory on the disk within which Realm database and support files should +// be stored. +NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier); diff --git a/Pods/Realm/include/Realm.h b/Pods/Realm/include/Realm.h new file mode 100644 index 0000000..9d568c2 --- /dev/null +++ b/Pods/Realm/include/Realm.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2014 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/Pods/Realm/include/binding_context.hpp b/Pods/Realm/include/binding_context.hpp new file mode 100644 index 0000000..52985bb --- /dev/null +++ b/Pods/Realm/include/binding_context.hpp @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef BINDING_CONTEXT_HPP +#define BINDING_CONTEXT_HPP + +#include "index_set.hpp" + +#include +#include + +namespace realm { +// BindingContext is the extension point for adding binding-specific behavior to +// a SharedRealm. It can be used to store additonal data associated with the +// Realm which is needed by the binding, and there are several methods which +// can be overridden to receive notifications of state changes within the Realm. +// +// A simple implementation which lets the user register functions to be +// called on refresh could look like the following: +// +// class BindingContextImplementation : public BindingContext { +// public: +// // A token returned from add_notification that can be used to remove the +// // notification later +// struct token : private std::list>::iterator { +// token(std::list>::iterator it) : std::list>::iterator(it) { } +// friend class DelegateImplementation; +// }; +// +// token add_notification(std::function func) +// { +// m_registered_notifications.push_back(std::move(func)); +// return token(std::prev(m_registered_notifications.end())); +// } +// +// void remove_notification(token entry) +// { +// m_registered_notifications.erase(entry); +// } +// +// // Override the did_change method to call each registered notification +// void did_change(std::vector const&, std::vector const&) override +// { +// // Loop oddly so that unregistering a notification from within the +// // registered function works +// for (auto it = m_registered_notifications.begin(); it != m_registered_notifications.end(); ) { +// (*it++)(); +// } +// } +// +// private: +// std::list> m_registered_notifications; +// }; +class BindingContext { +public: + virtual ~BindingContext() = default; + + // If the user adds a notification handler to the Realm, will it ever + // actually be called? + virtual bool can_deliver_notifications() const noexcept { return true; } + + // Called by the Realm when a write transaction is committed to the file by + // a different Realm instance (possibly in a different process) + virtual void changes_available() { } + + struct ObserverState; + + // Override this function if you want to receive detailed information about + // external changes to a specific set of objects. + // This is called before each operation which may advance the read + // transaction to include + // ObserverStates for each row for which detailed change information is + // desired. + virtual std::vector get_observed_rows() { return {}; } + + // Called immediately before the read transaction is advanced if detailed + // change information was requested (by returning a non-empty array from + // get_observed_rows()). + // The observers vector is the vector returned by get_observed_row(), + // updated with change information. The invalidated vector is a list of the + // `info` fields of observed rows which will be deleted. + virtual void will_change(std::vector const& observers, + std::vector const& invalidated); + + // Called immediately after the read transaction version is advanced. Unlike + // will_change(), this is called even if detailed change information was not + // requested or if the Realm is not actually in a read transaction, although + // both vectors will be empty in that case. + virtual void did_change(std::vector const& observers, + std::vector const& invalidated); + + // Change information for a single field of a row + struct ColumnInfo { + // The index of this column prior to the changes in the tracked + // transaction, or -1 for newly inserted columns. + size_t initial_column_index = -1; + // What kind of change occurred? + // Always Set or None for everything but LinkList columns. + enum class Kind { + None, // No change + Set, // The value or entries at `indices` were assigned to + Insert, // New values were inserted at each of the indices given + Remove, // Values were removed at each of the indices given + SetAll // The entire LinkList has been replaced with a new set of values + } kind = Kind::None; + // The indices where things happened for Set, Insert and Remove on + // LinkList columns. Not used for other types or for None or SetAll. + IndexSet indices; + }; + + // Information about an observed row in a table + // + // Each object which needs detailed change information should have an + // ObserverState entry in the vector returned from get_observed_rows(), with + // the initial table and row indexes set (and optionally the info field). + // The Realm parses the transaction log, and populates the `changes` vector + // in each ObserverState with information about what changes were made. + struct ObserverState { + // Initial table and row which is observed + // May be updated by row insertions and removals + size_t table_ndx; + size_t row_ndx; + + // Opaque userdata for the delegate's use + void* info; + + // Populated with information about which columns were changed + // May be shorter than the actual number of columns if the later columns + // are not modified + std::vector changes; + + // Simple lexographic ordering + friend bool operator<(ObserverState const& lft, ObserverState const& rgt) + { + return std::tie(lft.table_ndx, lft.row_ndx) < std::tie(rgt.table_ndx, rgt.row_ndx); + } + }; +}; + +inline void BindingContext::will_change(std::vector const&, std::vector const&) { } +inline void BindingContext::did_change(std::vector const&, std::vector const&) { } +} // namespace realm + +#endif /* BINDING_CONTEXT_HPP */ diff --git a/Pods/Realm/include/collection_notifications.hpp b/Pods/Realm/include/collection_notifications.hpp new file mode 100644 index 0000000..35515fa --- /dev/null +++ b/Pods/Realm/include/collection_notifications.hpp @@ -0,0 +1,176 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_COLLECTION_NOTIFICATIONS_HPP +#define REALM_COLLECTION_NOTIFICATIONS_HPP + +#include "index_set.hpp" +#include "util/atomic_shared_ptr.hpp" + +#include +#include +#include +#include + +namespace realm { +namespace _impl { + class CollectionNotifier; +} + +// A token which keeps an asynchronous query alive +struct NotificationToken { + NotificationToken() = default; + NotificationToken(std::shared_ptr<_impl::CollectionNotifier> notifier, size_t token); + ~NotificationToken(); + + NotificationToken(NotificationToken&&); + NotificationToken& operator=(NotificationToken&&); + + NotificationToken(NotificationToken const&) = delete; + NotificationToken& operator=(NotificationToken const&) = delete; + +private: + util::AtomicSharedPtr<_impl::CollectionNotifier> m_notifier; + size_t m_token; +}; + +struct CollectionChangeSet { + struct Move { + size_t from; + size_t to; + + bool operator==(Move m) const { return from == m.from && to == m.to; } + }; + + // Indices which were removed from the _old_ collection + IndexSet deletions; + + // Indices in the _new_ collection which are new insertions + IndexSet insertions; + + // Indices of objects in the _old_ collection which were modified + IndexSet modifications; + + // Indices in the _new_ collection which were modified. This will always + // have the same number of indices as `modifications` and conceptually + // represents the same entries, just in different versions of the collection. + // It exists for the sake of code which finds it easier to process + // modifications after processing deletions and insertions rather than before. + IndexSet modifications_new; + + // Rows in the collection which moved. + // + // Every `from` index will also be present in `deletions` and every `to` + // index will be present in `insertions`. + // + // This is currently not reliably calculated for all types of collections. A + // reported move will always actually be a move, but there may also be + // unreported moves which show up only as a delete/insert pair. + std::vector moves; + + bool empty() const + { + return deletions.empty() && insertions.empty() && modifications.empty() + && modifications_new.empty() && moves.empty(); + } +}; + +// A type-erasing wrapper for the callback for collection notifications. Can be +// constructed with either any callable compatible with the signature +// `void (CollectionChangeSet, std::exception_ptr)`, an object with member +// functions `void before(CollectionChangeSet)`, `void after(CollectionChangeSet)`, +// `void error(std::exception_ptr)`, or a pointer to such an object. If a pointer +// is given, the caller is responsible for ensuring that the pointed-to object +// outlives the collection. +class CollectionChangeCallback { +public: + CollectionChangeCallback(std::nullptr_t={}) { } + + template + CollectionChangeCallback(Callback cb) : m_impl(make_impl(std::move(cb))) { } + template + CollectionChangeCallback& operator=(Callback cb) { m_impl = make_impl(std::move(cb)); return *this; } + + // Explicitly default the copy/move constructors as otherwise they'll use + // the above ones and add an extra layer of wrapping + CollectionChangeCallback(CollectionChangeCallback&&) = default; + CollectionChangeCallback(CollectionChangeCallback const&) = default; + CollectionChangeCallback& operator=(CollectionChangeCallback&&) = default; + CollectionChangeCallback& operator=(CollectionChangeCallback const&) = default; + + void before(CollectionChangeSet const& c) { m_impl->before(c); } + void after(CollectionChangeSet const& c) { m_impl->after(c); } + void error(std::exception_ptr e) { m_impl->error(e); } + + explicit operator bool() const { return !!m_impl; } + +private: + struct Base { + virtual void before(CollectionChangeSet const&)=0; + virtual void after(CollectionChangeSet const&)=0; + virtual void error(std::exception_ptr)=0; + }; + + template()(CollectionChangeSet(), std::exception_ptr()))> + std::shared_ptr make_impl(Callback cb) + { + return std::make_shared>(std::move(cb)); + } + + template().after(CollectionChangeSet())), typename = void> + std::shared_ptr make_impl(Callback cb) + { + return std::make_shared>(std::move(cb)); + } + + template().after(CollectionChangeSet())), typename = void> + std::shared_ptr make_impl(Callback* cb) + { + return std::make_shared>(cb); + } + + template + struct Impl : public Base { + T impl; + Impl(T impl) : impl(std::move(impl)) { } + void before(CollectionChangeSet const&) override { } + void after(CollectionChangeSet const& change) override { impl(change, {}); } + void error(std::exception_ptr error) override { impl({}, error); } + }; + template + struct Impl2 : public Base { + T impl; + Impl2(T impl) : impl(std::move(impl)) { } + void before(CollectionChangeSet const& c) override { impl.before(c); } + void after(CollectionChangeSet const& c) override { impl.after(c); } + void error(std::exception_ptr error) override { impl.error(error); } + }; + template + struct Impl3 : public Base { + T* impl; + Impl3(T* impl) : impl(impl) { } + void before(CollectionChangeSet const& c) override { impl->before(c); } + void after(CollectionChangeSet const& c) override { impl->after(c); } + void error(std::exception_ptr error) override { impl->error(error); } + }; + + std::shared_ptr m_impl; +}; +} // namespace realm + +#endif // REALM_COLLECTION_NOTIFICATIONS_HPP diff --git a/Pods/Realm/include/core/realm.hpp b/Pods/Realm/include/core/realm.hpp new file mode 100644 index 0000000..29e4844 --- /dev/null +++ b/Pods/Realm/include/core/realm.hpp @@ -0,0 +1,27 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_HPP +#define REALM_HPP + +#include +#include +#include +#include + +#endif // REALM_HPP diff --git a/Pods/Realm/include/core/realm/alloc.hpp b/Pods/Realm/include/core/realm/alloc.hpp new file mode 100644 index 0000000..9ffa54a --- /dev/null +++ b/Pods/Realm/include/core/realm/alloc.hpp @@ -0,0 +1,443 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ALLOC_HPP +#define REALM_ALLOC_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { + +class Allocator; + +class Replication; + +using ref_type = size_t; + +int_fast64_t from_ref(ref_type) noexcept; +ref_type to_ref(int_fast64_t) noexcept; +int64_t to_int64(size_t value) noexcept; + +class MemRef { +public: + MemRef() noexcept; + ~MemRef() noexcept; + + MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept; + MemRef(ref_type ref, Allocator& alloc) noexcept; + + char* get_addr(); + ref_type get_ref(); + void set_ref(ref_type ref); + void set_addr(char* addr); + +private: + char* m_addr; + ref_type m_ref; +#if REALM_ENABLE_MEMDEBUG + // Allocator that created m_ref. Used to verify that the ref is valid whenever you call + // get_ref()/get_addr and that it e.g. has not been free'ed + const Allocator* m_alloc = nullptr; +#endif +}; + + +/// The common interface for Realm allocators. +/// +/// A Realm allocator must associate a 'ref' to each allocated +/// object and be able to efficiently map any 'ref' to the +/// corresponding memory address. The 'ref' is an integer and it must +/// always be divisible by 8. Also, a value of zero is used to +/// indicate a null-reference, and must therefore never be returned by +/// Allocator::alloc(). +/// +/// The purpose of the 'refs' is to decouple the memory reference from +/// the actual address and thereby allowing objects to be relocated in +/// memory without having to modify stored references. +/// +/// \sa SlabAlloc +class Allocator { +public: + static constexpr int CURRENT_FILE_FORMAT_VERSION = 6; + + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// \throw std::bad_alloc If insufficient memory was available. + MemRef alloc(size_t size); + + /// Calls do_realloc(). + /// + /// Note: The underscore has been added because the name `realloc` + /// would conflict with a macro on the Windows platform. + MemRef realloc_(ref_type, const char* addr, size_t old_size, size_t new_size); + + /// Calls do_free(). + /// + /// Note: The underscore has been added because the name `free + /// would conflict with a macro on the Windows platform. + void free_(ref_type, const char* addr) noexcept; + + /// Shorthand for free_(mem.get_ref(), mem.get_addr()). + void free_(MemRef mem) noexcept; + + /// Calls do_translate(). + char* translate(ref_type ref) const noexcept; + + /// Returns true if, and only if the object at the specified 'ref' + /// is in the immutable part of the memory managed by this + /// allocator. The method by which some objects become part of the + /// immuatble part is entirely up to the class that implements + /// this interface. + bool is_read_only(ref_type) const noexcept; + + /// Returns a simple allocator that can be used with free-standing + /// Realm objects (such as a free-standing table). A + /// free-standing object is one that is not part of a Group, and + /// therefore, is not part of an actual database. + static Allocator& get_default() noexcept; + + virtual ~Allocator() noexcept; + + virtual void verify() const = 0; + +#ifdef REALM_DEBUG + /// Terminate the program precisely when the specified 'ref' is + /// freed (or reallocated). You can use this to detect whether the + /// ref is freed (or reallocated), and even to get a stacktrace at + /// the point where it happens. Call watch(0) to stop watching + /// that ref. + void watch(ref_type ref) + { + m_debug_watch = ref; + } +#endif + + Replication* get_replication() noexcept; + + /// \brief The version of the format of the the node structure (in file or + /// in memory) in use by Realm objects associated with this allocator. + /// + /// Every allocator contains a file format version field, which is returned + /// by this function. In some cases (as mentioned below) the file format can + /// change. + /// + /// A value of zero means the the file format is not yet decided. This is + /// only possible for empty Realms where top-ref is zero. + /// + /// For the default allocator (get_default()), the file format version field + /// can never change, is never zero, and is set to whatever + /// Group::get_target_file_format_version_for_session() would return if the + /// original file format version was undecided and the request history type + /// was Replication::hist_None. + /// + /// For the slab allocator (AllocSlab), the file format version field is set + /// to the file format version specified by the attached file (or attached + /// memory buffer) at the time of attachment. If no file (or buffer) is + /// currently attached, the returned value has no meaning. If the Realm file + /// format is later upgraded, the file format version filed must be updated + /// to reflect that fact. + /// + /// In shared mode (when a Realm file is opened via a SharedGroup instance) + /// it can happen that the file format is upgraded asyncronously (via + /// another SharedGroup instance), and in that case the file format version + /// field of the allocator can get out of date, but only for a short + /// while. It is always garanteed to be, and remain up to date after the + /// opening process completes (when SharedGroup::do_open() returns). + /// + /// An empty Realm file (one whose top-ref is zero) may specify a file + /// format version of zero to indicate that the format is not yet + /// decided. In that case, this function will return zero immediately after + /// AllocSlab::attach_file() returns. It shall be guaranteed, however, that + /// the zero is changed to a proper file format version before the opening + /// process completes (Group::open() or SharedGroup::open()). It is the duty + /// of the caller of AllocSlab::attach_file() to ensure this. + /// + /// File format versions: + /// + /// 1 Initial file format version + /// + /// 2 Various changes. + /// + /// 3 Supporting null on string columns broke the file format in following + /// way: Index appends an 'X' character to all strings except the null + /// string, to be able to distinguish between null and empty + /// string. Bumped to 3 because of null support of String columns and + /// because of new format of index. + /// + /// 4 Introduction of optional in-Realm history of changes (additional + /// entries in Group::m_top). Since this change is not forward + /// compatible, the file format version had to be bumped. This change is + /// implemented in a way that achieves backwards compatibility with + /// version 3 (and in turn with version 2). + /// + /// 5 Introduced the new Timestamp column type that replaces DateTime. + /// When opening an older database file, all DateTime columns will be + /// automatically upgraded Timestamp columns. + /// + /// 6 Introduced a new structure for the StringIndex. Moved the commit + /// logs into the Realm file. Changes to the transaction log format + /// including reshuffling instructions. This is the format used in + /// milestone 2.0.0. + /// + /// IMPORTANT: When introducing a new file format version, be sure to review + /// the file validity checks in AllocSlab::validate_buffer(), the file + /// format selection logic in + /// Group::get_target_file_format_version_for_session(), and the file format + /// upgrade logic in Group::upgrade_file_format(). + int get_file_format_version() const noexcept; + +protected: + size_t m_baseline = 0; // Separation line between immutable and mutable refs. + + Replication* m_replication = nullptr; + + /// See get_file_format_version(). + int m_file_format_version = 0; + + ref_type m_debug_watch = 0; + + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// \throw std::bad_alloc If insufficient memory was available. + virtual MemRef do_alloc(size_t size) = 0; + + /// The specified size must be divisible by 8, and must not be + /// zero. + /// + /// The default version of this function simply allocates a new + /// chunk of memory, copies over the old contents, and then frees + /// the old chunk. + /// + /// \throw std::bad_alloc If insufficient memory was available. + virtual MemRef do_realloc(ref_type, const char* addr, size_t old_size, size_t new_size) = 0; + + /// Release the specified chunk of memory. + virtual void do_free(ref_type, const char* addr) noexcept = 0; + + /// Map the specified \a ref to the corresponding memory + /// address. Note that if is_read_only(ref) returns true, then the + /// referenced object is to be considered immutable, and it is + /// then entirely the responsibility of the caller that the memory + /// is not modified by way of the returned memory pointer. + virtual char* do_translate(ref_type ref) const noexcept = 0; + + Allocator() noexcept; + + // FIXME: This really doesn't belong in an allocator, but it is the best + // place for now, because every table has a pointer leading here. It would + // be more obvious to place it in Group, but that would add a runtime overhead, + // and access is time critical. + // + // This means that multiple threads that allocate Realm objects through the + // default allocator will share this variable, which is a logical design flaw + // that can make sync_if_needed() re-run queries even though it is not required. + // It must be atomic because it's shared. + std::atomic m_table_versioning_counter; + + /// Bump the global version counter. This method should be called when + /// version bumping is initiated. Then following calls to should_propagate_version() + /// can be used to prune the version bumping. + uint_fast64_t bump_global_version() noexcept; + + /// Determine if the "local_version" is out of sync, so that it should + /// be updated. In that case: also update it. Called from Table::bump_version + /// to control propagation of version updates on tables within the group. + bool should_propagate_version(uint_fast64_t& local_version) noexcept; + + friend class Table; + friend class Group; +}; + +inline uint_fast64_t Allocator::bump_global_version() noexcept +{ + ++m_table_versioning_counter; + return m_table_versioning_counter; +} + + +inline bool Allocator::should_propagate_version(uint_fast64_t& local_version) noexcept +{ + if (local_version != m_table_versioning_counter) { + local_version = m_table_versioning_counter; + return true; + } + else { + return false; + } +} + + +// Implementation: + +inline int_fast64_t from_ref(ref_type v) noexcept +{ + // Check that v is divisible by 8 (64-bit aligned). + REALM_ASSERT_DEBUG(v % 8 == 0); + return util::from_twos_compl(v); +} + +inline ref_type to_ref(int_fast64_t v) noexcept +{ + REALM_ASSERT_DEBUG(!util::int_cast_has_overflow(v)); + // Check that v is divisible by 8 (64-bit aligned). + REALM_ASSERT_DEBUG(v % 8 == 0); + return ref_type(v); +} + +inline int64_t to_int64(size_t value) noexcept +{ + // FIXME: Enable once we get clang warning flags correct + // REALM_ASSERT_DEBUG(value <= std::numeric_limits::max()); + return static_cast(value); +} + + +inline MemRef::MemRef() noexcept + : m_addr(nullptr) + , m_ref(0) +{ +} + +inline MemRef::~MemRef() noexcept +{ +} + +inline MemRef::MemRef(char* addr, ref_type ref, Allocator& alloc) noexcept + : m_addr(addr) + , m_ref(ref) +{ + static_cast(alloc); +#if REALM_ENABLE_MEMDEBUG + m_alloc = &alloc; +#endif +} + +inline MemRef::MemRef(ref_type ref, Allocator& alloc) noexcept + : m_addr(alloc.translate(ref)) + , m_ref(ref) +{ + static_cast(alloc); +#if REALM_ENABLE_MEMDEBUG + m_alloc = &alloc; +#endif +} + +inline char* MemRef::get_addr() +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(m_ref); +#endif + return m_addr; +} + +inline ref_type MemRef::get_ref() +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(m_ref); +#endif + return m_ref; +} + +inline void MemRef::set_ref(ref_type ref) +{ +#if REALM_ENABLE_MEMDEBUG + // Asserts if the ref has been freed + m_alloc->translate(ref); +#endif + m_ref = ref; +} + +inline void MemRef::set_addr(char* addr) +{ + m_addr = addr; +} + +inline MemRef Allocator::alloc(size_t size) +{ + return do_alloc(size); +} + +inline MemRef Allocator::realloc_(ref_type ref, const char* addr, size_t old_size, size_t new_size) +{ +#ifdef REALM_DEBUG + if (ref == m_debug_watch) + REALM_TERMINATE("Allocator watch: Ref was reallocated"); +#endif + return do_realloc(ref, addr, old_size, new_size); +} + +inline void Allocator::free_(ref_type ref, const char* addr) noexcept +{ +#ifdef REALM_DEBUG + if (ref == m_debug_watch) + REALM_TERMINATE("Allocator watch: Ref was freed"); +#endif + return do_free(ref, addr); +} + +inline void Allocator::free_(MemRef mem) noexcept +{ + free_(mem.get_ref(), mem.get_addr()); +} + +inline char* Allocator::translate(ref_type ref) const noexcept +{ + return do_translate(ref); +} + +inline bool Allocator::is_read_only(ref_type ref) const noexcept +{ + REALM_ASSERT_DEBUG(ref != 0); + REALM_ASSERT_DEBUG(m_baseline != 0); // Attached SlabAlloc + return ref < m_baseline; +} + +inline Allocator::Allocator() noexcept +{ + m_table_versioning_counter = 0; +} + +inline Allocator::~Allocator() noexcept +{ +} + +inline Replication* Allocator::get_replication() noexcept +{ + return m_replication; +} + +inline int Allocator::get_file_format_version() const noexcept +{ + return m_file_format_version; +} + + +} // namespace realm + +#endif // REALM_ALLOC_HPP diff --git a/Pods/Realm/include/core/realm/alloc_slab.hpp b/Pods/Realm/include/core/realm/alloc_slab.hpp new file mode 100644 index 0000000..b80406d --- /dev/null +++ b/Pods/Realm/include/core/realm/alloc_slab.hpp @@ -0,0 +1,590 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ALLOC_SLAB_HPP +#define REALM_ALLOC_SLAB_HPP + +#include // unint8_t etc +#include +#include +#include + +#include +#include +#include +#include + +namespace realm { + +// Pre-declarations +class Group; +class GroupWriter; + + +/// Thrown by Group and SharedGroup constructors if the specified file +/// (or memory buffer) does not appear to contain a valid Realm +/// database. +struct InvalidDatabase; + + +/// The allocator that is used to manage the memory of a Realm +/// group, i.e., a Realm database. +/// +/// Optionally, it can be attached to an pre-existing database (file +/// or memory buffer) which then becomes an immuatble part of the +/// managed memory. +/// +/// To attach a slab allocator to a pre-existing database, call +/// attach_file() or attach_buffer(). To create a new database +/// in-memory, call attach_empty(). +/// +/// For efficiency, this allocator manages its mutable memory as a set +/// of slabs. +class SlabAlloc : public Allocator { +public: + ~SlabAlloc() noexcept override; + SlabAlloc(); + + /// \struct Config + /// \brief Storage for combining setup flags for initialization to + /// the SlabAlloc. + /// + /// \var Config::is_shared + /// Must be true if, and only if we are called on behalf of SharedGroup. + /// + /// \var Config::read_only + /// Open the file in read-only mode. This implies \a Config::no_create. + /// + /// \var Config::no_create + /// Fail if the file does not already exist. + /// + /// \var Config::skip_validate + /// Skip validation of file header. In a + /// set of overlapping SharedGroups, only the first one (the one + /// that creates/initlializes the coordination file) may validate + /// the header, otherwise it will result in a race condition. + /// + /// \var Config::encryption_key + /// 32-byte key to use to encrypt and decrypt the backing storage, + /// or nullptr to disable encryption. + /// + /// \var Config::session_initiator + /// If set, the caller is the session initiator and + /// guarantees exclusive access to the file. If attaching in + /// read/write mode, the file is modified: files on streaming form + /// is changed to non-streaming form, and if needed the file size + /// is adjusted to match mmap boundaries. + /// Must be set to false if is_shared is false. + /// + /// \var Config::clear_file + /// Always initialize the file as if it was a newly + /// created file and ignore any pre-existing contents. Requires that + /// Config::session_initiator be true as well. + struct Config { + bool is_shared = false; + bool read_only = false; + bool no_create = false; + bool skip_validate = false; + bool session_initiator = false; + bool clear_file = false; + const char* encryption_key = nullptr; + }; + + struct Retry { + }; + + /// \brief Attach this allocator to the specified file. + /// + /// It is an error if this function is called at a time where the specified + /// Realm file (file system inode) is modified asynchronously. + /// + /// In non-shared mode (when this function is called on behalf of a + /// free-standing Group instance), it is the responsibility of the + /// application to ensure that the Realm file is not modified concurrently + /// from any other thread or process. + /// + /// In shared mode (when this function is called on behalf of a SharedGroup + /// instance), the caller (SharedGroup::do_open()) must take steps to ensure + /// cross-process mutual exclusion. + /// + /// If the attached file contains an empty Realm (one whose top-ref is + /// zero), the file format version may remain undecided upon return from + /// this function. The file format is undecided if, and only if + /// get_file_format_version() returns zero. The caller is required to check + /// for this case, and decide on a file format version. This must happen + /// before the Realm opening process completes, and the decided file format + /// must be set in the allocator by calling set_file_format_version(). + /// + /// Except for \a path, the parameters are passed in through a + /// configuration object. + /// + /// \return The `ref` of the root node, or zero if there is none. + /// + /// Please note that attach_file can fail to attach to a file due to a + /// collision with a writer extending the file. This can only happen if the + /// caller is *not* the session initiator. When this happens, attach_file() + /// throws SlabAlloc::Retry, and the caller must retry the call. The caller + /// should check if it has become the session initiator before retrying. + /// This can happen if the conflicting thread (or process) terminates or + /// crashes before the next retry. + /// + /// \throw util::File::AccessError + /// \throw SlabAlloc::Retry + ref_type attach_file(const std::string& path, Config& cfg); + + /// Get the attached file. Only valid when called on an allocator with + /// an attached file. + util::File& get_file(); + + /// Attach this allocator to the specified memory buffer. + /// + /// If the attached buffer contains an empty Realm (one whose top-ref is + /// zero), the file format version may remain undecided upon return from + /// this function. The file format is undecided if, and only if + /// get_file_format_version() returns zero. The caller is required to check + /// for this case, and decide on a file format version. This must happen + /// before the Realm opening process completes, and the decided file format + /// must be set in the allocator by calling set_file_format_version(). + /// + /// It is an error to call this function on an attached + /// allocator. Doing so will result in undefined behavor. + /// + /// \return The `ref` of the root node, or zero if there is none. + /// + /// \sa own_buffer() + /// + /// \throw InvalidDatabase + ref_type attach_buffer(const char* data, size_t size); + + /// Reads file format from file header. Must be called from within a write + /// transaction. + int get_committed_file_format_version() const noexcept; + + /// Attach this allocator to an empty buffer. + /// + /// Upon return from this function, the file format is undecided + /// (get_file_format_version() returns zero). The caller is required to + /// decide on a file format version. This must happen before the Realm + /// opening process completes, and the decided file format must be set in + /// the allocator by calling set_file_format_version(). + /// + /// It is an error to call this function on an attached + /// allocator. Doing so will result in undefined behavor. + void attach_empty(); + + /// Detach from a previously attached file or buffer. + /// + /// This function does not reset free space tracking. To + /// completely reset the allocator, you must also call + /// reset_free_space_tracking(). + /// + /// This function has no effect if the allocator is already in the + /// detached state (idempotency). + void detach() noexcept; + + class DetachGuard; + + /// If a memory buffer has been attached using attach_buffer(), + /// mark it as owned by this slab allocator. Behaviour is + /// undefined if this function is called on a detached allocator, + /// one that is not attached using attach_buffer(), or one for + /// which this function has already been called during the latest + /// attachment. + void own_buffer() noexcept; + + /// Returns true if, and only if this allocator is currently + /// in the attached state. + bool is_attached() const noexcept; + + /// Returns true if, and only if this allocator is currently in + /// the attached state and attachment was not established using + /// attach_empty(). + bool nonempty_attachment() const noexcept; + + /// Reserve disk space now to avoid allocation errors at a later + /// point in time, and to minimize on-disk fragmentation. In some + /// cases, less fragmentation translates into improved + /// performance. On flash or SSD-drives this is likely a waste. + /// + /// Note: File::prealloc() may misbehave under race conditions (see + /// documentation of File::prealloc()). For that reason, to avoid race + /// conditions, when this allocator is used in a transactional mode, this + /// function may be called only when the caller has exclusive write + /// access. In non-transactional mode it is the responsibility of the user + /// to ensure non-concurrent file mutation. + /// + /// This function will call File::sync(). + /// + /// It is an error to call this function on an allocator that is not + /// attached to a file. Doing so will result in undefined behavior. + void resize_file(size_t new_file_size); + + /// Reserve disk space now to avoid allocation errors at a later point in + /// time, and to minimize on-disk fragmentation. In some cases, less + /// fragmentation translates into improved performance. On SSD-drives + /// preallocation is likely a waste. + /// + /// When supported by the system, a call to this function will make the + /// database file at least as big as the specified size, and cause space on + /// the target device to be allocated (note that on many systems on-disk + /// allocation is done lazily by default). If the file is already bigger + /// than the specified size, the size will be unchanged, and on-disk + /// allocation will occur only for the initial section that corresponds to + /// the specified size. On systems that do not support preallocation, this + /// function has no effect. To know whether preallocation is supported by + /// Realm on your platform, call util::File::is_prealloc_supported(). + /// + /// This function will call File::sync() if it changes the size of the file. + /// + /// It is an error to call this function on an allocator that is not + /// attached to a file. Doing so will result in undefined behavior. + void reserve_disk_space(size_t size_in_bytes); + + /// Get the size of the attached database file or buffer in number + /// of bytes. This size is not affected by new allocations. After + /// attachment, it can only be modified by a call to remap(). + /// + /// It is an error to call this function on a detached allocator, + /// or one that was attached using attach_empty(). Doing so will + /// result in undefined behavior. + size_t get_baseline() const noexcept; + + /// Get the total amount of managed memory. This is the baseline plus the + /// sum of the sizes of the allocated slabs. It includes any free space. + /// + /// It is an error to call this function on a detached + /// allocator. Doing so will result in undefined behavior. + size_t get_total_size() const noexcept; + + /// Mark all mutable memory (ref-space outside the attached file) as free + /// space. + void reset_free_space_tracking(); + + /// Remap the attached file such that a prefix of the specified + /// size becomes available in memory. If sucessfull, + /// get_baseline() will return the specified new file size. + /// + /// It is an error to call this function on a detached allocator, + /// or one that was not attached using attach_file(). Doing so + /// will result in undefined behavior. + /// + /// The file_size argument must be aligned to a *section* boundary: + /// The database file is logically split into sections, each section + /// guaranteed to be mapped as a contiguous address range. The allocation + /// of memory in the file must ensure that no allocation crosses the + /// boundary between two sections. + void remap(size_t file_size); + + /// Returns true initially, and after a call to reset_free_space_tracking() + /// up until the point of the first call to SlabAlloc::alloc(). Note that a + /// call to SlabAlloc::alloc() corresponds to a mutation event. + bool is_free_space_clean() const noexcept; + + /// \brief Update the file format version field of the allocator. + /// + /// This must be done during the opening of the Realm if the stored file + /// format version is zero (empty Realm), or after the file format is + /// upgraded. + /// + /// Note that this does not modify the attached file, only the "cached" + /// value subsequenty returned by get_file_format_version(). + /// + /// \sa get_file_format_version() + void set_file_format_version(int) noexcept; + + void verify() const override; +#ifdef REALM_DEBUG + void enable_debug(bool enable) + { + m_debug_out = enable; + } + bool is_all_free() const; + void print() const; +#endif + struct MappedFile; + +protected: + MemRef do_alloc(const size_t size) override; + MemRef do_realloc(ref_type, const char*, size_t old_size, size_t new_size) override; + // FIXME: It would be very nice if we could detect an invalid free operation in debug mode + void do_free(ref_type, const char*) noexcept override; + char* do_translate(ref_type) const noexcept override; + void invalidate_cache() noexcept; + +private: + enum AttachMode { + attach_None, // Nothing is attached + attach_OwnedBuffer, // We own the buffer (m_data = nullptr for empty buffer) + attach_UsersBuffer, // We do not own the buffer + attach_SharedFile, // On behalf of SharedGroup + attach_UnsharedFile // Not on behalf of SharedGroup + }; + + // A slab is a dynamically allocated contiguous chunk of memory used to + // extend the amount of space available for database node + // storage. Inter-node references are represented as file offsets + // (a.k.a. "refs"), and each slab creates an apparently seamless extension + // of this file offset addressable space. Slabes are stored as rows in the + // Slabs table in order of ascending file offsets. + struct Slab { + ref_type ref_end; + char* addr; + }; + struct Chunk { + ref_type ref; + size_t size; + }; + + // Values of each used bit in m_flags + enum { + flags_SelectBit = 1, + }; + + // 24 bytes + struct Header { + uint64_t m_top_ref[2]; // 2 * 8 bytes + // Info-block 8-bytes + uint8_t m_mnemonic[4]; // "T-DB" + uint8_t m_file_format[2]; // See `library_file_format` + uint8_t m_reserved; + // bit 0 of m_flags is used to select between the two top refs. + uint8_t m_flags; + }; + + // 16 bytes + struct StreamingFooter { + uint64_t m_top_ref; + uint64_t m_magic_cookie; + }; + + static_assert(sizeof(Header) == 24, "Bad header size"); + static_assert(sizeof(StreamingFooter) == 16, "Bad footer size"); + + static const Header empty_file_header; + static void init_streaming_header(Header*, int file_format_version); + + static const uint_fast64_t footer_magic_cookie = 0x3034125237E526C8ULL; + + // The mappings are shared, if they are from a file + std::shared_ptr m_file_mappings; + + // We are caching local copies of all the additional mappings to allow + // for lock-free lookup during ref->address translation (we do not need + // to cache the first mapping, because it is immutable) (well, all the + // mappings are immutable, but the array holding them is not - it may + // have to be relocated) + std::unique_ptr>[]> m_local_mappings; + size_t m_num_local_mappings = 0; + + const char* m_data = nullptr; + size_t m_initial_chunk_size = 0; + size_t m_initial_section_size = 0; + int m_section_shifts = 0; + std::unique_ptr m_section_bases; + size_t m_num_section_bases = 0; + AttachMode m_attach_mode = attach_None; + bool m_file_on_streaming_form = false; + enum FeeeSpaceState { + free_space_Clean, + free_space_Dirty, + free_space_Invalid, + }; + + /// When set to free_space_Invalid, the free lists are no longer + /// up-to-date. This happens if do_free() or + /// reset_free_space_tracking() fails, presumably due to + /// std::bad_alloc being thrown during updating of the free space + /// list. In this this case, alloc(), realloc_(), and + /// get_free_read_only() must throw. This member is deliberately + /// placed here (after m_attach_mode) in the hope that it leads to + /// less padding between members due to alignment requirements. + FeeeSpaceState m_free_space_state = free_space_Clean; + + typedef std::vector slabs; + typedef std::vector chunks; + slabs m_slabs; + chunks m_free_space; + chunks m_free_read_only; + + bool m_debug_out = false; + struct hash_entry { + ref_type ref = 0; + const char* addr = nullptr; + size_t version = 0; + }; + mutable hash_entry cache[256]; + mutable size_t version = 1; + + /// Throws if free-lists are no longer valid. + const chunks& get_free_read_only() const; + + /// Throws InvalidDatabase if the file is not a Realm file, if the file is + /// corrupted, or if the specified encryption key is incorrect. This + /// function will not detect all forms of corruption, though. + void validate_buffer(const char* data, size_t len, const std::string& path, bool is_shared); + + /// Read the top_ref from the given buffer and set m_file_on_streaming_form + /// if the buffer contains a file in streaming form + ref_type get_top_ref(const char* data, size_t len); + + class ChunkRefEq; + class ChunkRefEndEq; + class SlabRefEndEq; + static bool ref_less_than_slab_ref_end(ref_type, const Slab&) noexcept; + + Replication* get_replication() const noexcept + { + return m_replication; + } + void set_replication(Replication* r) noexcept + { + m_replication = r; + } + + /// Returns the first section boundary *above* the given position. + size_t get_upper_section_boundary(size_t start_pos) const noexcept; + + /// Returns the first section boundary *at or below* the given position. + size_t get_lower_section_boundary(size_t start_pos) const noexcept; + + /// Returns true if the given position is at a section boundary + bool matches_section_boundary(size_t pos) const noexcept; + + /// Returns the index of the section holding a given address. + /// The section index is determined solely by the minimal section size, + /// and does not necessarily reflect the mapping. A mapping may + /// cover multiple sections - the initial mapping often does. + size_t get_section_index(size_t pos) const noexcept; + + /// Reverse: get the base offset of a section at a given index. Since the + /// computation is very time critical, this method just looks it up in + /// a table. The actual computation and setup of that table is done + /// during initialization with the help of compute_section_base() below. + inline size_t get_section_base(size_t index) const noexcept; + + /// Actually compute the starting offset of a section. Only used to initialize + /// a table of predefined results, which are then used by get_section_base(). + size_t compute_section_base(size_t index) const noexcept; + + /// Find a possible allocation of 'request_size' that will fit into a section + /// which is inside the range from 'start_pos' to 'start_pos'+'free_chunk_size' + /// If found return the position, if not return 0. + size_t find_section_in_range(size_t start_pos, size_t free_chunk_size, size_t request_size) const noexcept; + + friend class Group; + friend class GroupWriter; +}; + +inline void SlabAlloc::invalidate_cache() noexcept +{ + ++version; +} + +class SlabAlloc::DetachGuard { +public: + DetachGuard(SlabAlloc& alloc) noexcept + : m_alloc(&alloc) + { + } + ~DetachGuard() noexcept; + SlabAlloc* release() noexcept; + +private: + SlabAlloc* m_alloc; +}; + + +// Implementation: + +struct InvalidDatabase : util::File::AccessError { + InvalidDatabase(const std::string& msg, const std::string& path) + : util::File::AccessError(msg, path) + { + } +}; + +inline void SlabAlloc::own_buffer() noexcept +{ + REALM_ASSERT_3(m_attach_mode, ==, attach_UsersBuffer); + REALM_ASSERT(m_data); + REALM_ASSERT(m_file_mappings == nullptr); + m_attach_mode = attach_OwnedBuffer; +} + +inline bool SlabAlloc::is_attached() const noexcept +{ + return m_attach_mode != attach_None; +} + +inline bool SlabAlloc::nonempty_attachment() const noexcept +{ + return is_attached() && m_data; +} + +inline size_t SlabAlloc::get_baseline() const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + return m_baseline; +} + +inline bool SlabAlloc::is_free_space_clean() const noexcept +{ + return m_free_space_state == free_space_Clean; +} + +inline SlabAlloc::DetachGuard::~DetachGuard() noexcept +{ + if (m_alloc) + m_alloc->detach(); +} + +inline SlabAlloc* SlabAlloc::DetachGuard::release() noexcept +{ + SlabAlloc* alloc = m_alloc; + m_alloc = nullptr; + return alloc; +} + +inline bool SlabAlloc::ref_less_than_slab_ref_end(ref_type ref, const Slab& slab) noexcept +{ + return ref < slab.ref_end; +} + +inline size_t SlabAlloc::get_upper_section_boundary(size_t start_pos) const noexcept +{ + return get_section_base(1 + get_section_index(start_pos)); +} + +inline size_t SlabAlloc::get_lower_section_boundary(size_t start_pos) const noexcept +{ + return get_section_base(get_section_index(start_pos)); +} + +inline bool SlabAlloc::matches_section_boundary(size_t pos) const noexcept +{ + return pos == get_lower_section_boundary(pos); +} + +inline size_t SlabAlloc::get_section_base(size_t index) const noexcept +{ + return m_section_bases[index]; +} + +} // namespace realm + +#endif // REALM_ALLOC_SLAB_HPP diff --git a/Pods/Realm/include/core/realm/array.hpp b/Pods/Realm/include/core/realm/array.hpp new file mode 100644 index 0000000..104ea6a --- /dev/null +++ b/Pods/Realm/include/core/realm/array.hpp @@ -0,0 +1,3472 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +/* +Searching: The main finding function is: + template + void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState *state, Callback callback) const + + cond: One of Equal, NotEqual, Greater, etc. classes + Action: One of act_ReturnFirst, act_FindAll, act_Max, act_CallbackIdx, etc, constants + Callback: Optional function to call for each search result. Will be called if action == act_CallbackIdx + + find() will call find_action_pattern() or find_action() that again calls match() for each search result which + optionally calls callback(): + + find() -> find_action() -------> bool match() -> bool callback() + | ^ + +-> find_action_pattern()----+ + + If callback() returns false, find() will exit, otherwise it will keep searching remaining items in array. +*/ + +#ifndef REALM_ARRAY_HPP +#define REALM_ARRAY_HPP + +#include +#include // size_t +#include +#include +#include +#include + +#include // unint8_t etc + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + MMX: mmintrin.h + SSE: xmmintrin.h + SSE2: emmintrin.h + SSE3: pmmintrin.h + SSSE3: tmmintrin.h + SSE4A: ammintrin.h + SSE4.1: smmintrin.h + SSE4.2: nmmintrin.h +*/ +#ifdef REALM_COMPILER_SSE +#include // SSE2 +#include // SSE42 +#endif + +namespace realm { + +enum Action { + act_ReturnFirst, + act_Sum, + act_Max, + act_Min, + act_Count, + act_FindAll, + act_CallIdx, + act_CallbackIdx, + act_CallbackVal, + act_CallbackNone, + act_CallbackBoth, + act_Average +}; + +template +inline T no0(T v) +{ + return v == 0 ? 1 : v; +} + +/// Special index value. It has various meanings depending on +/// context. It is returned by some search functions to indicate 'not +/// found'. It is similar in function to std::string::npos. +const size_t npos = size_t(-1); + +// Maximum number of bytes that the payload of an array can be +const size_t max_array_payload = 0x00ffffffL; + +/// Alias for realm::npos. +const size_t not_found = npos; + +// clang-format off +/* wid == 16/32 likely when accessing offsets in B tree */ +#define REALM_TEMPEX(fun, wid, arg) \ + if (wid == 16) {fun<16> arg;} \ + else if (wid == 32) {fun<32> arg;} \ + else if (wid == 0) {fun<0> arg;} \ + else if (wid == 1) {fun<1> arg;} \ + else if (wid == 2) {fun<2> arg;} \ + else if (wid == 4) {fun<4> arg;} \ + else if (wid == 8) {fun<8> arg;} \ + else if (wid == 64) {fun<64> arg;} \ + else {REALM_ASSERT_DEBUG(false); fun<0> arg;} + +#define REALM_TEMPEX2(fun, targ, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX3(fun, targ1, targ2, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX4(fun, targ1, targ2, wid, targ3, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} + +#define REALM_TEMPEX5(fun, targ1, targ2, targ3, targ4, wid, arg) \ + if (wid == 16) {fun arg;} \ + else if (wid == 32) {fun arg;} \ + else if (wid == 0) {fun arg;} \ + else if (wid == 1) {fun arg;} \ + else if (wid == 2) {fun arg;} \ + else if (wid == 4) {fun arg;} \ + else if (wid == 8) {fun arg;} \ + else if (wid == 64) {fun arg;} \ + else {REALM_ASSERT_DEBUG(false); fun arg;} +// clang-format on + + +// Pre-definitions +class Array; +class StringColumn; +class GroupWriter; +template +class QueryState; +namespace _impl { +class ArrayWriterBase; +} + + +#ifdef REALM_DEBUG +struct MemStats { + size_t allocated = 0; + size_t used = 0; + size_t array_count = 0; +}; +template +std::basic_ostream& operator<<(std::basic_ostream& out, MemStats stats); +#endif + + +// Stores a value obtained from Array::get(). It is a ref if the least +// significant bit is clear, otherwise it is a tagged integer. A tagged interger +// is obtained from a logical integer value by left shifting by one bit position +// (multiplying by two), and then setting the least significant bit to +// one. Clearly, this means that the maximum value that can be stored as a +// tagged integer is 2**63 - 1. +class RefOrTagged { +public: + bool is_ref() const noexcept; + bool is_tagged() const noexcept; + ref_type get_as_ref() const noexcept; + uint_fast64_t get_as_int() const noexcept; + + static RefOrTagged make_ref(ref_type) noexcept; + static RefOrTagged make_tagged(uint_fast64_t) noexcept; + +private: + int_fast64_t m_value; + RefOrTagged(int_fast64_t) noexcept; + friend class Array; +}; + + +class ArrayParent { +public: + virtual ~ArrayParent() noexcept + { + } + +protected: + virtual void update_child_ref(size_t child_ndx, ref_type new_ref) = 0; + + virtual ref_type get_child_ref(size_t child_ndx) const noexcept = 0; + + // Used only by Array::to_dot(). + virtual std::pair get_to_dot_parent(size_t ndx_in_parent) const = 0; + + friend class Array; +}; + + +/// Provides access to individual array nodes of the database. +/// +/// This class serves purely as an accessor, it assumes no ownership of the +/// referenced memory. +/// +/// An array accessor can be in one of two states: attached or unattached. It is +/// in the attached state if, and only if is_attached() returns true. Most +/// non-static member functions of this class have undefined behaviour if the +/// accessor is in the unattached state. The exceptions are: is_attached(), +/// detach(), create(), init_from_ref(), init_from_mem(), init_from_parent(), +/// has_parent(), get_parent(), set_parent(), get_ndx_in_parent(), +/// set_ndx_in_parent(), adjust_ndx_in_parent(), and get_ref_from_parent(). +/// +/// An array accessor contains information about the parent of the referenced +/// array node. This 'reverse' reference is not explicitely present in the +/// underlying node hierarchy, but it is needed when modifying an array. A +/// modification may lead to relocation of the underlying array node, and the +/// parent must be updated accordingly. Since this applies recursivly all the +/// way to the root node, it is essential that the entire chain of parent +/// accessors is constructed and propperly maintained when a particular array is +/// modified. +/// +/// The parent reference (`pointer to parent`, `index in parent`) is updated +/// independently from the state of attachment to an underlying node. In +/// particular, the parent reference remains valid and is unannfected by changes +/// in attachment. These two aspects of the state of the accessor is updated +/// independently, and it is entirely the responsibility of the caller to update +/// them such that they are consistent with the underlying node hierarchy before +/// calling any method that modifies the underlying array node. +/// +/// FIXME: This class currently has fragments of ownership, in particular the +/// constructors that allocate underlying memory. On the other hand, the +/// destructor never frees the memory. This is a problematic situation, because +/// it so easily becomes an obscure source of leaks. There are three options for +/// a fix of which the third is most attractive but hardest to implement: (1) +/// Remove all traces of ownership semantics, that is, remove the constructors +/// that allocate memory, but keep the trivial copy constructor. For this to +/// work, it is important that the constness of the accessor has nothing to do +/// with the constness of the underlying memory, otherwise constness can be +/// violated simply by copying the accessor. (2) Disallov copying but associate +/// the constness of the accessor with the constness of the underlying +/// memory. (3) Provide full ownership semantics like is done for Table +/// accessors, and provide a proper copy constructor that really produces a copy +/// of the array. For this to work, the class should assume ownership if, and +/// only if there is no parent. A copy produced by a copy constructor will not +/// have a parent. Even if the original was part of a database, the copy will be +/// free-standing, that is, not be part of any database. For intra, or inter +/// database copying, one would have to also specify the target allocator. +class Array : public ArrayParent { +public: + // void state_init(int action, QueryState *state); + // bool match(int action, size_t index, int64_t value, QueryState *state); + + /// Create an array accessor in the unattached state. + explicit Array(Allocator&) noexcept; + + ~Array() noexcept override + { + } + + enum Type { + type_Normal, + + /// This array is the main array of an innner node of a B+-tree as used + /// in table columns. + type_InnerBptreeNode, + + /// This array may contain refs to subarrays. An element whose least + /// significant bit is zero, is a ref pointing to a subarray. An element + /// whose least significant bit is one, is just a value. It is the + /// responsibility of the application to ensure that non-ref values have + /// their least significant bit set. This will generally be done by + /// shifting the desired vlue to the left by one bit position, and then + /// setting the vacated bit to one. + type_HasRefs + }; + + /// Create a new integer array of the specified type and size, and filled + /// with the specified value, and attach this accessor to it. This does not + /// modify the parent reference information of this accessor. + /// + /// Note that the caller assumes ownership of the allocated underlying + /// node. It is not owned by the accessor. + void create(Type, bool context_flag = false, size_t size = 0, int_fast64_t value = 0); + + /// Reinitialize this array accessor to point to the specified new + /// underlying memory. This does not modify the parent reference information + /// of this accessor. + void init_from_ref(ref_type) noexcept; + + /// Same as init_from_ref(ref_type) but avoid the mapping of 'ref' to memory + /// pointer. + void init_from_mem(MemRef) noexcept; + + /// Same as `init_from_ref(get_ref_from_parent())`. + void init_from_parent() noexcept; + + /// Update the parents reference to this child. This requires, of course, + /// that the parent information stored in this child is up to date. If the + /// parent pointer is set to null, this function has no effect. + void update_parent(); + + /// Called in the context of Group::commit() to ensure that attached + /// accessors stay valid across a commit. Please note that this works only + /// for non-transactional commits. Accessors obtained during a transaction + /// are always detached when the transaction ends. + /// + /// Returns true if, and only if the array has changed. If the array has not + /// changed, then its children are guaranteed to also not have changed. + bool update_from_parent(size_t old_baseline) noexcept; + + /// Change the type of an already attached array node. + /// + /// The effect of calling this function on an unattached accessor is + /// undefined. + void set_type(Type); + + /// Construct a complete copy of this array (including its subarrays) using + /// the specified target allocator and return just the reference to the + /// underlying memory. + MemRef clone_deep(Allocator& target_alloc) const; + + /// Construct an empty integer array of the specified type, and return just + /// the reference to the underlying memory. + static MemRef create_empty_array(Type, bool context_flag, Allocator&); + + /// Construct an integer array of the specified type and size, and return + /// just the reference to the underlying memory. All elements will be + /// initialized to the specified value. + static MemRef create_array(Type, bool context_flag, size_t size, int_fast64_t value, Allocator&); + + /// Construct a shallow copy of the specified slice of this array using the + /// specified target allocator. Subarrays will **not** be cloned. See + /// slice_and_clone_children() for an alternative. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + /// Construct a deep copy of the specified slice of this array using the + /// specified target allocator. Subarrays will be cloned. + MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + // Parent tracking + bool has_parent() const noexcept; + ArrayParent* get_parent() const noexcept; + + /// Setting a new parent affects ownership of the attached array node, if + /// any. If a non-null parent is specified, and there was no parent + /// originally, then the caller passes ownership to the parent, and vice + /// versa. This assumes, of course, that the change in parentship reflects a + /// corresponding change in the list of children in the affected parents. + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t) noexcept; + void adjust_ndx_in_parent(int diff) noexcept; + + /// Get the ref of this array as known to the parent. The caller must ensure + /// that the parent information ('pointer to parent' and 'index in parent') + /// is correct before calling this function. + ref_type get_ref_from_parent() const noexcept; + + bool is_attached() const noexcept; + + /// Detach from the underlying array node. This method has no effect if the + /// accessor is currently unattached (idempotency). + void detach() noexcept; + + size_t size() const noexcept; + bool is_empty() const noexcept; + Type get_type() const noexcept; + + static void add_to_column(IntegerColumn* column, int64_t value); + + void insert(size_t ndx, int_fast64_t value); + void add(int_fast64_t value); + + /// This function is guaranteed to not throw if the current width is + /// sufficient for the specified value (e.g. if you have called + /// ensure_minimum_width(value)) and get_alloc().is_read_only(get_ref()) + /// returns false (noexcept:array-set). Note that for a value of zero, the + /// first criterion is trivially satisfied. + void set(size_t ndx, int64_t value); + + void set_as_ref(size_t ndx, ref_type ref); + + template + void set(size_t ndx, int64_t value); + + int64_t get(size_t ndx) const noexcept; + + template + int64_t get(size_t ndx) const noexcept; + + void get_chunk(size_t ndx, int64_t res[8]) const noexcept; + + template + void get_chunk(size_t ndx, int64_t res[8]) const noexcept; + + ref_type get_as_ref(size_t ndx) const noexcept; + + RefOrTagged get_as_ref_or_tagged(size_t ndx) const noexcept; + void set(size_t ndx, RefOrTagged); + void add(RefOrTagged); + void ensure_minimum_width(RefOrTagged); + + int64_t front() const noexcept; + int64_t back() const noexcept; + + /// Remove the element at the specified index, and move elements at higher + /// indexes to the next lower index. + /// + /// This function does **not** destroy removed subarrays. That is, if the + /// erased element is a 'ref' pointing to a subarray, then that subarray + /// will not be destroyed automatically. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the + /// call. This is automatically guaranteed if the array is used in a + /// non-transactional context, or if the array has already been successfully + /// modified within the current write transaction. + void erase(size_t ndx); + + /// Same as erase(size_t), but remove all elements in the specified + /// range. + /// + /// Please note that this function does **not** destroy removed subarrays. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void erase(size_t begin, size_t end); + + /// Reduce the size of this array to the specified number of elements. It is + /// an error to specify a size that is greater than the current size of this + /// array. The effect of doing so is undefined. This is just a shorthand for + /// calling the ranged erase() function with appropriate arguments. + /// + /// Please note that this function does **not** destroy removed + /// subarrays. See clear_and_destroy_children() for an alternative. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void truncate(size_t new_size); + + /// Reduce the size of this array to the specified number of elements. It is + /// an error to specify a size that is greater than the current size of this + /// array. The effect of doing so is undefined. Subarrays will be destroyed + /// recursively, as if by a call to `destroy_deep(subarray_ref, alloc)`. + /// + /// This function is guaranteed not to throw if + /// get_alloc().is_read_only(get_ref()) returns false. + void truncate_and_destroy_children(size_t new_size); + + /// Remove every element from this array. This is just a shorthand for + /// calling truncate(0). + /// + /// Please note that this function does **not** destroy removed + /// subarrays. See clear_and_destroy_children() for an alternative. + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void clear(); + + /// Remove every element in this array. Subarrays will be destroyed + /// recursively, as if by a call to `destroy_deep(subarray_ref, + /// alloc)`. This is just a shorthand for calling + /// truncate_and_destroy_children(0). + /// + /// This function guarantees that no exceptions will be thrown if + /// get_alloc().is_read_only(get_ref()) would return false before the call. + void clear_and_destroy_children(); + + /// If neccessary, expand the representation so that it can store the + /// specified value. + void ensure_minimum_width(int_fast64_t value); + + typedef StringData (*StringGetter)(void*, size_t, char*); // Pre-declare getter function from string index + size_t index_string_find_first(StringData value, ColumnBase* column) const; + void index_string_find_all(IntegerColumn& result, StringData value, ColumnBase* column) const; + FindRes index_string_find_all_no_copy(StringData value, ColumnBase* column, InternalFindResult& result) const; + size_t index_string_count(StringData value, ColumnBase* column) const; + + /// This one may change the represenation of the array, so be carefull if + /// you call it after ensure_minimum_width(). + void set_all_to_zero(); + + /// Add \a diff to the element at the specified index. + void adjust(size_t ndx, int_fast64_t diff); + + /// Add \a diff to all the elements in the specified index range. + void adjust(size_t begin, size_t end, int_fast64_t diff); + + /// Add signed \a diff to all elements that are greater than, or equal to \a + /// limit. + void adjust_ge(int_fast64_t limit, int_fast64_t diff); + + //@{ + /// These are similar in spirit to std::move() and std::move_backward from + /// ``. \a dest_begin must not be in the range [`begin`,`end`), and + /// \a dest_end must not be in the range (`begin`,`end`]. + /// + /// These functions are guaranteed to not throw if + /// `get_alloc().is_read_only(get_ref())` returns false. + void move(size_t begin, size_t end, size_t dest_begin); + void move_backward(size_t begin, size_t end, size_t dest_end); + //@} + + /// move_rotate moves one element from \a from to be located at index \a to, + /// shifting all elements inbetween by one. + /// + /// If \a from is larger than \a to, the elements inbetween are shifted down. + /// If \a to is larger than \a from, the elements inbetween are shifted up. + /// + /// This function is guaranteed to not throw if + /// `get_alloc().is_read_only(get_ref())` returns false. + void move_rotate(size_t from, size_t to, size_t num_elems = 1); + + //@{ + /// Find the lower/upper bound of the specified value in a sequence of + /// integers which must already be sorted ascendingly. + /// + /// For an integer value '`v`', lower_bound_int(v) returns the index '`l`' + /// of the first element such that `get(l) ≥ v`, and upper_bound_int(v) + /// returns the index '`u`' of the first element such that `get(u) > + /// v`. In both cases, if no such element is found, the returned value is + /// the number of elements in the array. + /// + /// 3 3 3 4 4 4 5 6 7 9 9 9 + /// ^ ^ ^ ^ ^ + /// | | | | | + /// | | | | -- Lower and upper bound of 15 + /// | | | | + /// | | | -- Lower and upper bound of 8 + /// | | | + /// | | -- Upper bound of 4 + /// | | + /// | -- Lower bound of 4 + /// | + /// -- Lower and upper bound of 1 + /// + /// These functions are similar to std::lower_bound() and + /// std::upper_bound(). + /// + /// We currently use binary search. See for example + /// http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary. + /// + /// FIXME: It may be worth considering if overall efficiency can be improved + /// by doing a linear search for short sequences. + size_t lower_bound_int(int64_t value) const noexcept; + size_t upper_bound_int(int64_t value) const noexcept; + //@} + + /// \brief Search the \c Array for a value greater or equal than \a target, + /// starting the search at the \a start index. If \a indirection is + /// provided, use it as a look-up table to iterate over the \c Array. + /// + /// If \a indirection is not provided, then the \c Array must be sorted in + /// ascending order. If \a indirection is provided, then its values should + /// point to indices in this \c Array in such a way that iteration happens + /// in ascending order. + /// + /// Behaviour is undefined if: + /// - a value in \a indirection is out of bounds for this \c Array; + /// - \a indirection does not contain at least as many elements as this \c + /// Array; + /// - sorting conditions are not respected; + /// - \a start is greater than the number of elements in this \c Array or + /// \a indirection (if provided). + /// + /// \param target the smallest value to search for + /// \param start the offset at which to start searching in the array + /// \param indirection an \c Array containing valid indices of values in + /// this \c Array, sorted in ascending order + /// \return the index of the value if found, or realm::not_found otherwise + size_t find_gte(const int64_t target, size_t start, size_t end = size_t(-1)) const; + void preset(int64_t min, int64_t max, size_t num_items); + void preset(size_t bitwidth, size_t num_items); + + int64_t sum(size_t start = 0, size_t end = size_t(-1)) const; + size_t count(int64_t value) const noexcept; + + bool maximum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const; + + bool minimum(int64_t& result, size_t start = 0, size_t end = size_t(-1), size_t* return_ndx = nullptr) const; + + /// This information is guaranteed to be cached in the array accessor. + bool is_inner_bptree_node() const noexcept; + + /// Returns true if type is either type_HasRefs or type_InnerColumnNode. + /// + /// This information is guaranteed to be cached in the array accessor. + bool has_refs() const noexcept; + + /// This information is guaranteed to be cached in the array accessor. + /// + /// Columns and indexes can use the context bit to differentiate leaf types. + bool get_context_flag() const noexcept; + void set_context_flag(bool) noexcept; + + ref_type get_ref() const noexcept; + MemRef get_mem() const noexcept; + + /// Destroy only the array that this accessor is attached to, not the + /// children of that array. See non-static destroy_deep() for an + /// alternative. If this accessor is already in the detached state, this + /// function has no effect (idempotency). + void destroy() noexcept; + + /// Recursively destroy children (as if calling + /// clear_and_destroy_children()), then put this accessor into the detached + /// state (as if calling detach()), then free the allocated memory. If this + /// accessor is already in the detached state, this function has no effect + /// (idempotency). + void destroy_deep() noexcept; + + /// Shorthand for `destroy(MemRef(ref, alloc), alloc)`. + static void destroy(ref_type ref, Allocator& alloc) noexcept; + + /// Destroy only the specified array node, not its children. See also + /// destroy_deep(MemRef, Allocator&). + static void destroy(MemRef, Allocator&) noexcept; + + /// Shorthand for `destroy_deep(MemRef(ref, alloc), alloc)`. + static void destroy_deep(ref_type ref, Allocator& alloc) noexcept; + + /// Destroy the specified array node and all of its children, recursively. + /// + /// This is done by freeing the specified array node after calling + /// destroy_deep() for every contained 'ref' element. + static void destroy_deep(MemRef, Allocator&) noexcept; + + Allocator& get_alloc() const noexcept + { + return m_alloc; + } + + // Serialization + + /// Returns the ref (position in the target stream) of the written copy of + /// this array, or the ref of the original array if \a only_if_modified is + /// true, and this array is unmodified (Alloc::is_read_only()). + /// + /// The number of bytes that will be written by a non-recursive invocation + /// of this function is exactly the number returned by get_byte_size(). + /// + /// \param out The destination stream (writer). + /// + /// \param deep If true, recursively write out subarrays, but still subject + /// to \a only_if_modified. + /// + /// \param only_if_modified Set to `false` to always write, or to `true` to + /// only write the array if it has been modified. + ref_type write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const; + + /// Same as non-static write() with `deep` set to true. This is for the + /// cases where you do not already have an array accessor available. + static ref_type write(ref_type, Allocator&, _impl::ArrayWriterBase&, bool only_if_modified); + + // Main finding function - used for find_first, find_all, sum, max, min, etc. + bool find(int cond, Action action, int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, bool nullable_array = false, bool find_null = false) const; + + // Templated find function to avoid conversion to and from integer represenation of condition + template + bool find(Action action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + bool nullable_array = false, bool find_null = false) const + { + if (action == act_ReturnFirst) { + REALM_TEMPEX3(return find, cond, act_ReturnFirst, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Sum) { + REALM_TEMPEX3(return find, cond, act_Sum, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Min) { + REALM_TEMPEX3(return find, cond, act_Min, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Max) { + REALM_TEMPEX3(return find, cond, act_Max, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_Count) { + REALM_TEMPEX3(return find, cond, act_Count, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_FindAll) { + REALM_TEMPEX3(return find, cond, act_FindAll, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + else if (action == act_CallbackIdx) { + REALM_TEMPEX3(return find, cond, act_CallbackIdx, m_width, + (value, start, end, baseindex, state, CallbackDummy(), nullable_array, find_null)) + } + REALM_ASSERT_DEBUG(false); + return false; + } + + + /* + bool find(int cond, Action action, null, size_t start, size_t end, size_t baseindex, + QueryState* state) const; + */ + + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + // This is the one installed into the m_vtable->finder slots. + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + /* + template + bool find(null, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const; + */ + + // Optimized implementation for release mode + template + bool find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array = false, bool find_null = false) const; + + // Called for each search result + template + bool find_action(size_t index, util::Optional value, QueryState* state, + Callback callback) const; + + template + bool find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const; + + // Wrappers for backwards compatibility and for simple use without + // setting up state initialization etc + template + size_t find_first(int64_t value, size_t start = 0, size_t end = size_t(-1)) const; + + void find_all(IntegerColumn* result, int64_t value, size_t col_offset = 0, size_t begin = 0, + size_t end = size_t(-1)) const; + + size_t find_first(int64_t value, size_t begin = 0, size_t end = size_t(-1)) const; + + // Non-SSE find for the four functions Equal/NotEqual/Less/Greater + template + bool compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Non-SSE find for Equal/NotEqual + template + inline bool compare_equality(int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const; + + // Non-SSE find for Less/Greater + template + bool compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + template + bool compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + +// SSE find for the four functions Equal/NotEqual/Less/Greater +#ifdef REALM_COMPILER_SSE + template + bool find_sse(int64_t value, __m128i* data, size_t items, QueryState* state, size_t baseindex, + Callback callback) const; + + template + REALM_FORCEINLINE bool find_sse_intern(__m128i* action_data, __m128i* data, size_t items, + QueryState* state, size_t baseindex, Callback callback) const; + +#endif + + template + inline bool test_zero(uint64_t value) const; // Tests value for 0-elements + + template + size_t find_zero(uint64_t v) const; // Finds position of 0/non-zero element + + template + uint64_t cascade(uint64_t a) const; // Sets lowermost bits of zero or non-zero elements + + template + int64_t + find_gtlt_magic(int64_t v) const; // Compute magic constant needed for searching for value 'v' using bit hacks + + template + inline int64_t lower_bits() const; // Return chunk with lower bit set in each element + + size_t first_set_bit(unsigned int v) const; + size_t first_set_bit64(int64_t v) const; + + template + int64_t get_universal(const char* const data, const size_t ndx) const; + + // Find value greater/less in 64-bit chunk - only works for positive values + template + bool find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, + Callback callback) const; + + // Find value greater/less in 64-bit chunk - no constraints + template + bool find_gtlt(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const; + + + /// Get the number of elements in the B+-tree rooted at this array + /// node. The root must not be a leaf. + /// + /// Please avoid using this function (consider it deprecated). It + /// will have to be removed if we choose to get rid of the last + /// element of the main array of an inner B+-tree node that stores + /// the total number of elements in the subtree. The motivation + /// for removing it, is that it will significantly improve the + /// efficiency when inserting after, and erasing the last element. + size_t get_bptree_size() const noexcept; + + /// The root must not be a leaf. + static size_t get_bptree_size_from_header(const char* root_header) noexcept; + + + /// Find the leaf node corresponding to the specified element + /// index index. The specified element index must refer to an + /// element that exists in the tree. This function must be called + /// on an inner B+-tree node, never a leaf. Note that according to + /// invar:bptree-nonempty-inner and invar:bptree-nonempty-leaf, an + /// inner B+-tree node can never be empty. + /// + /// This function is not obliged to instantiate intermediate array + /// accessors. For this reason, this function cannot be used for + /// operations that modify the tree, as that requires an unbroken + /// chain of parent array accessors between the root and the + /// leaf. Thus, despite the fact that the returned MemRef object + /// appears to allow modification of the referenced memory, the + /// caller must handle the memory reference as if it was + /// const-qualified. + /// + /// \return (`leaf_header`, `ndx_in_leaf`) where `leaf_header` + /// points to the the header of the located leaf, and + /// `ndx_in_leaf` is the local index within that leaf + /// corresponding to the specified element index. + std::pair get_bptree_leaf(size_t elem_ndx) const noexcept; + + + class NodeInfo; + class VisitHandler; + + /// Visit leaves of the B+-tree rooted at this inner node, + /// starting with the leaf that contains the element at the + /// specified element index start offset, and ending when the + /// handler returns false. The specified element index offset must + /// refer to an element that exists in the tree. This function + /// must be called on an inner B+-tree node, never a leaf. Note + /// that according to invar:bptree-nonempty-inner and + /// invar:bptree-nonempty-leaf, an inner B+-tree node can never be + /// empty. + /// + /// \param elem_ndx_offset The start position (must be valid). + /// + /// \param elems_in_tree The total number of elements in the tree. + /// + /// \param handler The callback which will get called for each leaf. + /// + /// \return True if, and only if the handler has returned true for + /// all visited leafs. + bool visit_bptree_leaves(size_t elem_ndx_offset, size_t elems_in_tree, VisitHandler& handler); + + + class UpdateHandler; + + /// Call the handler for every leaf. This function must be called + /// on an inner B+-tree node, never a leaf. + void update_bptree_leaves(UpdateHandler&); + + /// Call the handler for the leaf that contains the element at the + /// specified index. This function must be called on an inner + /// B+-tree node, never a leaf. + void update_bptree_elem(size_t elem_ndx, UpdateHandler&); + + + class EraseHandler; + + /// Erase the element at the specified index in the B+-tree with + /// the specified root. When erasing the last element, you must + /// pass npos in place of the index. This function must be called + /// with a root that is an inner B+-tree node, never a leaf. + /// + /// This function is guaranteed to succeed (not throw) if the + /// specified element was inserted during the current transaction, + /// and no other modifying operation has been carried out since + /// then (noexcept:bptree-erase-alt). + /// + /// FIXME: ExceptionSafety: The exception guarantee explained + /// above is not as powerfull as we would like it to be. Here is + /// what we would like: This function is guaranteed to succeed + /// (not throw) if the specified element was inserted during the + /// current transaction (noexcept:bptree-erase). This must be true + /// even if the element is modified after insertion, and/or if + /// other elements are inserted or erased around it. There are two + /// aspects of the current design that stand in the way of this + /// guarantee: (A) The fact that the node accessor, that is cached + /// in the column accessor, has to be reallocated/reinstantiated + /// when the root switches between being a leaf and an inner + /// node. This problem would go away if we always cached the last + /// used leaf accessor in the column accessor instead. (B) The + /// fact that replacing one child ref with another can fail, + /// because it may require reallocation of memory to expand the + /// bit-width. This can be fixed in two ways: Either have the + /// inner B+-tree nodes always have a bit-width of 64, or allow + /// the root node to be discarded and the column ref to be set to + /// zero in Table::m_columns. + static void erase_bptree_elem(Array* root, size_t elem_ndx, EraseHandler&); + + + struct TreeInsertBase { + size_t m_split_offset; + size_t m_split_size; + }; + + template + struct TreeInsert : TreeInsertBase { + typename TreeTraits::value_type m_value; + bool m_nullable; + }; + + /// Same as bptree_insert() but insert after the last element. + template + ref_type bptree_append(TreeInsert& state); + + /// Insert an element into the B+-subtree rooted at this array + /// node. The element is inserted before the specified element + /// index. This function must be called on an inner B+-tree node, + /// never a leaf. If this inner node had to be split, this + /// function returns the `ref` of the new sibling. + template + ref_type bptree_insert(size_t elem_ndx, TreeInsert& state); + + ref_type bptree_leaf_insert(size_t ndx, int64_t, TreeInsertBase& state); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static int_fast64_t get(const char* header, size_t ndx) noexcept; + + /// Like get(const char*, size_t) but gets two consecutive + /// elements. + static std::pair get_two(const char* header, size_t ndx) noexcept; + + static void get_three(const char* data, size_t ndx, ref_type& v0, ref_type& v1, ref_type& v2) noexcept; + + /// The meaning of 'width' depends on the context in which this + /// array is used. + size_t get_width() const noexcept + { + return m_width; + } + + static char* get_data_from_header(char*) noexcept; + static char* get_header_from_data(char*) noexcept; + static const char* get_data_from_header(const char*) noexcept; + + enum WidthType { + wtype_Bits = 0, + wtype_Multiply = 1, + wtype_Ignore = 2, + }; + + static bool get_is_inner_bptree_node_from_header(const char*) noexcept; + static bool get_hasrefs_from_header(const char*) noexcept; + static bool get_context_flag_from_header(const char*) noexcept; + static WidthType get_wtype_from_header(const char*) noexcept; + static uint_least8_t get_width_from_header(const char*) noexcept; + static size_t get_size_from_header(const char*) noexcept; + + static Type get_type_from_header(const char*) noexcept; + + /// Get the number of bytes currently in use by this array. This + /// includes the array header, but it does not include allocated + /// bytes corresponding to excess capacity. The result is + /// guaranteed to be a multiple of 8 (i.e., 64-bit aligned). + /// + /// This number is exactly the number of bytes that will be + /// written by a non-recursive invocation of write(). + size_t get_byte_size() const noexcept; + + /// Get the maximum number of bytes that can be written by a + /// non-recursive invocation of write() on an array with the + /// specified number of elements, that is, the maximum value that + /// can be returned by get_byte_size(). + static size_t get_max_byte_size(size_t num_elems) noexcept; + + /// FIXME: Belongs in IntegerArray + static size_t calc_aligned_byte_size(size_t size, int width); + +#ifdef REALM_DEBUG + void print() const; + void verify() const; + typedef size_t (*LeafVerifier)(MemRef, Allocator&); + void verify_bptree(LeafVerifier) const; + class MemUsageHandler { + public: + virtual void handle(ref_type ref, size_t allocated, size_t used) = 0; + }; + void report_memory_usage(MemUsageHandler&) const; + void stats(MemStats& stats_dest) const; + typedef void (*LeafDumper)(MemRef, Allocator&, std::ostream&, int level); + void dump_bptree_structure(std::ostream&, int level, LeafDumper) const; + void to_dot(std::ostream&, StringData title = StringData()) const; + class ToDotHandler { + public: + virtual void to_dot(MemRef leaf_mem, ArrayParent*, size_t ndx_in_parent, std::ostream&) = 0; + ~ToDotHandler() + { + } + }; + void bptree_to_dot(std::ostream&, ToDotHandler&) const; + void to_dot_parent_edge(std::ostream&) const; +#endif + + static const int header_size = 8; // Number of bytes used by header + + // The encryption layer relies on headers always fitting within a single page. + static_assert(header_size == 8, "Header must always fit in entirely on a page"); + +private: + Array& operator=(const Array&); // not allowed +protected: + typedef bool (*CallbackDummy)(int64_t); + + /// Insert a new child after original. If the parent has to be + /// split, this function returns the `ref` of the new parent node. + ref_type insert_bptree_child(Array& offsets, size_t orig_child_ndx, ref_type new_sibling_ref, + TreeInsertBase& state); + + void ensure_bptree_offsets(Array& offsets); + void create_bptree_offsets(Array& offsets, int_fast64_t first_value); + + bool do_erase_bptree_elem(size_t elem_ndx, EraseHandler&); + + template + size_t from_list(StringData value, IntegerColumn& result, InternalFindResult& result_ref, + const IntegerColumn& rows, ColumnBase* column) const; + + template + size_t index_string(StringData value, IntegerColumn& result, InternalFindResult& result_ref, + ColumnBase* column) const; + +protected: + // Includes array header. Not necessarily 8-byte aligned. + virtual size_t calc_byte_len(size_t num_items, size_t width) const; + + virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept; + + bool get_is_inner_bptree_node_from_header() const noexcept; + bool get_hasrefs_from_header() const noexcept; + bool get_context_flag_from_header() const noexcept; + WidthType get_wtype_from_header() const noexcept; + uint_least8_t get_width_from_header() const noexcept; + size_t get_size_from_header() const noexcept; + + // Undefined behavior if m_alloc.is_read_only(m_ref) returns true + size_t get_capacity_from_header() const noexcept; + + void set_header_is_inner_bptree_node(bool value) noexcept; + void set_header_hasrefs(bool value) noexcept; + void set_header_context_flag(bool value) noexcept; + void set_header_wtype(WidthType value) noexcept; + void set_header_width(int value) noexcept; + void set_header_size(size_t value) noexcept; + void set_header_capacity(size_t value) noexcept; + + static void set_header_is_inner_bptree_node(bool value, char* header) noexcept; + static void set_header_hasrefs(bool value, char* header) noexcept; + static void set_header_context_flag(bool value, char* header) noexcept; + static void set_header_wtype(WidthType value, char* header) noexcept; + static void set_header_width(int value, char* header) noexcept; + static void set_header_size(size_t value, char* header) noexcept; + static void set_header_capacity(size_t value, char* header) noexcept; + + static void init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag, + WidthType width_type, int width, size_t size, size_t capacity) noexcept; + + + // This returns the minimum value ("lower bound") of the representable values + // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64. + template + static int_fast64_t lbound_for_width() noexcept; + + static int_fast64_t lbound_for_width(size_t width) noexcept; + + // This returns the maximum value ("inclusive upper bound") of the representable values + // for the given bit width. Valid widths are 0, 1, 2, 4, 8, 16, 32, and 64. + template + static int_fast64_t ubound_for_width() noexcept; + + static int_fast64_t ubound_for_width(size_t width) noexcept; + + template + void set_width() noexcept; + void set_width(size_t) noexcept; + void alloc(size_t init_size, size_t width); + void copy_on_write(); + +private: + template + int64_t sum(size_t start, size_t end) const; + + template + bool minmax(int64_t& result, size_t start, size_t end, size_t* return_ndx) const; + + template + size_t find_gte(const int64_t target, size_t start, size_t end) const; + + template + size_t adjust_ge(size_t start, size_t end, int_fast64_t limit, int_fast64_t diff); + +protected: + /// The total size in bytes (including the header) of a new empty + /// array. Must be a multiple of 8 (i.e., 64-bit aligned). + static const size_t initial_capacity = 128; + + /// It is an error to specify a non-zero value unless the width + /// type is wtype_Bits. It is also an error to specify a non-zero + /// size if the width type is wtype_Ignore. + static MemRef create(Type, bool context_flag, WidthType, size_t size, int_fast64_t value, Allocator&); + + static MemRef clone(MemRef header, Allocator& alloc, Allocator& target_alloc); + + /// Get the address of the header of this array. + char* get_header() noexcept; + + /// Same as get_byte_size(). + static size_t get_byte_size_from_header(const char*) noexcept; + + // Undefined behavior if array is in immutable memory + static size_t get_capacity_from_header(const char*) noexcept; + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + void destroy_children(size_t offset = 0) noexcept; + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + +protected: + // Getters and Setters for adaptive-packed arrays + typedef int64_t (Array::*Getter)(size_t) const; // Note: getters must not throw + typedef void (Array::*Setter)(size_t, int64_t); + typedef bool (Array::*Finder)(int64_t, size_t, size_t, size_t, QueryState*) const; + typedef void (Array::*ChunkGetter)(size_t, int64_t res[8]) const; // Note: getters must not throw + + struct VTable { + Getter getter; + ChunkGetter chunk_getter; + Setter setter; + Finder finder[cond_VTABLE_FINDER_COUNT]; // one for each active function pointer + }; + template + struct VTableForWidth; + +protected: + /// Takes a 64-bit value and returns the minimum number of bits needed + /// to fit the value. For alignment this is rounded up to nearest + /// log2. Posssible results {0, 1, 2, 4, 8, 16, 32, 64} + static size_t bit_width(int64_t value); + +#ifdef REALM_DEBUG + void report_memory_usage_2(MemUsageHandler&) const; +#endif + +private: + Getter m_getter = nullptr; // cached to avoid indirection + const VTable* m_vtable = nullptr; + +public: + // FIXME: Should not be public + char* m_data = nullptr; // Points to first byte after header + +#if REALM_ENABLE_MEMDEBUG + // If m_no_relocation is false, then copy_on_write() will always relocate this array, regardless if it's + // required or not. If it's true, then it will never relocate, which is currently only expeted inside + // GroupWriter::write_group() due to a unique chicken/egg problem (see description there). + bool m_no_relocation = false; +#endif + +protected: + int64_t m_lbound; // min number that can be stored with current m_width + int64_t m_ubound; // max number that can be stored with current m_width + + size_t m_size = 0; // Number of elements currently stored. + size_t m_capacity = 0; // Number of elements that fit inside the allocated memory. + + Allocator& m_alloc; + +private: + size_t m_ref; + ArrayParent* m_parent = nullptr; + size_t m_ndx_in_parent = 0; // Ignored if m_parent is null. + +protected: + uint_least8_t m_width = 0; // Size of an element (meaning depend on type of array). + bool m_is_inner_bptree_node; // This array is an inner node of B+-tree. + bool m_has_refs; // Elements whose first bit is zero are refs to subarrays. + bool m_context_flag; // Meaning depends on context. + +private: + ref_type do_write_shallow(_impl::ArrayWriterBase&) const; + ref_type do_write_deep(_impl::ArrayWriterBase&, bool only_if_modified) const; + static size_t calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept; + + friend class SlabAlloc; + friend class GroupWriter; + friend class StringColumn; +}; + + +class Array::NodeInfo { +public: + MemRef m_mem; + Array* m_parent; + size_t m_ndx_in_parent; + size_t m_offset, m_size; +}; + +class Array::VisitHandler { +public: + virtual bool visit(const NodeInfo& leaf_info) = 0; + virtual ~VisitHandler() noexcept + { + } +}; + + +class Array::UpdateHandler { +public: + virtual void update(MemRef, ArrayParent*, size_t leaf_ndx_in_parent, size_t elem_ndx_in_leaf) = 0; + virtual ~UpdateHandler() noexcept + { + } +}; + + +class Array::EraseHandler { +public: + /// If the specified leaf has more than one element, this function + /// must erase the specified element from the leaf and return + /// false. Otherwise, when the leaf has a single element, this + /// function must return true without modifying the leaf. If \a + /// elem_ndx_in_leaf is `npos`, it refers to the last element in + /// the leaf. The implementation of this function must be + /// exception safe. This function is guaranteed to be called at + /// most once during each execution of Array::erase_bptree_elem(), + /// and *exactly* once during each *successful* execution of + /// Array::erase_bptree_elem(). + virtual bool erase_leaf_elem(MemRef, ArrayParent*, size_t leaf_ndx_in_parent, size_t elem_ndx_in_leaf) = 0; + + virtual void destroy_leaf(MemRef leaf_mem) noexcept = 0; + + /// Must replace the current root with the specified leaf. The + /// implementation of this function must not destroy the + /// underlying root node, or any of its children, as that will be + /// done by Array::erase_bptree_elem(). The implementation of this + /// function must be exception safe. + virtual void replace_root_by_leaf(MemRef leaf_mem) = 0; + + /// Same as replace_root_by_leaf(), but must replace the root with + /// an empty leaf. Also, if this function is called during an + /// execution of Array::erase_bptree_elem(), it is guaranteed that + /// it will be preceeded by a call to erase_leaf_elem(). + virtual void replace_root_by_empty_leaf() = 0; + + virtual ~EraseHandler() noexcept + { + } +}; + + +// Implementation: + +class QueryStateBase { + virtual void dyncast() + { + } +}; + +template <> +class QueryState : public QueryStateBase { +public: + int64_t m_state; + size_t m_match_count; + size_t m_limit; + size_t m_minmax_index; // used only for min/max, to save index of current min/max value + + template + bool uses_val() + { + if (action == act_Max || action == act_Min || action == act_Sum) + return true; + else + return false; + } + + void init(Action action, IntegerColumn* akku, size_t limit) + { + m_match_count = 0; + m_limit = limit; + m_minmax_index = not_found; + + if (action == act_Max) + m_state = -0x7fffffffffffffffLL - 1LL; + else if (action == act_Min) + m_state = 0x7fffffffffffffffLL; + else if (action == act_ReturnFirst) + m_state = not_found; + else if (action == act_Sum) + m_state = 0; + else if (action == act_Count) + m_state = 0; + else if (action == act_FindAll) + m_state = reinterpret_cast(akku); + else if (action == act_CallbackIdx) { + } + else { + REALM_ASSERT_DEBUG(false); + } + } + + template + inline bool match(size_t index, uint64_t indexpattern, int64_t value) + { + if (pattern) { + if (action == act_Count) { + // If we are close to 'limit' argument in query, we cannot count-up a complete chunk. Count up single + // elements instead + if (m_match_count + 64 >= m_limit) + return false; + + m_state += fast_popcount64(indexpattern); + m_match_count = size_t(m_state); + return true; + } + // Other aggregates cannot (yet) use bit pattern for anything. Make Array-finder call with pattern = false + // instead + return false; + } + + ++m_match_count; + + if (action == act_Max) { + if (value > m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Sum) + m_state += value; + else if (action == act_Count) { + m_state++; + m_match_count = size_t(m_state); + } + else if (action == act_FindAll) { + Array::add_to_column(reinterpret_cast(m_state), index); + } + else if (action == act_ReturnFirst) { + m_state = index; + return false; + } + else { + REALM_ASSERT_DEBUG(false); + } + return (m_limit > m_match_count); + } + + template + inline bool match(size_t index, uint64_t indexpattern, util::Optional value) + { + // FIXME: This is a temporary hack for nullable integers. + if (value) { + return match(index, indexpattern, *value); + } + + // If value is null, the only sensible actions are count, find_all, and return first. + // Max, min, and sum should all have no effect. + if (action == act_Count) { + m_state++; + m_match_count = size_t(m_state); + } + else if (action == act_FindAll) { + Array::add_to_column(reinterpret_cast(m_state), index); + } + else if (action == act_ReturnFirst) { + m_match_count++; + m_state = index; + return false; + } + return m_limit > m_match_count; + } +}; + +// Used only for Basic-types: currently float and double +template +class QueryState : public QueryStateBase { +public: + R m_state; + size_t m_match_count; + size_t m_limit; + size_t m_minmax_index; // used only for min/max, to save index of current min/max value + + template + bool uses_val() + { + return (action == act_Max || action == act_Min || action == act_Sum || action == act_Count); + } + + void init(Action action, Array*, size_t limit) + { + REALM_ASSERT((std::is_same::value || std::is_same::value)); + m_match_count = 0; + m_limit = limit; + m_minmax_index = not_found; + + if (action == act_Max) + m_state = -std::numeric_limits::infinity(); + else if (action == act_Min) + m_state = std::numeric_limits::infinity(); + else if (action == act_Sum) + m_state = 0.0; + else { + REALM_ASSERT_DEBUG(false); + } + } + + template + inline bool match(size_t index, uint64_t /*indexpattern*/, resulttype value) + { + if (pattern) + return false; + + static_assert(action == act_Sum || action == act_Max || action == act_Min || action == act_Count, + "Search action not supported"); + + if (action == act_Count) { + ++m_match_count; + } + else if (!null::is_null_float(value)) { + ++m_match_count; + if (action == act_Max) { + if (value > m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Min) { + if (value < m_state) { + m_state = value; + m_minmax_index = index; + } + } + else if (action == act_Sum) + m_state += value; + else { + REALM_ASSERT_DEBUG(false); + } + } + + return (m_limit > m_match_count); + } +}; + +inline bool RefOrTagged::is_ref() const noexcept +{ + return (m_value & 1) == 0; +} + +inline bool RefOrTagged::is_tagged() const noexcept +{ + return !is_ref(); +} + +inline ref_type RefOrTagged::get_as_ref() const noexcept +{ + // to_ref() is defined in + return to_ref(m_value); +} + +inline uint_fast64_t RefOrTagged::get_as_int() const noexcept +{ + // The bitwise AND is there in case uint_fast64_t is wider than 64 bits. + return (uint_fast64_t(m_value) & 0xFFFFFFFFFFFFFFFFULL) >> 1; +} + +inline RefOrTagged RefOrTagged::make_ref(ref_type ref) noexcept +{ + // from_ref() is defined in + int_fast64_t value = from_ref(ref); + return RefOrTagged(value); +} + +inline RefOrTagged RefOrTagged::make_tagged(uint_fast64_t i) noexcept +{ + REALM_ASSERT(i < (1ULL << 63)); + int_fast64_t value = util::from_twos_compl((i << 1) | 1); + return RefOrTagged(value); +} + +inline RefOrTagged::RefOrTagged(int_fast64_t value) noexcept + : m_value(value) +{ +} + +inline Array::Array(Allocator& allocator) noexcept + : m_alloc(allocator) +{ +} + +inline void Array::create(Type type, bool context_flag, size_t length, int_fast64_t value) +{ + MemRef mem = create_array(type, context_flag, length, value, m_alloc); // Throws + init_from_mem(mem); +} + + +inline void Array::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT_DEBUG(ref); + char* header = m_alloc.translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); +} + + +inline void Array::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + + +inline Array::Type Array::get_type() const noexcept +{ + if (m_is_inner_bptree_node) { + REALM_ASSERT_DEBUG(m_has_refs); + return type_InnerBptreeNode; + } + if (m_has_refs) + return type_HasRefs; + return type_Normal; +} + + +inline void Array::get_chunk(size_t ndx, int64_t res[8]) const noexcept +{ + REALM_ASSERT_DEBUG(ndx < m_size); + (this->*(m_vtable->chunk_getter))(ndx, res); +} + + +inline int64_t Array::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + REALM_ASSERT_DEBUG(ndx < m_size); + return (this->*m_getter)(ndx); + + // Two ideas that are not efficient but may be worth looking into again: + /* + // Assume correct width is found early in REALM_TEMPEX, which is the case for B tree offsets that + // are probably either 2^16 long. Turns out to be 25% faster if found immediately, but 50-300% slower + // if found later + REALM_TEMPEX(return get, (ndx)); + */ + /* + // Slightly slower in both of the if-cases. Also needs an matchcount m_size check too, to avoid + // reading beyond array. + if (m_width >= 8 && m_size > ndx + 7) + return get<64>(ndx >> m_shift) & m_widthmask; + else + return (this->*(m_vtable->getter))(ndx); + */ +} + +inline int64_t Array::front() const noexcept +{ + return get(0); +} + +inline int64_t Array::back() const noexcept +{ + return get(m_size - 1); +} + +inline ref_type Array::get_as_ref(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + REALM_ASSERT_DEBUG(m_has_refs); + int64_t v = get(ndx); + return to_ref(v); +} + +inline RefOrTagged Array::get_as_ref_or_tagged(size_t ndx) const noexcept +{ + REALM_ASSERT(has_refs()); + return RefOrTagged(get(ndx)); +} + +inline void Array::set(size_t ndx, RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + set(ndx, ref_or_tagged.m_value); // Throws +} + +inline void Array::add(RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + add(ref_or_tagged.m_value); // Throws +} + +inline void Array::ensure_minimum_width(RefOrTagged ref_or_tagged) +{ + REALM_ASSERT(has_refs()); + ensure_minimum_width(ref_or_tagged.m_value); // Throws +} + +inline bool Array::is_inner_bptree_node() const noexcept +{ + return m_is_inner_bptree_node; +} + +inline bool Array::has_refs() const noexcept +{ + return m_has_refs; +} + +inline bool Array::get_context_flag() const noexcept +{ + return m_context_flag; +} + +inline void Array::set_context_flag(bool value) noexcept +{ + m_context_flag = value; + set_header_context_flag(value); +} + +inline ref_type Array::get_ref() const noexcept +{ + return m_ref; +} + +inline MemRef Array::get_mem() const noexcept +{ + return MemRef(get_header_from_data(m_data), m_ref, m_alloc); +} + +inline void Array::destroy() noexcept +{ + if (!is_attached()) + return; + char* header = get_header_from_data(m_data); + m_alloc.free_(m_ref, header); + m_data = nullptr; +} + +inline void Array::destroy_deep() noexcept +{ + if (!is_attached()) + return; + + if (m_has_refs) + destroy_children(); + + char* header = get_header_from_data(m_data); + m_alloc.free_(m_ref, header); + m_data = nullptr; +} + +inline ref_type Array::write(_impl::ArrayWriterBase& out, bool deep, bool only_if_modified) const +{ + REALM_ASSERT(is_attached()); + + if (only_if_modified && m_alloc.is_read_only(m_ref)) + return m_ref; + + if (!deep || !m_has_refs) + return do_write_shallow(out); // Throws + + return do_write_deep(out, only_if_modified); // Throws +} + +inline ref_type Array::write(ref_type ref, Allocator& alloc, _impl::ArrayWriterBase& out, bool only_if_modified) +{ + if (only_if_modified && alloc.is_read_only(ref)) + return ref; + + Array array(alloc); + array.init_from_ref(ref); + + if (!array.m_has_refs) + return array.do_write_shallow(out); // Throws + + return array.do_write_deep(out, only_if_modified); // Throws +} + +inline void Array::add(int_fast64_t value) +{ + insert(m_size, value); +} + +inline void Array::erase(size_t ndx) +{ + // This can throw, but only if array is currently in read-only + // memory. + move(ndx + 1, size(), ndx); + + // Update size (also in header) + --m_size; + set_header_size(m_size); +} + + +inline void Array::erase(size_t begin, size_t end) +{ + if (begin != end) { + // This can throw, but only if array is currently in read-only memory. + move(end, size(), begin); // Throws + + // Update size (also in header) + m_size -= end - begin; + set_header_size(m_size); + } +} + +inline void Array::clear() +{ + truncate(0); // Throws +} + +inline void Array::clear_and_destroy_children() +{ + truncate_and_destroy_children(0); +} + +inline void Array::destroy(ref_type ref, Allocator& alloc) noexcept +{ + destroy(MemRef(ref, alloc), alloc); +} + +inline void Array::destroy(MemRef mem, Allocator& alloc) noexcept +{ + alloc.free_(mem); +} + +inline void Array::destroy_deep(ref_type ref, Allocator& alloc) noexcept +{ + destroy_deep(MemRef(ref, alloc), alloc); +} + +inline void Array::destroy_deep(MemRef mem, Allocator& alloc) noexcept +{ + if (!get_hasrefs_from_header(mem.get_addr())) { + alloc.free_(mem); + return; + } + Array array(alloc); + array.init_from_mem(mem); + array.destroy_deep(); +} + + +inline void Array::adjust(size_t ndx, int_fast64_t diff) +{ + // FIXME: Should be optimized + REALM_ASSERT_3(ndx, <=, m_size); + int_fast64_t v = get(ndx); + set(ndx, int64_t(v + diff)); // Throws +} + +inline void Array::adjust(size_t begin, size_t end, int_fast64_t diff) +{ + // FIXME: Should be optimized + for (size_t i = begin; i != end; ++i) + adjust(i, diff); // Throws +} + + +//------------------------------------------------- + +inline bool Array::get_is_inner_bptree_node_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x80) != 0; +} +inline bool Array::get_hasrefs_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x40) != 0; +} +inline bool Array::get_context_flag_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (int(h[4]) & 0x20) != 0; +} +inline Array::WidthType Array::get_wtype_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return WidthType((int(h[4]) & 0x18) >> 3); +} +inline uint_least8_t Array::get_width_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return uint_least8_t((1 << (int(h[4]) & 0x07)) >> 1); +} +inline size_t Array::get_size_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (size_t(h[5]) << 16) + (size_t(h[6]) << 8) + h[7]; +} +inline size_t Array::get_capacity_from_header(const char* header) noexcept +{ + typedef unsigned char uchar; + const uchar* h = reinterpret_cast(header); + return (size_t(h[0]) << 16) + (size_t(h[1]) << 8) + h[2]; +} + + +inline char* Array::get_data_from_header(char* header) noexcept +{ + return header + header_size; +} +inline char* Array::get_header_from_data(char* data) noexcept +{ + return data - header_size; +} +inline const char* Array::get_data_from_header(const char* header) noexcept +{ + return get_data_from_header(const_cast(header)); +} + + +inline bool Array::get_is_inner_bptree_node_from_header() const noexcept +{ + return get_is_inner_bptree_node_from_header(get_header_from_data(m_data)); +} +inline bool Array::get_hasrefs_from_header() const noexcept +{ + return get_hasrefs_from_header(get_header_from_data(m_data)); +} +inline bool Array::get_context_flag_from_header() const noexcept +{ + return get_context_flag_from_header(get_header_from_data(m_data)); +} +inline Array::WidthType Array::get_wtype_from_header() const noexcept +{ + return get_wtype_from_header(get_header_from_data(m_data)); +} +inline uint_least8_t Array::get_width_from_header() const noexcept +{ + return get_width_from_header(get_header_from_data(m_data)); +} +inline size_t Array::get_size_from_header() const noexcept +{ + return get_size_from_header(get_header_from_data(m_data)); +} +inline size_t Array::get_capacity_from_header() const noexcept +{ + return get_capacity_from_header(get_header_from_data(m_data)); +} + + +inline void Array::set_header_is_inner_bptree_node(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x80) | int(value) << 7); +} + +inline void Array::set_header_hasrefs(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x40) | int(value) << 6); +} + +inline void Array::set_header_context_flag(bool value, char* header) noexcept +{ + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x20) | int(value) << 5); +} + +inline void Array::set_header_wtype(WidthType value, char* header) noexcept +{ + // Indicates how to calculate size in bytes based on width + // 0: bits (width/8) * size + // 1: multiply width * size + // 2: ignore 1 * size + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x18) | int(value) << 3); +} + +inline void Array::set_header_width(int value, char* header) noexcept +{ + // Pack width in 3 bits (log2) + int w = 0; + while (value) { + ++w; + value >>= 1; + } + REALM_ASSERT_3(w, <, 8); + + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[4] = uchar((int(h[4]) & ~0x7) | w); +} + +inline void Array::set_header_size(size_t value, char* header) noexcept +{ + REALM_ASSERT_3(value, <=, max_array_payload); + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[5] = uchar((value >> 16) & 0x000000FF); + h[6] = uchar((value >> 8) & 0x000000FF); + h[7] = uchar(value & 0x000000FF); +} + +// Note: There is a copy of this function is test_alloc.cpp +inline void Array::set_header_capacity(size_t value, char* header) noexcept +{ + REALM_ASSERT_3(value, <=, max_array_payload); + typedef unsigned char uchar; + uchar* h = reinterpret_cast(header); + h[0] = uchar((value >> 16) & 0x000000FF); + h[1] = uchar((value >> 8) & 0x000000FF); + h[2] = uchar(value & 0x000000FF); +} + + +inline void Array::set_header_is_inner_bptree_node(bool value) noexcept +{ + set_header_is_inner_bptree_node(value, get_header_from_data(m_data)); +} +inline void Array::set_header_hasrefs(bool value) noexcept +{ + set_header_hasrefs(value, get_header_from_data(m_data)); +} +inline void Array::set_header_context_flag(bool value) noexcept +{ + set_header_context_flag(value, get_header_from_data(m_data)); +} +inline void Array::set_header_wtype(WidthType value) noexcept +{ + set_header_wtype(value, get_header_from_data(m_data)); +} +inline void Array::set_header_width(int value) noexcept +{ + set_header_width(value, get_header_from_data(m_data)); +} +inline void Array::set_header_size(size_t value) noexcept +{ + set_header_size(value, get_header_from_data(m_data)); +} +inline void Array::set_header_capacity(size_t value) noexcept +{ + set_header_capacity(value, get_header_from_data(m_data)); +} + + +inline Array::Type Array::get_type_from_header(const char* header) noexcept +{ + if (get_is_inner_bptree_node_from_header(header)) + return type_InnerBptreeNode; + if (get_hasrefs_from_header(header)) + return type_HasRefs; + return type_Normal; +} + + +inline char* Array::get_header() noexcept +{ + return get_header_from_data(m_data); +} + +inline size_t Array::calc_byte_size(WidthType wtype, size_t size, uint_least8_t width) noexcept +{ + size_t num_bytes = 0; + switch (wtype) { + case wtype_Bits: { + // Current assumption is that size is at most 2^24 and that width is at most 64. + // In that case the following will never overflow. (Assuming that size_t is at least 32 bits) + REALM_ASSERT_3(size, <, 0x1000000); + size_t num_bits = size * width; + num_bytes = (num_bits + 7) >> 3; + break; + } + case wtype_Multiply: { + num_bytes = size * width; + break; + } + case wtype_Ignore: + num_bytes = size; + break; + } + + // Ensure 8-byte alignment + num_bytes = (num_bytes + 7) & ~size_t(7); + + num_bytes += header_size; + + return num_bytes; +} + +inline size_t Array::get_byte_size() const noexcept +{ + const char* header = get_header_from_data(m_data); + WidthType wtype = get_wtype_from_header(header); + size_t num_bytes = calc_byte_size(wtype, m_size, m_width); + + REALM_ASSERT_7(m_alloc.is_read_only(m_ref), ==, true, ||, num_bytes, <=, get_capacity_from_header(header)); + + return num_bytes; +} + + +inline size_t Array::get_byte_size_from_header(const char* header) noexcept +{ + size_t size = get_size_from_header(header); + uint_least8_t width = get_width_from_header(header); + WidthType wtype = get_wtype_from_header(header); + size_t num_bytes = calc_byte_size(wtype, size, width); + + return num_bytes; +} + + +inline void Array::init_header(char* header, bool is_inner_bptree_node, bool has_refs, bool context_flag, + WidthType width_type, int width, size_t size, size_t capacity) noexcept +{ + // Note: Since the header layout contains unallocated bit and/or + // bytes, it is important that we put the entire header into a + // well defined state initially. + std::fill(header, header + header_size, 0); + set_header_is_inner_bptree_node(is_inner_bptree_node, header); + set_header_hasrefs(has_refs, header); + set_header_context_flag(context_flag, header); + set_header_wtype(width_type, header); + set_header_width(width, header); + set_header_size(size, header); + set_header_capacity(capacity, header); +} + + +//------------------------------------------------- + +inline MemRef Array::clone_deep(Allocator& target_alloc) const +{ + char* header = get_header_from_data(m_data); + return clone(MemRef(header, m_ref, m_alloc), m_alloc, target_alloc); // Throws +} + +inline MemRef Array::create_empty_array(Type type, bool context_flag, Allocator& alloc) +{ + size_t size = 0; + int_fast64_t value = 0; + return create_array(type, context_flag, size, value, alloc); // Throws +} + +inline MemRef Array::create_array(Type type, bool context_flag, size_t size, int_fast64_t value, Allocator& alloc) +{ + return create(type, context_flag, wtype_Bits, size, value, alloc); // Throws +} + +inline bool Array::has_parent() const noexcept +{ + return m_parent != nullptr; +} + +inline ArrayParent* Array::get_parent() const noexcept +{ + return m_parent; +} + +inline void Array::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_parent = parent; + m_ndx_in_parent = ndx_in_parent; +} + +inline size_t Array::get_ndx_in_parent() const noexcept +{ + return m_ndx_in_parent; +} + +inline void Array::set_ndx_in_parent(size_t ndx) noexcept +{ + m_ndx_in_parent = ndx; +} + +inline void Array::adjust_ndx_in_parent(int diff) noexcept +{ + // Note that `diff` is promoted to an unsigned type, and that + // C++03 still guarantees the expected result regardless of the + // sizes of `int` and `decltype(m_ndx_in_parent)`. + m_ndx_in_parent += diff; +} + +inline ref_type Array::get_ref_from_parent() const noexcept +{ + ref_type ref = m_parent->get_child_ref(m_ndx_in_parent); + return ref; +} + +inline bool Array::is_attached() const noexcept +{ + return m_data != nullptr; +} + +inline void Array::detach() noexcept +{ + m_data = nullptr; +} + +inline size_t Array::size() const noexcept +{ + REALM_ASSERT_DEBUG(is_attached()); + return m_size; +} + +inline bool Array::is_empty() const noexcept +{ + return size() == 0; +} + +inline size_t Array::get_max_byte_size(size_t num_elems) noexcept +{ + int max_bytes_per_elem = 8; + return header_size + num_elems * max_bytes_per_elem; +} + +inline void Array::update_parent() +{ + if (m_parent) + m_parent->update_child_ref(m_ndx_in_parent, m_ref); +} + + +inline void Array::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + set(child_ndx, new_ref); +} + +inline ref_type Array::get_child_ref(size_t child_ndx) const noexcept +{ + return get_as_ref(child_ndx); +} + +inline size_t Array::get_bptree_size() const noexcept +{ + REALM_ASSERT_DEBUG(is_inner_bptree_node()); + int_fast64_t v = back(); + return size_t(v / 2); // v = 1 + 2*total_elems_in_tree +} + +inline size_t Array::get_bptree_size_from_header(const char* root_header) noexcept +{ + REALM_ASSERT_DEBUG(get_is_inner_bptree_node_from_header(root_header)); + size_t root_size = get_size_from_header(root_header); + int_fast64_t v = get(root_header, root_size - 1); + return size_t(v / 2); // v = 1 + 2*total_elems_in_tree +} + +inline void Array::ensure_bptree_offsets(Array& offsets) +{ + int_fast64_t first_value = get(0); + if (first_value % 2 == 0) { + offsets.init_from_ref(to_ref(first_value)); + } + else { + create_bptree_offsets(offsets, first_value); // Throws + } + offsets.set_parent(this, 0); +} + + +template +ref_type Array::bptree_append(TreeInsert& state) +{ + // FIXME: Consider exception safety. Especially, how can the split + // be carried out in an exception safe manner? + // + // Can split be done as a separate preparation step, such that if + // the actual insert fails, the split will still have occured. + // + // Unfortunately, it requires a rather significant rearrangement + // of the insertion flow. Instead of returning the sibling ref + // from insert functions, the leaf-insert functions must instead + // call the special bptree_insert() function on the parent, which + // will then cascade the split towards the root as required. + // + // At each level where a split is required (starting at the leaf): + // + // 1. Create the new sibling. + // + // 2. Copy relevant entries over such that new sibling is in + // its final state. + // + // 3. Call Array::bptree_insert() on parent with sibling ref. + // + // 4. Rearrange entries in original sibling and truncate as + // required (must not throw). + // + // What about the 'offsets' array? It will always be + // present. Consider this carefully. + + REALM_ASSERT_DEBUG(size() >= 1 + 1 + 1); // At least one child + + ArrayParent& childs_parent = *this; + size_t child_ref_ndx = size() - 2; + ref_type child_ref = get_as_ref(child_ref_ndx), new_sibling_ref; + char* child_header = static_cast(m_alloc.translate(child_ref)); + + bool child_is_leaf = !get_is_inner_bptree_node_from_header(child_header); + if (child_is_leaf) { + size_t elem_ndx_in_child = npos; // Append + new_sibling_ref = TreeTraits::leaf_insert(MemRef(child_header, child_ref, m_alloc), childs_parent, + child_ref_ndx, m_alloc, elem_ndx_in_child, state); // Throws + } + else { + Array child(m_alloc); + child.init_from_mem(MemRef(child_header, child_ref, m_alloc)); + child.set_parent(&childs_parent, child_ref_ndx); + new_sibling_ref = child.bptree_append(state); // Throws + } + + if (REALM_LIKELY(!new_sibling_ref)) { + // +2 because stored value is 1 + 2*total_elems_in_subtree + adjust(size() - 1, +2); // Throws + return 0; // Child was not split, so parent was not split either + } + + Array offsets(m_alloc); + int_fast64_t first_value = get(0); + if (first_value % 2 == 0) { + // Offsets array is present (general form) + offsets.init_from_ref(to_ref(first_value)); + offsets.set_parent(this, 0); + } + size_t child_ndx = child_ref_ndx - 1; + return insert_bptree_child(offsets, child_ndx, new_sibling_ref, state); // Throws +} + + +template +ref_type Array::bptree_insert(size_t elem_ndx, TreeInsert& state) +{ + REALM_ASSERT_3(size(), >=, 1 + 1 + 1); // At least one child + + // Conversion to general form if in compact form. Since this + // conversion will occur from root to leaf, it will maintain + // invar:bptree-node-form. + Array offsets(m_alloc); + ensure_bptree_offsets(offsets); // Throws + + size_t child_ndx, elem_ndx_in_child; + if (elem_ndx == 0) { + // Optimization for prepend + child_ndx = 0; + elem_ndx_in_child = 0; + } + else { + // There is a choice to be made when the element is to be + // inserted between two subtrees. It can either be appended to + // the first subtree, or it can be prepended to the second + // one. We currently always append to the first subtree. It is + // essentially a matter of using the lower vs. the upper bound + // when searching through the offsets array. + child_ndx = offsets.lower_bound_int(elem_ndx); + REALM_ASSERT_3(child_ndx, <, size() - 2); + size_t elem_ndx_offset = child_ndx == 0 ? 0 : to_size_t(offsets.get(child_ndx - 1)); + elem_ndx_in_child = elem_ndx - elem_ndx_offset; + } + + ArrayParent& childs_parent = *this; + size_t child_ref_ndx = child_ndx + 1; + ref_type child_ref = get_as_ref(child_ref_ndx), new_sibling_ref; + char* child_header = static_cast(m_alloc.translate(child_ref)); + bool child_is_leaf = !get_is_inner_bptree_node_from_header(child_header); + if (child_is_leaf) { + REALM_ASSERT_3(elem_ndx_in_child, <=, REALM_MAX_BPNODE_SIZE); + new_sibling_ref = TreeTraits::leaf_insert(MemRef(child_header, child_ref, m_alloc), childs_parent, + child_ref_ndx, m_alloc, elem_ndx_in_child, state); // Throws + } + else { + Array child(m_alloc); + child.init_from_mem(MemRef(child_header, child_ref, m_alloc)); + child.set_parent(&childs_parent, child_ref_ndx); + new_sibling_ref = child.bptree_insert(elem_ndx_in_child, state); // Throws + } + + if (REALM_LIKELY(!new_sibling_ref)) { + // +2 because stored value is 1 + 2*total_elems_in_subtree + adjust(size() - 1, +2); // Throws + offsets.adjust(child_ndx, offsets.size(), +1); + return 0; // Child was not split, so parent was not split either + } + + return insert_bptree_child(offsets, child_ndx, new_sibling_ref, state); // Throws +} + + +//************************************************************************************* +// Finding code * +//************************************************************************************* + +template +int64_t Array::get(size_t ndx) const noexcept +{ + return get_universal(m_data, ndx); +} + +template +int64_t Array::get_universal(const char* data, size_t ndx) const +{ + if (w == 0) { + return 0; + } + else if (w == 1) { + size_t offset = ndx >> 3; + return (data[offset] >> (ndx & 7)) & 0x01; + } + else if (w == 2) { + size_t offset = ndx >> 2; + return (data[offset] >> ((ndx & 3) << 1)) & 0x03; + } + else if (w == 4) { + size_t offset = ndx >> 1; + return (data[offset] >> ((ndx & 1) << 2)) & 0x0F; + } + else if (w == 8) { + return *reinterpret_cast(data + ndx); + } + else if (w == 16) { + size_t offset = ndx * 2; + return *reinterpret_cast(data + offset); + } + else if (w == 32) { + size_t offset = ndx * 4; + return *reinterpret_cast(data + offset); + } + else if (w == 64) { + size_t offset = ndx * 8; + return *reinterpret_cast(data + offset); + } + else { + REALM_ASSERT_DEBUG(false); + return int64_t(-1); + } +} + +/* +find() (calls find_optimized()) will call match() for each search result. + +If pattern == true: + 'indexpattern' contains a 64-bit chunk of elements, each of 'width' bits in size where each element indicates a + match if its lower bit is set, otherwise it indicates a non-match. 'index' tells the database row index of the + first element. You must return true if you chose to 'consume' the chunk or false if not. If not, then Array-finder + will afterwards call match() successive times with pattern == false. + +If pattern == false: + 'index' tells the row index of a single match and 'value' tells its value. Return false to make Array-finder break + its search or return true to let it continue until 'end' or 'limit'. + +Array-finder decides itself if - and when - it wants to pass you an indexpattern. It depends on array bit width, match +frequency, and whether the arithemetic and computations for the given search criteria makes it feasible to construct +such a pattern. +*/ + +// These wrapper functions only exist to enable a possibility to make the compiler see that 'value' and/or 'index' are +// unused, such that caller's computation of these values will not be made. Only works if find_action() and +// find_action_pattern() rewritten as macros. Note: This problem has been fixed in next upcoming array.hpp version +template +bool Array::find_action(size_t index, util::Optional value, QueryState* state, + Callback callback) const +{ + if (action == act_CallbackIdx) + return callback(index); + else + return state->match(index, 0, value); +} +template +bool Array::find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const +{ + static_cast(callback); + if (action == act_CallbackIdx) { + // Possible future optimization: call callback(index) like in above find_action(), in a loop for each bit set + // in 'pattern' + return false; + } + return state->match(index, pattern, 0); +} + + +template +uint64_t Array::cascade(uint64_t a) const +{ + // Takes a chunk of values as argument and sets the least significant bit for each + // element which is zero or non-zero, depending on the template parameter. + // Example for zero=true: + // width == 4 and a = 0x5fd07a107610f610 + // will return: 0x0001000100010001 + + // static values needed for fast population count + const uint64_t m1 = 0x5555555555555555ULL; + + if (width == 1) { + return zero ? ~a : a; + } + else if (width == 2) { + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0x3 * 0x1; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a &= m1; // isolate single bit in each segment + if (zero) + a ^= m1; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 4) { + const uint64_t m = ~0ULL / 0xF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xF * 0x7; + const uint64_t c2 = ~0ULL / 0xF * 0x3; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 8) { + const uint64_t m = ~0ULL / 0xFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFF * 0x7F; + const uint64_t c2 = ~0ULL / 0xFF * 0x3F; + const uint64_t c3 = ~0ULL / 0xFF * 0x0F; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 16) { + const uint64_t m = ~0ULL / 0xFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFFFF * 0x7FFF; + const uint64_t c2 = ~0ULL / 0xFFFF * 0x3FFF; + const uint64_t c3 = ~0ULL / 0xFFFF * 0x0FFF; + const uint64_t c4 = ~0ULL / 0xFFFF * 0x00FF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + + else if (width == 32) { + const uint64_t m = ~0ULL / 0xFFFFFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL / 0xFFFFFFFF * 0x7FFFFFFF; + const uint64_t c2 = ~0ULL / 0xFFFFFFFF * 0x3FFFFFFF; + const uint64_t c3 = ~0ULL / 0xFFFFFFFF * 0x0FFFFFFF; + const uint64_t c4 = ~0ULL / 0xFFFFFFFF * 0x00FFFFFF; + const uint64_t c5 = ~0ULL / 0xFFFFFFFF * 0x0000FFFF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a |= (a >> 16) & c5; + a &= m; // isolate single bit in each segment + if (zero) + a ^= m; // reverse isolated bits if checking for zeroed segments + + return a; + } + else if (width == 64) { + return (a == 0) == zero; + } + else { + REALM_ASSERT_DEBUG(false); + return uint64_t(-1); + } +} + +// This is the main finding function for Array. Other finding functions are just wrappers around this one. +// Search for 'value' using condition cond (Equal, NotEqual, Less, etc) and call find_action() or +// find_action_pattern() for each match. Break and return if find_action() returns false or 'end' is reached. + +// If nullable_array is set, then find_optimized() will treat the array is being nullable, i.e. it will skip the +// first entry and compare correctly against null, etc. +// +// If find_null is set, it means that we search for a null. In that case, `value` is ignored. If find_null is set, +// then nullable_array must be set too. +template +bool Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + REALM_ASSERT(!(find_null && !nullable_array)); + REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + + size_t start2 = start; + cond c; + + if (end == npos) + end = nullable_array ? size() - 1 : size(); + + if (nullable_array) { + // We were called by find() of a nullable array. So skip first entry, take nulls in count, etc, etc. Fixme: + // Huge speed optimizations are possible here! This is a very simple generic method. + for (; start2 < end; start2++) { + int64_t v = get(start2 + 1); + if (c(v, value, v == get(0), find_null)) { + util::Optional v2(v == get(0) ? util::none : util::make_optional(v)); + if (!find_action(start2 + baseindex, v2, state, callback)) + return false; // tell caller to stop aggregating/search + } + } + return true; // tell caller to continue aggregating/search (on next array leafs) + } + + + // Test first few items with no initial time overhead + if (start2 > 0) { + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + + if (m_size > start2 && c(get(start2), value) && start2 < end) { + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + + ++start2; + } + + if (!(m_size > start2 && start2 < end)) + return true; + + if (end == size_t(-1)) + end = m_size; + + // Return immediately if no items in array can match (such as if cond == Greater && value == 100 && + // m_ubound == 15) + if (!c.can_match(value, m_lbound, m_ubound)) + return true; + + // optimization if all items are guaranteed to match (such as cond == NotEqual && value == 100 && m_ubound == 15) + if (c.will_match(value, m_lbound, m_ubound)) { + size_t end2; + + if (action == act_CallbackIdx) + end2 = end; + else { + REALM_ASSERT_DEBUG(state->m_match_count < state->m_limit); + size_t process = state->m_limit - state->m_match_count; + end2 = end - start2 > process ? start2 + process : end; + } + if (action == act_Sum || action == act_Max || action == act_Min) { + int64_t res; + size_t res_ndx = 0; + if (action == act_Sum) + res = Array::sum(start2, end2); + if (action == act_Max) + Array::maximum(res, start2, end2, &res_ndx); + if (action == act_Min) + Array::minimum(res, start2, end2, &res_ndx); + + find_action(res_ndx + baseindex, res, state, callback); + // find_action will increment match count by 1, so we need to `-1` from the number of elements that + // we performed the fast Array methods on. + state->m_match_count += end2 - start2 - 1; + } + else if (action == act_Count) { + state->m_state += end2 - start2; + } + else { + for (; start2 < end2; start2++) + if (!find_action(start2 + baseindex, get(start2), state, callback)) + return false; + } + return true; + } + + // finder cannot handle this bitwidth + REALM_ASSERT_3(m_width, !=, 0); + +#if defined(REALM_COMPILER_SSE) + // Only use SSE if payload is at least one SSE chunk (128 bits) in size. Also note taht SSE doesn't support + // Less-than comparison for 64-bit values. + if ((!(std::is_same::value && m_width == 64)) && end - start2 >= sizeof(__m128i) && m_width >= 8 && + (sseavx<42>() || (sseavx<30>() && std::is_same::value && m_width < 64))) { + + // find_sse() must start2 at 16-byte boundary, so search area before that using compare_equality() + __m128i* const a = reinterpret_cast<__m128i*>(round_up(m_data + start2 * bitwidth / 8, sizeof(__m128i))); + __m128i* const b = reinterpret_cast<__m128i*>(round_down(m_data + end * bitwidth / 8, sizeof(__m128i))); + + if (!compare( + value, start2, (reinterpret_cast(a) - m_data) * 8 / no0(bitwidth), baseindex, state, callback)) + return false; + + // Search aligned area with SSE + if (b > a) { + if (sseavx<42>()) { + if (!find_sse( + value, a, b - a, state, + baseindex + ((reinterpret_cast(a) - m_data) * 8 / no0(bitwidth)), callback)) + return false; + } + else if (sseavx<30>()) { + + if (!find_sse( + value, a, b - a, state, + baseindex + ((reinterpret_cast(a) - m_data) * 8 / no0(bitwidth)), callback)) + return false; + } + } + + // Search remainder with compare_equality() + if (!compare( + value, (reinterpret_cast(b) - m_data) * 8 / no0(bitwidth), end, baseindex, state, callback)) + return false; + + return true; + } + else { + return compare(value, start2, end, baseindex, state, callback); + } +#else + return compare(value, start2, end, baseindex, state, callback); +#endif +} + +template +inline int64_t Array::lower_bits() const +{ + if (width == 1) + return 0xFFFFFFFFFFFFFFFFULL; + else if (width == 2) + return 0x5555555555555555ULL; + else if (width == 4) + return 0x1111111111111111ULL; + else if (width == 8) + return 0x0101010101010101ULL; + else if (width == 16) + return 0x0001000100010001ULL; + else if (width == 32) + return 0x0000000100000001ULL; + else if (width == 64) + return 0x0000000000000001ULL; + else { + REALM_ASSERT_DEBUG(false); + return int64_t(-1); + } +} + +// Tests if any chunk in 'value' is 0 +template +inline bool Array::test_zero(uint64_t value) const +{ + uint64_t hasZeroByte; + uint64_t lower = lower_bits(); + uint64_t upper = lower_bits() * 1ULL << (width == 0 ? 0 : (width - 1ULL)); + hasZeroByte = (value - lower) & ~value & upper; + return hasZeroByte != 0; +} + +// Finds first zero (if eq == true) or non-zero (if eq == false) element in v and returns its position. +// IMPORTANT: This function assumes that at least 1 item matches (test this with test_zero() or other means first)! +template +size_t Array::find_zero(uint64_t v) const +{ + size_t start = 0; + uint64_t hasZeroByte; + // Warning free way of computing (1ULL << width) - 1 + uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); + + if (eq == (((v >> (width * start)) & mask) == 0)) { + return 0; + } + + // Bisection optimization, speeds up small bitwidths with high match frequency. More partions than 2 do NOT pay + // off because the work done by test_zero() is wasted for the cases where the value exists in first half, but + // useful if it exists in last half. Sweet spot turns out to be the widths and partitions below. + if (width <= 8) { + hasZeroByte = test_zero(v | 0xffffffff00000000ULL); + if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) { + // 00?? -> increasing + start += 64 / no0(width) / 2; + if (width <= 4) { + hasZeroByte = test_zero(v | 0xffff000000000000ULL); + if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) { + // 000? + start += 64 / no0(width) / 4; + } + } + } + else { + if (width <= 4) { + // ??00 + hasZeroByte = test_zero(v | 0xffffffffffff0000ULL); + if (eq ? !hasZeroByte : (v & 0x000000000000ffffULL) == 0) { + // 0?00 + start += 64 / no0(width) / 4; + } + } + } + } + + while (eq == (((v >> (width * start)) & mask) != 0)) { + // You must only call find_zero() if you are sure that at least 1 item matches + REALM_ASSERT_3(start, <=, 8 * sizeof(v)); + start++; + } + + return start; +} + +// Generate a magic constant used for later bithacks +template +int64_t Array::find_gtlt_magic(int64_t v) const +{ + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t magic = gt ? (~0ULL / no0(mask1) * (mask2 - v)) : (~0ULL / no0(mask1) * v); + return magic; +} + +template +bool Array::find_gtlt_fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, + Callback callback) const +{ + // Tests if a a chunk of values contains values that are greater (if gt == true) or less (if gt == false) than v. + // Fast, but limited to work when all values in the chunk are positive. + + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t m = gt ? (((chunk + magic) | chunk) & ~0ULL / no0(mask1) * (mask2 + 1)) + : ((chunk - magic) & ~chunk & ~0ULL / no0(mask1) * (mask2 + 1)); + size_t p = 0; + while (m) { + if (find_action_pattern(baseindex, m >> (no0(width) - 1), state, callback)) + break; // consumed, so do not call find_action() + + size_t t = first_set_bit64(m) / no0(width); + p += t; + if (!find_action(p + baseindex, (chunk >> (p * width)) & mask1, state, callback)) + return false; + + if ((t + 1) * width == 64) + m = 0; + else + m >>= (t + 1) * width; + p++; + } + + return true; +} + +// clang-format off +template +bool Array::find_gtlt(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const +{ + // Find items in 'chunk' that are greater (if gt == true) or smaller (if gt == false) than 'v'. Fixme, __forceinline can make it crash in vS2010 - find out why + if (width == 1) { + for (size_t t = 0; t < 64; t++) { + if (gt ? static_cast(chunk & 0x1) > v : static_cast(chunk & 0x1) < v) {if (!find_action( t + baseindex, static_cast(chunk & 0x1), state, callback)) return false;} + chunk >>= 1; + } + } + else if (width == 2) { + // Alot (50% +) faster than loop/compiler-unrolled loop + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 0 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 1 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 2 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 3 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 4 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 5 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 6 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 7 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 8 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 9 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 10 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 11 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 12 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 13 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 14 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 15 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 16 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 17 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 18 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 19 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 20 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 21 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 22 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 23 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 24 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 25 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 26 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 27 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 28 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 29 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 30 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + if (gt ? static_cast(chunk & 0x3) > v : static_cast(chunk & 0x3) < v) {if (!find_action( 31 + baseindex, static_cast(chunk & 0x3), state, callback)) return false;} + chunk >>= 2; + } + else if (width == 4) { + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 0 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 1 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 2 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 3 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 4 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 5 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 6 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 7 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 8 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 9 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 10 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 11 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 12 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 13 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 14 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + if (gt ? static_cast(chunk & 0xf) > v : static_cast(chunk & 0xf) < v) {if (!find_action( 15 + baseindex, static_cast(chunk & 0xf), state, callback)) return false;} + chunk >>= 4; + } + else if (width == 8) { + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 0 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 1 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 2 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 3 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 4 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 5 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 6 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 7 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 8; + } + else if (width == 16) { + + if (gt ? static_cast(chunk >> 0 * 16) > v : static_cast(chunk >> 0 * 16) < v) {if (!find_action( 0 + baseindex, static_cast(chunk >> 0 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 1 * 16) > v : static_cast(chunk >> 1 * 16) < v) {if (!find_action( 1 + baseindex, static_cast(chunk >> 1 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 2 * 16) > v : static_cast(chunk >> 2 * 16) < v) {if (!find_action( 2 + baseindex, static_cast(chunk >> 2 * 16), state, callback)) return false;}; + if (gt ? static_cast(chunk >> 3 * 16) > v : static_cast(chunk >> 3 * 16) < v) {if (!find_action( 3 + baseindex, static_cast(chunk >> 3 * 16), state, callback)) return false;}; + } + else if (width == 32) { + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 0 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 32; + if (gt ? static_cast(chunk) > v : static_cast(chunk) < v) {if (!find_action( 1 + baseindex, static_cast(chunk), state, callback)) return false;} + chunk >>= 32; + } + else if (width == 64) { + if (gt ? static_cast(v) > v : static_cast(v) < v) {if (!find_action( 0 + baseindex, static_cast(v), state, callback)) return false;}; + } + + return true; +} +// clang-format on + +/// Find items in this Array that are equal (eq == true) or different (eq = false) from 'value' +template +inline bool Array::compare_equality(int64_t value, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + REALM_ASSERT_DEBUG(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + + size_t ee = round_up(start, 64 / no0(width)); + ee = ee > end ? end : ee; + for (; start < ee; ++start) + if (eq ? (get(start) == value) : (get(start) != value)) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + + if (start >= end) + return true; + + if (width != 32 && width != 64) { + const int64_t* p = reinterpret_cast(m_data + (start * width / 8)); + const int64_t* const e = reinterpret_cast(m_data + (end * width / 8)) - 1; + const uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + const uint64_t valuemask = + ~0ULL / no0(mask) * (value & mask); // the "== ? :" is to avoid division by 0 compiler error + + while (p < e) { + uint64_t chunk = *p; + uint64_t v2 = chunk ^ valuemask; + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(width); + size_t a = 0; + + while (eq ? test_zero(v2) : v2) { + + if (find_action_pattern(start + baseindex, cascade(v2), state, callback)) + break; // consumed + + size_t t = find_zero(v2); + a += t; + + if (a >= 64 / no0(width)) + break; + + if (!find_action(a + start + baseindex, get(start + t), state, callback)) + return false; + v2 >>= (t + 1) * width; + a += 1; + } + + ++p; + } + + // Loop ended because we are near end or end of array. No need to optimize search in remainder in this case + // because end of array means that + // lots of search work has taken place prior to ending here. So time spent searching remainder is relatively + // tiny + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(width); + } + + while (start < end) { + if (eq ? get(start) == value : get(start) != value) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + ++start; + } + + return true; +} + +// There exists a couple of find() functions that take more or less template arguments. Always call the one that +// takes as most as possible to get best performance. + +// This is the one installed into the m_vtable->finder slots. +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const +{ + return find(value, start, end, baseindex, state, CallbackDummy()); +} + +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + REALM_TEMPEX4(return find, cond, action, m_width, Callback, + (value, start, end, baseindex, state, callback, nullable_array, find_null)); +} + +template +bool Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback, bool nullable_array, bool find_null) const +{ + return find_optimized(value, start, end, baseindex, state, callback, + nullable_array, find_null); +} + +#ifdef REALM_COMPILER_SSE +// 'items' is the number of 16-byte SSE chunks. Returns index of packed element relative to first integer of first +// chunk +template +bool Array::find_sse(int64_t value, __m128i* data, size_t items, QueryState* state, size_t baseindex, + Callback callback) const +{ + __m128i search = {0}; + + if (width == 8) + search = _mm_set1_epi8(static_cast(value)); + else if (width == 16) + search = _mm_set1_epi16(static_cast(value)); + else if (width == 32) + search = _mm_set1_epi32(static_cast(value)); + else if (width == 64) { + if (std::is_same::value) + REALM_ASSERT(false); + else + search = _mm_set_epi64x(value, value); + } + + return find_sse_intern(data, &search, items, state, baseindex, callback); +} + +// Compares packed action_data with packed data (equal, less, etc) and performs aggregate action (max, min, sum, +// find_all, etc) on value inside action_data for first match, if any +template +REALM_FORCEINLINE bool Array::find_sse_intern(__m128i* action_data, __m128i* data, size_t items, + QueryState* state, size_t baseindex, Callback callback) const +{ + size_t i = 0; + __m128i compare_result = {0}; + unsigned int resmask; + + // Search loop. Unrolling it has been tested to NOT increase performance (apparently mem bound) + for (i = 0; i < items; ++i) { + // equal / not-equal + if (std::is_same::value || std::is_same::value) { + if (width == 8) + compare_result = _mm_cmpeq_epi8(action_data[i], *data); + if (width == 16) + compare_result = _mm_cmpeq_epi16(action_data[i], *data); + if (width == 32) + compare_result = _mm_cmpeq_epi32(action_data[i], *data); + if (width == 64) { + compare_result = _mm_cmpeq_epi64(action_data[i], *data); // SSE 4.2 only + } + } + + // greater + else if (std::is_same::value) { + if (width == 8) + compare_result = _mm_cmpgt_epi8(action_data[i], *data); + if (width == 16) + compare_result = _mm_cmpgt_epi16(action_data[i], *data); + if (width == 32) + compare_result = _mm_cmpgt_epi32(action_data[i], *data); + if (width == 64) + compare_result = _mm_cmpgt_epi64(action_data[i], *data); + } + // less + else if (std::is_same::value) { + if (width == 8) + compare_result = _mm_cmplt_epi8(action_data[i], *data); + else if (width == 16) + compare_result = _mm_cmplt_epi16(action_data[i], *data); + else if (width == 32) + compare_result = _mm_cmplt_epi32(action_data[i], *data); + else + REALM_ASSERT(false); + } + + resmask = _mm_movemask_epi8(compare_result); + + if (std::is_same::value) + resmask = ~resmask & 0x0000ffff; + + size_t s = i * sizeof(__m128i) * 8 / no0(width); + + while (resmask != 0) { + + uint64_t upper = lower_bits() << (no0(width / 8) - 1); + uint64_t pattern = + resmask & + upper; // fixme, bits at wrong offsets. Only OK because we only use them in 'count' aggregate + if (find_action_pattern(s + baseindex, pattern, state, callback)) + break; + + size_t idx = first_set_bit(resmask) * 8 / no0(width); + s += idx; + if (!find_action( + s + baseindex, get_universal(reinterpret_cast(action_data), s), state, callback)) + return false; + resmask >>= (idx + 1) * no0(width) / 8; + ++s; + } + } + + return true; +} +#endif // REALM_COMPILER_SSE + +template +bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + cond c; + REALM_ASSERT_3(start, <=, end); + if (start == end) + return true; + + + int64_t v; + + // We can compare first element without checking for out-of-range + v = get(start); + if (c(v, foreign->get(start))) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + + start++; + + if (start + 3 < end) { + v = get(start); + if (c(v, foreign->get(start))) + if (!find_action(start + baseindex, v, state, callback)) + return false; + + v = get(start + 1); + if (c(v, foreign->get(start + 1))) + if (!find_action(start + 1 + baseindex, v, state, callback)) + return false; + + v = get(start + 2); + if (c(v, foreign->get(start + 2))) + if (!find_action(start + 2 + baseindex, v, state, callback)) + return false; + + start += 3; + } + else if (start == end) { + return true; + } + + bool r; + REALM_TEMPEX4(r = compare_leafs, cond, action, m_width, Callback, + (foreign, start, end, baseindex, state, callback)) + return r; +} + + +template +bool Array::compare_leafs(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + size_t fw = foreign->m_width; + bool r; + REALM_TEMPEX5(r = compare_leafs_4, cond, action, width, Callback, fw, + (foreign, start, end, baseindex, state, callback)) + return r; +} + + +template +bool Array::compare_leafs_4(const Array* foreign, size_t start, size_t end, size_t baseindex, + QueryState* state, Callback callback) const +{ + cond c; + char* foreign_m_data = foreign->m_data; + + if (width == 0 && foreign_width == 0) { + if (c(0, 0)) { + while (start < end) { + if (!find_action(start + baseindex, 0, state, callback)) + return false; + start++; + } + } + else { + return true; + } + } + + +#if defined(REALM_COMPILER_SSE) + if (sseavx<42>() && width == foreign_width && (width == 8 || width == 16 || width == 32)) { + // We can only use SSE if both bitwidths are equal and above 8 bits and all values are signed + while (start < end && (((reinterpret_cast(m_data) & 0xf) * 8 + start * width) % (128) != 0)) { + int64_t v = get_universal(m_data, start); + int64_t fv = get_universal(foreign_m_data, start); + if (c(v, fv)) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + start++; + } + if (start == end) + return true; + + + size_t sse_items = (end - start) * width / 128; + size_t sse_end = start + sse_items * 128 / no0(width); + + while (start < sse_end) { + __m128i* a = reinterpret_cast<__m128i*>(m_data + start * width / 8); + __m128i* b = reinterpret_cast<__m128i*>(foreign_m_data + start * width / 8); + + bool continue_search = + find_sse_intern(a, b, 1, state, baseindex + start, callback); + + if (!continue_search) + return false; + + start += 128 / no0(width); + } + } +#endif + + while (start < end) { + int64_t v = get_universal(m_data, start); + int64_t fv = get_universal(foreign_m_data, start); + + if (c(v, fv)) { + if (!find_action(start + baseindex, v, state, callback)) + return false; + } + + start++; + } + + return true; +} + + +template +bool Array::compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + bool ret = false; + + if (std::is_same::value) + ret = compare_equality(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_equality(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_relation(value, start, end, baseindex, state, callback); + else if (std::is_same::value) + ret = compare_relation(value, start, end, baseindex, state, callback); + else + REALM_ASSERT_DEBUG(false); + + return ret; +} + +template +bool Array::compare_relation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + uint64_t mask = (bitwidth == 64 ? ~0ULL : ((1ULL << (bitwidth == 64 ? 0 : bitwidth)) - + 1ULL)); // Warning free way of computing (1ULL << width) - 1 + + size_t ee = round_up(start, 64 / no0(bitwidth)); + ee = ee > end ? end : ee; + for (; start < ee; start++) { + if (gt ? (get(start) > value) : (get(start) < value)) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + } + + if (start >= end) + return true; // none found, continue (return true) regardless what find_action() would have returned on match + + const int64_t* p = reinterpret_cast(m_data + (start * bitwidth / 8)); + const int64_t* const e = reinterpret_cast(m_data + (end * bitwidth / 8)) - 1; + + // Matches are rare enough to setup fast linear search for remaining items. We use + // bit hacks from http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + + if (bitwidth == 1 || bitwidth == 2 || bitwidth == 4 || bitwidth == 8 || bitwidth == 16) { + uint64_t magic = find_gtlt_magic(value); + + // Bit hacks only work if searched item has its most significant bit clear for 'greater than' or + // 'item <= 1 << bitwidth' for 'less than' + if (value != int64_t((magic & mask)) && value >= 0 && bitwidth >= 2 && + value <= static_cast((mask >> 1) - (gt ? 1 : 0))) { + // 15 ms + while (p < e) { + uint64_t upper = lower_bits() << (no0(bitwidth) - 1); + + const int64_t v = *p; + size_t idx; + + // Bit hacks only works if all items in chunk have their most significant bit clear. Test this: + upper = upper & v; + + if (!upper) { + idx = find_gtlt_fast( + v, magic, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback); + } + else + idx = find_gtlt( + value, v, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback); + + if (!idx) + return false; + ++p; + } + } + else { + // 24 ms + while (p < e) { + int64_t v = *p; + if (!find_gtlt( + value, v, state, (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth) + baseindex, + callback)) + return false; + ++p; + } + } + start = (p - reinterpret_cast(m_data)) * 8 * 8 / no0(bitwidth); + } + + // matchcount logic in SIMD no longer pays off for 32/64 bit ints because we have just 4/2 elements + + // Test unaligned end and/or values of width > 16 manually + while (start < end) { + if (gt ? get(start) > value : get(start) < value) { + if (!find_action(start + baseindex, get(start), state, callback)) + return false; + } + ++start; + } + return true; +} + +template +size_t Array::find_first(int64_t value, size_t start, size_t end) const +{ + REALM_ASSERT(start <= m_size && (end <= m_size || end == size_t(-1)) && start <= end); + QueryState state; + state.init(act_ReturnFirst, nullptr, + 1); // todo, would be nice to avoid this in order to speed up find_first loops + Finder finder = m_vtable->finder[cond::condition]; + (this->*finder)(value, start, end, 0, &state); + + return static_cast(state.m_state); +} + +//************************************************************************************* +// Finding code ends * +//************************************************************************************* + + +} // namespace realm + +#endif // REALM_ARRAY_HPP diff --git a/Pods/Realm/include/core/realm/array_basic.hpp b/Pods/Realm/include/core/realm/array_basic.hpp new file mode 100644 index 0000000..317f11f --- /dev/null +++ b/Pods/Realm/include/core/realm/array_basic.hpp @@ -0,0 +1,116 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BASIC_HPP +#define REALM_ARRAY_BASIC_HPP + +#include + +namespace realm { + +/// A BasicArray can currently only be used for simple unstructured +/// types like float, double. +template +class BasicArray : public Array { +public: + explicit BasicArray(Allocator&) noexcept; + ~BasicArray() noexcept override + { + } + + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept; + void add(T value); + void set(size_t ndx, T value); + void set_null(size_t ndx); + void insert(size_t ndx, T value); + void erase(size_t ndx); + void truncate(size_t size); + void clear(); + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn* result, T value, size_t add_offset = 0, size_t begin = 0, size_t end = npos) const; + + size_t count(T value, size_t begin = 0, size_t end = npos) const; + bool maximum(T& result, size_t begin = 0, size_t end = npos) const; + bool minimum(T& result, size_t begin = 0, size_t end = npos) const; + + /// Compare two arrays for equality. + bool compare(const BasicArray&) const; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static T get(const char* header, size_t ndx) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, T, TreeInsertBase& state); + + size_t lower_bound(T value) const noexcept; + size_t upper_bound(T value) const noexcept; + + /// Construct a basic array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to `T()`. + static MemRef create_array(size_t size, Allocator&); + + static MemRef create_array(Array::Type leaf_type, bool context_flag, size_t size, T value, Allocator&); + + /// Create a new empty array and attach this accessor to it. This + /// does not modify the parent reference information of this + /// accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(Array::Type = type_Normal, bool context_flag = false); + + /// Construct a copy of the specified slice of this basic array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t size, Allocator& target_alloc) const; + MemRef slice_and_clone_children(size_t offset, size_t size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t find(T target, size_t begin, size_t end) const; + + size_t calc_byte_len(size_t count, size_t width) const override; + virtual size_t calc_item_count(size_t bytes, size_t width) const noexcept override; + + template + bool minmax(T& result, size_t begin, size_t end) const; + + /// Calculate the total number of bytes needed for a basic array + /// with the specified number of elements. This includes the size + /// of the header. The result will be upwards aligned to the + /// closest 8-byte boundary. + static size_t calc_aligned_byte_size(size_t size); +}; + + +// Class typedefs for BasicArray's: ArrayFloat and ArrayDouble +typedef BasicArray ArrayFloat; +typedef BasicArray ArrayDouble; + +} // namespace realm + +#include + +#endif // REALM_ARRAY_BASIC_HPP diff --git a/Pods/Realm/include/core/realm/array_basic_tpl.hpp b/Pods/Realm/include/core/realm/array_basic_tpl.hpp new file mode 100644 index 0000000..99b21d1 --- /dev/null +++ b/Pods/Realm/include/core/realm/array_basic_tpl.hpp @@ -0,0 +1,457 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BASIC_TPL_HPP +#define REALM_ARRAY_BASIC_TPL_HPP + +#include +#include +#include +#include + +#include + +namespace realm { + +template +inline BasicArray::BasicArray(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +template +inline MemRef BasicArray::create_array(size_t init_size, Allocator& allocator) +{ + size_t byte_size_0 = calc_aligned_byte_size(init_size); // Throws + // Adding zero to Array::initial_capacity to avoid taking the + // address of that member + size_t byte_size = std::max(byte_size_0, Array::initial_capacity + 0); // Throws + + MemRef mem = allocator.alloc(byte_size); // Throws + + bool is_inner_bptree_node = false; + bool has_refs = false; + bool context_flag = false; + int width = sizeof(T); + init_header(mem.get_addr(), is_inner_bptree_node, has_refs, context_flag, wtype_Multiply, width, init_size, + byte_size); + + return mem; +} + + +template +inline MemRef BasicArray::create_array(Array::Type type, bool context_flag, size_t init_size, T value, + Allocator& allocator) +{ + REALM_ASSERT(type == Array::type_Normal); + REALM_ASSERT(!context_flag); + MemRef mem = create_array(init_size, allocator); + if (init_size) { + BasicArray tmp(allocator); + tmp.init_from_mem(mem); + for (size_t i = 0; i < init_size; ++i) { + tmp.set(i, value); + } + return tmp.get_mem(); + } + return mem; +} + + +template +inline void BasicArray::create(Array::Type type, bool context_flag) +{ + REALM_ASSERT(type == Array::type_Normal); + REALM_ASSERT(!context_flag); + size_t length = 0; + MemRef mem = create_array(length, get_alloc()); // Throws + init_from_mem(mem); +} + + +template +MemRef BasicArray::slice(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + REALM_ASSERT(is_attached()); + + // FIXME: This can be optimized as a single contiguous copy + // operation. + BasicArray array_slice(target_alloc); + _impl::ShallowArrayDestroyGuard dg(&array_slice); + array_slice.create(); // Throws + size_t begin = offset; + size_t end = offset + slice_size; + for (size_t i = begin; i != end; ++i) { + T value = get(i); + array_slice.add(value); // Throws + } + dg.release(); + return array_slice.get_mem(); +} + +template +MemRef BasicArray::slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + // BasicArray never contains refs, so never has children. + return slice(offset, slice_size, target_alloc); +} + + +template +inline void BasicArray::add(T value) +{ + insert(m_size, value); +} + + +template +inline T BasicArray::get(size_t ndx) const noexcept +{ + return *(reinterpret_cast(m_data) + ndx); +} + + +template +inline bool BasicArray::is_null(size_t ndx) const noexcept +{ + // FIXME: This assumes BasicArray will only ever be instantiated for float-like T. + static_assert(realm::is_any::value, "T can only be float or double"); + auto x = get(ndx); + return null::is_null_float(x); +} + + +template +inline T BasicArray::get(const char* header, size_t ndx) noexcept +{ + const char* data = get_data_from_header(header); + // This casting assumes that T can be aliged on an 8-bype + // boundary (since data is aligned on an 8-byte boundary.) + return *(reinterpret_cast(data) + ndx); +} + + +template +inline void BasicArray::set(size_t ndx, T value) +{ + REALM_ASSERT_3(ndx, <, m_size); + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // Set the value + T* data = reinterpret_cast(m_data) + ndx; + *data = value; +} + +template +inline void BasicArray::set_null(size_t ndx) +{ + // FIXME: This assumes BasicArray will only ever be instantiated for float-like T. + set(ndx, null::get_null_float()); +} + +template +void BasicArray::insert(size_t ndx, T value) +{ + REALM_ASSERT_3(ndx, <=, m_size); + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // Make room for the new value + alloc(m_size + 1, m_width); // Throws + + // Move values below insertion + if (ndx != m_size) { + char* src_begin = m_data + ndx * m_width; + char* src_end = m_data + m_size * m_width; + char* dst_end = src_end + m_width; + std::copy_backward(src_begin, src_end, dst_end); + } + + // Set the value + T* data = reinterpret_cast(m_data) + ndx; + *data = value; + + ++m_size; +} + +template +void BasicArray::erase(size_t ndx) +{ + REALM_ASSERT_3(ndx, <, m_size); + + // Check if we need to copy before modifying + copy_on_write(); // Throws + + // move data under deletion up + if (ndx < m_size - 1) { + char* dst_begin = m_data + ndx * m_width; + const char* src_begin = dst_begin + m_width; + const char* src_end = m_data + m_size * m_width; + std::copy(src_begin, src_end, dst_begin); + } + + // Update size (also in header) + --m_size; + set_header_size(m_size); +} + +template +void BasicArray::truncate(size_t to_size) +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT_3(to_size, <=, m_size); + + copy_on_write(); // Throws + + // Update size in accessor and in header. This leaves the capacity + // unchanged. + m_size = to_size; + set_header_size(to_size); +} + +template +inline void BasicArray::clear() +{ + truncate(0); // Throws +} + +template +bool BasicArray::compare(const BasicArray& a) const +{ + size_t n = size(); + if (a.size() != n) + return false; + const T* data_1 = reinterpret_cast(m_data); + const T* data_2 = reinterpret_cast(a.m_data); + return std::equal(data_1, data_1 + n, data_2); +} + + +template +size_t BasicArray::calc_byte_len(size_t for_size, size_t) const +{ + // FIXME: Consider calling `calc_aligned_byte_size(size)` + // instead. Note however, that calc_byte_len() is supposed to return + // the unaligned byte size. It is probably the case that no harm + // is done by returning the aligned version, and most callers of + // calc_byte_len() will actually benefit if calc_byte_len() was + // changed to always return the aligned byte size. + return header_size + for_size * sizeof(T); +} + +template +size_t BasicArray::calc_item_count(size_t bytes, size_t) const noexcept +{ + size_t bytes_without_header = bytes - header_size; + return bytes_without_header / sizeof(T); +} + +template +size_t BasicArray::find(T value, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + const T* i = std::find(data + begin, data + end, value); + return i == data + end ? not_found : size_t(i - data); +} + +template +inline size_t BasicArray::find_first(T value, size_t begin, size_t end) const +{ + return this->find(value, begin, end); +} + +template +void BasicArray::find_all(IntegerColumn* result, T value, size_t add_offset, size_t begin, size_t end) const +{ + size_t first = begin - 1; + for (;;) { + first = this->find(value, first + 1, end); + if (first == not_found) + break; + + Array::add_to_column(result, first + add_offset); + } +} + +template +size_t BasicArray::count(T value, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + return std::count(data + begin, data + end, value); +} + +#if 0 +// currently unused +template +double BasicArray::sum(size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + REALM_ASSERT(begin <= m_size && end <= m_size && begin <= end); + const T* data = reinterpret_cast(m_data); + return std::accumulate(data + begin, data + end, double(0)); +} +#endif + +template +template +bool BasicArray::minmax(T& result, size_t begin, size_t end) const +{ + if (end == npos) + end = m_size; + if (m_size == 0) + return false; + REALM_ASSERT(begin < m_size && end <= m_size && begin < end); + + T m = get(begin); + ++begin; + for (; begin < end; ++begin) { + T val = get(begin); + if (find_max ? val > m : val < m) + m = val; + } + result = m; + return true; +} + +template +bool BasicArray::maximum(T& result, size_t begin, size_t end) const +{ + return minmax(result, begin, end); +} + +template +bool BasicArray::minimum(T& result, size_t begin, size_t end) const +{ + return minmax(result, begin, end); +} + + +template +ref_type BasicArray::bptree_leaf_insert(size_t ndx, T value, TreeInsertBase& state) +{ + size_t leaf_size = size(); + REALM_ASSERT_3(leaf_size, <=, REALM_MAX_BPNODE_SIZE); + if (leaf_size < ndx) + ndx = leaf_size; + if (REALM_LIKELY(leaf_size < REALM_MAX_BPNODE_SIZE)) { + insert(ndx, value); + return 0; // Leaf was not split + } + + // Split leaf node + BasicArray new_leaf(get_alloc()); + new_leaf.create(); // Throws + if (ndx == leaf_size) { + new_leaf.add(value); + state.m_split_offset = ndx; + } + else { + // FIXME: Could be optimized by first resizing the target + // array, then copy elements with std::copy(). + for (size_t i = ndx; i != leaf_size; ++i) + new_leaf.add(get(i)); + truncate(ndx); + add(value); + state.m_split_offset = ndx + 1; + } + state.m_split_size = leaf_size + 1; + return new_leaf.get_ref(); +} + +template +inline size_t BasicArray::lower_bound(T value) const noexcept +{ + const T* begin = reinterpret_cast(m_data); + const T* end = begin + size(); + return std::lower_bound(begin, end, value) - begin; +} + +template +inline size_t BasicArray::upper_bound(T value) const noexcept +{ + const T* begin = reinterpret_cast(m_data); + const T* end = begin + size(); + return std::upper_bound(begin, end, value) - begin; +} + +template +inline size_t BasicArray::calc_aligned_byte_size(size_t size) +{ + size_t max = std::numeric_limits::max(); + size_t max_2 = max & ~size_t(7); // Allow for upwards 8-byte alignment + if (size > (max_2 - header_size) / sizeof(T)) + throw std::runtime_error("Byte size overflow"); + size_t byte_size = header_size + size * sizeof(T); + REALM_ASSERT_3(byte_size, >, 0); + size_t aligned_byte_size = ((byte_size - 1) | 7) + 1; // 8-byte alignment + return aligned_byte_size; +} + + +#ifdef REALM_DEBUG + +// LCOV_EXCL_START +template +void BasicArray::to_dot(std::ostream& out, StringData title) const +{ + ref_type ref = get_ref(); + if (title.size() != 0) { + out << "subgraph cluster_" << ref << " {\n"; + out << " label = \"" << title << "\";\n"; + out << " color = white;\n"; + } + + out << "n" << std::hex << ref << std::dec << "[shape=none,label=<"; + out << "
\n"; + + // Header + out << "\n"; + + // Values + size_t n = m_size; + for (size_t i = 0; i != n; ++i) + out << "\n"; + + out << "
"; + out << "0x" << std::hex << ref << std::dec << "
"; + out << "
" << get(i) << "
>];\n"; + + if (title.size() != 0) + out << "}\n"; + + to_dot_parent_edge(out); +} +// LCOV_EXCL_STOP + +#endif // REALM_DEBUG + + +} // namespace realm + +#endif // REALM_ARRAY_BASIC_TPL_HPP diff --git a/Pods/Realm/include/core/realm/array_binary.hpp b/Pods/Realm/include/core/realm/array_binary.hpp new file mode 100644 index 0000000..414b0df --- /dev/null +++ b/Pods/Realm/include/core/realm/array_binary.hpp @@ -0,0 +1,255 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BINARY_HPP +#define REALM_ARRAY_BINARY_HPP + +#include +#include +#include +#include + +namespace realm { + +/* +STORAGE FORMAT +--------------------------------------------------------------------------------------- +ArrayBinary stores binary elements using two ArrayInteger and one ArrayBlob. The ArrayBlob can only store one +single concecutive array of bytes (contrary to its 'Array' name that misleadingly indicates it could store multiple +elements). + +Assume we have the strings "a", "", "abc", null, "ab". Then the three arrays will contain: + +ArrayInteger m_offsets 1, 1, 5, 5, 6 +ArrayBlob m_blob aabcab +ArrayInteger m_nulls 0, 0, 0, 1, 0 // 1 indicates null, 0 indicates non-null + +So for each element the ArrayInteger, the ArrayInteger points into the ArrayBlob at the position of the first +byte of the next element. + +m_nulls is always present (except for old database files; see below), so any ArrayBinary is always nullable! +The nullable property (such as throwing exception upon set(null) on non-nullable column, etc) is handled on +column level only. + +DATABASE FILE VERSION CHANGES +--------------------------------------------------------------------------------------- +Old database files do not have any m_nulls array. To be backwardscompatible, many methods will have tests like +`if(Array::size() == 3)` and have a backwards compatible code paths for these (e.g. avoid writing to m_nulls +in set(), etc). This way no file format upgrade is needed to support nulls for BinaryData. +*/ + +class ArrayBinary : public Array { +public: + explicit ArrayBinary(Allocator&) noexcept; + ~ArrayBinary() noexcept override + { + } + + /// Create a new empty binary array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + // Old database files will not have the m_nulls array, so we need code paths for + // backwards compatibility for these cases. + bool legacy_array_type() const noexcept; + + //@{ + /// Overriding functions of Array + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + //@} + + bool is_empty() const noexcept; + size_t size() const noexcept; + + BinaryData get(size_t ndx) const noexcept; + size_t read(size_t ndx, size_t pos, char* buffer, size_t max_size) const noexcept; + + void add(BinaryData value, bool add_zero_term = false); + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void insert(size_t ndx, BinaryData value, bool add_zero_term = false); + void erase(size_t ndx); + void truncate(size_t new_size); + void clear(); + void destroy(); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static BinaryData get(const char* header, size_t ndx, Allocator&) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, BinaryData, bool add_zero_term, TreeInsertBase& state); + + static size_t get_size_from_header(const char*, Allocator&) noexcept; + + /// Construct a binary array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to the binary value `defaults`, which can be either + /// null or zero-length non-null (value with size > 0 is not allowed as + /// initialization value). + static MemRef create_array(size_t size, Allocator&, BinaryData defaults); + + /// Construct a copy of the specified slice of this binary array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, bool is_strings, StringData title = StringData()) const; +#endif + bool update_from_parent(size_t old_baseline) noexcept; + +private: + ArrayInteger m_offsets; + ArrayBlob m_blob; + ArrayInteger m_nulls; +}; + + +// Implementation: + +inline ArrayBinary::ArrayBinary(Allocator& allocator) noexcept + : Array(allocator) + , m_offsets(allocator) + , m_blob(allocator) + , m_nulls(allocator) +{ + m_offsets.set_parent(this, 0); + m_blob.set_parent(this, 1); + m_nulls.set_parent(this, 2); +} + +inline void ArrayBinary::create() +{ + size_t init_size = 0; + BinaryData defaults = BinaryData{}; // This init value is ignored because size = 0 + MemRef mem = create_array(init_size, get_alloc(), defaults); // Throws + init_from_mem(mem); +} + +inline void ArrayBinary::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT(ref); + char* header = get_alloc().translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); +} + +inline void ArrayBinary::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + +inline bool ArrayBinary::is_empty() const noexcept +{ + return m_offsets.is_empty(); +} + +// Old database files will not have the m_nulls array, so we need code paths for +// backwards compatibility for these cases. We can test if m_nulls exists by looking +// at number of references in this ArrayBinary. +inline bool ArrayBinary::legacy_array_type() const noexcept +{ + if (Array::size() == 3) + return false; // New database file + else if (Array::size() == 2) + return true; // Old database file + else + REALM_ASSERT(false); // Should never happen + return false; +} + +inline size_t ArrayBinary::size() const noexcept +{ + return m_offsets.size(); +} + +inline BinaryData ArrayBinary::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_offsets.size()); + + if (!legacy_array_type() && m_nulls.get(ndx)) { + return BinaryData(); + } + else { + size_t begin = ndx ? to_size_t(m_offsets.get(ndx - 1)) : 0; + size_t end = to_size_t(m_offsets.get(ndx)); + + BinaryData bd = BinaryData(m_blob.get(begin), end - begin); + // Old database file (non-nullable column should never return null) + REALM_ASSERT(!bd.is_null()); + return bd; + } +} + +inline void ArrayBinary::truncate(size_t new_size) +{ + REALM_ASSERT_3(new_size, <, m_offsets.size()); + + size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + + m_offsets.truncate(new_size); + m_blob.truncate(blob_size); + if (!legacy_array_type()) + m_nulls.truncate(new_size); +} + +inline void ArrayBinary::clear() +{ + m_blob.clear(); + m_offsets.clear(); + if (!legacy_array_type()) + m_nulls.clear(); +} + +inline void ArrayBinary::destroy() +{ + m_blob.destroy(); + m_offsets.destroy(); + if (!legacy_array_type()) + m_nulls.destroy(); + Array::destroy(); +} + +inline size_t ArrayBinary::get_size_from_header(const char* header, Allocator& alloc) noexcept +{ + ref_type offsets_ref = to_ref(Array::get(header, 0)); + const char* offsets_header = alloc.translate(offsets_ref); + return Array::get_size_from_header(offsets_header); +} + +inline bool ArrayBinary::update_from_parent(size_t old_baseline) noexcept +{ + bool res = Array::update_from_parent(old_baseline); + if (res) { + m_blob.update_from_parent(old_baseline); + m_offsets.update_from_parent(old_baseline); + if (!legacy_array_type()) + m_nulls.update_from_parent(old_baseline); + } + return res; +} + +} // namespace realm + +#endif // REALM_ARRAY_BINARY_HPP diff --git a/Pods/Realm/include/core/realm/array_blob.hpp b/Pods/Realm/include/core/realm/array_blob.hpp new file mode 100644 index 0000000..7fcf181 --- /dev/null +++ b/Pods/Realm/include/core/realm/array_blob.hpp @@ -0,0 +1,143 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BLOB_HPP +#define REALM_ARRAY_BLOB_HPP + +#include + +namespace realm { + + +class ArrayBlob : public Array { +public: + static constexpr size_t max_binary_size = 0xFFFFF8 - Array::header_size; + + explicit ArrayBlob(Allocator&) noexcept; + ~ArrayBlob() noexcept override + { + } + + const char* get(size_t index) const noexcept; + BinaryData get_at(size_t& pos) const noexcept; + bool is_null(size_t index) const noexcept; + ref_type add(const char* data, size_t data_size, bool add_zero_term = false); + void insert(size_t pos, const char* data, size_t data_size, bool add_zero_term = false); + ref_type replace(size_t begin, size_t end, const char* data, size_t data_size, bool add_zero_term = false); + void erase(size_t begin, size_t end); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static const char* get(const char* header, size_t index) noexcept; + + /// Create a new empty blob (binary) array and attach this + /// accessor to it. This does not modify the parent reference + /// information of this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a blob of the specified size and return just the + /// reference to the underlying memory. All bytes will be + /// initialized to zero. + static MemRef create_array(size_t init_size, Allocator&); + +#ifdef REALM_DEBUG + size_t blob_size() const noexcept; + void verify() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t calc_byte_len(size_t for_size, size_t width) const override; + size_t calc_item_count(size_t bytes, size_t width) const noexcept override; +}; + + +// Implementation: + +// Creates new array (but invalid, call init_from_ref() to init) +inline ArrayBlob::ArrayBlob(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +inline bool ArrayBlob::is_null(size_t index) const noexcept +{ + return (get(index) == nullptr); +} + +inline const char* ArrayBlob::get(size_t index) const noexcept +{ + return m_data + index; +} + +inline ref_type ArrayBlob::add(const char* data, size_t data_size, bool add_zero_term) +{ + return replace(m_size, m_size, data, data_size, add_zero_term); +} + +inline void ArrayBlob::insert(size_t pos, const char* data, size_t data_size, bool add_zero_term) +{ + replace(pos, pos, data, data_size, add_zero_term); +} + +inline void ArrayBlob::erase(size_t begin, size_t end) +{ + const char* data = nullptr; + size_t data_size = 0; + replace(begin, end, data, data_size); +} + +inline const char* ArrayBlob::get(const char* header, size_t pos) noexcept +{ + const char* data = get_data_from_header(header); + return data + pos; +} + +inline void ArrayBlob::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc()); // Throws + init_from_mem(mem); +} + +inline MemRef ArrayBlob::create_array(size_t init_size, Allocator& allocator) +{ + bool context_flag = false; + int_fast64_t value = 0; + return Array::create(type_Normal, context_flag, wtype_Ignore, init_size, value, allocator); // Throws +} + +inline size_t ArrayBlob::calc_byte_len(size_t for_size, size_t) const +{ + return header_size + for_size; +} + +inline size_t ArrayBlob::calc_item_count(size_t bytes, size_t) const noexcept +{ + return bytes - header_size; +} + + +} // namespace realm + +#endif // REALM_ARRAY_BLOB_HPP diff --git a/Pods/Realm/include/core/realm/array_blobs_big.hpp b/Pods/Realm/include/core/realm/array_blobs_big.hpp new file mode 100644 index 0000000..d931837 --- /dev/null +++ b/Pods/Realm/include/core/realm/array_blobs_big.hpp @@ -0,0 +1,218 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_BIG_BLOBS_HPP +#define REALM_ARRAY_BIG_BLOBS_HPP + +#include + +namespace realm { + + +class ArrayBigBlobs : public Array { +public: + typedef BinaryData value_type; + + explicit ArrayBigBlobs(Allocator&, bool nullable) noexcept; + + BinaryData get(size_t ndx) const noexcept; + BinaryData get_at(size_t ndx, size_t& pos) const noexcept; + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void add(BinaryData value, bool add_zero_term = false); + void insert(size_t ndx, BinaryData value, bool add_zero_term = false); + void erase(size_t ndx); + void truncate(size_t new_size); + void clear(); + void destroy(); + + size_t count(BinaryData value, bool is_string = false, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(BinaryData value, bool is_string = false, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, BinaryData value, bool is_string = false, size_t add_offset = 0, + size_t begin = 0, size_t end = npos); + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static BinaryData get(const char* header, size_t ndx, Allocator&) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, BinaryData, bool add_zero_term, TreeInsertBase& state); + + //@{ + /// Those that return a string, discard the terminating zero from + /// the stored value. Those that accept a string argument, add a + /// terminating zero before storing the value. + StringData get_string(size_t ndx) const noexcept; + void add_string(StringData value); + void set_string(size_t ndx, StringData value); + void insert_string(size_t ndx, StringData value); + static StringData get_string(const char* header, size_t ndx, Allocator&, bool nullable) noexcept; + ref_type bptree_leaf_insert_string(size_t ndx, StringData, TreeInsertBase& state); + //@} + + /// Create a new empty big blobs array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a copy of the specified slice of this big blobs + /// array using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void verify() const; + void to_dot(std::ostream&, bool is_strings, StringData title = StringData()) const; +#endif + +private: + bool m_nullable; +}; + + +// Implementation: + +inline ArrayBigBlobs::ArrayBigBlobs(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_nullable(nullable) +{ +} + +inline BinaryData ArrayBigBlobs::get(size_t ndx) const noexcept +{ + ref_type ref = get_as_ref(ndx); + if (ref == 0) + return {}; // realm::null(); + + const char* blob_header = get_alloc().translate(ref); + if (!get_context_flag_from_header(blob_header)) { + const char* value = ArrayBlob::get(blob_header, 0); + size_t blob_size = get_size_from_header(blob_header); + return BinaryData(value, blob_size); + } + return {}; +} + +inline BinaryData ArrayBigBlobs::get(const char* header, size_t ndx, Allocator& alloc) noexcept +{ + ref_type blob_ref = to_ref(Array::get(header, ndx)); + if (blob_ref == 0) + return {}; + + const char* blob_header = alloc.translate(blob_ref); + if (!get_context_flag_from_header(blob_header)) { + const char* blob_data = Array::get_data_from_header(blob_header); + size_t blob_size = Array::get_size_from_header(blob_header); + return BinaryData(blob_data, blob_size); + } + return {}; +} + +inline void ArrayBigBlobs::erase(size_t ndx) +{ + ref_type blob_ref = Array::get_as_ref(ndx); + if (blob_ref != 0) { // nothing to destroy if null + Array::destroy(blob_ref, get_alloc()); // Shallow + } + Array::erase(ndx); +} + +inline void ArrayBigBlobs::truncate(size_t new_size) +{ + Array::truncate_and_destroy_children(new_size); +} + +inline void ArrayBigBlobs::clear() +{ + Array::clear_and_destroy_children(); +} + +inline void ArrayBigBlobs::destroy() +{ + Array::destroy_deep(); +} + +inline StringData ArrayBigBlobs::get_string(size_t ndx) const noexcept +{ + BinaryData bin = get(ndx); + if (bin.is_null()) + return realm::null(); + else + return StringData(bin.data(), bin.size() - 1); // Do not include terminating zero +} + +inline void ArrayBigBlobs::set_string(size_t ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + set(ndx, bin, add_zero_term); +} + +inline void ArrayBigBlobs::add_string(StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + add(bin, add_zero_term); +} + +inline void ArrayBigBlobs::insert_string(size_t ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + insert(ndx, bin, add_zero_term); +} + +inline StringData ArrayBigBlobs::get_string(const char* header, size_t ndx, Allocator& alloc, bool nullable) noexcept +{ + static_cast(nullable); + BinaryData bin = get(header, ndx, alloc); + REALM_ASSERT_DEBUG(!(!nullable && bin.is_null())); + if (bin.is_null()) + return realm::null(); + else + return StringData(bin.data(), bin.size() - 1); // Do not include terminating zero +} + +inline ref_type ArrayBigBlobs::bptree_leaf_insert_string(size_t ndx, StringData value, TreeInsertBase& state) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + return bptree_leaf_insert(ndx, bin, add_zero_term, state); +} + +inline void ArrayBigBlobs::create() +{ + bool context_flag = true; + Array::create(type_HasRefs, context_flag); // Throws +} + +inline MemRef ArrayBigBlobs::slice(size_t offset, size_t slice_size, Allocator& target_alloc) const +{ + return slice_and_clone_children(offset, slice_size, target_alloc); +} + + +} // namespace realm + +#endif // REALM_ARRAY_BIG_BLOBS_HPP diff --git a/Pods/Realm/include/core/realm/array_integer.hpp b/Pods/Realm/include/core/realm/array_integer.hpp new file mode 100644 index 0000000..40822a8 --- /dev/null +++ b/Pods/Realm/include/core/realm/array_integer.hpp @@ -0,0 +1,625 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_INTEGER_HPP +#define REALM_ARRAY_INTEGER_HPP + +#include +#include +#include + +namespace realm { + +class ArrayInteger : public Array { +public: + typedef int64_t value_type; + + explicit ArrayInteger(Allocator&) noexcept; + ~ArrayInteger() noexcept override + { + } + + void create(Type type = type_Normal, bool context_flag = false); + + void add(int64_t value); + void set(size_t ndx, int64_t value); + void set_uint(size_t ndx, uint_fast64_t value) noexcept; + int64_t get(size_t ndx) const noexcept; + uint64_t get_uint(size_t ndx) const noexcept; + static int64_t get(const char* header, size_t ndx) noexcept; + bool compare(const ArrayInteger& a) const noexcept; + + /// Add \a diff to the element at the specified index. + void adjust(size_t ndx, int_fast64_t diff); + + /// Add \a diff to all the elements in the specified index range. + void adjust(size_t begin, size_t end, int_fast64_t diff); + + /// Add signed \a diff to all elements that are greater than, or equal to \a + /// limit. + void adjust_ge(int_fast64_t limit, int_fast64_t diff); + + int64_t operator[](size_t ndx) const noexcept + { + return get(ndx); + } + int64_t front() const noexcept; + int64_t back() const noexcept; + + size_t lower_bound(int64_t value) const noexcept; + size_t upper_bound(int64_t value) const noexcept; + + std::vector to_vector() const; + +private: + template + bool minmax(size_t from, size_t to, uint64_t maxdiff, int64_t* min, int64_t* max) const; +}; + +class ArrayIntNull : public Array { +public: + using value_type = util::Optional; + + explicit ArrayIntNull(Allocator&) noexcept; + ~ArrayIntNull() noexcept override; + + /// Construct an array of the specified type and size, and return just the + /// reference to the underlying memory. All elements will be initialized to + /// the specified value. + static MemRef create_array(Type, bool context_flag, size_t size, value_type value, Allocator&); + void create(Type = type_Normal, bool context_flag = false); + + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + + size_t size() const noexcept; + bool is_empty() const noexcept; + + void insert(size_t ndx, value_type value); + void add(value_type value); + void set(size_t ndx, value_type value) noexcept; + value_type get(size_t ndx) const noexcept; + static value_type get(const char* header, size_t ndx) noexcept; + void get_chunk(size_t ndx, value_type res[8]) const noexcept; + void set_null(size_t ndx) noexcept; + bool is_null(size_t ndx) const noexcept; + int64_t null_value() const noexcept; + + value_type operator[](size_t ndx) const noexcept; + value_type front() const noexcept; + value_type back() const noexcept; + void erase(size_t ndx); + void erase(size_t begin, size_t end); + void truncate(size_t size); + void clear(); + void set_all_to_zero(); + + void move(size_t begin, size_t end, size_t dest_begin); + void move_backward(size_t begin, size_t end, size_t dest_end); + + size_t lower_bound(int64_t value) const noexcept; + size_t upper_bound(int64_t value) const noexcept; + + int64_t sum(size_t start = 0, size_t end = npos) const; + size_t count(int64_t value) const noexcept; + bool maximum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + bool minimum(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + + bool find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex, + QueryState* state) const; + + template + bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // This is the one installed into the m_finder slots. + template + bool find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + bool find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Optimized implementation for release mode + template + bool find_optimized(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const; + + // Called for each search result + template + bool find_action(size_t index, value_type value, QueryState* state, Callback callback) const; + + template + bool find_action_pattern(size_t index, uint64_t pattern, QueryState* state, Callback callback) const; + + // Wrappers for backwards compatibility and for simple use without + // setting up state initialization etc + template + size_t find_first(value_type value, size_t start = 0, size_t end = npos) const; + + void find_all(IntegerColumn* result, value_type value, size_t col_offset = 0, size_t begin = 0, + size_t end = npos) const; + + + size_t find_first(value_type value, size_t begin = 0, size_t end = npos) const; + + + // Overwrite Array::bptree_leaf_insert to correctly split nodes. + ref_type bptree_leaf_insert(size_t ndx, value_type value, TreeInsertBase& state); + + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + + /// Construct a deep copy of the specified slice of this array using the + /// specified target allocator. Subarrays will be cloned. + MemRef slice_and_clone_children(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +protected: + void avoid_null_collision(int64_t value); + +private: + template + bool minmax_helper(int64_t& result, size_t start = 0, size_t end = npos, size_t* return_ndx = nullptr) const; + + int_fast64_t choose_random_null(int64_t incoming) const; + void replace_nulls_with(int64_t new_null); + bool can_use_as_null(int64_t value) const; +}; + + +// Implementation: + +inline ArrayInteger::ArrayInteger(Allocator& allocator) noexcept + : Array(allocator) +{ + m_is_inner_bptree_node = false; +} + +inline void ArrayInteger::add(int64_t value) +{ + Array::add(value); +} + +inline int64_t ArrayInteger::get(size_t ndx) const noexcept +{ + return Array::get(ndx); +} + +inline int64_t ArrayInteger::get(const char* header, size_t ndx) noexcept +{ + return Array::get(header, ndx); +} + +inline void ArrayInteger::set(size_t ndx, int64_t value) +{ + Array::set(ndx, value); +} + +inline void ArrayInteger::set_uint(size_t ndx, uint_fast64_t value) noexcept +{ + // When a value of a signed type is converted to an unsigned type, the C++ + // standard guarantees that negative values are converted from the native + // representation to 2's complement, but the effect of conversions in the + // opposite direction is left unspecified by the + // standard. `realm::util::from_twos_compl()` is used here to perform the + // correct opposite unsigned-to-signed conversion, which reduces to a no-op + // when 2's complement is the native representation of negative values. + set(ndx, util::from_twos_compl(value)); +} + +inline bool ArrayInteger::compare(const ArrayInteger& a) const noexcept +{ + if (a.size() != size()) + return false; + + for (size_t i = 0; i < size(); ++i) { + if (get(i) != a.get(i)) + return false; + } + + return true; +} + +inline int64_t ArrayInteger::front() const noexcept +{ + return Array::front(); +} + +inline int64_t ArrayInteger::back() const noexcept +{ + return Array::back(); +} + +inline void ArrayInteger::adjust(size_t ndx, int_fast64_t diff) +{ + Array::adjust(ndx, diff); +} + +inline void ArrayInteger::adjust(size_t begin, size_t end, int_fast64_t diff) +{ + Array::adjust(begin, end, diff); +} + +inline void ArrayInteger::adjust_ge(int_fast64_t limit, int_fast64_t diff) +{ + Array::adjust_ge(limit, diff); +} + +inline size_t ArrayInteger::lower_bound(int64_t value) const noexcept +{ + return lower_bound_int(value); +} + +inline size_t ArrayInteger::upper_bound(int64_t value) const noexcept +{ + return upper_bound_int(value); +} + + +inline ArrayIntNull::ArrayIntNull(Allocator& allocator) noexcept + : Array(allocator) +{ +} + +inline ArrayIntNull::~ArrayIntNull() noexcept +{ +} + +inline void ArrayIntNull::create(Type type, bool context_flag) +{ + MemRef r = create_array(type, context_flag, 0, util::none, m_alloc); + init_from_mem(r); +} + + +inline size_t ArrayIntNull::size() const noexcept +{ + return Array::size() - 1; +} + +inline bool ArrayIntNull::is_empty() const noexcept +{ + return size() == 0; +} + +inline void ArrayIntNull::insert(size_t ndx, value_type value) +{ + if (value) { + avoid_null_collision(*value); + Array::insert(ndx + 1, *value); + } + else { + Array::insert(ndx + 1, null_value()); + } +} + +inline void ArrayIntNull::add(value_type value) +{ + if (value) { + avoid_null_collision(*value); + Array::add(*value); + } + else { + Array::add(null_value()); + } +} + +inline void ArrayIntNull::set(size_t ndx, value_type value) noexcept +{ + if (value) { + avoid_null_collision(*value); + Array::set(ndx + 1, *value); + } + else { + Array::set(ndx + 1, null_value()); + } +} + +inline void ArrayIntNull::set_null(size_t ndx) noexcept +{ + Array::set(ndx + 1, null_value()); +} + +inline ArrayIntNull::value_type ArrayIntNull::get(size_t ndx) const noexcept +{ + int64_t value = Array::get(ndx + 1); + if (value == null_value()) { + return util::none; + } + return util::some(value); +} + +inline ArrayIntNull::value_type ArrayIntNull::get(const char* header, size_t ndx) noexcept +{ + int64_t null_value = Array::get(header, 0); + int64_t value = Array::get(header, ndx + 1); + if (value == null_value) { + return util::none; + } + else { + return util::some(value); + } +} + +inline bool ArrayIntNull::is_null(size_t ndx) const noexcept +{ + return !get(ndx); +} + +inline int64_t ArrayIntNull::null_value() const noexcept +{ + return Array::get(0); +} + +inline ArrayIntNull::value_type ArrayIntNull::operator[](size_t ndx) const noexcept +{ + return get(ndx); +} + +inline ArrayIntNull::value_type ArrayIntNull::front() const noexcept +{ + return get(0); +} + +inline ArrayIntNull::value_type ArrayIntNull::back() const noexcept +{ + return Array::back(); +} + +inline void ArrayIntNull::erase(size_t ndx) +{ + Array::erase(ndx + 1); +} + +inline void ArrayIntNull::erase(size_t begin, size_t end) +{ + Array::erase(begin + 1, end + 1); +} + +inline void ArrayIntNull::truncate(size_t to_size) +{ + Array::truncate(to_size + 1); +} + +inline void ArrayIntNull::clear() +{ + truncate(0); +} + +inline void ArrayIntNull::set_all_to_zero() +{ + // FIXME: Array::set_all_to_zero does something else + for (size_t i = 0; i < size(); ++i) { + set(i, 0); + } +} + +inline void ArrayIntNull::move(size_t begin, size_t end, size_t dest_begin) +{ + Array::move(begin + 1, end + 1, dest_begin + 1); +} + +inline void ArrayIntNull::move_backward(size_t begin, size_t end, size_t dest_end) +{ + Array::move_backward(begin + 1, end + 1, dest_end + 1); +} + +inline size_t ArrayIntNull::lower_bound(int64_t value) const noexcept +{ + // FIXME: Consider this behaviour with NULLs. + // Array::lower_bound_int assumes an already sorted array, but + // this array could be sorted with nulls first or last. + return Array::lower_bound_int(value); +} + +inline size_t ArrayIntNull::upper_bound(int64_t value) const noexcept +{ + // FIXME: see lower_bound + return Array::upper_bound_int(value); +} + +inline int64_t ArrayIntNull::sum(size_t start, size_t end) const +{ + // FIXME: Optimize + int64_t sum_of_range = 0; + if (end == npos) + end = size(); + for (size_t i = start; i < end; ++i) { + value_type x = get(i); + if (x) { + sum_of_range += *x; + } + } + return sum_of_range; +} + +inline size_t ArrayIntNull::count(int64_t value) const noexcept +{ + size_t count_of_value = Array::count(value); + if (value == null_value()) { + --count_of_value; + } + return count_of_value; +} + +// FIXME: Optimize +template +inline bool ArrayIntNull::minmax_helper(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + size_t best_index = 1; + + if (end == npos) { + end = m_size; + } + + ++start; + + REALM_ASSERT(start < m_size && end <= m_size && start < end); + + if (m_size == 1) { + // empty array + return false; + } + + if (m_width == 0) { + if (return_ndx) + *return_ndx = best_index - 1; + result = 0; + return true; + } + + int64_t m = Array::get(start); + + const int64_t null_val = null_value(); + for (; start < end; ++start) { + const int64_t v = Array::get(start); + if (find_max ? v > m : v < m) { + if (v == null_val) { + continue; + } + m = v; + best_index = start; + } + } + + result = m; + if (return_ndx) { + *return_ndx = best_index - 1; + } + return true; +} + +inline bool ArrayIntNull::maximum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + return minmax_helper(result, start, end, return_ndx); +} + +inline bool ArrayIntNull::minimum(int64_t& result, size_t start, size_t end, size_t* return_ndx) const +{ + return minmax_helper(result, start, end, return_ndx); +} + +inline bool ArrayIntNull::find(int cond, Action action, value_type value, size_t start, size_t end, size_t baseindex, + QueryState* state) const +{ + if (value) { + return Array::find(cond, action, *value, start, end, baseindex, state, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(cond, action, 0 /* unused dummy*/, start, end, baseindex, state, + true /*treat as nullable array*/, true /*search for null, ignore value argument*/); + } +} + +template +bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + if (value) { + return Array::find(*value, start, end, baseindex, state, std::forward(callback), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(0 /*ignored*/, start, end, baseindex, state, + std::forward(callback), true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const +{ + return Array::find(value, start, end, baseindex, state, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); +} + + +template +bool ArrayIntNull::find(value_type value, size_t start, size_t end, size_t baseindex, QueryState* state, + Callback callback) const +{ + if (value) { + return Array::find(*value, start, end, baseindex, state, std::forward(callback), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find(0 /*ignored*/, start, end, baseindex, state, + std::forward(callback), true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find_action(size_t index, value_type value, QueryState* state, Callback callback) const +{ + if (value) { + return Array::find_action(index, *value, state, callback, true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + return Array::find_action(index, 0 /* ignored */, state, callback, + true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } +} + + +template +bool ArrayIntNull::find_action_pattern(size_t index, uint64_t pattern, QueryState* state, + Callback callback) const +{ + return Array::find_action_pattern(index, pattern, state, callback, + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); +} + + +template +size_t ArrayIntNull::find_first(value_type value, size_t start, size_t end) const +{ + QueryState state; + state.init(act_ReturnFirst, nullptr, 1); + if (value) { + Array::find(*value, start, end, 0, &state, Array::CallbackDummy(), + true /*treat as nullable array*/, + false /*search parameter given in 'value' argument*/); + } + else { + Array::find(0 /*ignored*/, start, end, 0, &state, Array::CallbackDummy(), + true /*treat as nullable array*/, + true /*search for null, ignore value argument*/); + } + + if (state.m_match_count > 0) + return to_size_t(state.m_state); + else + return not_found; +} + +inline size_t ArrayIntNull::find_first(value_type value, size_t begin, size_t end) const +{ + return find_first(value, begin, end); +} +} + +#endif // REALM_ARRAY_INTEGER_HPP diff --git a/Pods/Realm/include/core/realm/array_string.hpp b/Pods/Realm/include/core/realm/array_string.hpp new file mode 100644 index 0000000..bbf3203 --- /dev/null +++ b/Pods/Realm/include/core/realm/array_string.hpp @@ -0,0 +1,180 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_STRING_HPP +#define REALM_ARRAY_STRING_HPP + +#include + +namespace realm { + +/* +ArrayString stores strings as a concecutive list of fixed-length blocks of m_width bytes. The +longest string it can store is (m_width - 1) bytes before it needs to expand. + +An example of the format for m_width = 4 is following sequence of bytes, where x is payload: + +xxx0 xx01 x002 0003 0004 (strings "xxx",. "xx", "x", "", realm::null()) + +So each string is 0 terminated, and the last byte in a block tells how many 0s are present, except +for a realm::null() which has the byte set to m_width (4). The byte is used to compute the length of a string +in various functions. + +New: If m_witdh = 0, then all elements are realm::null(). So to add an empty string we must expand m_width +New: StringData is null() if-and-only-if StringData::data() == 0. +*/ + +class ArrayString : public Array { +public: + static const size_t max_width = 64; + + typedef StringData value_type; + // Constructor defaults to non-nullable because we use non-nullable ArrayString so many places internally in core + // (data which isn't user payload) where null isn't needed. + explicit ArrayString(Allocator&, bool nullable = false) noexcept; + ~ArrayString() noexcept override + { + } + + bool is_null(size_t ndx) const; + void set_null(size_t ndx); + StringData get(size_t ndx) const noexcept; + void add(); + void add(StringData value); + void set(size_t ndx, StringData value); + void insert(size_t ndx, StringData value); + void erase(size_t ndx); + + size_t count(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, StringData value, size_t add_offset = 0, size_t begin = 0, + size_t end = npos); + + /// Compare two string arrays for equality. + bool compare_string(const ArrayString&) const noexcept; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static StringData get(const char* header, size_t ndx, bool nullable) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, StringData, TreeInsertBase& state); + + /// Construct a string array of the specified size and return just + /// the reference to the underlying memory. All elements will be + /// initialized to the empty string. + static MemRef create_array(size_t size, Allocator&); + + /// Create a new empty string array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + /// Construct a copy of the specified slice of this string array + /// using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void string_stats() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + size_t calc_byte_len(size_t num_items, size_t width) const override; + size_t calc_item_count(size_t bytes, size_t width) const noexcept override; + + bool m_nullable; +}; + + +// Implementation: + +// Creates new array (but invalid, call init_from_ref() to init) +inline ArrayString::ArrayString(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_nullable(nullable) +{ +} + +inline void ArrayString::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc()); // Throws + init_from_mem(mem); +} + +inline MemRef ArrayString::create_array(size_t init_size, Allocator& allocator) +{ + bool context_flag = false; + int_fast64_t value = 0; + return Array::create(type_Normal, context_flag, wtype_Multiply, init_size, value, allocator); // Throws +} + +inline StringData ArrayString::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_size); + if (m_width == 0) + return m_nullable ? realm::null() : StringData(""); + + const char* data = m_data + (ndx * m_width); + size_t array_size = (m_width - 1) - data[m_width - 1]; + + if (array_size == static_cast(-1)) + return m_nullable ? realm::null() : StringData(""); + + REALM_ASSERT_EX(data[array_size] == 0, data[array_size], + array_size); // Realm guarantees 0 terminated return strings + return StringData(data, array_size); +} + +inline void ArrayString::add(StringData value) +{ + REALM_ASSERT(!(!m_nullable && value.is_null())); + insert(m_size, value); // Throws +} + +inline void ArrayString::add() +{ + add(m_nullable ? realm::null() : StringData("")); // Throws +} + +inline StringData ArrayString::get(const char* header, size_t ndx, bool nullable) noexcept +{ + REALM_ASSERT(ndx < get_size_from_header(header)); + uint_least8_t width = get_width_from_header(header); + const char* data = get_data_from_header(header) + (ndx * width); + + if (width == 0) + return nullable ? realm::null() : StringData(""); + + size_t size = (width - 1) - data[width - 1]; + + if (size == static_cast(-1)) + return nullable ? realm::null() : StringData(""); + + return StringData(data, size); +} + + +} // namespace realm + +#endif // REALM_ARRAY_STRING_HPP diff --git a/Pods/Realm/include/core/realm/array_string_long.hpp b/Pods/Realm/include/core/realm/array_string_long.hpp new file mode 100644 index 0000000..af9911e --- /dev/null +++ b/Pods/Realm/include/core/realm/array_string_long.hpp @@ -0,0 +1,224 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_STRING_LONG_HPP +#define REALM_ARRAY_STRING_LONG_HPP + +#include +#include + +namespace realm { + + +class ArrayStringLong : public Array { +public: + typedef StringData value_type; + + explicit ArrayStringLong(Allocator&, bool nullable) noexcept; + ~ArrayStringLong() noexcept override + { + } + + /// Create a new empty long string array and attach this accessor to + /// it. This does not modify the parent reference information of + /// this accessor. + /// + /// Note that the caller assumes ownership of the allocated + /// underlying node. It is not owned by the accessor. + void create(); + + //@{ + /// Overriding functions of Array + void init_from_ref(ref_type) noexcept; + void init_from_mem(MemRef) noexcept; + void init_from_parent() noexcept; + //@} + + bool is_empty() const noexcept; + size_t size() const noexcept; + + StringData get(size_t ndx) const noexcept; + + + void add(StringData value); + void set(size_t ndx, StringData value); + void insert(size_t ndx, StringData value); + void erase(size_t ndx); + void truncate(size_t size); + void clear(); + void destroy(); + + bool is_null(size_t ndx) const; + void set_null(size_t ndx); + + size_t count(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const noexcept; + void find_all(IntegerColumn& result, StringData value, size_t add_offset = 0, size_t begin = 0, + size_t end = npos) const; + + /// Get the specified element without the cost of constructing an + /// array instance. If an array instance is already available, or + /// you need to get multiple values, then this method will be + /// slower. + static StringData get(const char* header, size_t ndx, Allocator&, bool nullable) noexcept; + + ref_type bptree_leaf_insert(size_t ndx, StringData, TreeInsertBase&); + + static size_t get_size_from_header(const char*, Allocator&) noexcept; + + /// Construct a long string array of the specified size and return + /// just the reference to the underlying memory. All elements will + /// be initialized to zero size blobs. + static MemRef create_array(size_t size, Allocator&, bool nullable); + + /// Construct a copy of the specified slice of this long string + /// array using the specified target allocator. + MemRef slice(size_t offset, size_t slice_size, Allocator& target_alloc) const; + +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + + bool update_from_parent(size_t old_baseline) noexcept; + +private: + ArrayInteger m_offsets; + ArrayBlob m_blob; + Array m_nulls; + bool m_nullable; +}; + + +// Implementation: +inline ArrayStringLong::ArrayStringLong(Allocator& allocator, bool nullable) noexcept + : Array(allocator) + , m_offsets(allocator) + , m_blob(allocator) + , m_nulls(nullable ? allocator : Allocator::get_default()) + , m_nullable(nullable) +{ + m_offsets.set_parent(this, 0); + m_blob.set_parent(this, 1); + if (nullable) + m_nulls.set_parent(this, 2); +} + +inline void ArrayStringLong::create() +{ + size_t init_size = 0; + MemRef mem = create_array(init_size, get_alloc(), m_nullable); // Throws + init_from_mem(mem); +} + +inline void ArrayStringLong::init_from_ref(ref_type ref) noexcept +{ + REALM_ASSERT(ref); + char* header = get_alloc().translate(ref); + init_from_mem(MemRef(header, ref, m_alloc)); + m_nullable = (Array::size() == 3); +} + +inline void ArrayStringLong::init_from_parent() noexcept +{ + ref_type ref = get_ref_from_parent(); + init_from_ref(ref); +} + +inline bool ArrayStringLong::is_empty() const noexcept +{ + return m_offsets.is_empty(); +} + +inline size_t ArrayStringLong::size() const noexcept +{ + return m_offsets.size(); +} + +inline StringData ArrayStringLong::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_offsets.size()); + + if (m_nullable && m_nulls.get(ndx) == 0) + return realm::null(); + + size_t begin, end; + if (0 < ndx) { + begin = to_size_t(m_offsets.get(ndx - 1)); + end = to_size_t(m_offsets.get(ndx)); + } + else { + begin = 0; + end = to_size_t(m_offsets.get(0)); + } + --end; // Discount the terminating zero + + return StringData(m_blob.get(begin), end - begin); +} + +inline void ArrayStringLong::truncate(size_t new_size) +{ + REALM_ASSERT_3(new_size, <, m_offsets.size()); + + size_t blob_size = new_size ? to_size_t(m_offsets.get(new_size - 1)) : 0; + + m_offsets.truncate(new_size); + m_blob.truncate(blob_size); + if (m_nullable) + m_nulls.truncate(new_size); +} + +inline void ArrayStringLong::clear() +{ + m_blob.clear(); + m_offsets.clear(); + if (m_nullable) + m_nulls.clear(); +} + +inline void ArrayStringLong::destroy() +{ + m_blob.destroy(); + m_offsets.destroy(); + if (m_nullable) + m_nulls.destroy(); + Array::destroy(); +} + +inline bool ArrayStringLong::update_from_parent(size_t old_baseline) noexcept +{ + bool res = Array::update_from_parent(old_baseline); + if (res) { + m_blob.update_from_parent(old_baseline); + m_offsets.update_from_parent(old_baseline); + if (m_nullable) + m_nulls.update_from_parent(old_baseline); + } + return res; +} + +inline size_t ArrayStringLong::get_size_from_header(const char* header, Allocator& alloc) noexcept +{ + ref_type offsets_ref = to_ref(Array::get(header, 0)); + const char* offsets_header = alloc.translate(offsets_ref); + return Array::get_size_from_header(offsets_header); +} + + +} // namespace realm + +#endif // REALM_ARRAY_STRING_LONG_HPP diff --git a/Pods/Realm/include/core/realm/binary_data.hpp b/Pods/Realm/include/core/realm/binary_data.hpp new file mode 100644 index 0000000..cc1c283 --- /dev/null +++ b/Pods/Realm/include/core/realm/binary_data.hpp @@ -0,0 +1,235 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_BINARY_DATA_HPP +#define REALM_BINARY_DATA_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace realm { + +/// A reference to a chunk of binary data. +/// +/// This class does not own the referenced memory, nor does it in any other way +/// attempt to manage the lifetime of it. +/// +/// \sa StringData +class BinaryData { +public: + BinaryData() noexcept + : m_data(nullptr) + , m_size(0) + { + } + BinaryData(const char* external_data, size_t data_size) noexcept + : m_data(external_data) + , m_size(data_size) + { + } + template + explicit BinaryData(const char (&external_data)[N]) + : m_data(external_data) + , m_size(N) + { + } + template + explicit BinaryData(const std::basic_string&); + + template + explicit operator std::basic_string() const; + + char operator[](size_t i) const noexcept + { + return m_data[i]; + } + + const char* data() const noexcept + { + return m_data; + } + size_t size() const noexcept + { + return m_size; + } + + /// Is this a null reference? + /// + /// An instance of BinaryData is a null reference when, and only when the + /// stored size is zero (size()) and the stored pointer is the null pointer + /// (data()). + /// + /// In the case of the empty byte sequence, the stored size is still zero, + /// but the stored pointer is **not** the null pointer. Note that the actual + /// value of the pointer is immaterial in this case (as long as it is not + /// zero), because when the size is zero, it is an error to dereference the + /// pointer. + /// + /// Conversion of a BinaryData object to `bool` yields the logical negation + /// of the result of calling this function. In other words, a BinaryData + /// object is converted to true if it is not the null reference, otherwise + /// it is converted to false. + /// + /// It is important to understand that all of the functions and operators in + /// this class, and most of the functions in the Realm API in general + /// makes no distinction between a null reference and a reference to the + /// empty byte sequence. These functions and operators never look at the + /// stored pointer if the stored size is zero. + bool is_null() const noexcept; + + friend bool operator==(const BinaryData&, const BinaryData&) noexcept; + friend bool operator!=(const BinaryData&, const BinaryData&) noexcept; + + //@{ + /// Trivial bytewise lexicographical comparison. + friend bool operator<(const BinaryData&, const BinaryData&) noexcept; + friend bool operator>(const BinaryData&, const BinaryData&) noexcept; + friend bool operator<=(const BinaryData&, const BinaryData&) noexcept; + friend bool operator>=(const BinaryData&, const BinaryData&) noexcept; + //@} + + bool begins_with(BinaryData) const noexcept; + bool ends_with(BinaryData) const noexcept; + bool contains(BinaryData) const noexcept; + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const BinaryData&); + + explicit operator bool() const noexcept; + +private: + const char* m_data; + size_t m_size; +}; + +/// A read-only chunk of binary data. +class OwnedBinaryData : public OwnedData { +public: + using OwnedData::OwnedData; + + OwnedBinaryData() = default; + OwnedBinaryData(const BinaryData& binary_data) + : OwnedData(binary_data.data(), binary_data.size()) + { + } + + BinaryData get() const + { + return {data(), size()}; + } +}; + + +// Implementation: + +template +inline BinaryData::BinaryData(const std::basic_string& s) + : m_data(s.data()) + , m_size(s.size()) +{ +} + +template +inline BinaryData::operator std::basic_string() const +{ + return std::basic_string(m_data, m_size); +} + +inline bool BinaryData::is_null() const noexcept +{ + return !m_data; +} + +inline bool operator==(const BinaryData& a, const BinaryData& b) noexcept +{ + return a.m_size == b.m_size && a.is_null() == b.is_null() && safe_equal(a.m_data, a.m_data + a.m_size, b.m_data); +} + +inline bool operator!=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(a == b); +} + +inline bool operator<(const BinaryData& a, const BinaryData& b) noexcept +{ + if (a.is_null() || b.is_null()) + return !a.is_null() < !b.is_null(); + + return std::lexicographical_compare(a.m_data, a.m_data + a.m_size, b.m_data, b.m_data + b.m_size); +} + +inline bool operator>(const BinaryData& a, const BinaryData& b) noexcept +{ + return b < a; +} + +inline bool operator<=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(b < a); +} + +inline bool operator>=(const BinaryData& a, const BinaryData& b) noexcept +{ + return !(a < b); +} + +inline bool BinaryData::begins_with(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size <= m_size && safe_equal(m_data, m_data + d.m_size, d.m_data); +} + +inline bool BinaryData::ends_with(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size <= m_size && safe_equal(m_data + m_size - d.m_size, m_data + m_size, d.m_data); +} + +inline bool BinaryData::contains(BinaryData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size == 0 || std::search(m_data, m_data + m_size, d.m_data, d.m_data + d.m_size) != m_data + m_size; +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const BinaryData& d) +{ + out << "BinaryData(" << static_cast(d.m_data) << ", " << d.m_size << ")"; + return out; +} + +inline BinaryData::operator bool() const noexcept +{ + return !is_null(); +} + +} // namespace realm + +#endif // REALM_BINARY_DATA_HPP diff --git a/Pods/Realm/include/core/realm/bptree.hpp b/Pods/Realm/include/core/realm/bptree.hpp new file mode 100644 index 0000000..752facf --- /dev/null +++ b/Pods/Realm/include/core/realm/bptree.hpp @@ -0,0 +1,879 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_BPTREE_HPP +#define REALM_BPTREE_HPP + +#include // std::unique_ptr +#include +#include +#include +#include +#include + +namespace realm { + +/// Specialize BpTree to implement column types. +template +class BpTree; + +class ArrayInteger; +class ArrayIntNull; + +class BpTreeBase { +public: + struct unattached_tag { + }; + + // Accessor concept: + Allocator& get_alloc() const noexcept; + void destroy() noexcept; + void detach(); + bool is_attached() const noexcept; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t ndx) noexcept; + void update_from_parent(size_t old_baseline) noexcept; + MemRef clone_deep(Allocator& alloc) const; + + // BpTree interface: + const Array& root() const noexcept; + Array& root() noexcept; + bool root_is_leaf() const noexcept; + void introduce_new_root(ref_type new_sibling_ref, Array::TreeInsertBase& state, bool is_append); + void replace_root(std::unique_ptr leaf); + +protected: + explicit BpTreeBase(std::unique_ptr root); + explicit BpTreeBase(BpTreeBase&&) = default; + BpTreeBase& operator=(BpTreeBase&&) = default; + std::unique_ptr m_root; + + struct SliceHandler { + virtual MemRef slice_leaf(MemRef leaf_mem, size_t offset, size_t size, Allocator& target_alloc) = 0; + ~SliceHandler() noexcept + { + } + }; + static ref_type write_subtree(const Array& root, size_t slice_offset, size_t slice_size, size_t table_size, + SliceHandler&, _impl::OutputStream&); + friend class ColumnBase; + friend class ColumnBaseSimple; + +private: + struct WriteSliceHandler; + + // FIXME: Move B+Tree functionality from Array to this class. +}; + + +// Default implementation of BpTree. This should work for all types that have monomorphic +// leaves (i.e. all leaves are of the same type). +template +class BpTree : public BpTreeBase { +public: + using value_type = T; + using LeafType = typename ColumnTypeTraits::leaf_type; + + /// LeafInfo is used by get_leaf() to provide access to a leaf + /// without instantiating unnecessary nodes along the way. + /// Upon return, out_leaf with hold a pointer to the leaf containing + /// the index given to get_leaf(). If the index happens to be + /// in the root node (i.e., the root is a leaf), it will point + /// to the root node. + /// If the index isn't in the root node, fallback will be initialized + /// to represent the leaf holding the node, and out_leaf will be set + /// to point to fallback. + struct LeafInfo { + const LeafType** out_leaf; + LeafType* fallback; + }; + + BpTree(); + explicit BpTree(BpTreeBase::unattached_tag); + explicit BpTree(Allocator& alloc); + explicit BpTree(std::unique_ptr init_root) + : BpTreeBase(std::move(init_root)) + { + } + BpTree(BpTree&&) = default; + BpTree& operator=(BpTree&&) = default; + void init_from_ref(Allocator& alloc, ref_type ref); + void init_from_mem(Allocator& alloc, MemRef mem); + void init_from_parent(); + + size_t size() const noexcept; + bool is_empty() const noexcept + { + return size() == 0; + } + + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept; + void set(size_t, T value); + void set_null(size_t); + void insert(size_t ndx, T value, size_t num_rows = 1); + void erase(size_t ndx, bool is_last = false); + void move_last_over(size_t ndx, size_t last_row_ndx); + void clear(); + T front() const noexcept; + T back() const noexcept; + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& out_indices, T value, size_t begin = 0, size_t end = npos) const; + + static MemRef create_leaf(Array::Type, size_t size, T value, Allocator&); + + /// See LeafInfo for information about what to put in the inout_leaf + /// parameter. + /// + /// This function cannot be used for modifying operations as it + /// does not ensure the presence of an unbroken chain of parent + /// accessors. For this reason, the identified leaf should always + /// be accessed through the returned const-qualified reference, + /// and never directly through the specfied fallback accessor. + void get_leaf(size_t ndx, size_t& out_ndx_in_leaf, LeafInfo& inout_leaf) const noexcept; + + void update_each(Array::UpdateHandler&); + void update_elem(size_t, Array::UpdateHandler&); + + void adjust(size_t ndx, T diff); + void adjust(T diff); + void adjust_ge(T limit, T diff); + + ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const; + +#if defined(REALM_DEBUG) + void verify() const; + static size_t verify_leaf(MemRef mem, Allocator& alloc); +#endif + static void leaf_to_dot(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out, + Allocator& alloc); + +private: + LeafType& root_as_leaf(); + const LeafType& root_as_leaf() const; + + std::unique_ptr create_root_from_ref(Allocator& alloc, ref_type ref); + std::unique_ptr create_root_from_mem(Allocator& alloc, MemRef mem); + + struct EraseHandler; + struct UpdateHandler; + struct SetNullHandler; + struct SliceHandler; + struct AdjustHandler; + struct AdjustGEHandler; + + struct LeafValueInserter; + struct LeafNullInserter; + + template + void bptree_insert(size_t row_ndx, Array::TreeInsert& state, size_t num_rows); +}; + + +/// Implementation: + +inline BpTreeBase::BpTreeBase(std::unique_ptr init_root) + : m_root(std::move(init_root)) +{ +} + +inline Allocator& BpTreeBase::get_alloc() const noexcept +{ + return m_root->get_alloc(); +} + +inline void BpTreeBase::destroy() noexcept +{ + if (m_root) + m_root->destroy_deep(); +} + +inline void BpTreeBase::detach() +{ + m_root->detach(); +} + +inline bool BpTreeBase::is_attached() const noexcept +{ + return m_root->is_attached(); +} + +inline bool BpTreeBase::root_is_leaf() const noexcept +{ + return !m_root->is_inner_bptree_node(); +} + +inline void BpTreeBase::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_root->set_parent(parent, ndx_in_parent); +} + +inline size_t BpTreeBase::get_ndx_in_parent() const noexcept +{ + return m_root->get_ndx_in_parent(); +} + +inline void BpTreeBase::set_ndx_in_parent(size_t ndx) noexcept +{ + m_root->set_ndx_in_parent(ndx); +} + +inline void BpTreeBase::update_from_parent(size_t old_baseline) noexcept +{ + m_root->update_from_parent(old_baseline); +} + +inline MemRef BpTreeBase::clone_deep(Allocator& alloc) const +{ + return m_root->clone_deep(alloc); +} + +inline const Array& BpTreeBase::root() const noexcept +{ + return *m_root; +} + +inline Array& BpTreeBase::root() noexcept +{ + return *m_root; +} + +template +BpTree::BpTree() + : BpTree(Allocator::get_default()) +{ +} + +template +BpTree::BpTree(Allocator& alloc) + : BpTreeBase(std::unique_ptr(new LeafType(alloc))) +{ +} + +template +BpTree::BpTree(BpTreeBase::unattached_tag) + : BpTreeBase(nullptr) +{ +} + +template +std::unique_ptr BpTree::create_root_from_mem(Allocator& alloc, MemRef mem) +{ + const char* header = mem.get_addr(); + std::unique_ptr new_root; + bool is_inner_bptree_node = Array::get_is_inner_bptree_node_from_header(header); + + bool can_reuse_root_accessor = + m_root && &m_root->get_alloc() == &alloc && m_root->is_inner_bptree_node() == is_inner_bptree_node; + if (can_reuse_root_accessor) { + if (is_inner_bptree_node) { + m_root->init_from_mem(mem); + } + else { + static_cast(*m_root).init_from_mem(mem); + } + return std::move(m_root); // Same root will be reinstalled. + } + + // Not reusing root note, allocating a new one. + if (is_inner_bptree_node) { + new_root.reset(new Array{alloc}); + new_root->init_from_mem(mem); + } + else { + std::unique_ptr leaf{new LeafType{alloc}}; + leaf->init_from_mem(mem); + new_root = std::move(leaf); + } + return new_root; +} + +template +std::unique_ptr BpTree::create_root_from_ref(Allocator& alloc, ref_type ref) +{ + MemRef mem = MemRef{alloc.translate(ref), ref, alloc}; + return create_root_from_mem(alloc, mem); +} + +template +void BpTree::init_from_ref(Allocator& alloc, ref_type ref) +{ + auto new_root = create_root_from_ref(alloc, ref); + replace_root(std::move(new_root)); +} + +template +void BpTree::init_from_mem(Allocator& alloc, MemRef mem) +{ + auto new_root = create_root_from_mem(alloc, mem); + replace_root(std::move(new_root)); +} + +template +void BpTree::init_from_parent() +{ + ref_type ref = root().get_ref_from_parent(); + ArrayParent* parent = m_root->get_parent(); + size_t ndx_in_parent = m_root->get_ndx_in_parent(); + auto new_root = create_root_from_ref(get_alloc(), ref); + new_root->set_parent(parent, ndx_in_parent); + m_root = std::move(new_root); +} + +template +typename BpTree::LeafType& BpTree::root_as_leaf() +{ + REALM_ASSERT_DEBUG(root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(m_root.get()) != nullptr); + return static_cast(root()); +} + +template +const typename BpTree::LeafType& BpTree::root_as_leaf() const +{ + REALM_ASSERT_DEBUG(root_is_leaf()); + REALM_ASSERT_DEBUG(dynamic_cast(m_root.get()) != nullptr); + return static_cast(root()); +} + +template +size_t BpTree::size() const noexcept +{ + if (root_is_leaf()) { + return root_as_leaf().size(); + } + return root().get_bptree_size(); +} + +template +T BpTree::back() const noexcept +{ + // FIXME: slow + return get(size() - 1); +} + +namespace _impl { + +// NullableOrNothing encapsulates the behavior of nullable and +// non-nullable leaf types, so that non-nullable leaf types +// don't have to implement is_null/set_null but BpTree can still +// support the interface (and return false / assert when null +// is not supported). +template +struct NullableOrNothing { + static bool is_null(const Leaf& leaf, size_t ndx) + { + return leaf.is_null(ndx); + } + static void set_null(Leaf& leaf, size_t ndx) + { + leaf.set_null(ndx); + } +}; +template <> +struct NullableOrNothing { + static bool is_null(const ArrayInteger&, size_t) + { + return false; + } + static void set_null(ArrayInteger&, size_t) + { + REALM_ASSERT_RELEASE(false); + } +}; +} + +template +bool BpTree::is_null(size_t ndx) const noexcept +{ + if (root_is_leaf()) { + return _impl::NullableOrNothing::is_null(root_as_leaf(), ndx); + } + LeafType fallback(get_alloc()); + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &fallback}; + size_t ndx_in_leaf; + get_leaf(ndx, ndx_in_leaf, leaf_info); + return _impl::NullableOrNothing::is_null(*leaf, ndx_in_leaf); +} + +template +T BpTree::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG_EX(ndx < size(), ndx, size()); + if (root_is_leaf()) { + return root_as_leaf().get(ndx); + } + + // Use direct getter to avoid initializing leaf array: + std::pair p = root().get_bptree_leaf(ndx); + const char* leaf_header = p.first.get_addr(); + size_t ndx_in_leaf = p.second; + return LeafType::get(leaf_header, ndx_in_leaf); +} + +template +template +void BpTree::bptree_insert(size_t row_ndx, Array::TreeInsert& state, size_t num_rows) +{ + ref_type new_sibling_ref; + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx == realm::npos ? realm::npos : row_ndx + i; + if (root_is_leaf()) { + REALM_ASSERT_DEBUG(row_ndx_2 == realm::npos || row_ndx_2 < REALM_MAX_BPNODE_SIZE); + new_sibling_ref = root_as_leaf().bptree_leaf_insert(row_ndx_2, state.m_value, state); + } + else { + if (row_ndx_2 == realm::npos) { + new_sibling_ref = root().bptree_append(state); // Throws + } + else { + new_sibling_ref = root().bptree_insert(row_ndx_2, state); // Throws + } + } + + if (REALM_UNLIKELY(new_sibling_ref)) { + bool is_append = row_ndx_2 == realm::npos; + introduce_new_root(new_sibling_ref, state, is_append); + } + } +} + +template +struct BpTree::LeafValueInserter { + using value_type = T; + T m_value; + LeafValueInserter(T value) + : m_value(std::move(value)) + { + } + + // TreeTraits concept: + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent& parent, size_t ndx_in_parent, Allocator& alloc, + size_t ndx_in_leaf, Array::TreeInsert& state) + { + LeafType leaf{alloc}; + leaf.init_from_mem(leaf_mem); + leaf.set_parent(&parent, ndx_in_parent); + // Should not move out of m_value, because the same inserter may be used to perform + // multiple insertions (for example, if num_rows > 1). + return leaf.bptree_leaf_insert(ndx_in_leaf, state.m_value, state); + } +}; + +template +struct BpTree::LeafNullInserter { + using value_type = null; + // TreeTraits concept: + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent& parent, size_t ndx_in_parent, Allocator& alloc, + size_t ndx_in_leaf, Array::TreeInsert& state) + { + LeafType leaf{alloc}; + leaf.init_from_mem(leaf_mem); + leaf.set_parent(&parent, ndx_in_parent); + return leaf.bptree_leaf_insert(ndx_in_leaf, null{}, state); + } +}; + +template +void BpTree::insert(size_t row_ndx, T value, size_t num_rows) +{ + REALM_ASSERT_DEBUG(row_ndx == npos || row_ndx < size()); + Array::TreeInsert inserter; + inserter.m_value = std::move(value); + inserter.m_nullable = std::is_same>::value; // FIXME + bptree_insert(row_ndx, inserter, num_rows); // Throws +} + +template +struct BpTree::UpdateHandler : Array::UpdateHandler { + LeafType m_leaf; + const T m_value; + UpdateHandler(BpTreeBase& tree, T value) noexcept + : m_leaf(tree.get_alloc()) + , m_value(std::move(value)) + { + } + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.set(elem_ndx_in_leaf, m_value); // Throws + } +}; + +template +struct BpTree::SetNullHandler : Array::UpdateHandler { + LeafType m_leaf; + SetNullHandler(BpTreeBase& tree) noexcept + : m_leaf(tree.get_alloc()) + { + } + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + _impl::NullableOrNothing::set_null(m_leaf, elem_ndx_in_leaf); // Throws + } +}; + +template +void BpTree::set(size_t ndx, T value) +{ + if (root_is_leaf()) { + root_as_leaf().set(ndx, std::move(value)); + } + else { + UpdateHandler set_leaf_elem(*this, std::move(value)); + m_root->update_bptree_elem(ndx, set_leaf_elem); // Throws + } +} + +template +void BpTree::set_null(size_t ndx) +{ + if (root_is_leaf()) { + _impl::NullableOrNothing::set_null(root_as_leaf(), ndx); + } + else { + SetNullHandler set_leaf_elem(*this); + m_root->update_bptree_elem(ndx, set_leaf_elem); // Throws; + } +} + +template +struct BpTree::EraseHandler : Array::EraseHandler { + BpTreeBase& m_tree; + LeafType m_leaf; + bool m_leaves_have_refs; // FIXME: Should be able to eliminate this. + EraseHandler(BpTreeBase& tree) noexcept + : m_tree(tree) + , m_leaf(tree.get_alloc()) + , m_leaves_have_refs(false) + { + } + bool erase_leaf_elem(MemRef leaf_mem, ArrayParent* parent, size_t leaf_ndx_in_parent, + size_t elem_ndx_in_leaf) override + { + m_leaf.init_from_mem(leaf_mem); + REALM_ASSERT_3(m_leaf.size(), >=, 1); + size_t last_ndx = m_leaf.size() - 1; + if (last_ndx == 0) { + m_leaves_have_refs = m_leaf.has_refs(); + return true; + } + m_leaf.set_parent(parent, leaf_ndx_in_parent); + size_t ndx = elem_ndx_in_leaf; + if (ndx == npos) + ndx = last_ndx; + m_leaf.erase(ndx); // Throws + return false; + } + void destroy_leaf(MemRef leaf_mem) noexcept override + { + // FIXME: Seems like this would cause file space leaks if + // m_leaves_have_refs is true, but consider carefully how + // m_leaves_have_refs get its value. + m_tree.get_alloc().free_(leaf_mem); + } + void replace_root_by_leaf(MemRef leaf_mem) override + { + std::unique_ptr leaf{new LeafType(m_tree.get_alloc())}; // Throws + leaf->init_from_mem(leaf_mem); + m_tree.replace_root(std::move(leaf)); // Throws + } + void replace_root_by_empty_leaf() override + { + std::unique_ptr leaf{new LeafType(m_tree.get_alloc())}; // Throws + leaf->create(m_leaves_have_refs ? Array::type_HasRefs : Array::type_Normal); // Throws + m_tree.replace_root(std::move(leaf)); // Throws + } +}; + +template +void BpTree::erase(size_t ndx, bool is_last) +{ + REALM_ASSERT_DEBUG_EX(ndx < size(), ndx, size()); + REALM_ASSERT_DEBUG(is_last == (ndx == size() - 1)); + if (root_is_leaf()) { + root_as_leaf().erase(ndx); + } + else { + size_t ndx_2 = is_last ? npos : ndx; + EraseHandler handler(*this); + Array::erase_bptree_elem(m_root.get(), ndx_2, handler); + } +} + +template +void BpTree::move_last_over(size_t row_ndx, size_t last_row_ndx) +{ + // Copy value from last row over + T value = get(last_row_ndx); + set(row_ndx, value); + erase(last_row_ndx, true); +} + +template +void BpTree::clear() +{ + if (root_is_leaf()) { + if (std::is_same::value && root().get_type() == Array::type_HasRefs) { + // FIXME: This is because some column types rely on integer columns + // to contain refs. + root().clear_and_destroy_children(); + } + else { + root_as_leaf().clear(); + } + } + else { + Allocator& alloc = get_alloc(); + root().destroy_deep(); + + std::unique_ptr new_root(new LeafType(alloc)); + new_root->create(); + replace_root(std::move(new_root)); + } +} + + +template +struct BpTree::AdjustHandler : Array::UpdateHandler { + LeafType m_leaf; + const T m_diff; + AdjustHandler(BpTreeBase& tree, T diff) + : m_leaf(tree.get_alloc()) + , m_diff(diff) + { + } + + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t) final + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.adjust(0, m_leaf.size(), m_diff); + } +}; + +template +void BpTree::adjust(T diff) +{ + if (root_is_leaf()) { + root_as_leaf().adjust(0, m_root->size(), std::move(diff)); // Throws + } + else { + AdjustHandler adjust_leaf_elem(*this, std::move(diff)); + m_root->update_bptree_leaves(adjust_leaf_elem); // Throws + } +} + +template +void BpTree::adjust(size_t ndx, T diff) +{ + static_assert(std::is_arithmetic::value, "adjust is undefined for non-arithmetic trees"); + set(ndx, get(ndx) + diff); +} + +template +struct BpTree::AdjustGEHandler : Array::UpdateHandler { + LeafType m_leaf; + const T m_limit, m_diff; + + AdjustGEHandler(BpTreeBase& tree, T limit, T diff) + : m_leaf(tree.get_alloc()) + , m_limit(limit) + , m_diff(diff) + { + } + + void update(MemRef mem, ArrayParent* parent, size_t ndx_in_parent, size_t) final + { + m_leaf.init_from_mem(mem); + m_leaf.set_parent(parent, ndx_in_parent); + m_leaf.adjust_ge(m_limit, m_diff); + } +}; + +template +void BpTree::adjust_ge(T limit, T diff) +{ + if (root_is_leaf()) { + root_as_leaf().adjust_ge(std::move(limit), std::move(diff)); // Throws + } + else { + AdjustGEHandler adjust_leaf_elem(*this, std::move(limit), std::move(diff)); + m_root->update_bptree_leaves(adjust_leaf_elem); // Throws + } +} + +template +struct BpTree::SliceHandler : public BpTreeBase::SliceHandler { +public: + SliceHandler(Allocator& alloc) + : m_leaf(alloc) + { + } + MemRef slice_leaf(MemRef leaf_mem, size_t offset, size_t size, Allocator& target_alloc) override + { + m_leaf.init_from_mem(leaf_mem); + return m_leaf.slice_and_clone_children(offset, size, target_alloc); // Throws + } + +private: + LeafType m_leaf; +}; + +template +ref_type BpTree::write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const +{ + ref_type ref; + if (root_is_leaf()) { + Allocator& alloc = Allocator::get_default(); + MemRef mem = root_as_leaf().slice_and_clone_children(slice_offset, slice_size, alloc); // Throws + Array slice(alloc); + _impl::DeepArrayDestroyGuard dg(&slice); + slice.init_from_mem(mem); + bool deep = true; + bool only_when_modified = false; + ref = slice.write(out, deep, only_when_modified); // Throws + } + else { + SliceHandler handler(get_alloc()); + ref = write_subtree(root(), slice_offset, slice_size, table_size, handler, out); // Throws + } + return ref; +} + +template +MemRef BpTree::create_leaf(Array::Type leaf_type, size_t size, T value, Allocator& alloc) +{ + bool context_flag = false; + return LeafType::create_array(leaf_type, context_flag, size, std::move(value), alloc); +} + +template +void BpTree::get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf_info) const noexcept +{ + if (root_is_leaf()) { + ndx_in_leaf = ndx; + *inout_leaf_info.out_leaf = &root_as_leaf(); + return; + } + std::pair p = root().get_bptree_leaf(ndx); + inout_leaf_info.fallback->init_from_mem(p.first); + ndx_in_leaf = p.second; + *inout_leaf_info.out_leaf = inout_leaf_info.fallback; +} + +template +size_t BpTree::find_first(T value, size_t begin, size_t end) const +{ + if (root_is_leaf()) { + return root_as_leaf().find_first(value, begin, end); + } + + // FIXME: It would be better to always require that 'end' is + // specified explicitly, since Table has the size readily + // available, and Array::get_bptree_size() is deprecated. + if (end == npos) + end = size(); + + LeafType leaf_cache(get_alloc()); + size_t ndx_in_tree = begin; + while (ndx_in_tree < end) { + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &leaf_cache}; + size_t ndx_in_leaf; + get_leaf(ndx_in_tree, ndx_in_leaf, leaf_info); + size_t leaf_offset = ndx_in_tree - ndx_in_leaf; + size_t end_in_leaf = std::min(leaf->size(), end - leaf_offset); + size_t ndx = leaf->find_first(value, ndx_in_leaf, end_in_leaf); // Throws (maybe) + if (ndx != not_found) + return leaf_offset + ndx; + ndx_in_tree = leaf_offset + end_in_leaf; + } + + return not_found; +} + +template +void BpTree::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const +{ + if (root_is_leaf()) { + root_as_leaf().find_all(&result, value, 0, begin, end); // Throws + return; + } + + // FIXME: It would be better to always require that 'end' is + // specified explicitely, since Table has the size readily + // available, and Array::get_bptree_size() is deprecated. + if (end == npos) + end = size(); + + LeafType leaf_cache(get_alloc()); + size_t ndx_in_tree = begin; + while (ndx_in_tree < end) { + const LeafType* leaf; + LeafInfo leaf_info{&leaf, &leaf_cache}; + size_t ndx_in_leaf; + get_leaf(ndx_in_tree, ndx_in_leaf, leaf_info); + size_t leaf_offset = ndx_in_tree - ndx_in_leaf; + size_t end_in_leaf = std::min(leaf->size(), end - leaf_offset); + leaf->find_all(&result, value, leaf_offset, ndx_in_leaf, end_in_leaf); // Throws + ndx_in_tree = leaf_offset + end_in_leaf; + } +} + +#if defined(REALM_DEBUG) +template +size_t BpTree::verify_leaf(MemRef mem, Allocator& alloc) +{ + LeafType leaf(alloc); + leaf.init_from_mem(mem); + leaf.verify(); + return leaf.size(); +} + +template +void BpTree::verify() const +{ + if (root_is_leaf()) { + root_as_leaf().verify(); + } + else { + root().verify_bptree(&verify_leaf); + } +} +#endif // REALM_DEBUG + +template +void BpTree::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out, + Allocator& alloc) +{ + LeafType leaf(alloc); + leaf.init_from_mem(leaf_mem); + leaf.set_parent(parent, ndx_in_parent); + leaf.to_dot(out); +} + +} // namespace realm + +#endif // REALM_BPTREE_HPP diff --git a/Pods/Realm/include/core/realm/column.hpp b/Pods/Realm/include/core/realm/column.hpp new file mode 100644 index 0000000..1dd5edf --- /dev/null +++ b/Pods/Realm/include/core/realm/column.hpp @@ -0,0 +1,1799 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_HPP +#define REALM_COLUMN_HPP + +#include // unint8_t etc +#include // size_t +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +// Pre-definitions +struct CascadeState; +class StringIndex; + +template +struct ImplicitNull; + +template +struct ImplicitNull> { + static constexpr bool value = true; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = false; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = true; +}; + +template <> +struct ImplicitNull { + static constexpr bool value = true; +}; + +// FIXME: Add specialization for ImplicitNull for float, double, StringData, BinaryData. + +template +R aggregate(const ColType& column, T target, size_t start, size_t end, size_t limit, size_t* return_ndx); + + +// Iterator with random access for Columns +template +class ColumnRandIterator : public std::iterator { +public: + ColumnRandIterator(const Column* src_col, size_t ndx = 0); + operator bool() const; + bool operator==(const ColumnRandIterator& other) const; + bool operator!=(const ColumnRandIterator& other) const; + ColumnRandIterator& operator+=(const ptrdiff_t& movement); + ColumnRandIterator& operator-=(const ptrdiff_t& movement); + ColumnRandIterator& operator++(); + ColumnRandIterator& operator--(); + ColumnRandIterator operator++(int); + ColumnRandIterator operator--(int); + ColumnRandIterator operator+(const ptrdiff_t& movement); + ColumnRandIterator operator-(const ptrdiff_t& movement); + ptrdiff_t operator-(const ColumnRandIterator& rawIterator); + const ColumnDataType operator*() const; + size_t get_col_ndx() const; + +protected: + size_t col_ndx; + size_t cached_column_size; + const Column* col; +}; + +/// Base class for all column types. +class ColumnBase { +public: + /// Get the number of entries in this column. This operation is relatively + /// slow. + virtual size_t size() const noexcept = 0; + + /// \throw LogicError Thrown if this column is not string valued. + virtual void set_string(size_t row_ndx, StringData value); + + /// Whether or not this column is nullable. + virtual bool is_nullable() const noexcept; + + /// Whether or not the value at \a row_ndx is NULL. If the column is not + /// nullable, always returns false. + virtual bool is_null(size_t row_ndx) const noexcept; + + /// Sets the value at \a row_ndx to be NULL. + /// \throw LogicError Thrown if this column is not nullable. + virtual void set_null(size_t row_ndx); + + /// Inserts the specified number of elements into this column + /// starting at the specified row index. The new elements will have the + /// default value for the column type. + /// + /// \param row_ndx The row to start insertion at. If the row_ndx is less + /// than prior_num_rows then previous rows from row_ndx onwards will be + /// moved ahead by num_rows_to_insert. + /// + /// \param num_rows_to_insert The number of rows to insert. There is no + /// restriction on this value. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param nullable Specifies whether or not this column is nullable. This + /// function may assert if nullable does not agree with \a is_nullable() + virtual void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) = 0; + + /// Removes the specified number of consecutive elements from + /// this column, starting at the specified row index. + /// + /// \param row_ndx The row to start removal at (inclusive). This must be + /// less than prior_num_rows. + /// + /// \param num_rows_to_erase The number of rows to erase. + /// The row_ndx + num_rows_to_erase must be less than prior_num_rows. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) = 0; + + /// Removes the element at the specified row index by + /// moving the element at the last row index over it. This reduces the + /// number of elements by one. + /// + /// \param row_ndx The row to erase. Must be less than prior_num_rows. + /// + /// \param prior_num_rows The number of elements in this column prior to the + /// modification. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool broken_reciprocal_backlinks) = 0; + + /// Remove all elements from this column. + /// + /// \param num_rows The total number of rows in this column. + /// + /// \param broken_reciprocal_backlinks If true, link columns must assume + /// that reciprocal backlinks have already been removed. Non-link columns + /// should ignore this argument. + virtual void clear(size_t num_rows, bool broken_reciprocal_backlinks) = 0; + + /// \brief Swap the elements at the specified indices. + /// + /// Behaviour is undefined if: + /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of + /// bounds) + /// - \a row_ndx_1 and \a row_ndx_2 point to the same value + virtual void swap_rows(size_t row_ndx_1, size_t row_ndx_2) = 0; + + virtual void destroy() noexcept = 0; + void move_assign(ColumnBase& col) noexcept; + + virtual ~ColumnBase() noexcept + { + } + + // Getter function for index. For integer index, the caller must supply a + // buffer that we can store the extracted value in (it may be bitpacked, so + // we cannot return a pointer in to the Array as we do with String index). + virtual StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept = 0; + + // Search index + virtual bool supports_search_index() const noexcept; + virtual bool has_search_index() const noexcept; + virtual StringIndex* create_search_index(); + virtual void destroy_search_index() noexcept; + virtual const StringIndex* get_search_index() const noexcept; + virtual StringIndex* get_search_index() noexcept; + virtual void set_search_index_ref(ref_type, ArrayParent*, size_t ndx_in_parent, bool allow_duplicate_values); + virtual void set_search_index_allow_duplicate_values(bool) noexcept; + + virtual Allocator& get_alloc() const noexcept = 0; + + /// Returns the 'ref' of the root array. + virtual ref_type get_ref() const noexcept = 0; + virtual MemRef get_mem() const noexcept = 0; + + virtual void replace_root_array(std::unique_ptr leaf) = 0; + virtual MemRef clone_deep(Allocator& alloc) const = 0; + virtual void detach(void) = 0; + virtual bool is_attached(void) const noexcept = 0; + + static size_t get_size_from_type_and_ref(ColumnType, ref_type, Allocator&, bool) noexcept; + + // These assume that the right column compile-time type has been + // figured out. + static size_t get_size_from_ref(ref_type root_ref, Allocator&); + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&); + + /// Write a slice of this column to the specified output stream. + virtual ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream&) const = 0; + + /// Get this column's logical index within the containing table, or npos + /// for free-standing or non-top-level columns. + size_t get_column_index() const noexcept + { + return m_column_ndx; + } + + virtual void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept = 0; + virtual size_t get_ndx_in_parent() const noexcept = 0; + virtual void set_ndx_in_parent(size_t ndx_in_parent) noexcept = 0; + + /// Called to update refs and memory pointers of this column accessor and + /// all its nested accessors, but only in cases where the logical contents + /// in strictly unchanged. Group::commit(), and + /// SharedGroup::commit_and_continue_as_read()() are examples of such + /// cases. In both those cases, the purpose is to keep user visible + /// accessors in a valid state across a commit. + virtual void update_from_parent(size_t old_baseline) noexcept = 0; + + //@{ + + /// cascade_break_backlinks_to() is called iteratively for each column by + /// Table::cascade_break_backlinks_to() with the same arguments as are + /// passed to Table::cascade_break_backlinks_to(). Link columns must + /// override it. The same is true for cascade_break_backlinks_to_all_rows(), + /// except that it is called from + /// Table::cascade_break_backlinks_to_all_rows(), and that it expects + /// Table::cascade_break_backlinks_to_all_rows() to pass the number of rows + /// in the table as \a num_rows. + + virtual void cascade_break_backlinks_to(size_t row_ndx, CascadeState&); + virtual void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&); + + //@} + + void discard_child_accessors() noexcept; + + /// For columns that are able to contain subtables, this function returns + /// the pointer to the subtable accessor at the specified row index if it + /// exists, otherwise it returns null. For other column types, this function + /// returns null. + virtual Table* get_subtable_accessor(size_t row_ndx) const noexcept; + + /// Detach and remove the subtable accessor at the specified row if it + /// exists. For column types that are unable to contain subtable, this + /// function does nothing. + virtual void discard_subtable_accessor(size_t row_ndx) noexcept; + + virtual void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + virtual void adj_acc_erase_row(size_t row_ndx) noexcept; + /// See Table::adj_acc_move_over() + virtual void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + virtual void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + virtual void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + virtual void adj_acc_clear_root_table() noexcept; + + enum { + mark_Recursive = 0x01, + mark_LinkTargets = 0x02, + mark_LinkOrigins = 0x04, + }; + + virtual void mark(int type) noexcept; + + virtual void bump_link_origin_table_version() noexcept; + + virtual int compare_values(size_t row1, size_t row2) const noexcept = 0; + + /// Refresh the dirty part of the accessor subtree rooted at this column + /// accessor. + /// + /// The following conditions are necessary and sufficient for the proper + /// operation of this function: + /// + /// - The parent table accessor (excluding its column accessors) is in a + /// valid state (already refreshed). + /// + /// - Every subtable accessor in the subtree is marked dirty if it needs to + /// be refreshed, or if it has a descendant accessor that needs to be + /// refreshed. + /// + /// - This column accessor, as well as all its descendant accessors, are in + /// structural correspondence with the underlying node hierarchy whose + /// root ref is stored in the parent (`Table::m_columns`) (see + /// AccessorConsistencyLevels). + /// + /// - The 'index in parent' property of the cached root array + /// (`root->m_ndx_in_parent`) is valid. + virtual void refresh_accessor_tree(size_t new_col_ndx, const Spec&); + + virtual void verify() const = 0; + virtual void verify(const Table&, size_t col_ndx) const; + virtual void to_dot(std::ostream&, StringData title = StringData()) const = 0; + virtual void do_dump_node_structure(std::ostream&, int level) const = 0; + +#ifdef REALM_DEBUG + void dump_node_structure() const; // To std::cerr (for GDB) + void bptree_to_dot(const Array* root, std::ostream& out) const; +#endif + +protected: + using SliceHandler = BpTreeBase::SliceHandler; + + ColumnBase(size_t column_ndx = npos) + : m_column_ndx(column_ndx) + { + } + ColumnBase(ColumnBase&&) = default; + + // Must not assume more than minimal consistency (see + // AccessorConsistencyLevels). + virtual void do_discard_child_accessors() noexcept + { + } + + //@{ + /// \tparam L Any type with an appropriate `value_type`, %size(), + /// and %get() members. + template + size_t lower_bound(const L& list, T value) const noexcept; + + template + size_t upper_bound(const L& list, T value) const noexcept; + //@} + + // Node functions + + class CreateHandler { + public: + virtual ref_type create_leaf(size_t size) = 0; + ~CreateHandler() noexcept + { + } + }; + + static ref_type create(Allocator&, size_t size, CreateHandler&); + + class LeafToDot; + virtual void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const = 0; + + template + static int compare_values(const Column* column, size_t row1, size_t row2) noexcept; + +private: + size_t m_column_ndx = npos; + + static ref_type build(size_t* rest_size_ptr, size_t fixed_height, Allocator&, CreateHandler&); +}; + + +// FIXME: Temporary class until all column types have been migrated to use BpTree interface +class ColumnBaseSimple : public ColumnBase { +public: + //@{ + /// Returns the array node at the root of this column, but note + /// that there is no guarantee that this node is an inner B+-tree + /// node or a leaf. This is the case for a MixedColumn in + /// particular. + Array* get_root_array() noexcept + { + return m_array.get(); + } + const Array* get_root_array() const noexcept + { + return m_array.get(); + } + //@} + + Allocator& get_alloc() const noexcept final + { + return m_array->get_alloc(); + } + void destroy() noexcept override + { + if (m_array) + m_array->destroy_deep(); + } + ref_type get_ref() const noexcept final + { + return m_array->get_ref(); + } + MemRef get_mem() const noexcept final + { + return m_array->get_mem(); + } + void detach() noexcept final + { + m_array->detach(); + } + bool is_attached() const noexcept final + { + return m_array->is_attached(); + } + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept final + { + m_array->set_parent(parent, ndx_in_parent); + } + size_t get_ndx_in_parent() const noexcept final + { + return m_array->get_ndx_in_parent(); + } + void set_ndx_in_parent(size_t ndx_in_parent) noexcept override + { + m_array->set_ndx_in_parent(ndx_in_parent); + } + void update_from_parent(size_t old_baseline) noexcept override + { + m_array->update_from_parent(old_baseline); + } + MemRef clone_deep(Allocator& alloc) const override + { + return m_array->clone_deep(alloc); + } + +protected: + ColumnBaseSimple(size_t column_ndx) + : ColumnBase(column_ndx) + { + } + ColumnBaseSimple(Array* root) + : m_array(root) + { + } + std::unique_ptr m_array; + + void replace_root_array(std::unique_ptr new_root) final; + bool root_is_leaf() const noexcept + { + return !m_array->is_inner_bptree_node(); + } + + /// Introduce a new root node which increments the height of the + /// tree by one. + void introduce_new_root(ref_type new_sibling_ref, Array::TreeInsertBase& state, bool is_append); + + static ref_type write(const Array* root, size_t slice_offset, size_t slice_size, size_t table_size, SliceHandler&, + _impl::OutputStream&); + +#if defined(REALM_DEBUG) + void tree_to_dot(std::ostream&) const; +#endif +}; + +class ColumnBaseWithIndex : public ColumnBase { +public: + ~ColumnBaseWithIndex() noexcept override + { + } + void set_ndx_in_parent(size_t ndx) noexcept override; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void move_assign(ColumnBaseWithIndex& col) noexcept; + void destroy() noexcept override; + + virtual bool supports_search_index() const noexcept override + { + return true; + } + bool has_search_index() const noexcept final + { + return bool(m_search_index); + } + StringIndex* get_search_index() noexcept final + { + return m_search_index.get(); + } + const StringIndex* get_search_index() const noexcept final + { + return m_search_index.get(); + } + void destroy_search_index() noexcept override; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, + bool allow_duplicate_valaues) final; + StringIndex* create_search_index() override = 0; + +protected: + using ColumnBase::ColumnBase; + ColumnBaseWithIndex(ColumnBaseWithIndex&&) = default; + std::unique_ptr m_search_index; +}; + + +/// A column (Column) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are arrays. +template +class Column : public ColumnBaseWithIndex { +public: + using value_type = T; + using LeafInfo = typename BpTree::LeafInfo; + using LeafType = typename BpTree::LeafType; + + static constexpr bool nullable = ImplicitNull::value; + + struct unattached_root_tag { + }; + + explicit Column() noexcept + : ColumnBaseWithIndex(npos) + , m_tree(Allocator::get_default()) + { + } + explicit Column(std::unique_ptr root) noexcept; + Column(Allocator&, ref_type, size_t column_ndx = npos); + Column(unattached_root_tag, Allocator&); + Column(Column&&) noexcept = default; + ~Column() noexcept override; + + void init_from_parent(); + void init_from_ref(Allocator&, ref_type); + void init_from_mem(Allocator&, MemRef); + // Accessor concept: + void destroy() noexcept override; + Allocator& get_alloc() const noexcept final; + ref_type get_ref() const noexcept final; + MemRef get_mem() const noexcept final; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept override; + size_t get_ndx_in_parent() const noexcept final; + void set_ndx_in_parent(size_t ndx) noexcept final; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void detach() noexcept final; + bool is_attached() const noexcept final; + MemRef clone_deep(Allocator&) const override; + + void move_assign(Column&); + + static size_t get_size_from_ref(ref_type root_ref, Allocator& alloc) + { + return ColumnBase::get_size_from_ref(root_ref, alloc); + } + + size_t size() const noexcept override; + bool is_empty() const noexcept + { + return size() == 0; + } + bool is_nullable() const noexcept override; + + /// Provides access to the leaf that contains the element at the + /// specified index. Upon return \a ndx_in_leaf will be set to the + /// corresponding index relative to the beginning of the leaf. + /// + /// LeafInfo is a struct defined by the underlying BpTree + /// data structure, that provides a way for the caller to do + /// leaf caching without instantiating too many objects along + /// the way. + /// + /// This function cannot be used for modifying operations as it + /// does not ensure the presence of an unbroken chain of parent + /// accessors. For this reason, the identified leaf should always + /// be accessed through the returned const-qualified reference, + /// and never directly through the specfied fallback accessor. + void get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf) const noexcept; + + // Getting and setting values + T get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept override; + T back() const noexcept; + void set(size_t, T value); + void set_null(size_t) override; + void add(T value = T{}); + void insert(size_t ndx, T value = T{}, size_t num_rows = 1); + void erase(size_t row_ndx); + void erase(size_t row_ndx, bool is_last); + void move_last_over(size_t row_ndx, size_t last_row_ndx); + void clear(); + + // Index support + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override; + + // FIXME: Remove these + uint64_t get_uint(size_t ndx) const noexcept; + ref_type get_as_ref(size_t ndx) const noexcept; + void set_uint(size_t ndx, uint64_t value); + void set_as_ref(size_t ndx, ref_type value); + + template + void adjust(size_t ndx, U diff); + + template + void adjust(U diff); + + template + void adjust_ge(T limit, U diff); + + size_t count(T target) const; + + typename ColumnTypeTraits::sum_type sum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + typename ColumnTypeTraits::minmax_type maximum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + typename ColumnTypeTraits::minmax_type minimum(size_t start = 0, size_t end = npos, size_t limit = npos, + size_t* return_ndx = nullptr) const; + + double average(size_t start = 0, size_t end = npos, size_t limit = npos, size_t* return_ndx = nullptr) const; + + size_t find_first(T value, size_t begin = 0, size_t end = npos) const; + void find_all(Column& out_indices, T value, size_t begin = 0, size_t end = npos) const; + + void populate_search_index(); + StringIndex* create_search_index() override; + inline bool supports_search_index() const noexcept override + { + if (realm::is_any::value) + return false; + else + return true; + } + + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to ordinary integer comparison. + size_t lower_bound(T value) const noexcept; + size_t upper_bound(T value) const noexcept; + //@} + + using const_iterator = ColumnRandIterator; + + const_iterator cbegin() const + { + return const_iterator(this, 0); // `this` is const in a const method + } + const_iterator cend() const + { + return const_iterator(this, size()); + } + + size_t find_gte(T target, size_t start) const; + + bool compare(const Column&) const noexcept; + int compare_values(size_t row1, size_t row2) const noexcept override; + + static ref_type create(Allocator&, Array::Type leaf_type = Array::type_Normal, size_t size = 0, T value = T{}); + + // Overriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + + /// \brief Swap the elements at the specified indices. + /// + /// If this \c Column has a search index defined, it will be updated to + /// reflect the changes induced by the swap. + /// + /// Behaviour is undefined if: + /// - \a row_ndx_1 or \a row_ndx_2 point to an invalid element (out-of + /// bounds) + /// - \a row_ndx_1 and \a row_ndx_2 point to the same value + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + + /// \param row_ndx Must be `realm::npos` if appending. + /// \param value The value to store at the specified row. + /// \param num_rows The number of rows to insert. + void insert_without_updating_index(size_t row_ndx, T value, size_t num_rows); + + void verify() const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; +#ifdef REALM_DEBUG + using ColumnBase::verify; + void tree_to_dot(std::ostream&) const; + MemStats stats() const; +#endif + + //@{ + /// Returns the array node at the root of this column, but note + /// that there is no guarantee that this node is an inner B+-tree + /// node or a leaf. This is the case for a MixedColumn in + /// particular. + Array* get_root_array() noexcept + { + return &m_tree.root(); + } + const Array* get_root_array() const noexcept + { + return &m_tree.root(); + } + //@} + +protected: + bool root_is_leaf() const noexcept + { + return m_tree.root_is_leaf(); + } + void replace_root_array(std::unique_ptr leaf) final + { + m_tree.replace_root(std::move(leaf)); + } + + void set_without_updating_index(size_t row_ndx, T value); + void erase_without_updating_index(size_t row_ndx, bool is_last); + void move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx); + void swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2); + + /// If any element points to an array node, this function recursively + /// destroys that array node. Note that the same is **not** true for + /// IntegerColumn::do_erase() and IntegerColumn::do_move_last_over(). + /// + /// FIXME: Be careful, clear_without_updating_index() currently forgets + /// if the leaf type is Array::type_HasRefs. + void clear_without_updating_index(); + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; +#ifdef REALM_DEBUG + static void dump_node_structure(const Array& root, std::ostream&, int level); +#endif + +private: + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + friend class Array; + friend class ColumnBase; + friend class StringIndex; + + BpTree m_tree; + + void do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last); +}; + +// Implementation: + + +template <> +inline size_t IntNullColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) +{ + // FIXME: Speed improvement possible by not creating instance, but tricky! This slow method is OK so far + // because it's only invoked by Table::get_size_from_ref() which is only used for subtables which we + // currently 2016) do not expose publicly. + IntNullColumn inc(alloc, root_ref); + return inc.size(); +} + + +inline bool ColumnBase::supports_search_index() const noexcept +{ + REALM_ASSERT(!has_search_index()); + return false; +} + +inline bool ColumnBase::has_search_index() const noexcept +{ + return get_search_index() != nullptr; +} + +inline StringIndex* ColumnBase::create_search_index() +{ + return nullptr; +} + +inline void ColumnBase::destroy_search_index() noexcept +{ +} + +inline const StringIndex* ColumnBase::get_search_index() const noexcept +{ + return nullptr; +} + +inline StringIndex* ColumnBase::get_search_index() noexcept +{ + return nullptr; +} + +inline void ColumnBase::set_search_index_ref(ref_type, ArrayParent*, size_t, bool) +{ +} + +inline void ColumnBase::set_search_index_allow_duplicate_values(bool) noexcept +{ +} + +inline void ColumnBase::discard_child_accessors() noexcept +{ + do_discard_child_accessors(); +} + +inline Table* ColumnBase::get_subtable_accessor(size_t) const noexcept +{ + return 0; +} + +inline void ColumnBase::discard_subtable_accessor(size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_insert_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_erase_row(size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_move_over(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_swap_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_merge_rows(size_t, size_t) noexcept +{ + // Noop +} + +inline void ColumnBase::adj_acc_clear_root_table() noexcept +{ + // Noop +} + +inline void ColumnBase::mark(int) noexcept +{ + // Noop +} + +inline void ColumnBase::bump_link_origin_table_version() noexcept +{ + // Noop +} + +template +int ColumnBase::compare_values(const Column* column, size_t row1, size_t row2) noexcept +{ + // we negate nullability such that the two ternary statements in this method can look identical to reduce + // risk of bugs + bool v1 = !column->is_null(row1); + bool v2 = !column->is_null(row2); + + if (!v1 || !v2) + return v1 == v2 ? 0 : v1 < v2 ? 1 : -1; + + auto a = column->get(row1); + auto b = column->get(row2); + return a == b ? 0 : a < b ? 1 : -1; +} + +template +void Column::set_without_updating_index(size_t ndx, T value) +{ + m_tree.set(ndx, std::move(value)); +} + +template +void Column::set(size_t ndx, T value) +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (has_search_index()) { + m_search_index->set(ndx, value); + } + set_without_updating_index(ndx, std::move(value)); +} + +template +void Column::set_null(size_t ndx) +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (!is_nullable()) { + throw LogicError{LogicError::column_not_nullable}; + } + if (has_search_index()) { + m_search_index->set(ndx, null{}); + } + m_tree.set_null(ndx); +} + +// When a value of a signed type is converted to an unsigned type, the C++ standard guarantees that negative values +// are converted from the native representation to 2's complement, but the opposite conversion is left as undefined. +// realm::util::from_twos_compl() is used here to perform the correct opposite unsigned-to-signed conversion, +// which reduces to a no-op when 2's complement is the native representation of negative values. +template +void Column::set_uint(size_t ndx, uint64_t value) +{ + set(ndx, util::from_twos_compl(value)); +} + +template +void Column::set_as_ref(size_t ndx, ref_type ref) +{ + set(ndx, from_ref(ref)); +} + +template +template +void Column::adjust(size_t ndx, U diff) +{ + REALM_ASSERT_3(ndx, <, size()); + m_tree.adjust(ndx, diff); +} + +template +template +void Column::adjust(U diff) +{ + m_tree.adjust(diff); +} + +template +template +void Column::adjust_ge(T limit, U diff) +{ + m_tree.adjust_ge(limit, diff); +} + +template +size_t Column::count(T target) const +{ + if (has_search_index()) { + return m_search_index->count(target); + } + return to_size_t(aggregate(*this, target, 0, size(), npos, nullptr)); +} + +template +typename ColumnTypeTraits::sum_type Column::sum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using sum_type = typename ColumnTypeTraits::sum_type; + if (nullable) + return aggregate(*this, 0, start, end, limit, return_ndx); + else + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +double Column::average(size_t start, size_t end, size_t limit, size_t* return_ndx) const +{ + if (end == size_t(-1)) + end = size(); + + auto s = sum(start, end, limit); + size_t cnt = to_size_t(aggregate(*this, 0, start, end, limit, nullptr)); + if (return_ndx) + *return_ndx = cnt; + double avg = double(s) / (cnt == 0 ? 1 : cnt); + return avg; +} + +template +typename ColumnTypeTraits::minmax_type Column::minimum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using R = typename ColumnTypeTraits::minmax_type; + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +typename ColumnTypeTraits::minmax_type Column::maximum(size_t start, size_t end, size_t limit, + size_t* return_ndx) const +{ + using R = typename ColumnTypeTraits::minmax_type; + return aggregate(*this, 0, start, end, limit, return_ndx); +} + +template +void Column::get_leaf(size_t ndx, size_t& ndx_in_leaf, LeafInfo& inout_leaf_info) const noexcept +{ + m_tree.get_leaf(ndx, ndx_in_leaf, inout_leaf_info); +} + +template +StringData Column::get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept +{ + T x = get(ndx); + return to_str(x, buffer); +} + +template +void Column::populate_search_index() +{ + REALM_ASSERT(has_search_index()); + // Populate the index + size_t num_rows = size(); + for (size_t row_ndx = 0; row_ndx != num_rows; ++row_ndx) { + bool is_append = true; + if (is_null(row_ndx)) { + m_search_index->insert(row_ndx, null{}, 1, is_append); // Throws + } + else { + T value = get(row_ndx); + m_search_index->insert(row_ndx, value, 1, is_append); // Throws + } + } +} + +template +StringIndex* Column::create_search_index() +{ + if (realm::is_any::value) + return nullptr; + + REALM_ASSERT(!has_search_index()); + REALM_ASSERT(supports_search_index()); + m_search_index.reset(new StringIndex(this, get_alloc())); // Throws + populate_search_index(); + return m_search_index.get(); +} + +template +size_t Column::find_first(T value, size_t begin, size_t end) const +{ + REALM_ASSERT_3(begin, <=, size()); + REALM_ASSERT(end == npos || (begin <= end && end <= size())); + + if (m_search_index && begin == 0 && end == npos) + return m_search_index->find_first(value); + return m_tree.find_first(value, begin, end); +} + +template +void Column::find_all(IntegerColumn& result, T value, size_t begin, size_t end) const +{ + REALM_ASSERT_3(begin, <=, size()); + REALM_ASSERT(end == npos || (begin <= end && end <= size())); + + if (m_search_index && begin == 0 && end == npos) + return m_search_index->find_all(result, value); + return m_tree.find_all(result, value, begin, end); +} + +inline size_t ColumnBase::get_size_from_ref(ref_type root_ref, Allocator& alloc) +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) + return Array::get_size_from_header(root_header); + return Array::get_bptree_size_from_header(root_header); +} + +template +size_t ColumnBase::lower_bound(const L& list, T value) const noexcept +{ + size_t i = 0; + size_t list_size = list.size(); + while (0 < list_size) { + size_t half = list_size / 2; + size_t mid = i + half; + typename L::value_type probe = list.get(mid); + if (probe < value) { + i = mid + 1; + list_size -= half + 1; + } + else { + list_size = half; + } + } + return i; +} + +template +size_t ColumnBase::upper_bound(const L& list, T value) const noexcept +{ + size_t i = 0; + size_t list_size = list.size(); + while (0 < list_size) { + size_t half = list_size / 2; + size_t mid = i + half; + typename L::value_type probe = list.get(mid); + if (!(value < probe)) { + i = mid + 1; + list_size -= half + 1; + } + else { + list_size = half; + } + } + return i; +} + + +inline ref_type ColumnBase::create(Allocator& alloc, size_t column_size, CreateHandler& handler) +{ + size_t rest_size = column_size; + size_t fixed_height = 0; // Not fixed + return build(&rest_size, fixed_height, alloc, handler); +} + +template +Column::Column(Allocator& alloc, ref_type ref, size_t column_ndx) + : ColumnBaseWithIndex(column_ndx) + , m_tree(BpTreeBase::unattached_tag{}) +{ + // fixme, must m_search_index be copied here? + m_tree.init_from_ref(alloc, ref); +} + +template +Column::Column(unattached_root_tag, Allocator& alloc) + : ColumnBaseWithIndex(npos) + , m_tree(alloc) +{ +} + +template +Column::Column(std::unique_ptr root) noexcept + : m_tree(std::move(root)) +{ +} + +template +Column::~Column() noexcept +{ +} + +template +void Column::init_from_parent() +{ + m_tree.init_from_parent(); +} + +template +void Column::init_from_ref(Allocator& alloc, ref_type ref) +{ + m_tree.init_from_ref(alloc, ref); +} + +template +void Column::init_from_mem(Allocator& alloc, MemRef mem) +{ + m_tree.init_from_mem(alloc, mem); +} + +template +void Column::destroy() noexcept +{ + ColumnBaseWithIndex::destroy(); + m_tree.destroy(); +} + +template +void Column::move_assign(Column& col) +{ + ColumnBaseWithIndex::move_assign(col); + m_tree = std::move(col.m_tree); +} + +template +Allocator& Column::get_alloc() const noexcept +{ + return m_tree.get_alloc(); +} + +template +void Column::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_tree.set_parent(parent, ndx_in_parent); +} + +template +size_t Column::get_ndx_in_parent() const noexcept +{ + return m_tree.get_ndx_in_parent(); +} + +template +void Column::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + ColumnBaseWithIndex::set_ndx_in_parent(ndx_in_parent); + m_tree.set_ndx_in_parent(ndx_in_parent); +} + +template +void Column::detach() noexcept +{ + m_tree.detach(); +} + +template +bool Column::is_attached() const noexcept +{ + return m_tree.is_attached(); +} + +template +ref_type Column::get_ref() const noexcept +{ + return get_root_array()->get_ref(); +} + +template +MemRef Column::get_mem() const noexcept +{ + return get_root_array()->get_mem(); +} + +template +void Column::update_from_parent(size_t old_baseline) noexcept +{ + ColumnBaseWithIndex::update_from_parent(old_baseline); + m_tree.update_from_parent(old_baseline); +} + +template +MemRef Column::clone_deep(Allocator& alloc) const +{ + return m_tree.clone_deep(alloc); +} + +template +size_t Column::size() const noexcept +{ + return m_tree.size(); +} + +template +bool Column::is_nullable() const noexcept +{ + return nullable; +} + +template +T Column::get(size_t ndx) const noexcept +{ + return m_tree.get(ndx); +} + +template +bool Column::is_null(size_t ndx) const noexcept +{ + return nullable && m_tree.is_null(ndx); +} + +template +T Column::back() const noexcept +{ + return m_tree.back(); +} + +template +ref_type Column::get_as_ref(size_t ndx) const noexcept +{ + return to_ref(get(ndx)); +} + +template +uint64_t Column::get_uint(size_t ndx) const noexcept +{ + static_assert(std::is_convertible::value, "T is not convertible to uint."); + return static_cast(get(ndx)); +} + +template +void Column::add(T value) +{ + insert(npos, std::move(value)); +} + +template +void Column::insert_without_updating_index(size_t row_ndx, T value, size_t num_rows) +{ + size_t column_size = this->size(); // Slow + bool is_append = row_ndx == column_size || row_ndx == npos; + size_t ndx_or_npos_if_append = is_append ? npos : row_ndx; + + m_tree.insert(ndx_or_npos_if_append, std::move(value), num_rows); // Throws +} + +template +void Column::insert(size_t row_ndx, T value, size_t num_rows) +{ + size_t column_size = this->size(); // Slow + bool is_append = row_ndx == column_size || row_ndx == npos; + size_t ndx_or_npos_if_append = is_append ? npos : row_ndx; + + m_tree.insert(ndx_or_npos_if_append, value, num_rows); // Throws + + if (has_search_index()) { + row_ndx = is_append ? column_size : row_ndx; + m_search_index->insert(row_ndx, value, num_rows, is_append); // Throws + } +} + +template +void Column::erase_without_updating_index(size_t row_ndx, bool is_last) +{ + m_tree.erase(row_ndx, is_last); +} + +template +void Column::erase(size_t row_ndx) +{ + REALM_ASSERT(size() >= 1); + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = (row_ndx == last_row_ndx); + erase(row_ndx, is_last); // Throws +} + +template +void Column::erase(size_t row_ndx, bool is_last) +{ + size_t num_rows_to_erase = 1; + do_erase(row_ndx, num_rows_to_erase, is_last); // Throws +} + +template +void Column::move_last_over_without_updating_index(size_t row_ndx, size_t last_row_ndx) +{ + m_tree.move_last_over(row_ndx, last_row_ndx); +} + +template +void Column::move_last_over(size_t row_ndx, size_t last_row_ndx) +{ + REALM_ASSERT_3(row_ndx, <=, last_row_ndx); + REALM_ASSERT_DEBUG(last_row_ndx + 1 == size()); + + if (has_search_index()) { + // remove the value to be overwritten from index + bool is_last = true; // This tells StringIndex::erase() to not adjust subsequent indexes + m_search_index->erase(row_ndx, is_last); // Throws + + // update index to point to new location + if (row_ndx != last_row_ndx) { + T moved_value = get(last_row_ndx); + m_search_index->update_ref(moved_value, last_row_ndx, row_ndx); // Throws + } + } + + move_last_over_without_updating_index(row_ndx, last_row_ndx); +} + +template +void Column::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + REALM_ASSERT_3(row_ndx_1, <, size()); + REALM_ASSERT_3(row_ndx_2, <, size()); + REALM_ASSERT_DEBUG(row_ndx_1 != row_ndx_2); + + if (has_search_index()) { + T value_1 = get(row_ndx_1); + T value_2 = get(row_ndx_2); + size_t column_size = this->size(); + bool row_ndx_1_is_last = row_ndx_1 == column_size - 1; + bool row_ndx_2_is_last = row_ndx_2 == column_size - 1; + m_search_index->erase(row_ndx_1, row_ndx_1_is_last); + m_search_index->insert(row_ndx_1, value_2, 1, row_ndx_1_is_last); + + m_search_index->erase(row_ndx_2, row_ndx_2_is_last); + m_search_index->insert(row_ndx_2, value_1, 1, row_ndx_2_is_last); + } + + swap_rows_without_updating_index(row_ndx_1, row_ndx_2); +} + +template +void Column::swap_rows_without_updating_index(size_t row_ndx_1, size_t row_ndx_2) +{ + // FIXME: This can be optimized with direct getters and setters. + T value_1 = get(row_ndx_1); + T value_2 = get(row_ndx_2); + m_tree.set(row_ndx_1, value_2); + m_tree.set(row_ndx_2, value_1); +} + +template +void Column::clear_without_updating_index() +{ + m_tree.clear(); // Throws +} + +template +void Column::clear() +{ + if (has_search_index()) { + m_search_index->clear(); + } + clear_without_updating_index(); +} + +template +struct NullOrDefaultValue; +template +struct NullOrDefaultValue::value>::type> { + static T null_or_default_value(bool is_null) + { + if (is_null) { + return null::get_null_float(); + } + else { + return T{}; + } + } +}; +template +struct NullOrDefaultValue, void> { + static util::Optional null_or_default_value(bool is_null) + { + if (is_null) { + return util::none; + } + else { + return util::some(T{}); + } + } +}; +template +struct NullOrDefaultValue::value>::type> { + static T null_or_default_value(bool is_null) + { + REALM_ASSERT(!is_null); + return T{}; + } +}; + +// Implementing pure virtual method of ColumnBase. +template +void Column::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + T value = NullOrDefaultValue::null_or_default_value(insert_nulls); + insert(row_ndx_2, value, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + do_erase(row_ndx, num_rows_to_erase, is_last); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +template +void Column::clear(size_t, bool) +{ + clear(); // Throws +} + + +template +size_t Column::lower_bound(T value) const noexcept +{ + if (root_is_leaf()) { + auto root = static_cast(get_root_array()); + return root->lower_bound(value); + } + return ColumnBase::lower_bound(*this, value); +} + +template +size_t Column::upper_bound(T value) const noexcept +{ + if (root_is_leaf()) { + auto root = static_cast(get_root_array()); + return root->upper_bound(value); + } + return ColumnBase::upper_bound(*this, value); +} + +// For a *sorted* Column, return first element E for which E >= target or return -1 if none +template +size_t Column::find_gte(T target, size_t start) const +{ + // fixme: slow reference implementation. See Array::find_gte for faster version + size_t ref = 0; + size_t idx; + for (idx = start; idx < size(); ++idx) { + if (get(idx) >= target) { + ref = idx; + break; + } + } + if (idx == size()) + ref = not_found; + + return ref; +} + + +template +bool Column::compare(const Column& c) const noexcept +{ + size_t n = size(); + if (c.size() != n) + return false; + for (size_t i = 0; i < n; ++i) { + bool left_is_null = is_null(i); + bool right_is_null = c.is_null(i); + if (left_is_null != right_is_null) { + return false; + } + if (!left_is_null) { + if (get(i) != c.get(i)) + return false; + } + } + return true; +} + +template +int Column::compare_values(size_t row1, size_t row2) const noexcept +{ + return ColumnBase::compare_values(this, row1, row2); +} + +template +class Column::CreateHandler : public ColumnBase::CreateHandler { +public: + CreateHandler(Array::Type leaf_type, T value, Allocator& alloc) + : m_value(value) + , m_alloc(alloc) + , m_leaf_type(leaf_type) + { + } + ref_type create_leaf(size_t size) override + { + MemRef mem = BpTree::create_leaf(m_leaf_type, size, m_value, m_alloc); // Throws + return mem.get_ref(); + } + +private: + const T m_value; + Allocator& m_alloc; + Array::Type m_leaf_type; +}; + +template +ref_type Column::create(Allocator& alloc, Array::Type leaf_type, size_t size, T value) +{ + CreateHandler handler(leaf_type, std::move(value), alloc); + return ColumnBase::create(alloc, size, handler); +} + +template +ref_type Column::write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream& out) const +{ + return m_tree.write(slice_offset, slice_size, table_size, out); +} + +template +void Column::refresh_accessor_tree(size_t new_col_ndx, const Spec& spec) +{ + m_tree.init_from_parent(); + ColumnBaseWithIndex::refresh_accessor_tree(new_col_ndx, spec); +} + +template +void Column::do_erase(size_t row_ndx, size_t num_rows_to_erase, bool is_last) +{ + if (has_search_index()) { + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + m_search_index->erase(row_ndx_2, is_last); // Throws + } + } + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + erase_without_updating_index(row_ndx_2, is_last); // Throws + } +} + +template +void Column::verify() const +{ +#ifdef REALM_DEBUG + m_tree.verify(); +#endif +} + +// LCOV_EXCL_START + +template +void Column::to_dot(std::ostream& out, StringData title) const +{ +#ifdef REALM_DEBUG + ref_type ref = get_root_array()->get_ref(); + out << "subgraph cluster_integer_column" << ref << " {" << std::endl; + out << " label = \"Integer column"; + if (title.size() != 0) + out << "\\n'" << title << "'"; + out << "\";" << std::endl; + tree_to_dot(out); + out << "}" << std::endl; +#else + static_cast(out); + static_cast(title); +#endif +} + +template +void Column::leaf_to_dot(MemRef leaf_mem, ArrayParent* parent, size_t ndx_in_parent, std::ostream& out) const +{ +#ifdef REALM_DEBUG + BpTree::leaf_to_dot(leaf_mem, parent, ndx_in_parent, out, get_alloc()); +#else + static_cast(leaf_mem); + static_cast(parent); + static_cast(ndx_in_parent); + static_cast(out); +#endif +} + +template +void Column::do_dump_node_structure(std::ostream& out, int level) const +{ +#ifdef REALM_DEBUG + dump_node_structure(*get_root_array(), out, level); +#else + static_cast(out); + static_cast(level); +#endif +} + +#ifdef REALM_DEBUG + +template +void Column::tree_to_dot(std::ostream& out) const +{ + ColumnBase::bptree_to_dot(get_root_array(), out); +} + + +template +MemStats Column::stats() const +{ + MemStats mem_stats; + get_root_array()->stats(mem_stats); + return mem_stats; +} + +namespace _impl { +void leaf_dumper(MemRef mem, Allocator& alloc, std::ostream& out, int level); +} + +template +void Column::dump_node_structure(const Array& root, std::ostream& out, int level) +{ + root.dump_bptree_structure(out, level, &_impl::leaf_dumper); +} + +#endif // LCOV_EXCL_STOP ignore debug functions + + +template +ColumnRandIterator::ColumnRandIterator(const Column* src_col, size_t ndx) + : col_ndx(ndx) + , col(src_col) +{ + cached_column_size = col->size(); +} + +template +ColumnRandIterator::operator bool() const +{ + return col_ndx >= 0 && col_ndx < cached_column_size; +} + +template +bool ColumnRandIterator::operator==(const ColumnRandIterator& other) const +{ + return (col_ndx == other.col_ndx); +} + +template +bool ColumnRandIterator::operator!=(const ColumnRandIterator& other) const +{ + return !(*this == other); +} + +template +ColumnRandIterator& ColumnRandIterator::operator+=(const ptrdiff_t& movement) +{ + col_ndx += movement; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator-=(const ptrdiff_t& movement) +{ + col_ndx -= movement; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator++() +{ + ++col_ndx; + return (*this); +} + +template +ColumnRandIterator& ColumnRandIterator::operator--() +{ + --col_ndx; + return (*this); +} + +template +ColumnRandIterator ColumnRandIterator::operator++(int) +{ + auto temp(*this); + ++col_ndx; + return temp; +} + +template +ColumnRandIterator ColumnRandIterator::operator--(int) +{ + auto temp(*this); + --col_ndx; + return temp; +} + +template +ColumnRandIterator ColumnRandIterator::operator+(const ptrdiff_t& movement) +{ + return ColumnRandIterator(col, col_ndx + movement); +} + +template +ColumnRandIterator ColumnRandIterator::operator-(const ptrdiff_t& movement) +{ + return ColumnRandIterator(col, col_ndx - movement); +} + +template +ptrdiff_t ColumnRandIterator::operator-(const ColumnRandIterator& other) +{ + return col_ndx - other.col_ndx; +} + +template +const ColumnDataType ColumnRandIterator::operator*() const +{ + return col->get(col_ndx); +} + +template +size_t ColumnRandIterator::get_col_ndx() const +{ + return col_ndx; +} + + +} // namespace realm + +#endif // REALM_COLUMN_HPP diff --git a/Pods/Realm/include/core/realm/column_backlink.hpp b/Pods/Realm/include/core/realm/column_backlink.hpp new file mode 100644 index 0000000..11ba6e5 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_backlink.hpp @@ -0,0 +1,228 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_BACKLINK_HPP +#define REALM_COLUMN_BACKLINK_HPP + +#include + +#include +#include +#include + +namespace realm { + +/// A column of backlinks (BacklinkColumn) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are single arrays of +/// type Array with the hasRefs bit set. +/// +/// The individual values in the column are either refs to Columns containing +/// the row indexes in the origin table that links to it, or in the case where +/// there is a single link, a tagged ref encoding the origin row position. +class BacklinkColumn : public IntegerColumn, public ArrayParent { +public: + BacklinkColumn(Allocator&, ref_type, size_t col_ndx = npos); + ~BacklinkColumn() noexcept override + { + } + + static ref_type create(Allocator&, size_t size = 0); + + bool has_backlinks(size_t row_ndx) const noexcept; + size_t get_backlink_count(size_t row_ndx) const noexcept; + size_t get_backlink(size_t row_ndx, size_t backlink_ndx) const noexcept; + + void add_backlink(size_t row_ndx, size_t origin_row_ndx); + void remove_one_backlink(size_t row_ndx, size_t origin_row_ndx); + void remove_all_backlinks(size_t num_rows); + void update_backlink(size_t row_ndx, size_t old_origin_row_ndx, size_t new_origin_row_ndx); + void swap_backlinks(size_t row_ndx, size_t origin_row_ndx_1, size_t origin_row_ndx_2); + + void add_row(); + + // Link origination info + Table& get_origin_table() const noexcept; + void set_origin_table(Table&) noexcept; + LinkColumnBase& get_origin_column() const noexcept; + size_t get_origin_column_index() const noexcept; + void set_origin_column(LinkColumnBase& column) noexcept; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + + void bump_link_origin_table_version() noexcept override; + + void cascade_break_backlinks_to(size_t row_ndx, CascadeState& state) override; + void cascade_break_backlinks_to_all_rows(size_t num_rows, CascadeState&) override; + + int compare_values(size_t, size_t) const noexcept override; + + void verify() const override; + void verify(const Table&, size_t) const override; +#ifdef REALM_DEBUG + struct VerifyPair { + size_t origin_row_ndx, target_row_ndx; + bool operator<(const VerifyPair&) const noexcept; + }; + void get_backlinks(std::vector&); // Sorts +#endif + +protected: + // ArrayParent overrides + void update_child_ref(size_t child_ndx, ref_type new_ref) override; + ref_type get_child_ref(size_t child_ndx) const noexcept override; + + std::pair get_to_dot_parent(size_t) const override; + +private: + TableRef m_origin_table; + LinkColumnBase* m_origin_column = nullptr; + + template + size_t for_each_link(size_t row_ndx, bool do_destroy, Func&& f); +}; + + +// Implementation + +inline BacklinkColumn::BacklinkColumn(Allocator& alloc, ref_type ref, size_t col_ndx) + : IntegerColumn(alloc, ref, col_ndx) // Throws +{ +} + +inline ref_type BacklinkColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline bool BacklinkColumn::has_backlinks(size_t ndx) const noexcept +{ + return IntegerColumn::get(ndx) != 0; +} + +inline Table& BacklinkColumn::get_origin_table() const noexcept +{ + return *m_origin_table; +} + +inline void BacklinkColumn::set_origin_table(Table& table) noexcept +{ + REALM_ASSERT(!m_origin_table); + m_origin_table = table.get_table_ref(); +} + +inline LinkColumnBase& BacklinkColumn::get_origin_column() const noexcept +{ + return *m_origin_column; +} + +inline size_t BacklinkColumn::get_origin_column_index() const noexcept +{ + return m_origin_column ? m_origin_column->get_column_index() : npos; +} + +inline void BacklinkColumn::set_origin_column(LinkColumnBase& column) noexcept +{ + m_origin_column = &column; +} + +inline void BacklinkColumn::add_row() +{ + IntegerColumn::add(0); +} + +inline void BacklinkColumn::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + IntegerColumn::adj_acc_insert_rows(row_ndx, num_rows); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_erase_row(size_t row_ndx) noexcept +{ + IntegerColumn::adj_acc_erase_row(row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + IntegerColumn::adj_acc_move_over(from_row_ndx, to_row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + Column::adj_acc_swap_rows(row_ndx_1, row_ndx_2); + + using tf = _impl::TableFriend; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::adj_acc_clear_root_table() noexcept +{ + IntegerColumn::adj_acc_clear_root_table(); + + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); +} + +inline void BacklinkColumn::mark(int type) noexcept +{ + if (type & mark_LinkOrigins) { + typedef _impl::TableFriend tf; + tf::mark(*m_origin_table); + } +} + +inline void BacklinkColumn::bump_link_origin_table_version() noexcept +{ + // It is important to mark connected tables as modified. + // Also see LinkColumnBase::bump_link_origin_table_version(). + typedef _impl::TableFriend tf; + if (m_origin_table) { + bool bump_global = false; + tf::bump_version(*m_origin_table, bump_global); + } +} + +#ifdef REALM_DEBUG + +inline bool BacklinkColumn::VerifyPair::operator<(const VerifyPair& p) const noexcept +{ + return origin_row_ndx < p.origin_row_ndx; +} + +#endif // REALM_DEBUG + +} // namespace realm + +#endif // REALM_COLUMN_BACKLINK_HPP diff --git a/Pods/Realm/include/core/realm/column_binary.hpp b/Pods/Realm/include/core/realm/column_binary.hpp new file mode 100644 index 0000000..f0dc7a1 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_binary.hpp @@ -0,0 +1,429 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_BINARY_HPP +#define REALM_COLUMN_BINARY_HPP + +#include +#include +#include + +namespace realm { + + +/// A binary column (BinaryColumn) is a single B+-tree, and the root +/// of the column is the root of the B+-tree. Leaf nodes are either of +/// type ArrayBinary (array of small blobs) or ArrayBigBlobs (array of +/// big blobs). +class BinaryColumn : public ColumnBaseSimple { +public: + typedef BinaryData value_type; + + BinaryColumn(Allocator&, ref_type, bool nullable = false, size_t column_ndx = npos); + + size_t size() const noexcept final; + bool is_empty() const noexcept + { + return size() == 0; + } + bool is_nullable() const noexcept override; + + BinaryData get(size_t ndx) const noexcept; + + /// Return data from position 'pos' and onwards. If the blob is distributed + /// across multiple arrays (if bigger than ~ 16M), you will only get data + /// from one array. 'pos' will be updated to be an index to next available + /// data. It will be 0 if no more data. + BinaryData get_at(size_t ndx, size_t& pos) const noexcept; + + bool is_null(size_t ndx) const noexcept override; + StringData get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept final; + + void add(BinaryData value); + void set(size_t ndx, BinaryData value, bool add_zero_term = false); + void set_null(size_t ndx) override; + void insert(size_t ndx, BinaryData value); + void erase(size_t row_ndx); + void erase(size_t row_ndx, bool is_last); + void move_last_over(size_t row_ndx); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void clear(); + size_t find_first(BinaryData value) const; + + // Requires that the specified entry was inserted as StringData. + StringData get_string(size_t ndx) const noexcept; + + void add_string(StringData value); + void set_string(size_t ndx, StringData value) override; + void insert_string(size_t ndx, StringData value); + + /// Compare two binary columns for equality. + bool compare_binary(const BinaryColumn&) const; + + int compare_values(size_t row1, size_t row2) const noexcept override; + + static ref_type create(Allocator&, size_t size, bool nullable); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overrriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + /// In contrast to update_from_parent(), this function is able to handle + /// cases where the accessed payload data has changed. In particular, it + /// handles cases where the B+-tree switches from having one level (root is + /// a leaf node), to having multiple levels (root is an inner node). Note + /// that this is at the expense of loosing the `noexcept` guarantee. + void update_from_ref(ref_type ref); + + void verify() const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, BinaryData value, bool add_zero_term, size_t num_rows); + + // Called by Array::bptree_insert(). + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent&, size_t ndx_in_parent, Allocator&, size_t insert_ndx, + Array::TreeInsert& state); + + struct InsertState : Array::TreeInsert { + bool m_add_zero_term; + }; + + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_clear(); + + /// Root must be a leaf. Upgrades the root leaf if + /// necessary. Returns true if, and only if the root is a 'big + /// blobs' leaf upon return. + bool upgrade_root_leaf(size_t value_size); + + bool m_nullable = false; + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + friend class Array; + friend class ColumnBase; +}; + +class BinaryIterator { +public: + BinaryIterator() + { + } + // TODO: When WriteLogCollector is removed, there is no need for this + BinaryIterator(BinaryData binary) + : m_binary(binary) + { + } + + BinaryIterator(BinaryColumn* col, size_t ndx) + : m_binary_col(col) + , m_ndx(ndx) + { + } + + BinaryData get_next() noexcept + { + if (!end_of_data) { + if (m_binary_col) { + BinaryData ret = m_binary_col->get_at(m_ndx, m_pos); + end_of_data = (m_pos == 0); + return ret; + } + else if (!m_binary.is_null()) { + end_of_data = true; + return m_binary; + } + } + return {}; + } + +private: + bool end_of_data = false; + BinaryColumn* m_binary_col = nullptr; + size_t m_ndx = 0; + size_t m_pos = 0; + BinaryData m_binary; +}; + + +// Implementation + +// LCOV_EXCL_START +inline StringData BinaryColumn::get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept +{ + REALM_ASSERT(false && "Index not implemented for BinaryColumn."); + REALM_UNREACHABLE(); +} +// LCOV_EXCL_STOP + +inline size_t BinaryColumn::size() const noexcept +{ + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Small blobs root leaf + ArrayBinary* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Big blobs root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Non-leaf root + return m_array->get_bptree_size(); +} + +inline bool BinaryColumn::is_nullable() const noexcept +{ + return m_nullable; +} + +inline void BinaryColumn::update_from_parent(size_t old_baseline) noexcept +{ + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Small blobs root leaf + REALM_ASSERT(dynamic_cast(m_array.get())); + ArrayBinary* leaf = static_cast(m_array.get()); + leaf->update_from_parent(old_baseline); + return; + } + // Big blobs root leaf + REALM_ASSERT(dynamic_cast(m_array.get())); + ArrayBigBlobs* leaf = static_cast(m_array.get()); + leaf->update_from_parent(old_baseline); + return; + } + // Non-leaf root + m_array->update_from_parent(old_baseline); +} + +inline BinaryData BinaryColumn::get(size_t ndx) const noexcept +{ + REALM_ASSERT_DEBUG(ndx < size()); + if (root_is_leaf()) { + bool is_big = m_array->get_context_flag(); + BinaryData ret; + if (!is_big) { + // Small blobs root leaf + ArrayBinary* leaf = static_cast(m_array.get()); + ret = leaf->get(ndx); + } + else { + // Big blobs root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + ret = leaf->get(ndx); + } + if (!m_nullable && ret.is_null()) + return BinaryData("", 0); // return empty string (non-null) + return ret; + } + + // Non-leaf root + std::pair p = m_array->get_bptree_leaf(ndx); + const char* leaf_header = p.first.get_addr(); + size_t ndx_in_leaf = p.second; + Allocator& alloc = m_array->get_alloc(); + bool is_big = Array::get_context_flag_from_header(leaf_header); + if (!is_big) { + // Small blobs + return ArrayBinary::get(leaf_header, ndx_in_leaf, alloc); + } + // Big blobs + return ArrayBigBlobs::get(leaf_header, ndx_in_leaf, alloc); +} + +inline bool BinaryColumn::is_null(size_t ndx) const noexcept +{ + return m_nullable && get(ndx).is_null(); +} + +inline StringData BinaryColumn::get_string(size_t ndx) const noexcept +{ + BinaryData bin = get(ndx); + REALM_ASSERT_3(0, <, bin.size()); + return StringData(bin.data(), bin.size() - 1); +} + +inline void BinaryColumn::set_string(size_t ndx, StringData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + BinaryData bin(value.data(), value.size()); + bool add_zero_term = true; + set(ndx, bin, add_zero_term); +} + +inline void BinaryColumn::add(BinaryData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + size_t row_ndx = realm::npos; + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx, value, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::insert(size_t row_ndx, BinaryData value) +{ + if (value.is_null() && !m_nullable) + throw LogicError(LogicError::column_not_nullable); + + size_t column_size = this->size(); // Slow + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t row_ndx_2 = row_ndx == column_size ? realm::npos : row_ndx; + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx_2, value, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::set_null(size_t row_ndx) +{ + set(row_ndx, BinaryData{}); +} + +inline size_t BinaryColumn::find_first(BinaryData value) const +{ + for (size_t t = 0; t < size(); t++) + if (get(t) == value) + return t; + + return not_found; +} + + +inline void BinaryColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + erase(row_ndx, is_last); // Throws +} + +inline void BinaryColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void BinaryColumn::clear() +{ + do_clear(); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + BinaryData value = m_nullable ? BinaryData() : BinaryData("", 0); + bool add_zero_term = false; + do_insert(row_ndx_2, value, add_zero_term, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + erase(row_ndx_2, is_last); // Throws + } +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void BinaryColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +inline void BinaryColumn::add_string(StringData value) +{ + size_t row_ndx = realm::npos; + BinaryData value_2(value.data(), value.size()); + bool add_zero_term = true; + size_t num_rows = 1; + do_insert(row_ndx, value_2, add_zero_term, num_rows); // Throws +} + +inline void BinaryColumn::insert_string(size_t row_ndx, StringData value) +{ + size_t column_size = this->size(); // Slow + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t row_ndx_2 = row_ndx == column_size ? realm::npos : row_ndx; + BinaryData value_2(value.data(), value.size()); + bool add_zero_term = false; + size_t num_rows = 1; + do_insert(row_ndx_2, value_2, add_zero_term, num_rows); // Throws +} + +inline size_t BinaryColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) { + bool is_big = Array::get_context_flag_from_header(root_header); + if (!is_big) { + // Small blobs leaf + return ArrayBinary::get_size_from_header(root_header, alloc); + } + // Big blobs leaf + return ArrayBigBlobs::get_size_from_header(root_header); + } + return Array::get_bptree_size_from_header(root_header); +} + + +} // namespace realm + +#endif // REALM_COLUMN_BINARY_HPP diff --git a/Pods/Realm/include/core/realm/column_fwd.hpp b/Pods/Realm/include/core/realm/column_fwd.hpp new file mode 100644 index 0000000..1eecf01 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_fwd.hpp @@ -0,0 +1,57 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_FWD_HPP +#define REALM_COLUMN_FWD_HPP + +#include + +namespace realm { + +// Regular classes +class ColumnBase; +class StringColumn; +class StringEnumColumn; +class BinaryColumn; +class SubtableColumn; +class MixedColumn; +class LinkColumn; +class LinkListColumn; + +// Templated classes +template +class Column; +template +class BasicColumn; +template +class ColumnRandIterator; + +namespace util { +template +class Optional; +} + +// Shortcuts, aka typedefs. +using IntegerColumn = Column; +using IntNullColumn = Column>; +using DoubleColumn = Column; +using FloatColumn = Column; +using IntegerColumnIterator = ColumnRandIterator; +} // namespace realm + +#endif // REALM_COLUMN_FWD_HPP diff --git a/Pods/Realm/include/core/realm/column_link.hpp b/Pods/Realm/include/core/realm/column_link.hpp new file mode 100644 index 0000000..18471bb --- /dev/null +++ b/Pods/Realm/include/core/realm/column_link.hpp @@ -0,0 +1,179 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINK_HPP +#define REALM_COLUMN_LINK_HPP + +#include +#include +#include + +namespace realm { + +/// A link column is an extension of an integer column (Column) and maintains +/// its node structure. +/// +/// The individual values in a link column are indexes of rows in the target +/// table (offset with one to allow zero to indicate null links.) The target +/// table is specified by the table descriptor. +class LinkColumn : public LinkColumnBase { +public: + using LinkColumnBase::LinkColumnBase; + ~LinkColumn() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + bool is_nullable() const noexcept override; + + //@{ + + /// is_null_link() is shorthand for `get_link() == realm::npos`, + /// nullify_link() is shorthand foe `set_link(realm::npos)`, and + /// insert_null_link() is shorthand for + /// `insert_link(realm::npos)`. set_link() returns the original link, with + /// `realm::npos` indicating that it was null. + + size_t get_link(size_t row_ndx) const noexcept; + bool is_null(size_t row_ndx) const noexcept override; + bool is_null_link(size_t row_ndx) const noexcept; + size_t set_link(size_t row_ndx, size_t target_row_ndx); + void set_null(size_t row_ndx) override; + void nullify_link(size_t row_ndx); + void insert_link(size_t row_ndx, size_t target_row_ndx); + void insert_null_link(size_t row_ndx); + + //@} + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void cascade_break_backlinks_to(size_t, CascadeState&) override; + void cascade_break_backlinks_to_all_rows(size_t, CascadeState&) override; + + void verify(const Table&, size_t) const override; + +protected: + friend class BacklinkColumn; + void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) override; + void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) override; + void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) override; + +private: + void remove_backlinks(size_t row_ndx); +}; + + +// Implementation + +inline LinkColumn::~LinkColumn() noexcept +{ +} + +inline bool LinkColumn::is_nullable() const noexcept +{ + return true; +} + +inline ref_type LinkColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_Normal, size); // Throws +} + +inline bool LinkColumn::is_null(size_t row_ndx) const noexcept +{ + // Null is represented by zero + return LinkColumnBase::get(row_ndx) == 0; +} + +inline size_t LinkColumn::get_link(size_t row_ndx) const noexcept +{ + // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index. + return to_size_t(LinkColumnBase::get(row_ndx)) - size_t(1); +} + +inline bool LinkColumn::is_null_link(size_t row_ndx) const noexcept +{ + return is_null(row_ndx); +} + +inline size_t LinkColumn::set_link(size_t row_ndx, size_t target_row_ndx) +{ + int_fast64_t old_value = LinkColumnBase::get(row_ndx); + size_t old_target_row_ndx = to_size_t(old_value) - size_t(1); + if (old_value != 0) + m_backlink_column->remove_one_backlink(old_target_row_ndx, row_ndx); // Throws + + int_fast64_t new_value = int_fast64_t(size_t(1) + target_row_ndx); + LinkColumnBase::set(row_ndx, new_value); // Throws + + if (target_row_ndx != realm::npos) + m_backlink_column->add_backlink(target_row_ndx, row_ndx); // Throws + + return old_target_row_ndx; +} + +inline void LinkColumn::set_null(size_t row_ndx) +{ + set_link(row_ndx, realm::npos); // Throws +} + +inline void LinkColumn::nullify_link(size_t row_ndx) +{ + set_null(row_ndx); // Throws +} + +inline void LinkColumn::insert_link(size_t row_ndx, size_t target_row_ndx) +{ + int_fast64_t value = int_fast64_t(size_t(1) + target_row_ndx); + LinkColumnBase::insert(row_ndx, value); // Throws + + if (target_row_ndx != realm::npos) + m_backlink_column->add_backlink(target_row_ndx, row_ndx); // Throws +} + +inline void LinkColumn::insert_null_link(size_t row_ndx) +{ + insert_link(row_ndx, realm::npos); // Throws +} + +inline void LinkColumn::do_update_link(size_t row_ndx, size_t, size_t new_target_row_ndx) +{ + // Row pos is offset by one, to allow null refs + LinkColumnBase::set(row_ndx, new_target_row_ndx + 1); +} + +inline void LinkColumn::do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) +{ + // Row pos is offset by one, to allow null refs + ++target_row_ndx_1; + ++target_row_ndx_2; + + uint64_t value = LinkColumnBase::get_uint(row_ndx); + if (value == target_row_ndx_1) { + LinkColumnBase::set_uint(row_ndx, target_row_ndx_2); + } + else if (value == target_row_ndx_2) { + LinkColumnBase::set_uint(row_ndx, target_row_ndx_1); + } +} + +} // namespace realm + +#endif // REALM_COLUMN_LINK_HPP diff --git a/Pods/Realm/include/core/realm/column_linkbase.hpp b/Pods/Realm/include/core/realm/column_linkbase.hpp new file mode 100644 index 0000000..cff46b2 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_linkbase.hpp @@ -0,0 +1,197 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINKBASE_HPP +#define REALM_COLUMN_LINKBASE_HPP + +#include + +namespace realm { + +class BacklinkColumn; +class Table; + +// Abstract base class for columns containing links +class LinkColumnBase : public IntegerColumn { +public: + // Create unattached root array aaccessor. + LinkColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~LinkColumnBase() noexcept override; + + bool is_nullable() const noexcept override = 0; + void set_null(size_t) override = 0; + bool is_null(size_t) const noexcept override = 0; + + bool supports_search_index() const noexcept final + { + return false; + } + StringIndex* create_search_index() override; + + bool get_weak_links() const noexcept; + void set_weak_links(bool) noexcept; + + Table& get_target_table() const noexcept; + void set_target_table(Table&) noexcept; + BacklinkColumn& get_backlink_column() const noexcept; + void set_backlink_column(BacklinkColumn&) noexcept; + + void swap_rows(size_t, size_t) override = 0; + + virtual void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) = 0; + virtual void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) = 0; + virtual void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) = 0; + + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + void bump_link_origin_table_version() noexcept override; + + void verify(const Table&, size_t) const override; + using IntegerColumn::verify; + +protected: + // A pointer to the table that this column is part of. + Table* const m_table; + + TableRef m_target_table; + BacklinkColumn* m_backlink_column = nullptr; + bool m_weak_links = false; // True if these links are weak (not strong) + + /// Call Table::cascade_break_backlinks_to() for the specified target row if + /// it is not already in \a state.rows, and the number of strong links to it + /// has dropped to zero. + void check_cascade_break_backlinks_to(size_t target_table_ndx, size_t target_row_ndx, CascadeState& state); +}; + + +// Implementation + +inline LinkColumnBase::LinkColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : IntegerColumn(alloc, ref, column_ndx) // Throws + , m_table(table) +{ +} + +inline LinkColumnBase::~LinkColumnBase() noexcept +{ +} + +inline StringIndex* LinkColumnBase::create_search_index() +{ + return nullptr; +} + +inline bool LinkColumnBase::get_weak_links() const noexcept +{ + return m_weak_links; +} + +inline void LinkColumnBase::set_weak_links(bool value) noexcept +{ + m_weak_links = value; +} + +inline Table& LinkColumnBase::get_target_table() const noexcept +{ + return *m_target_table; +} + +inline void LinkColumnBase::set_target_table(Table& table) noexcept +{ + REALM_ASSERT(!m_target_table); + m_target_table = table.get_table_ref(); +} + +inline BacklinkColumn& LinkColumnBase::get_backlink_column() const noexcept +{ + return *m_backlink_column; +} + +inline void LinkColumnBase::set_backlink_column(BacklinkColumn& column) noexcept +{ + m_backlink_column = &column; +} + +inline void LinkColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + IntegerColumn::adj_acc_insert_rows(row_ndx, num_rows); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept +{ + IntegerColumn::adj_acc_erase_row(row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + IntegerColumn::adj_acc_move_over(from_row_ndx, to_row_ndx); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + IntegerColumn::adj_acc_swap_rows(row_ndx_1, row_ndx_2); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::adj_acc_clear_root_table() noexcept +{ + IntegerColumn::adj_acc_clear_root_table(); + + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); +} + +inline void LinkColumnBase::mark(int type) noexcept +{ + if (type & mark_LinkTargets) { + typedef _impl::TableFriend tf; + tf::mark(*m_target_table); + } +} + +inline void LinkColumnBase::bump_link_origin_table_version() noexcept +{ + // It is important to mark connected tables as modified. + // Also see BacklinkColumn::bump_link_origin_table_version(). + typedef _impl::TableFriend tf; + if (m_target_table) { + bool bump_global = false; + tf::bump_version(*m_target_table, bump_global); + } +} + + +} // namespace realm + +#endif // REALM_COLUMN_LINKBASE_HPP diff --git a/Pods/Realm/include/core/realm/column_linklist.hpp b/Pods/Realm/include/core/realm/column_linklist.hpp new file mode 100644 index 0000000..445066e --- /dev/null +++ b/Pods/Realm/include/core/realm/column_linklist.hpp @@ -0,0 +1,243 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_LINKLIST_HPP +#define REALM_COLUMN_LINKLIST_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace realm { + +namespace _impl { +class TransactLogConvenientEncoder; +} + + +/// A column of link lists (LinkListColumn) is a single B+-tree, and the root of +/// the column is the root of the B+-tree. All leaf nodes are single arrays of +/// type Array with the hasRefs bit set. +/// +/// The individual values in the column are either refs to Columns containing the +/// row positions in the target table, or in the case where they are empty, a zero +/// ref. +class LinkListColumn : public LinkColumnBase, public ArrayParent { +public: + using LinkColumnBase::LinkColumnBase; + LinkListColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~LinkListColumn() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + bool is_nullable() const noexcept final; + + bool has_links(size_t row_ndx) const noexcept; + size_t get_link_count(size_t row_ndx) const noexcept; + + ConstLinkViewRef get(size_t row_ndx) const; + LinkViewRef get(size_t row_ndx); + + bool is_null(size_t row_ndx) const noexcept final; + void set_null(size_t row_ndx) final; + + /// Compare two columns for equality. + bool compare_link_list(const LinkListColumn&) const; + + void to_json_row(size_t row_ndx, std::ostream& out) const; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void cascade_break_backlinks_to(size_t, CascadeState&) override; + void cascade_break_backlinks_to_all_rows(size_t, CascadeState&) override; + void update_from_parent(size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_merge_rows(size_t, size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + +protected: + void do_discard_child_accessors() noexcept override; + +private: + struct list_entry { + size_t m_row_ndx; + std::weak_ptr m_list; + bool operator<(const list_entry& other) const + { + return m_row_ndx < other.m_row_ndx; + } + }; + + // The accessors stored in `m_list_accessors` are sorted by their row index. + // When a LinkList accessor is destroyed because the last shared_ptr pointing + // to it dies, its entry is implicitly replaced by a tombstone (an entry with + // an empty `m_list`). These tombstones are pruned at a later time by + // `prune_list_accessor_tombstones`. This is done to amortize the O(n) cost + // of `std::vector::erase` that would otherwise be incurred each time an + // accessor is removed. + mutable std::vector m_list_accessors; + mutable std::atomic m_list_accessors_contains_tombstones; + + std::shared_ptr get_ptr(size_t row_ndx) const; + + void do_nullify_link(size_t row_ndx, size_t old_target_row_ndx) override; + void do_update_link(size_t row_ndx, size_t old_target_row_ndx, size_t new_target_row_ndx) override; + void do_swap_link(size_t row_ndx, size_t target_row_ndx_1, size_t target_row_ndx_2) override; + + void unregister_linkview(); + ref_type get_row_ref(size_t row_ndx) const noexcept; + void set_row_ref(size_t row_ndx, ref_type new_ref); + void add_backlink(size_t target_row, size_t source_row); + void remove_backlink(size_t target_row, size_t source_row); + + // ArrayParent overrides + void update_child_ref(size_t child_ndx, ref_type new_ref) override; + ref_type get_child_ref(size_t child_ndx) const noexcept override; + + // These helpers are needed because of the way the B+-tree of links is + // traversed in cascade_break_backlinks_to() and + // cascade_break_backlinks_to_all_rows(). + void cascade_break_backlinks_to__leaf(size_t row_ndx, const Array& link_list_leaf, CascadeState&); + void cascade_break_backlinks_to_all_rows__leaf(const Array& link_list_leaf, CascadeState&); + + void discard_child_accessors() noexcept; + + template + void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept; + + template + void adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept; + + template + void adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + template + void adj_swap(size_t row_ndx_1, size_t row_ndx_2) noexcept; + + void prune_list_accessor_tombstones() noexcept; + void validate_list_accessors() const noexcept; + + std::pair get_to_dot_parent(size_t) const override; + + friend class BacklinkColumn; + friend class LinkView; + friend class _impl::TransactLogConvenientEncoder; +}; + + +// Implementation + +inline LinkListColumn::LinkListColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : LinkColumnBase(alloc, ref, table, column_ndx) +{ + m_list_accessors_contains_tombstones.store(false); +} + +inline LinkListColumn::~LinkListColumn() noexcept +{ + discard_child_accessors(); +} + +inline ref_type LinkListColumn::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline bool LinkListColumn::is_nullable() const noexcept +{ + return false; +} + +inline bool LinkListColumn::has_links(size_t row_ndx) const noexcept +{ + ref_type ref = LinkColumnBase::get_as_ref(row_ndx); + return (ref != 0); +} + +inline size_t LinkListColumn::get_link_count(size_t row_ndx) const noexcept +{ + ref_type ref = LinkColumnBase::get_as_ref(row_ndx); + if (ref == 0) + return 0; + return ColumnBase::get_size_from_ref(ref, get_alloc()); +} + +inline ConstLinkViewRef LinkListColumn::get(size_t row_ndx) const +{ + return get_ptr(row_ndx); +} + +inline LinkViewRef LinkListColumn::get(size_t row_ndx) +{ + return get_ptr(row_ndx); +} + +inline bool LinkListColumn::is_null(size_t) const noexcept +{ + return false; +} + +inline void LinkListColumn::set_null(size_t) +{ + throw LogicError{LogicError::column_not_nullable}; +} + +inline void LinkListColumn::do_discard_child_accessors() noexcept +{ + discard_child_accessors(); +} + +inline ref_type LinkListColumn::get_row_ref(size_t row_ndx) const noexcept +{ + return LinkColumnBase::get_as_ref(row_ndx); +} + +inline void LinkListColumn::set_row_ref(size_t row_ndx, ref_type new_ref) +{ + LinkColumnBase::set(row_ndx, new_ref); // Throws +} + +inline void LinkListColumn::add_backlink(size_t target_row, size_t source_row) +{ + m_backlink_column->add_backlink(target_row, source_row); // Throws +} + +inline void LinkListColumn::remove_backlink(size_t target_row, size_t source_row) +{ + m_backlink_column->remove_one_backlink(target_row, source_row); // Throws +} + + +} // namespace realm + +#endif // REALM_COLUMN_LINKLIST_HPP diff --git a/Pods/Realm/include/core/realm/column_mixed.hpp b/Pods/Realm/include/core/realm/column_mixed.hpp new file mode 100644 index 0000000..326edad --- /dev/null +++ b/Pods/Realm/include/core/realm/column_mixed.hpp @@ -0,0 +1,268 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_MIXED_HPP +#define REALM_COLUMN_MIXED_HPP + +#include + +#include +#include +#include +#include +#include +#include + + +namespace realm { + + +// Pre-declarations +class BinaryColumn; + + +/// A mixed column (MixedColumn) is composed of three subcolumns. The first +/// subcolumn is an integer column (Column) and stores value types. The second +/// one stores values and is a subtable parent column (SubtableColumnBase), +/// which is a subclass of an integer column (Column). The last one is a binary +/// column (BinaryColumn) and stores additional data for values of type string +/// or binary data. The last subcolumn is optional. The root of a mixed column +/// is an array node of type Array that stores the root refs of the subcolumns. +class MixedColumn : public ColumnBaseSimple { +public: + /// Create a mixed column wrapper and attach it to a preexisting + /// underlying structure of arrays. + /// + /// \param alloc The memory allocator to change the underlying + /// structure in memory. + /// + /// \param ref The memory reference of the MixedColumn for which + /// this accessor should be creator for. + /// + /// \param table If this column is used as part of a table you + /// must pass a pointer to that table. Otherwise you must pass + /// null + /// + /// \param column_ndx If this column is used as part of a table + /// you must pass the logical index of the column within that + /// table. Otherwise you should pass zero. + MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + + ~MixedColumn() noexcept override; + + DataType get_type(size_t ndx) const noexcept; + size_t size() const noexcept final + { + return m_types->size(); + } + bool is_empty() const noexcept + { + return size() == 0; + } + + int64_t get_int(size_t ndx) const noexcept; + bool get_bool(size_t ndx) const noexcept; + OldDateTime get_olddatetime(size_t ndx) const noexcept; + Timestamp get_timestamp(size_t ndx) const noexcept; + float get_float(size_t ndx) const noexcept; + double get_double(size_t ndx) const noexcept; + StringData get_string(size_t ndx) const noexcept; + BinaryData get_binary(size_t ndx) const noexcept; + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept override; + + /// The returned array ref is zero if the specified row does not + /// contain a subtable. + ref_type get_subtable_ref(size_t row_ndx) const noexcept; + + /// The returned size is zero if the specified row does not + /// contain a subtable. + size_t get_subtable_size(size_t row_ndx) const noexcept; + + Table* get_subtable_accessor(size_t row_ndx) const noexcept override; + + void discard_subtable_accessor(size_t row_ndx) noexcept override; + + /// If the value at the specified index is a subtable, return a + /// pointer to that accessor for that subtable. Otherwise return + /// null. The accessor will be created if it does not already + /// exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t row_ndx); + + const Table* get_subtable_ptr(size_t subtable_ndx) const; + + void set_int(size_t ndx, int64_t value); + void set_bool(size_t ndx, bool value); + void set_olddatetime(size_t ndx, OldDateTime value); + void set_timestamp(size_t ndx, Timestamp value); + void set_float(size_t ndx, float value); + void set_double(size_t ndx, double value); + void set_string(size_t ndx, StringData value) override; + void set_binary(size_t ndx, BinaryData value); + void set_subtable(size_t ndx, const Table* value); + + void insert_int(size_t ndx, int_fast64_t value); + void insert_bool(size_t ndx, bool value); + void insert_olddatetime(size_t ndx, OldDateTime value); + void insert_timestamp(size_t ndx, Timestamp value); + void insert_float(size_t ndx, float value); + void insert_double(size_t ndx, double value); + void insert_string(size_t ndx, StringData value); + void insert_binary(size_t ndx, BinaryData value); + void insert_subtable(size_t ndx, const Table* value); + + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void clear(); + + /// Compare two mixed columns for equality. + bool compare_mixed(const MixedColumn&) const; + + int compare_values(size_t row1, size_t row2) const noexcept override; + + void discard_child_accessors() noexcept; + + static ref_type create(Allocator&, size_t size = 0); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void swap_rows(size_t, size_t) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void mark(int) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + enum MixedColType { + // NOTE: below numbers must be kept in sync with ColumnType + // Column types used in Mixed + mixcol_Int = 0, + mixcol_Bool = 1, + mixcol_String = 2, + // 3, used for STRING_ENUM in ColumnType + mixcol_Binary = 4, + mixcol_Table = 5, + mixcol_Mixed = 6, + mixcol_OldDateTime = 7, + mixcol_Timestamp = 8, + mixcol_Float = 9, + mixcol_Double = 10, // Positive Double + mixcol_DoubleNeg = 11, // Negative Double + mixcol_IntNeg = 12 // Negative Integers + }; + + class RefsColumn; + + /// Stores the MixedColType of each value at the given index. For + /// values that uses all 64 bits, the type also encodes the sign + /// bit by having distinct types for positive negative values. + std::unique_ptr m_types; + + /// Stores the data for each entry. For a subtable, the stored + /// value is the ref of the subtable. For string, binary data, + /// the stored value is an index within `m_binary_data`. Likewise, + /// for timestamp, an index into `m_timestamp` is stored. For other + /// types the stored value is itself. Since we only have 63 bits + /// available for a non-ref value, the sign of numeric values is + /// encoded as part of the type in `m_types`. + std::unique_ptr m_data; + + /// For string and binary data types, the bytes are stored here. + std::unique_ptr m_binary_data; + + /// Timestamps are stored here. + std::unique_ptr m_timestamp_data; + + void do_erase(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows); + void do_move_last_over(size_t row_ndx, size_t prior_num_rows); + void do_swap_rows(size_t, size_t); + void do_clear(size_t num_rows); + + void create(Allocator&, ref_type, Table*, size_t column_ndx); + void ensure_binary_data_column(); + void ensure_timestamp_column(); + + MixedColType clear_value(size_t ndx, MixedColType new_type); // Returns old type + void clear_value_and_discard_subtab_acc(size_t ndx, MixedColType new_type); + + // Get/set/insert 64-bit values in m_data/m_types + int64_t get_value(size_t ndx) const noexcept; + void set_value(size_t ndx, int64_t value, MixedColType); + void set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type); + + void insert_value(size_t row_ndx, int_fast64_t types_value, int_fast64_t data_value); + void insert_int(size_t ndx, int_fast64_t value, MixedColType type); + void insert_pos_neg(size_t ndx, int_fast64_t value, MixedColType pos_type, MixedColType neg_type); + + void do_discard_child_accessors() noexcept override; + +#ifdef REALM_DEBUG + void do_verify(const Table*, size_t col_ndx) const; +#endif + void leaf_to_dot(MemRef, ArrayParent*, size_t, std::ostream&) const override; +}; + +// LCOV_EXCL_START +inline StringData MixedColumn::get_index_data(size_t, StringIndex::StringConversionBuffer&) const noexcept +{ + REALM_ASSERT(false && "Index not supported for MixedColumn yet."); + REALM_UNREACHABLE(); + return {}; +} +// LCOV_EXCL_STOP + + +class MixedColumn::RefsColumn : public SubtableColumnBase { +public: + RefsColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + ~RefsColumn() noexcept override; + + using SubtableColumnBase::get_subtable_ptr; + + void refresh_accessor_tree(size_t, const Spec&) override; + + friend class MixedColumn; +}; + + +} // namespace realm + + +// Implementation +#include + + +#endif // REALM_COLUMN_MIXED_HPP diff --git a/Pods/Realm/include/core/realm/column_mixed_tpl.hpp b/Pods/Realm/include/core/realm/column_mixed_tpl.hpp new file mode 100644 index 0000000..ca5d4d4 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_mixed_tpl.hpp @@ -0,0 +1,505 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +namespace realm { + +inline MixedColumn::MixedColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : ColumnBaseSimple(column_ndx) +{ + create(alloc, ref, table, column_ndx); +} + +inline void MixedColumn::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + m_data->adj_acc_insert_rows(row_ndx, num_rows); +} + +inline void MixedColumn::adj_acc_erase_row(size_t row_ndx) noexcept +{ + m_data->adj_acc_erase_row(row_ndx); +} + +inline void MixedColumn::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + m_data->adj_acc_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void MixedColumn::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + m_data->adj_acc_move_over(from_row_ndx, to_row_ndx); +} + +inline void MixedColumn::adj_acc_clear_root_table() noexcept +{ + m_data->adj_acc_clear_root_table(); +} + +inline ref_type MixedColumn::get_subtable_ref(size_t row_ndx) const noexcept +{ + REALM_ASSERT_3(row_ndx, <, m_types->size()); + if (m_types->get(row_ndx) != type_Table) + return 0; + return m_data->get_as_ref(row_ndx); +} + +inline size_t MixedColumn::get_subtable_size(size_t row_ndx) const noexcept +{ + ref_type top_ref = get_subtable_ref(row_ndx); + if (top_ref == 0) + return 0; + return _impl::TableFriend::get_size_from_ref(top_ref, m_data->get_alloc()); +} + +inline Table* MixedColumn::get_subtable_accessor(size_t row_ndx) const noexcept +{ + return m_data->get_subtable_accessor(row_ndx); +} + +inline void MixedColumn::discard_subtable_accessor(size_t row_ndx) noexcept +{ + m_data->discard_subtable_accessor(row_ndx); +} + +inline Table* MixedColumn::get_subtable_ptr(size_t row_ndx) +{ + REALM_ASSERT_3(row_ndx, <, m_types->size()); + if (m_types->get(row_ndx) != type_Table) + return 0; + return m_data->get_subtable_ptr(row_ndx); // Throws +} + +inline const Table* MixedColumn::get_subtable_ptr(size_t subtable_ndx) const +{ + return const_cast(this)->get_subtable_ptr(subtable_ndx); +} + +inline void MixedColumn::discard_child_accessors() noexcept +{ + m_data->discard_child_accessors(); +} + + +// +// Getters +// + +#define REALM_BIT63 0x8000000000000000ULL + +inline int64_t MixedColumn::get_value(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // Shift the unsigned value right - ensuring 0 gets in from left. + // Shifting signed integers right doesn't ensure 0's. + uint64_t value = uint64_t(m_data->get(ndx)) >> 1; + return int64_t(value); +} + +inline int64_t MixedColumn::get_int(size_t ndx) const noexcept +{ + // Get first 63 bits of the integer value + int64_t value = get_value(ndx); + + // restore 'sign'-bit from the column-type + MixedColType col_type = MixedColType(m_types->get(ndx)); + if (col_type == mixcol_IntNeg) { + // FIXME: Bad cast of result of '|' from unsigned to signed + value |= REALM_BIT63; // set sign bit (63) + } + else { + REALM_ASSERT_3(col_type, ==, mixcol_Int); + } + return value; +} + +inline bool MixedColumn::get_bool(size_t ndx) const noexcept +{ + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Bool); + + return (get_value(ndx) != 0); +} + +inline OldDateTime MixedColumn::get_olddatetime(size_t ndx) const noexcept +{ + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_OldDateTime); + + return OldDateTime(get_value(ndx)); +} + +inline float MixedColumn::get_float(size_t ndx) const noexcept +{ + static_assert(std::numeric_limits::is_iec559, "'float' is not IEEE"); + static_assert((sizeof(float) * CHAR_BIT == 32), "Assume 32 bit float."); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Float); + + return type_punning(get_value(ndx)); +} + +inline double MixedColumn::get_double(size_t ndx) const noexcept +{ + static_assert(std::numeric_limits::is_iec559, "'double' is not IEEE"); + static_assert((sizeof(double) * CHAR_BIT == 64), "Assume 64 bit double."); + + int64_t int_val = get_value(ndx); + + // restore 'sign'-bit from the column-type + MixedColType col_type = MixedColType(m_types->get(ndx)); + if (col_type == mixcol_DoubleNeg) + int_val |= REALM_BIT63; // set sign bit (63) + else { + REALM_ASSERT_3(col_type, ==, mixcol_Double); + } + return type_punning(int_val); +} + +inline StringData MixedColumn::get_string(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_String); + REALM_ASSERT(m_binary_data); + + size_t data_ndx = size_t(int64_t(m_data->get(ndx)) >> 1); + return m_binary_data->get_string(data_ndx); +} + +inline BinaryData MixedColumn::get_binary(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Binary); + REALM_ASSERT(m_binary_data); + + size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1); + return m_binary_data->get(data_ndx); +} + +inline Timestamp MixedColumn::get_timestamp(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + REALM_ASSERT_3(m_types->get(ndx), ==, mixcol_Timestamp); + REALM_ASSERT(m_timestamp_data); + size_t data_ndx = size_t(uint64_t(m_data->get(ndx)) >> 1); + return m_timestamp_data->get(data_ndx); +} + +// +// Setters +// + +// Set a int64 value. +// Store 63 bit of the value in m_data. Store sign bit in m_types. + +inline void MixedColumn::set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // If sign-bit is set in value, 'store' it in the column-type + MixedColType coltype = ((value & REALM_BIT63) == 0) ? pos_type : neg_type; + + // Remove refs or binary data (sets type to double) + clear_value_and_discard_subtab_acc(ndx, coltype); // Throws + + // Shift value one bit and set lowest bit to indicate that this is not a ref + value = (value << 1) + 1; + m_data->set(ndx, value); +} + +inline void MixedColumn::set_int(size_t ndx, int64_t value) +{ + set_int64(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws +} + +inline void MixedColumn::set_double(size_t ndx, double value) +{ + int64_t val64 = type_punning(value); + set_int64(ndx, val64, mixcol_Double, mixcol_DoubleNeg); // Throws +} + +inline void MixedColumn::set_value(size_t ndx, int64_t value, MixedColType coltype) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + + // Remove refs or binary data (sets type to float) + clear_value_and_discard_subtab_acc(ndx, coltype); // Throws + + // Shift value one bit and set lowest bit to indicate that this is not a ref + int64_t v = (value << 1) + 1; + m_data->set(ndx, v); // Throws +} + +inline void MixedColumn::set_float(size_t ndx, float value) +{ + int64_t val64 = type_punning(value); + set_value(ndx, val64, mixcol_Float); // Throws +} + +inline void MixedColumn::set_bool(size_t ndx, bool value) +{ + set_value(ndx, (value ? 1 : 0), mixcol_Bool); // Throws +} + +inline void MixedColumn::set_olddatetime(size_t ndx, OldDateTime value) +{ + set_value(ndx, int64_t(value.get_olddatetime()), mixcol_OldDateTime); // Throws +} + +inline void MixedColumn::set_subtable(size_t ndx, const Table* t) +{ + REALM_ASSERT_3(ndx, <, m_types->size()); + typedef _impl::TableFriend tf; + ref_type ref; + if (t) { + ref = tf::clone(*t, get_alloc()); // Throws + } + else { + ref = tf::create_empty_table(get_alloc()); // Throws + } + // Remove any previous refs or binary data + clear_value_and_discard_subtab_acc(ndx, mixcol_Table); // Throws + m_data->set(ndx, ref); // Throws +} + +// +// Inserts +// + +inline void MixedColumn::insert_value(size_t row_ndx, int_fast64_t types_value, int_fast64_t data_value) +{ + size_t types_size = m_types->size(); // Slow + bool is_append = row_ndx == types_size; + size_t row_ndx_2 = is_append ? realm::npos : row_ndx; + size_t num_rows = 1; + m_types->insert_without_updating_index(row_ndx_2, types_value, num_rows); // Throws + m_data->do_insert(row_ndx_2, data_value, num_rows); // Throws +} + +// Insert a int64 value. +// Store 63 bit of the value in m_data. Store sign bit in m_types. + +inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value, MixedColType type) +{ + int_fast64_t types_value = type; + // Shift value one bit and set lowest bit to indicate that this is not a ref + int_fast64_t data_value = 1 + (value << 1); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::insert_pos_neg(size_t ndx, int_fast64_t value, MixedColType pos_type, MixedColType neg_type) +{ + // 'store' the sign-bit in the integer-type + MixedColType type = (value & REALM_BIT63) == 0 ? pos_type : neg_type; + int_fast64_t types_value = type; + // Shift value one bit and set lowest bit to indicate that this is not a ref + int_fast64_t data_value = 1 + (value << 1); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::insert_int(size_t ndx, int_fast64_t value) +{ + insert_pos_neg(ndx, value, mixcol_Int, mixcol_IntNeg); // Throws +} + +inline void MixedColumn::insert_double(size_t ndx, double value) +{ + int_fast64_t value_2 = type_punning(value); + insert_pos_neg(ndx, value_2, mixcol_Double, mixcol_DoubleNeg); // Throws +} + +inline void MixedColumn::insert_float(size_t ndx, float value) +{ + int_fast64_t value_2 = type_punning(value); + insert_int(ndx, value_2, mixcol_Float); // Throws +} + +inline void MixedColumn::insert_bool(size_t ndx, bool value) +{ + int_fast64_t value_2 = int_fast64_t(value); + insert_int(ndx, value_2, mixcol_Bool); // Throws +} + +inline void MixedColumn::insert_olddatetime(size_t ndx, OldDateTime value) +{ + int_fast64_t value_2 = int_fast64_t(value.get_olddatetime()); + insert_int(ndx, value_2, mixcol_OldDateTime); // Throws +} + +inline void MixedColumn::insert_timestamp(size_t ndx, Timestamp value) +{ + ensure_timestamp_column(); + size_t data_ndx = m_timestamp_data->size(); + m_timestamp_data->add(value); // Throws + insert_int(ndx, int_fast64_t(data_ndx), mixcol_Timestamp); +} + +inline void MixedColumn::insert_string(size_t ndx, StringData value) +{ + ensure_binary_data_column(); + size_t blob_ndx = m_binary_data->size(); + m_binary_data->add_string(value); // Throws + + int_fast64_t value_2 = int_fast64_t(blob_ndx); + insert_int(ndx, value_2, mixcol_String); // Throws +} + +inline void MixedColumn::insert_binary(size_t ndx, BinaryData value) +{ + ensure_binary_data_column(); + size_t blob_ndx = m_binary_data->size(); + m_binary_data->add(value); // Throws + + int_fast64_t value_2 = int_fast64_t(blob_ndx); + insert_int(ndx, value_2, mixcol_Binary); // Throws +} + +inline void MixedColumn::insert_subtable(size_t ndx, const Table* t) +{ + typedef _impl::TableFriend tf; + ref_type ref; + if (t) { + ref = tf::clone(*t, get_alloc()); // Throws + } + else { + ref = tf::create_empty_table(get_alloc()); // Throws + } + int_fast64_t types_value = mixcol_Table; + int_fast64_t data_value = int_fast64_t(ref); + insert_value(ndx, types_value, data_value); // Throws +} + +inline void MixedColumn::erase(size_t row_ndx) +{ + size_t num_rows_to_erase = 1; + size_t prior_num_rows = size(); // Note that size() is slow + do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws +} + +inline void MixedColumn::move_last_over(size_t row_ndx) +{ + size_t prior_num_rows = size(); // Note that size() is slow + do_move_last_over(row_ndx, prior_num_rows); // Throws +} + +inline void MixedColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + do_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void MixedColumn::clear() +{ + size_t num_rows = size(); // Note that size() is slow + do_clear(num_rows); // Throws +} + +inline size_t MixedColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + ref_type types_ref = to_ref(Array::get(root_header, 0)); + return IntegerColumn::get_size_from_ref(types_ref, alloc); +} + +inline void MixedColumn::clear_value_and_discard_subtab_acc(size_t row_ndx, MixedColType new_type) +{ + MixedColType old_type = clear_value(row_ndx, new_type); + if (old_type == mixcol_Table) + m_data->discard_subtable_accessor(row_ndx); +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + + int_fast64_t type_value = mixcol_Int; + m_types->insert_without_updating_index(row_ndx_2, type_value, num_rows_to_insert); // Throws + + // The least significant bit indicates that the rest of the bits form an + // integer value, so 1 is actually zero. + int_fast64_t data_value = 1; + m_data->do_insert(row_ndx_2, data_value, num_rows_to_insert); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + do_erase(row_ndx, num_rows_to_erase, prior_num_rows); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + do_move_last_over(row_ndx, prior_num_rows); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void MixedColumn::clear(size_t num_rows, bool) +{ + do_clear(num_rows); // Throws +} + +inline void MixedColumn::mark(int type) noexcept +{ + m_data->mark(type); +} + +inline void MixedColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + ColumnBaseSimple::refresh_accessor_tree(col_ndx, spec); + + get_root_array()->init_from_parent(); + m_types->refresh_accessor_tree(col_ndx, spec); // Throws + m_data->refresh_accessor_tree(col_ndx, spec); // Throws + if (m_binary_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 3); + m_binary_data->refresh_accessor_tree(col_ndx, spec); // Throws + } + if (m_timestamp_data) { + REALM_ASSERT_3(get_root_array()->size(), >=, 4); + m_timestamp_data->refresh_accessor_tree(col_ndx, spec); // Throws + } + + + // See if m_binary_data needs to be created. + if (get_root_array()->size() >= 3) { + ref_type ref = get_root_array()->get_as_ref(2); + m_binary_data.reset(new BinaryColumn(get_alloc(), ref)); // Throws + m_binary_data->set_parent(get_root_array(), 2); + } + + // See if m_timestamp_data needs to be created. + if (get_root_array()->size() >= 4) { + ref_type ref = get_root_array()->get_as_ref(3); + // When adding/creating a Mixed column the user cannot specify nullability, so the "true" below + // makes it implicitly nullable, which may not be wanted. But it's OK since Mixed columns are not + // publicly supported + m_timestamp_data.reset(new TimestampColumn(true /*fixme*/, get_alloc(), ref)); // Throws + m_timestamp_data->set_parent(get_root_array(), 3); + } +} + +inline void MixedColumn::RefsColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws + size_t spec_ndx_in_parent = 0; // Ignored because these are root tables + m_subtable_map.refresh_accessor_tree(spec_ndx_in_parent); // Throws +} + +} // namespace realm diff --git a/Pods/Realm/include/core/realm/column_string.hpp b/Pods/Realm/include/core/realm/column_string.hpp new file mode 100644 index 0000000..3484fe9 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_string.hpp @@ -0,0 +1,376 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_STRING_HPP +#define REALM_COLUMN_STRING_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { + +// Pre-declarations +class StringIndex; + + +/// A string column (StringColumn) is a single B+-tree, and +/// the root of the column is the root of the B+-tree. Leaf nodes are +/// either of type ArrayString (array of small strings), +/// ArrayStringLong (array of medium strings), or ArrayBigBlobs (array +/// of big strings). +/// +/// A string column can optionally be equipped with a search index. If +/// it is, then the root ref of the index is stored in +/// Table::m_columns immediately after the root ref of the string +/// column. +class StringColumn : public ColumnBaseSimple { +public: + typedef StringData value_type; + + StringColumn(Allocator&, ref_type, bool nullable = false, size_t column_ndx = npos); + ~StringColumn() noexcept override; + + void destroy() noexcept override; + + size_t size() const noexcept final; + bool is_empty() const noexcept + { + return size() == 0; + } + + bool is_null(size_t ndx) const noexcept final; + void set_null(size_t ndx) final; + StringData get(size_t ndx) const noexcept; + void set(size_t ndx, StringData); + void add(); + void add(StringData value); + void insert(size_t ndx); + void insert(size_t ndx, StringData value); + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void clear(); + + size_t count(StringData value) const; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& result, StringData value, size_t begin = 0, size_t end = npos) const; + FindRes find_all_no_copy(StringData value, InternalFindResult& result) const; + + int compare_values(size_t, size_t) const noexcept override; + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to StringData::operator<(). + size_t lower_bound_string(StringData value) const noexcept; + size_t upper_bound_string(StringData value) const noexcept; + //@} + + void set_string(size_t, StringData) override; + + bool is_nullable() const noexcept final; + + // Search index + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; + bool has_search_index() const noexcept override; + void set_search_index_ref(ref_type, ArrayParent*, size_t, bool) override; + void set_search_index_allow_duplicate_values(bool) noexcept override; + StringIndex* get_search_index() noexcept override; + const StringIndex* get_search_index() const noexcept override; + std::unique_ptr release_search_index() noexcept; + bool supports_search_index() const noexcept final + { + return true; + } + StringIndex* create_search_index() override; + + // Simply inserts all column values in the index in a loop + void populate_search_index(); + void destroy_search_index() noexcept override; + + // Optimizing data layout. enforce == true will enforce enumeration; + // enforce == false will auto-evaluate if it should be enumerated or not + bool auto_enumerate(ref_type& keys, ref_type& values, bool enforce = false) const; + + /// Compare two string columns for equality. + bool compare_string(const StringColumn&) const; + + enum LeafType { + leaf_type_Small, ///< ArrayString + leaf_type_Medium, ///< ArrayStringLong + leaf_type_Big ///< ArrayBigBlobs + }; + + std::unique_ptr get_leaf(size_t ndx, size_t& out_ndx_in_parent, LeafType& out_leaf_type) const; + + static ref_type create(Allocator&, size_t size = 0); + + static size_t get_size_from_ref(ref_type root_ref, Allocator&) noexcept; + + // Overrriding method in ColumnBase + ref_type write(size_t, size_t, size_t, _impl::OutputStream&) const override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void set_ndx_in_parent(size_t ndx_in_parent) noexcept override; + void update_from_parent(size_t old_baseline) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + void verify() const override; + void verify(const Table&, size_t) const override; + void to_dot(std::ostream&, StringData title) const override; + void do_dump_node_structure(std::ostream&, int) const override; + +private: + std::unique_ptr m_search_index; + bool m_nullable; + + LeafType get_block(size_t ndx, ArrayParent**, size_t& off, bool use_retval = false) const; + + /// If you are appending and have the size of the column readily available, + /// call the 4 argument version instead. If you are not appending, either + /// one is fine. + /// + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, StringData value, size_t num_rows); + + /// If you are appending and you do not have the size of the column readily + /// available, call the 3 argument version instead. If you are not + /// appending, either one is fine. + /// + /// \param is_append Must be true if, and only if `row_ndx` is equal to the + /// size of the column (before insertion). + void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append); + + /// \param row_ndx Must be `realm::npos` if appending. + void bptree_insert(size_t row_ndx, StringData value, size_t num_rows); + + // Called by Array::bptree_insert(). + static ref_type leaf_insert(MemRef leaf_mem, ArrayParent&, size_t ndx_in_parent, Allocator&, size_t insert_ndx, + Array::TreeInsert& state); + + class EraseLeafElem; + class CreateHandler; + class SliceHandler; + + void do_erase(size_t row_ndx, bool is_last); + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void do_clear(); + + /// Root must be a leaf. Upgrades the root leaf as + /// necessary. Returns the type of the root leaf as it is upon + /// return. + LeafType upgrade_root_leaf(size_t value_size); + + void refresh_root_accessor(); + + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + friend class Array; + friend class ColumnBase; +}; + + +// Implementation: + +inline size_t StringColumn::size() const noexcept +{ + if (root_is_leaf()) { + bool long_strings = m_array->has_refs(); + if (!long_strings) { + // Small strings root leaf + ArrayString* leaf = static_cast(m_array.get()); + return leaf->size(); + } + bool is_big = m_array->get_context_flag(); + if (!is_big) { + // Medium strings root leaf + ArrayStringLong* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Big strings root leaf + ArrayBigBlobs* leaf = static_cast(m_array.get()); + return leaf->size(); + } + // Non-leaf root + return m_array->get_bptree_size(); +} + +inline void StringColumn::add(StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + size_t row_ndx = realm::npos; + size_t num_rows = 1; + do_insert(row_ndx, value, num_rows); // Throws +} + +inline void StringColumn::add() +{ + add(m_nullable ? realm::null() : StringData("")); +} + +inline void StringColumn::insert(size_t row_ndx, StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + size_t column_size = this->size(); + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t num_rows = 1; + bool is_append = row_ndx == column_size; + do_insert(row_ndx, value, num_rows, is_append); // Throws +} + +inline void StringColumn::insert(size_t row_ndx) +{ + insert(row_ndx, m_nullable ? realm::null() : StringData("")); +} + +inline void StringColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + do_erase(row_ndx, is_last); // Throws +} + +inline void StringColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void StringColumn::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + do_swap_rows(row_ndx_1, row_ndx_2); // Throws +} + +inline void StringColumn::clear() +{ + do_clear(); // Throws +} + +inline int StringColumn::compare_values(size_t row1, size_t row2) const noexcept +{ + StringData a = get(row1); + StringData b = get(row2); + + if (a.is_null() && !b.is_null()) + return 1; + else if (b.is_null() && !a.is_null()) + return -1; + else if (a.is_null() && b.is_null()) + return 0; + + if (a == b) + return 0; + return utf8_compare(a, b) ? 1 : -1; +} + +inline void StringColumn::set_string(size_t row_ndx, StringData value) +{ + REALM_ASSERT(!(value.is_null() && !m_nullable)); + set(row_ndx, value); // Throws +} + +inline bool StringColumn::has_search_index() const noexcept +{ + return m_search_index != 0; +} + +inline StringIndex* StringColumn::get_search_index() noexcept +{ + return m_search_index.get(); +} + +inline const StringIndex* StringColumn::get_search_index() const noexcept +{ + return m_search_index.get(); +} + +inline size_t StringColumn::get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept +{ + const char* root_header = alloc.translate(root_ref); + bool root_is_leaf = !Array::get_is_inner_bptree_node_from_header(root_header); + if (root_is_leaf) { + bool long_strings = Array::get_hasrefs_from_header(root_header); + if (!long_strings) { + // Small strings leaf + return ArrayString::get_size_from_header(root_header); + } + bool is_big = Array::get_context_flag_from_header(root_header); + if (!is_big) { + // Medium strings leaf + return ArrayStringLong::get_size_from_header(root_header, alloc); + } + // Big strings leaf + return ArrayBigBlobs::get_size_from_header(root_header); + } + return Array::get_bptree_size_from_header(root_header); +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + StringData value = m_nullable ? realm::null() : StringData(""); + bool is_append = (row_ndx == prior_num_rows); + do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + do_erase(row_ndx_2, is_last); // Throws + } +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Implementing pure virtual method of ColumnBase. +inline void StringColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +} // namespace realm + +#endif // REALM_COLUMN_STRING_HPP diff --git a/Pods/Realm/include/core/realm/column_string_enum.hpp b/Pods/Realm/include/core/realm/column_string_enum.hpp new file mode 100644 index 0000000..be27ba0 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_string_enum.hpp @@ -0,0 +1,311 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_STRING_ENUM_HPP +#define REALM_COLUMN_STRING_ENUM_HPP + +#include + +namespace realm { + +// Pre-declarations +class StringIndex; + + +/// From the point of view of the application, an enumerated strings column +/// (StringEnumColumn) is like a string column (StringColumn), yet it manages +/// its strings in such a way that each unique string is stored only once. In +/// fact, an enumerated strings column is a combination of two subcolumns; a +/// regular string column (StringColumn) that stores the unique strings, and an +/// integer column that stores one unique string index for each entry in the +/// enumerated strings column. +/// +/// In terms of the underlying node structure, the subcolumn containing the +/// unique strings is not a true part of the enumerated strings column. Instead +/// it is a part of the spec structure that describes the table of which the +/// enumerated strings column is a part. This way, the unique strings can be +/// shared across enumerated strings columns of multiple subtables. This also +/// means that the root of an enumerated strings column coincides with the root +/// of the integer subcolumn, and in some sense, an enumerated strings column is +/// just the integer subcolumn. +/// +/// An enumerated strings column can optionally be equipped with a +/// search index. If it is, then the root ref of the index is stored +/// in Table::m_columns immediately after the root ref of the +/// enumerated strings column. +class StringEnumColumn : public IntegerColumn { +public: + typedef StringData value_type; + + StringEnumColumn(Allocator&, ref_type ref, ref_type keys_ref, bool nullable, size_t column_ndx = npos); + ~StringEnumColumn() noexcept override; + void destroy() noexcept override; + MemRef clone_deep(Allocator& alloc) const override; + + int compare_values(size_t row1, size_t row2) const noexcept override + { + StringData a = get(row1); + StringData b = get(row2); + + if (a.is_null() && !b.is_null()) + return 1; + else if (b.is_null() && !a.is_null()) + return -1; + else if (a.is_null() && b.is_null()) + return 0; + + if (a == b) + return 0; + + return utf8_compare(a, b) ? 1 : -1; + } + + StringData get(size_t ndx) const noexcept; + bool is_null(size_t ndx) const noexcept final; + void set(size_t ndx, StringData value); + void set_null(size_t ndx) override; + void add(); + void add(StringData value); + void insert(size_t ndx); + void insert(size_t ndx, StringData value); + void erase(size_t row_ndx); + void move_last_over(size_t row_ndx); + void clear(); + bool is_nullable() const noexcept final; + + size_t count(StringData value) const; + size_t find_first(StringData value, size_t begin = 0, size_t end = npos) const; + void find_all(IntegerColumn& res, StringData value, size_t begin = 0, size_t end = npos) const; + FindRes find_all_no_copy(StringData value, InternalFindResult& result) const; + + size_t count(size_t key_index) const; + size_t find_first(size_t key_index, size_t begin = 0, size_t end = -1) const; + void find_all(IntegerColumn& res, size_t key_index, size_t begin = 0, size_t end = -1) const; + + //@{ + /// Find the lower/upper bound for the specified value assuming + /// that the elements are already sorted in ascending order + /// according to StringData::operator<(). + size_t lower_bound_string(StringData value) const noexcept; + size_t upper_bound_string(StringData value) const noexcept; + //@} + + void set_string(size_t, StringData) override; + + void adjust_keys_ndx_in_parent(int diff) noexcept; + + // Search index + StringData get_index_data(size_t ndx, StringIndex::StringConversionBuffer& buffer) const noexcept final; + void set_search_index_allow_duplicate_values(bool) noexcept override; + bool supports_search_index() const noexcept final + { + return true; + } + StringIndex* create_search_index() override; + void install_search_index(std::unique_ptr) noexcept; + void destroy_search_index() noexcept override; + + // Compare two string columns for equality + bool compare_string(const StringColumn&) const; + bool compare_string(const StringEnumColumn&) const; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void update_from_parent(size_t) noexcept override; + void refresh_accessor_tree(size_t, const Spec&) override; + + size_t get_key_ndx(StringData value) const; + size_t get_key_ndx_or_add(StringData value); + + StringColumn& get_keys(); + const StringColumn& get_keys() const; + +#ifdef REALM_DEBUG + void verify() const override; + void verify(const Table&, size_t) const override; + void do_dump_node_structure(std::ostream&, int) const override; + void to_dot(std::ostream&, StringData title) const override; +#endif + +private: + // Member variables + StringColumn m_keys; + bool m_nullable; + + /// If you are appending and have the size of the column readily available, + /// call the 4 argument version instead. If you are not appending, either + /// one is fine. + /// + /// \param row_ndx Must be `realm::npos` if appending. + void do_insert(size_t row_ndx, StringData value, size_t num_rows); + + /// If you are appending and you do not have the size of the column readily + /// available, call the 3 argument version instead. If you are not + /// appending, either one is fine. + /// + /// \param is_append Must be true if, and only if `row_ndx` is equal to the + /// size of the column (before insertion). + void do_insert(size_t row_ndx, StringData value, size_t num_rows, bool is_append); + + void do_erase(size_t row_ndx, bool is_last); + void do_move_last_over(size_t row_ndx, size_t last_row_ndx); + void do_clear(); +}; + + +// Implementation: + +inline StringData StringEnumColumn::get(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, IntegerColumn::size()); + size_t key_ndx = to_size_t(IntegerColumn::get(ndx)); + StringData sd = m_keys.get(key_ndx); + REALM_ASSERT_DEBUG(!(!m_nullable && sd.is_null())); + return sd; +} + +inline bool StringEnumColumn::is_null(size_t ndx) const noexcept +{ + return is_nullable() && get(ndx).is_null(); +} + +inline void StringEnumColumn::add() +{ + add(m_nullable ? realm::null() : StringData("")); +} + +inline void StringEnumColumn::add(StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + size_t row_ndx = realm::npos; + size_t num_rows = 1; + do_insert(row_ndx, value, num_rows); // Throws +} + +inline void StringEnumColumn::insert(size_t row_ndx) +{ + insert(row_ndx, m_nullable ? realm::null() : StringData("")); +} + +inline void StringEnumColumn::insert(size_t row_ndx, StringData value) +{ + REALM_ASSERT_DEBUG(!(!m_nullable && value.is_null())); + size_t column_size = this->size(); + REALM_ASSERT_3(row_ndx, <=, column_size); + size_t num_rows = 1; + bool is_append = row_ndx == column_size; + do_insert(row_ndx, value, num_rows, is_append); // Throws +} + +inline void StringEnumColumn::erase(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + bool is_last = row_ndx == last_row_ndx; + do_erase(row_ndx, is_last); // Throws +} + +inline void StringEnumColumn::move_last_over(size_t row_ndx) +{ + size_t last_row_ndx = size() - 1; // Note that size() is slow + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +inline void StringEnumColumn::clear() +{ + do_clear(); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls || m_nullable); + + StringData value = m_nullable ? realm::null() : StringData(""); + bool is_append = (row_ndx == prior_num_rows); + do_insert(row_ndx, value, num_rows_to_insert, is_append); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(num_rows_to_erase <= prior_num_rows); + REALM_ASSERT(row_ndx <= prior_num_rows - num_rows_to_erase); + + bool is_last = (row_ndx + num_rows_to_erase == prior_num_rows); + for (size_t i = num_rows_to_erase; i > 0; --i) { + size_t row_ndx_2 = row_ndx + i - 1; + do_erase(row_ndx_2, is_last); // Throws + } +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx < prior_num_rows); + + size_t last_row_ndx = prior_num_rows - 1; + do_move_last_over(row_ndx, last_row_ndx); // Throws +} + +// Overriding virtual method of Column. +inline void StringEnumColumn::clear(size_t, bool) +{ + do_clear(); // Throws +} + +inline size_t StringEnumColumn::lower_bound_string(StringData value) const noexcept +{ + return ColumnBase::lower_bound(*this, value); +} + +inline size_t StringEnumColumn::upper_bound_string(StringData value) const noexcept +{ + return ColumnBase::upper_bound(*this, value); +} + +inline void StringEnumColumn::set_string(size_t row_ndx, StringData value) +{ + set(row_ndx, value); // Throws +} + +inline void StringEnumColumn::set_null(size_t row_ndx) +{ + set(row_ndx, realm::null{}); +} + +inline StringColumn& StringEnumColumn::get_keys() +{ + return m_keys; +} + +inline const StringColumn& StringEnumColumn::get_keys() const +{ + return m_keys; +} + + +} // namespace realm + +#endif // REALM_COLUMN_STRING_ENUM_HPP diff --git a/Pods/Realm/include/core/realm/column_table.hpp b/Pods/Realm/include/core/realm/column_table.hpp new file mode 100644 index 0000000..ca5e38b --- /dev/null +++ b/Pods/Realm/include/core/realm/column_table.hpp @@ -0,0 +1,601 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TABLE_HPP +#define REALM_COLUMN_TABLE_HPP + +#include + +#include +#include +#include +#include + +namespace realm { + + +/// Base class for any type of column that can contain subtables. +// FIXME: Don't derive from IntegerColumn, but define a BpTree specialization. +class SubtableColumnBase : public IntegerColumn, public Table::Parent { +public: + void discard_child_accessors() noexcept; + + ~SubtableColumnBase() noexcept override; + + static ref_type create(Allocator&, size_t size = 0); + + Table* get_subtable_accessor(size_t) const noexcept override; + + void insert_rows(size_t, size_t, size_t, bool) override; + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + void clear(size_t, bool) override; + void swap_rows(size_t, size_t) override; + void discard_subtable_accessor(size_t) noexcept override; + void update_from_parent(size_t) noexcept override; + void adj_acc_insert_rows(size_t, size_t) noexcept override; + void adj_acc_erase_row(size_t) noexcept override; + void adj_acc_move_over(size_t, size_t) noexcept override; + void adj_acc_clear_root_table() noexcept override; + void adj_acc_swap_rows(size_t, size_t) noexcept override; + void mark(int) noexcept override; + bool supports_search_index() const noexcept override + { + return false; + } + StringIndex* create_search_index() override + { + return nullptr; + } + + void verify() const override; + void verify(const Table&, size_t) const override; + +protected: + /// A pointer to the table that this column is part of. For a free-standing + /// column, this pointer is null. + Table* const m_table; + + struct SubtableMap { + ~SubtableMap() noexcept + { + } + bool empty() const noexcept + { + return m_entries.empty(); + } + Table* find(size_t subtable_ndx) const noexcept; + void add(size_t subtable_ndx, Table*); + // Returns true if, and only if at least one entry was detached and + // removed from the map. + bool detach_and_remove_all() noexcept; + // Returns true if, and only if the entry was found and removed, and it + // was the last entry in the map. + bool detach_and_remove(size_t subtable_ndx) noexcept; + // Returns true if, and only if the entry was found and removed, and it + // was the last entry in the map. + bool remove(Table*) noexcept; + void update_from_parent(size_t old_baseline) const noexcept; + template + void adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept; + // Returns true if, and only if an entry was found and removed, and it + // was the last entry in the map. + template + bool adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept; + // Returns true if, and only if an entry was found and removed, and it + // was the last entry in the map. + template + bool adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + template + void adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + + void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater&); + void recursive_mark() noexcept; + void refresh_accessor_tree(size_t spec_ndx_in_parent); + + private: + struct SubtableEntry { + size_t m_subtable_ndx; + Table* m_table; + }; + typedef std::vector entries; + entries m_entries; + }; + + /// Contains all existing accessors that are attached to a subtable in this + /// column. It can map a row index into a pointer to the corresponding + /// accessor when it exists. + /// + /// There is an invariant in force: Either `m_table` is null, or there is an + /// additional referece count on `*m_table` when, and only when the map is + /// non-empty. + mutable SubtableMap m_subtable_map; + + SubtableColumnBase(Allocator&, ref_type, Table*, size_t column_ndx); + + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + /// + /// NOTE: This method must be used only for subtables with + /// independent specs, i.e. for elements of a MixedColumn. + Table* get_subtable_ptr(size_t subtable_ndx); + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + // Overriding method in Table::Parent + Table* get_parent_table(size_t*) noexcept override; + + // Overriding method in Table::Parent + void child_accessor_destroyed(Table*) noexcept override; + + /// Assumes that the two tables have the same spec. + static bool compare_subtable_rows(const Table&, const Table&); + + /// Construct a copy of the columns array of the specified table + /// and return just the ref to that array. + /// + /// In the clone, no string column will be of the enumeration + /// type. + ref_type clone_table_columns(const Table*); + + size_t* record_subtable_path(size_t* begin, size_t* end) noexcept override; + + void update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater&); + + /// \param row_ndx Must be `realm::npos` if appending. + /// \param value The value to place in any newly created rows. + /// \param num_rows The number of rows to insert. + void do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows); + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + + friend class Table; +}; + + +class SubtableColumn : public SubtableColumnBase { +public: + /// Create a subtable column accessor and attach it to a + /// preexisting underlying structure of arrays. + /// + /// \param alloc The allocator to provide new memory. + /// + /// \param ref The memory reference of the underlying subtable that + /// we are creating an accessor for. + /// + /// \param table If this column is used as part of a table you must + /// pass a pointer to that table. Otherwise you must pass null. + /// + /// \param column_ndx If this column is used as part of a table + /// you must pass the logical index of the column within that + /// table. Otherwise you should pass zero. + SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx); + + ~SubtableColumn() noexcept override + { + } + + size_t get_subtable_size(size_t ndx) const noexcept; + + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t subtable_ndx); + + const Table* get_subtable_ptr(size_t subtable_ndx) const; + + // When passing a table to add() or insert() it is assumed that + // the table spec is compatible with this column. The number of + // columns must be the same, and the corresponding columns must + // have the same data type (as returned by + // Table::get_column_type()). + + void add(const Table* value = nullptr); + void insert(size_t ndx, const Table* value = nullptr); + void set(size_t ndx, const Table*); + void clear_table(size_t ndx); + + using SubtableColumnBase::insert; + + void erase_rows(size_t, size_t, size_t, bool) override; + void move_last_row_over(size_t, size_t, bool) override; + + /// Compare two subtable columns for equality. + bool compare_table(const SubtableColumn&) const; + + void refresh_accessor_tree(size_t, const Spec&) override; + +#ifdef REALM_DEBUG + void verify(const Table&, size_t) const override; + void do_dump_node_structure(std::ostream&, int) const override; + void to_dot(std::ostream&, StringData title) const override; +#endif + +private: + mutable size_t m_subspec_ndx; // Unknown if equal to `npos` + + size_t get_subspec_ndx() const noexcept; + + void destroy_subtable(size_t ndx) noexcept; + + void do_discard_child_accessors() noexcept override; +}; + + +// Implementation + +// Overriding virtual method of Column. +inline void SubtableColumnBase::insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool insert_nulls) +{ + REALM_ASSERT_DEBUG(prior_num_rows == size()); + REALM_ASSERT(row_ndx <= prior_num_rows); + REALM_ASSERT(!insert_nulls); + + size_t row_ndx_2 = (row_ndx == prior_num_rows ? realm::npos : row_ndx); + int_fast64_t value = 0; + do_insert(row_ndx_2, value, num_rows_to_insert); // Throws +} + +// Overriding virtual method of Column. +inline void SubtableColumnBase::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) +{ + IntegerColumn::erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks); // Throws + + const bool fix_ndx_in_parent = true; + bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_to_erase); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +// Overriding virtual method of Column. +inline void SubtableColumnBase::move_last_row_over(size_t row_ndx, size_t prior_num_rows, + bool broken_reciprocal_backlinks) +{ + IntegerColumn::move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks); // Throws + + const bool fix_ndx_in_parent = true; + size_t last_row_ndx = prior_num_rows - 1; + bool last_entry_removed = m_subtable_map.adj_move_over(last_row_ndx, row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::clear(size_t, bool) +{ + discard_child_accessors(); + clear_without_updating_index(); // Throws + // FIXME: This one is needed because + // IntegerColumn::clear_without_updating_index() forgets about the + // leaf type. A better solution should probably be sought after. + get_root_array()->set_type(Array::type_HasRefs); +} + +inline void SubtableColumnBase::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + IntegerColumn::swap_rows(row_ndx_1, row_ndx_2); // Throws + + const bool fix_ndx_in_parent = true; + m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); +} + +inline void SubtableColumnBase::mark(int type) noexcept +{ + if (type & mark_Recursive) + m_subtable_map.recursive_mark(); +} + +inline void SubtableColumnBase::adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + m_subtable_map.adj_insert_rows(row_ndx, num_rows); +} + +inline void SubtableColumnBase::adj_acc_erase_row(size_t row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + size_t num_rows_erased = 1; + bool last_entry_removed = m_subtable_map.adj_erase_rows(row_ndx, num_rows_erased); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + const bool fix_ndx_in_parent = false; + bool last_entry_removed = m_subtable_map.adj_move_over(from_row_ndx, to_row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::adj_acc_clear_root_table() noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + IntegerColumn::adj_acc_clear_root_table(); + discard_child_accessors(); +} + +inline void SubtableColumnBase::adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + const bool fix_ndx_in_parent = false; + m_subtable_map.adj_swap_rows(row_ndx_1, row_ndx_2); +} + +inline Table* SubtableColumnBase::get_subtable_accessor(size_t row_ndx) const noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + Table* subtable = m_subtable_map.find(row_ndx); + return subtable; +} + +inline void SubtableColumnBase::discard_subtable_accessor(size_t row_ndx) noexcept +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + bool last_entry_removed = m_subtable_map.detach_and_remove(row_ndx); + typedef _impl::TableFriend tf; + if (last_entry_removed) + tf::unbind_ptr(*m_table); +} + +inline void SubtableColumnBase::SubtableMap::add(size_t subtable_ndx, Table* table) +{ + SubtableEntry e; + e.m_subtable_ndx = subtable_ndx; + e.m_table = table; + m_entries.push_back(e); +} + +template +void SubtableColumnBase::SubtableMap::adj_insert_rows(size_t row_ndx, size_t num_rows_inserted) noexcept +{ + for (auto& entry : m_entries) { + if (entry.m_subtable_ndx >= row_ndx) { + entry.m_subtable_ndx += num_rows_inserted; + typedef _impl::TableFriend tf; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } +} + +template +bool SubtableColumnBase::SubtableMap::adj_erase_rows(size_t row_ndx, size_t num_rows_erased) noexcept +{ + if (m_entries.empty()) + return false; + typedef _impl::TableFriend tf; + auto end = m_entries.end(); + auto i = m_entries.begin(); + do { + if (i->m_subtable_ndx >= row_ndx + num_rows_erased) { + i->m_subtable_ndx -= num_rows_erased; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(i->m_table), i->m_subtable_ndx); + } + else if (i->m_subtable_ndx >= row_ndx) { + // Must hold a counted reference while detaching + TableRef table(i->m_table); + tf::detach(*table); + // Move last over + *i = *--end; + continue; + } + ++i; + } while (i != end); + m_entries.erase(end, m_entries.end()); + return m_entries.empty(); +} + + +template +bool SubtableColumnBase::SubtableMap::adj_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept +{ + typedef _impl::TableFriend tf; + + size_t i = 0, n = m_entries.size(); + // We return true if, and only if we remove the last entry in the map. We + // need special handling for the case, where the set of entries are already + // empty, otherwise the final return statement would return true in this + // case, even though we didn't actually remove an entry. + if (n == 0) + return false; + + while (i < n) { + SubtableEntry& e = m_entries[i]; + if (REALM_UNLIKELY(e.m_subtable_ndx == to_row_ndx)) { + // Must hold a counted reference while detaching + TableRef table(e.m_table); + tf::detach(*table); + // Delete entry by moving last over (faster and avoids invalidating + // iterators) + e = m_entries[--n]; + m_entries.pop_back(); + } + else { + if (REALM_UNLIKELY(e.m_subtable_ndx == from_row_ndx)) { + e.m_subtable_ndx = to_row_ndx; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(e.m_table), e.m_subtable_ndx); + } + ++i; + } + } + return m_entries.empty(); +} + +template +void SubtableColumnBase::SubtableMap::adj_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept +{ + using tf = _impl::TableFriend; + for (auto& entry : m_entries) { + if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_1)) { + entry.m_subtable_ndx = row_ndx_2; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + else if (REALM_UNLIKELY(entry.m_subtable_ndx == row_ndx_2)) { + entry.m_subtable_ndx = row_ndx_1; + if (fix_ndx_in_parent) + tf::set_ndx_in_parent(*(entry.m_table), entry.m_subtable_ndx); + } + } +} + +inline SubtableColumnBase::SubtableColumnBase(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : IntegerColumn(alloc, ref, column_ndx) // Throws + , m_table(table) +{ +} + +inline void SubtableColumnBase::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + set(child_ndx, new_ref); +} + +inline ref_type SubtableColumnBase::get_child_ref(size_t child_ndx) const noexcept +{ + return get_as_ref(child_ndx); +} + +inline void SubtableColumnBase::discard_child_accessors() noexcept +{ + bool last_entry_removed = m_subtable_map.detach_and_remove_all(); + if (last_entry_removed && m_table) + _impl::TableFriend::unbind_ptr(*m_table); +} + +inline SubtableColumnBase::~SubtableColumnBase() noexcept +{ + discard_child_accessors(); +} + +inline bool SubtableColumnBase::compare_subtable_rows(const Table& a, const Table& b) +{ + return _impl::TableFriend::compare_rows(a, b); +} + +inline ref_type SubtableColumnBase::clone_table_columns(const Table* t) +{ + return _impl::TableFriend::clone_columns(*t, get_root_array()->get_alloc()); +} + +inline ref_type SubtableColumnBase::create(Allocator& alloc, size_t size) +{ + return IntegerColumn::create(alloc, Array::type_HasRefs, size); // Throws +} + +inline size_t* SubtableColumnBase::record_subtable_path(size_t* begin, size_t* end) noexcept +{ + if (end == begin) + return 0; // Error, not enough space in buffer + *begin++ = get_column_index(); + if (end == begin) + return 0; // Error, not enough space in buffer + return _impl::TableFriend::record_subtable_path(*m_table, begin, end); +} + +inline void SubtableColumnBase::update_table_accessors(const size_t* col_path_begin, const size_t* col_path_end, + _impl::TableFriend::AccessorUpdater& updater) +{ + // This function must assume no more than minimal consistency of the + // accessor hierarchy. This means in particular that it cannot access the + // underlying node structure. See AccessorConsistencyLevels. + + m_subtable_map.update_accessors(col_path_begin, col_path_end, updater); // Throws +} + +inline void SubtableColumnBase::do_insert(size_t row_ndx, int_fast64_t value, size_t num_rows) +{ + IntegerColumn::insert_without_updating_index(row_ndx, value, num_rows); // Throws + bool is_append = row_ndx == realm::npos; + if (!is_append) { + const bool fix_ndx_in_parent = true; + m_subtable_map.adj_insert_rows(row_ndx, num_rows); + } +} + + +inline SubtableColumn::SubtableColumn(Allocator& alloc, ref_type ref, Table* table, size_t column_ndx) + : SubtableColumnBase(alloc, ref, table, column_ndx) + , m_subspec_ndx(realm::npos) +{ +} + +inline const Table* SubtableColumn::get_subtable_ptr(size_t subtable_ndx) const +{ + return const_cast(this)->get_subtable_ptr(subtable_ndx); +} + +inline void SubtableColumn::refresh_accessor_tree(size_t col_ndx, const Spec& spec) +{ + SubtableColumnBase::refresh_accessor_tree(col_ndx, spec); // Throws + m_subspec_ndx = spec.get_subspec_ndx(col_ndx); + m_subtable_map.refresh_accessor_tree(m_subspec_ndx); // Throws +} + +inline size_t SubtableColumn::get_subspec_ndx() const noexcept +{ + if (REALM_UNLIKELY(m_subspec_ndx == realm::npos)) { + typedef _impl::TableFriend tf; + const Spec& spec = tf::get_spec(*m_table); + m_subspec_ndx = spec.get_subspec_ndx(get_column_index()); + } + return m_subspec_ndx; +} + + +} // namespace realm + +#endif // REALM_COLUMN_TABLE_HPP diff --git a/Pods/Realm/include/core/realm/column_timestamp.hpp b/Pods/Realm/include/core/realm/column_timestamp.hpp new file mode 100644 index 0000000..7e3ce07 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_timestamp.hpp @@ -0,0 +1,155 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TIMESTAMP_HPP +#define REALM_COLUMN_TIMESTAMP_HPP + +#include +#include + +namespace realm { + +// Inherits from ColumnTemplate to get a compare_values() that can be called without knowing the +// column type +class TimestampColumn : public ColumnBaseSimple { +public: + TimestampColumn(bool nullable, Allocator& alloc, ref_type ref, size_t col_ndx = npos); + + static ref_type create(Allocator& alloc, size_t size, bool nullable); + static size_t get_size_from_ref(ref_type root_ref, Allocator& alloc) noexcept; + + /// Get the number of entries in this column. This operation is relatively + /// slow. + size_t size() const noexcept override; + /// Whether or not this column is nullable. + bool is_nullable() const noexcept override; + /// Whether or not the value at \a row_ndx is NULL. If the column is not + /// nullable, always returns false. + bool is_null(size_t row_ndx) const noexcept override; + /// Sets the value at \a row_ndx to be NULL. + /// \throw LogicError Thrown if this column is not nullable. + void set_null(size_t row_ndx) override; + void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) override; + void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool broken_reciprocal_backlinks) override; + void move_last_row_over(size_t row_ndx, size_t prior_num_rows, bool broken_reciprocal_backlinks) override; + void clear(size_t num_rows, bool broken_reciprocal_backlinks) override; + void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override; + void destroy() noexcept override; + + bool has_search_index() const noexcept final + { + return bool(m_search_index); + } + StringIndex* get_search_index() noexcept final + { + return m_search_index.get(); + } + StringIndex* get_search_index() const noexcept final + { + return m_search_index.get(); + } + void destroy_search_index() noexcept override; + void set_search_index_ref(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, + bool allow_duplicate_values) final; + void populate_search_index(); + StringIndex* create_search_index() override; + bool supports_search_index() const noexcept final + { + return true; + } + + StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept override; + ref_type write(size_t slice_offset, size_t slice_size, size_t table_size, _impl::OutputStream&) const override; + void update_from_parent(size_t old_baseline) noexcept override; + void set_ndx_in_parent(size_t ndx) noexcept override; + void refresh_accessor_tree(size_t new_col_ndx, const Spec&) override; + + void verify() const override; + void to_dot(std::ostream&, StringData title = StringData()) const override; + void do_dump_node_structure(std::ostream&, int level) const override; + void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent, std::ostream&) const override; + + void add(const Timestamp& ts = Timestamp{}); + Timestamp get(size_t row_ndx) const noexcept; + void set(size_t row_ndx, const Timestamp& ts); + bool compare(const TimestampColumn& c) const noexcept; + int compare_values(size_t row1, size_t row2) const noexcept override; + + Timestamp maximum(size_t* result_index) const; + Timestamp minimum(size_t* result_index) const; + size_t count(Timestamp) const; + void erase(size_t row_ndx, bool is_last); + + template + size_t find(Timestamp value, size_t begin, size_t end) const noexcept + { + // FIXME: Here we can do all sorts of clever optimizations. Use bithack-search on seconds, then for each match + // check nanoseconds, etc. Lots of possibilities. Below code is naive and slow but works. + + Condition cond; + for (size_t t = begin; t < end; t++) { + Timestamp ts = get(t); + if (cond(ts, value, ts.is_null(), value.is_null())) + return t; + } + return npos; + } + + typedef Timestamp value_type; + +private: + std::unique_ptr>> m_seconds; + std::unique_ptr> m_nanoseconds; + + std::unique_ptr m_search_index; + bool m_nullable; + + template + class CreateHandler; + + template + Timestamp minmax(size_t* result_index) const noexcept + { + // Condition is realm::Greater for maximum and realm::Less for minimum. + + if (size() == 0) { + if (result_index) + *result_index = npos; + return Timestamp{}; + } + + Timestamp best = get(0); + size_t best_index = 0; + + for (size_t i = 1; i < size(); ++i) { + Timestamp candidate = get(i); + if (Condition()(candidate, best, candidate.is_null(), best.is_null())) { + best = candidate; + best_index = i; + } + } + if (result_index) + *result_index = best_index; + return best; + } +}; + +} // namespace realm + +#endif // REALM_COLUMN_TIMESTAMP_HPP diff --git a/Pods/Realm/include/core/realm/column_tpl.hpp b/Pods/Realm/include/core/realm/column_tpl.hpp new file mode 100644 index 0000000..2411007 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_tpl.hpp @@ -0,0 +1,143 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TPL_HPP +#define REALM_COLUMN_TPL_HPP + +#include + +#include +#include +#include + +namespace realm { + +template +class FloatDoubleNode; +template +class IntegerNode; +template +class SequentialGetter; + +template +struct ColumnTypeTraits2; + +template +struct ColumnTypeTraits2 { + typedef IntegerColumn column_type; + typedef ArrayInteger array_type; +}; +template +struct ColumnTypeTraits2 { + typedef IntegerColumn column_type; + typedef ArrayInteger array_type; +}; +template +struct ColumnTypeTraits2 { + typedef FloatColumn column_type; + typedef ArrayFloat array_type; +}; +template +struct ColumnTypeTraits2 { + typedef DoubleColumn column_type; + typedef ArrayDouble array_type; +}; + + +namespace _impl { + +template +struct FindInLeaf { + using LeafType = typename ColType::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + Condition cond; + bool cont = true; + // todo, make an additional loop with hard coded `false` instead of is_null(v) for non-nullable columns + bool null_target = null::is_null_float(target); + for (size_t local_index = local_start; cont && local_index < local_end; local_index++) { + auto v = leaf.get(local_index); + if (cond(v, target, null::is_null_float(v), null_target)) { + cont = state.template match(leaf_start + local_index, 0, static_cast(v)); + } + } + return cont; + } +}; + +template <> +struct FindInLeaf { + using LeafType = IntegerColumn::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + const int c = Condition::condition; + return leaf.find(c, action, target, local_start, local_end, leaf_start, &state); + } +}; + +template <> +struct FindInLeaf { + using LeafType = IntNullColumn::LeafType; + + template + static bool find(const LeafType& leaf, T target, size_t local_start, size_t local_end, size_t leaf_start, + QueryState& state) + { + constexpr int cond = Condition::condition; + return leaf.find(cond, action, target, local_start, local_end, leaf_start, &state); + } +}; + +} // namespace _impl + +template +R aggregate(const ColType& column, T target, size_t start, size_t end, size_t limit, size_t* return_ndx) +{ + if (end == npos) + end = column.size(); + + QueryState state; + state.init(action, nullptr, limit); + SequentialGetter sg{&column}; + + bool cont = true; + for (size_t s = start; cont && s < end;) { + sg.cache_next(s); + size_t start2 = s - sg.m_leaf_start; + size_t end2 = sg.local_end(end); + cont = _impl::FindInLeaf::template find(*sg.m_leaf_ptr, target, start2, end2, + sg.m_leaf_start, state); + s = sg.m_leaf_start + end2; + } + + if (return_ndx) + *return_ndx = action == act_Sum ? state.m_match_count : state.m_minmax_index; + + return state.m_state; +} + + +} // namespace realm + +#endif // REALM_COLUMN_TPL_HPP diff --git a/Pods/Realm/include/core/realm/column_type.hpp b/Pods/Realm/include/core/realm/column_type.hpp new file mode 100644 index 0000000..5a6e21c --- /dev/null +++ b/Pods/Realm/include/core/realm/column_type.hpp @@ -0,0 +1,70 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TYPE_HPP +#define REALM_COLUMN_TYPE_HPP + +namespace realm { + + +// Note: Enumeration value assignments must be kept in sync with +// . +enum ColumnType { + // Column types + col_type_Int = 0, + col_type_Bool = 1, + col_type_String = 2, + col_type_StringEnum = 3, // double refs + col_type_Binary = 4, + col_type_Table = 5, + col_type_Mixed = 6, + col_type_OldDateTime = 7, + col_type_Timestamp = 8, + col_type_Float = 9, + col_type_Double = 10, + col_type_Reserved4 = 11, // Decimal + col_type_Link = 12, + col_type_LinkList = 13, + col_type_BackLink = 14 +}; + + +// Column attributes can be combined using bitwise or. +enum ColumnAttr { + col_attr_None = 0, + col_attr_Indexed = 1, + + /// Specifies that this column forms a unique constraint. It requires + /// `col_attr_Indexed`. + col_attr_Unique = 2, + + /// Reserved for future use. + col_attr_Reserved = 4, + + /// Specifies that the links of this column are strong, not weak. Applies + /// only to link columns (`type_Link` and `type_LinkList`). + col_attr_StrongLinks = 8, + + /// Specifies that elements in the column can be null. + col_attr_Nullable = 16 +}; + + +} // namespace realm + +#endif // REALM_COLUMN_TYPE_HPP diff --git a/Pods/Realm/include/core/realm/column_type_traits.hpp b/Pods/Realm/include/core/realm/column_type_traits.hpp new file mode 100644 index 0000000..df9c236 --- /dev/null +++ b/Pods/Realm/include/core/realm/column_type_traits.hpp @@ -0,0 +1,163 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_COLUMN_TYPE_TRAITS_HPP +#define REALM_COLUMN_TYPE_TRAITS_HPP + +#include +#include +#include + +namespace realm { + +class OldDateTime; +class ArrayBinary; +class ArrayInteger; +class ArrayIntNull; +template +class BasicArray; + +template +struct ColumnTypeTraits; + +template <> +struct ColumnTypeTraits { + using column_type = Column; + using leaf_type = ArrayInteger; + using sum_type = int64_t; + using minmax_type = int64_t; + static const DataType id = type_Int; + static const ColumnType column_id = col_type_Int; + static const ColumnType real_column_type = col_type_Int; +}; + +template <> +struct ColumnTypeTraits> { + using column_type = Column>; + using leaf_type = ArrayIntNull; + using sum_type = int64_t; + using minmax_type = int64_t; + static const DataType id = type_Int; + static const ColumnType column_id = col_type_Int; + static const ColumnType real_column_type = col_type_Int; +}; + +template <> +struct ColumnTypeTraits : ColumnTypeTraits { + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits> : ColumnTypeTraits> { + static const DataType id = type_Bool; + static const ColumnType column_id = col_type_Bool; +}; + +template <> +struct ColumnTypeTraits { + using column_type = FloatColumn; + using leaf_type = BasicArray; + using sum_type = double; + using minmax_type = float; + static const DataType id = type_Float; + static const ColumnType column_id = col_type_Float; + static const ColumnType real_column_type = col_type_Float; +}; + +template <> +struct ColumnTypeTraits { + using column_type = DoubleColumn; + using leaf_type = BasicArray; + using sum_type = double; + using minmax_type = double; + static const DataType id = type_Double; + static const ColumnType column_id = col_type_Double; + static const ColumnType real_column_type = col_type_Double; +}; + +template <> +struct ColumnTypeTraits : ColumnTypeTraits { + static const DataType id = type_OldDateTime; + static const ColumnType column_id = col_type_OldDateTime; +}; + +template <> +struct ColumnTypeTraits> : ColumnTypeTraits> { + static const DataType id = type_OldDateTime; + static const ColumnType column_id = col_type_OldDateTime; +}; + +template <> +struct ColumnTypeTraits { + using column_type = StringEnumColumn; + using leaf_type = ArrayInteger; + using sum_type = int64_t; + static const DataType id = type_String; + static const ColumnType column_id = col_type_String; + static const ColumnType real_column_type = col_type_String; +}; + +template <> +struct ColumnTypeTraits { + using column_type = BinaryColumn; + using leaf_type = ArrayBinary; + static const DataType id = type_Binary; + static const ColumnType column_id = col_type_Binary; + static const ColumnType real_column_type = col_type_Binary; +}; + +template +struct GetColumnType; +template <> +struct GetColumnType { + using type = IntegerColumn; +}; +template <> +struct GetColumnType { + using type = IntNullColumn; +}; +template +struct GetColumnType { + // FIXME: Null definition + using type = FloatColumn; +}; +template +struct GetColumnType { + // FIXME: Null definition + using type = DoubleColumn; +}; + +// Only purpose is to return 'double' if and only if source column (T) is float and you're doing a sum (A) +template +struct ColumnTypeTraitsSum { + typedef T sum_type; +}; + +template <> +struct ColumnTypeTraitsSum { + typedef double sum_type; +}; + +template +struct ColumnTypeTraitsSum, A> { + using sum_type = int64_t; +}; +} + +#endif // REALM_COLUMN_TYPE_TRAITS_HPP diff --git a/Pods/Realm/include/core/realm/data_type.hpp b/Pods/Realm/include/core/realm/data_type.hpp new file mode 100644 index 0000000..580982f --- /dev/null +++ b/Pods/Realm/include/core/realm/data_type.hpp @@ -0,0 +1,52 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_DATA_TYPE_HPP +#define REALM_DATA_TYPE_HPP + +namespace realm { + +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with +// Note: Value assignments must be kept in sync with "com/realm/ColumnType.java" +// Note: Any change to this enum is a file-format breaking change. +enum DataType { + type_Int = 0, + type_Bool = 1, + type_Float = 9, + type_Double = 10, + type_String = 2, + type_Binary = 4, + type_OldDateTime = 7, + type_Timestamp = 8, + type_Table = 5, + type_Mixed = 6, + type_Link = 12, + type_LinkList = 13 +}; + +/// See Descriptor::set_link_type(). +enum LinkType { + link_Strong, + link_Weak, +}; + +} // namespace realm + +#endif // REALM_DATA_TYPE_HPP diff --git a/Pods/Realm/include/core/realm/descriptor.hpp b/Pods/Realm/include/core/realm/descriptor.hpp new file mode 100644 index 0000000..10433eb --- /dev/null +++ b/Pods/Realm/include/core/realm/descriptor.hpp @@ -0,0 +1,820 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_DESCRIPTOR_HPP +#define REALM_DESCRIPTOR_HPP + +#include + +#include +#include +#include + + +namespace realm { + +namespace _impl { +class DescriptorFriend; +} + + +/// Accessor for table type descriptors. +/// +/// A table type descriptor is an entity that specifies the dynamic +/// type of a Realm table. Objects of this class are accessors +/// through which the descriptor can be inspected and +/// changed. Accessors can become detached, see is_attached() for more +/// on this. The descriptor itself is stored inside the database file, +/// or elsewhere in case of a free-standing table or a table in a +/// free-standing group. +/// +/// The dynamic type consists first, and foremost of an ordered list +/// of column descriptors. Each column descriptor specifies the name +/// and type of the column. +/// +/// When a table has a subtable column, every cell in than column +/// contains a subtable. All those subtables have the same dynamic +/// type, and therefore have a shared descriptor. See is_root() for +/// more on this. +/// +/// The Table class contains convenience methods, such as +/// Table::get_column_count() and Table::add_column(), that allow you +/// to inspect and change the dynamic type of simple tables without +/// resorting to use of descriptors. For example, the following two +/// statements have the same effect: +/// +/// table->add_column(type, name); +/// table->get_descriptor()->add_column(type, name); +/// +/// Note, however, that this equivalence holds only as long as no +/// shared subtable descriptors are involved. +/// +/// \sa Table::get_descriptor() +class Descriptor : public std::enable_shared_from_this { +public: + /// Get the number of columns in the associated tables. + size_t get_column_count() const noexcept; + + /// Get the type of the column at the specified index. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + DataType get_column_type(size_t column_ndx) const noexcept; + + /// Get the name of the column at the specified index. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + StringData get_column_name(size_t column_ndx) const noexcept; + + /// Search for a column with the specified name. + /// + /// This function finds the first column with the specified name, + /// and returns its index. If there are no such columns, it + /// returns `not_found`. + size_t get_column_index(StringData name) const noexcept; + + /// Get the index of the column to which links in the column at the + /// specified index refer. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + /// + /// The consequences of specifying a column index that does not refer + /// to a link column, are undefined. + size_t get_column_link_target(size_t column_ndx) const noexcept; + + /// Get whether or not the specified column is nullable. + /// + /// The consequences of specifying a column index that is out of + /// range, are undefined. + bool is_nullable(size_t column_ndx) const noexcept; + + /// \defgroup descriptor_column_accessors Accessing Columns Via A Descriptor + /// + /// add_column() and add_column_link() are a shorthands for calling + /// insert_column() and insert_column_link(), respectively, with a column + /// index equal to the original number of columns. The returned value is + /// that column index. + /// + /// insert_column() inserts a new column into all the tables associated with + /// this descriptor. If any of the tables are not empty, the new column will + /// be filled with the default value associated with the specified data + /// type. This function cannot be used to insert link-type columns. For + /// that, you have to use insert_column_link() instead. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. It does this by inserting a new column + /// with the specified name and type into the descriptor at the + /// specified index, and into each of the tables that share this + /// descriptor. + /// + /// insert_column_link() is like insert_column(), but inserts a link-type + /// column to a group-level table. It is not possible to add link-type + /// columns to tables that are not group-level tables. This functions must + /// be used in place of insert_column() when the column type is `type_Link` + /// or `type_LinkList`. A link-type column is associated with a particular + /// target table. All links in a link-type column refer to rows in the + /// target table of that column. The target table must also be a group-level + /// table, and it must belong to the same group as the origin table. + /// + /// \param name Name of new column. All strings are valid column names as + /// long as they are valid UTF-8 encodings and the number of bytes does not + /// exceed `max_column_name_length`. An attempt to add a column with a name + /// that is longer than `max_column_name_length` will cause an exception to + /// be thrown. + /// + /// \param subdesc If a non-null pointer is passed, and the + /// specified type is `type_Table`, then this function + /// automatically retrieves the descriptor associated with the new + /// subtable column, and stores a reference to its accessor in + /// `*subdesc`. + /// + /// \param col_ndx Insert the new column at this index. Preexisting columns + /// at indexes equal to, or greater than `col_ndx` will be shifted to the + /// next higher index. It is an error to specify an index that is greater + /// than the number of columns prior to the insertion. + /// + /// \param link_type See set_link_type(). + /// + /// \sa Table::add_column() + /// \sa Table::insert_column() + /// \sa Table::add_column_link() + /// \sa Table::insert_column_link() + /// \sa is_root() + //@{ + + static const size_t max_column_name_length = 63; + + size_t add_column(DataType type, StringData name, DescriptorRef* subdesc = nullptr, bool nullable = false); + + void insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc = nullptr, + bool nullable = false); + + size_t add_column_link(DataType type, StringData name, Table& target, LinkType = link_Weak); + void insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target, LinkType = link_Weak); + //@} + + /// Remove the specified column from each of the associated + /// tables. If the removed column is the only column in the + /// descriptor, then the table size will drop to zero for all + /// tables that were not already empty. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. It does this by removing the column at + /// the specified index from the descriptor, and from each of the + /// tables that share this descriptor. The consequences of + /// specifying a column index that is out of range, are undefined. + /// + /// If the removed column was a subtable column, then the + /// associated descriptor accessor will be detached, if it + /// exists. This function will also detach all accessors of + /// subtables of the root table. Only the accessor of the root + /// table will remain attached. The root table is the table + /// associated with the root descriptor. + /// + /// \param col_ndx The index of the column to be removed. It is an error to + /// specify an index that is greater than, or equal to the number of + /// columns. + /// + /// \sa is_root() + /// \sa Table::remove_column() + void remove_column(size_t col_ndx); + + /// Rename the specified column. + /// + /// This function modifies the dynamic type of all the tables that + /// share this descriptor. The consequences of specifying a column + /// index that is out of range, are undefined. + /// + /// This function will detach all accessors of subtables of the + /// root table. Only the accessor of the root table will remain + /// attached. The root table is the table associated with the root + /// descriptor. + /// + /// \param col_ndx The index of the column to be renamed. It is an error to + /// specify an index that is greater than, or equal to the number of + /// columns. + /// + /// \param new_name The new name of the column. + /// + /// \sa is_root() + /// \sa Table::rename_column() + void rename_column(size_t col_ndx, StringData new_name); + + /// There are two kinds of links, 'weak' and 'strong'. A strong link is one + /// that implies ownership, i.e., that the origin row (parent) owns the + /// target row (child). Simply stated, this means that when the origin row + /// (parent) is removed, so is the target row (child). If there are multiple + /// strong links to a target row, the origin rows share ownership, and the + /// target row is removed when the last owner disappears. Weak links do not + /// imply ownership, and will be nullified or removed when the target row + /// disappears. + /// + /// To put this in precise terms; when a strong link is broken, and the + /// target row has no other strong links to it, the target row is removed. A + /// row that is implicitly removed in this way, is said to be + /// *cascade-removed*. When a weak link is broken, nothing is + /// cascade-removed. + /// + /// A link is considered broken if + /// + /// - the link is nullified, removed, or replaced by a different link + /// (Row::nullify_link(), Row::set_link(), LinkView::remove_link(), + /// LinkView::set_link(), LinkView::clear()), or if + /// + /// - the origin row is explicitly removed (Row::move_last_over(), + /// Table::clear()), or if + /// + /// - the origin row is cascade-removed, or if + /// + /// - the origin column is removed from the table (Table::remove_column()), + /// or if + /// + /// - the origin table is removed from the group. + /// + /// Note that a link is *not* considered broken when it is replaced by a + /// link to the same target row. I.e., no no rows will be cascade-removed + /// due to such an operation. + /// + /// When a row is explicitly removed (such as by Table::move_last_over()), + /// all links to it are automatically removed or nullified. For single link + /// columns (type_Link), links to the removed row are nullified. For link + /// list columns (type_LinkList), links to the removed row are removed from + /// the list. + /// + /// When a row is cascade-removed there can no longer be any strong links to + /// it, but if there are any weak links, they will be removed or nullified. + /// + /// It is important to understand that this cascade-removal scheme is too + /// simplistic to enable detection and removal of orphaned link-cycles. In + /// this respect, it suffers from the same limitations as a reference + /// counting scheme generally does. + /// + /// It is also important to understand, that the possible presence of a link + /// cycle can cause a row to be cascade-removed as a consequence of being + /// modified. This happens, for example, if two rows, A and B, have strong + /// links to each other, and there are no other strong links to either of + /// them. In this case, if A->B is changed to A->C, then both A and B will + /// be cascade-removed. This can lead to obscure bugs in some applications, + /// such as in the following case: + /// + /// table.set_link(col_ndx_1, row_ndx, ...); + /// table.set_int(col_ndx_2, row_ndx, ...); // Oops, `row_ndx` may no longer refer to the same row + /// + /// To be safe, applications, that may encounter cycles, are advised to + /// adopt the following pattern: + /// + /// Row row = table[row_ndx]; + /// row.set_link(col_ndx_1, ...); + /// if (row) + /// row.set_int(col_ndx_2, ...); // Ok, because we check whether the row has disappeared + /// + /// \param col_ndx The index of the link column (`type_Link` or + /// `type_LinkList`) to be modified. It is an error to specify an index that + /// is greater than, or equal to the number of columns, or to specify the + /// index of a non-link column. + /// + /// \param link_type The type of links the column should store. + void set_link_type(size_t col_ndx, LinkType link_type); + + //@{ + /// Get the descriptor for the specified subtable column. + /// + /// This function provides access to the shared subtable + /// descriptor for the subtables in the specified column. The + /// specified column must be a column whose type is 'table'. The + /// consequences of specifying a column of a different type, or + /// specifying an index that is out of range, are undefined. + /// + /// Note that this function cannot be used with 'mixed' columns, + /// since subtables of that kind have independent dynamic types, + /// and therefore, have independent descriptors. You can only get + /// access to the descriptor of a subtable in a mixed column by + /// first getting access to the subtable itself. + /// + /// \sa is_root() + DescriptorRef get_subdescriptor(size_t column_ndx); + ConstDescriptorRef get_subdescriptor(size_t column_ndx) const; + //@} + + //@{ + /// Returns the parent table descriptor, if any. + /// + /// If this descriptor is the *root descriptor*, then this + /// function returns null. Otherwise it returns the accessor of + /// the parent descriptor. + /// + /// \sa is_root() + DescriptorRef get_parent() noexcept; + ConstDescriptorRef get_parent() const noexcept; + //@} + + //@{ + /// Get the table associated with the root descriptor. + /// + /// \sa get_parent() + /// \sa is_root() + TableRef get_root_table() noexcept; + ConstTableRef get_root_table() const noexcept; + //@} + + //@{ + /// Get the target table associated with the specified link column. This + /// descriptor must be a root descriptor (is_root()), and the specified + /// column must be a link column (`type_Link` or `type_LinkList`). + TableRef get_link_target(size_t col_ndx) noexcept; + ConstTableRef get_link_target(size_t col_ndx) const noexcept; + //@} + + /// Is this a root descriptor? + /// + /// Descriptors of tables with independent dynamic type are root + /// descriptors. Root descriptors are never shared. Tables that + /// are direct members of groups have independent dynamic + /// types. The same is true for free-standing tables and subtables + /// in columns of type 'mixed'. + /// + /// When a table has a column of type 'table', the cells in that + /// column contain subtables. All those subtables have the same + /// dynamic type, and they share a single dynamic type + /// descriptor. Such shared descriptors are never root + /// descriptors. + /// + /// A type descriptor can even be shared by subtables with + /// different parent tables, but only if the parent tables + /// themselves have a shared type descriptor. For example, if a + /// table has a column `foo` of type 'table', and each of the + /// subtables in `foo` has a column `bar` of type 'table', then + /// all the subtables in all the `bar` columns share the same + /// dynamic type descriptor. + /// + /// \sa Table::has_shared_type() + bool is_root() const noexcept; + + /// Determine whether this accessor is still attached. + /// + /// A table descriptor accessor may get detached from the + /// underlying descriptor for various reasons (see below). When it + /// does, it no longer refers to that descriptor, and can no + /// longer be used, except for calling is_attached(). The + /// consequences of calling other methods on a detached accessor + /// are undefined. Descriptor accessors obtained by calling + /// functions in the Realm API are always in the 'attached' + /// state immediately upon return from those functions. + /// + /// A descriptor accessor that is obtained directly from a table + /// becomes detached if the table becomes detached. A shared + /// subtable descriptor accessor that is obtained by a call to + /// get_subdescriptor() becomes detached if the parent descriptor + /// accessor becomes detached, or if the corresponding subtable + /// column is removed. A descriptor accessor does not get detached + /// under any other circumstances. + bool is_attached() const noexcept; + + //@{ + /// \brief Compare two table descriptors. + /// + /// Two table descriptors are equal if they have the same number of columns, + /// and for each column index, the two columns have the same name, data + /// type, and set of attributes. + /// + /// For link columns (`type_Link` and `type_LinkList`), the target table + /// (get_link_target()) of the two columns must be the same. + /// + /// For subtable columns (`type_Table`), the two corresponding + /// subdescriptors must themselves be equal, as if by a recursive call to + /// operator==(). + /// + /// The consequences of comparing a detached descriptor are + /// undefined. + bool operator==(const Descriptor&) const noexcept; + bool operator!=(const Descriptor&) const noexcept; + //@} + + /// If the specified column is optimized to store only unique values, then + /// this function returns the number of unique values currently + /// stored. Otherwise it returns zero. This function is mainly intended for + /// debugging purposes. + size_t get_num_unique_values(size_t column_ndx) const; + + ~Descriptor() noexcept; + +private: + // for initialization through make_shared + struct PrivateTag { + }; + +public: + Descriptor(const PrivateTag&) + : Descriptor() + { + } + +private: + // Table associated with root descriptor. Detached iff null. + TableRef m_root_table; + DescriptorRef m_parent; // Null iff detached or root descriptor. + Spec* m_spec; // Valid if attached. Owned iff valid and `m_parent`. + + // Whenever a subtable descriptor accessor is created, it is + // stored in this map. This ensures that when get_subdescriptor() + // is called to created multiple DescriptorRef objects that + // overlap in time, then they will all refer to the same + // descriptor object. + // + // It also enables the necessary recursive detaching of descriptor + // objects. + struct subdesc_entry { + size_t m_column_ndx; + std::weak_ptr m_subdesc; + subdesc_entry(size_t column_ndx, DescriptorRef); + }; + typedef std::vector subdesc_map; + mutable subdesc_map m_subdesc_map; + + Descriptor() noexcept; + + // Called by the root table if this becomes the root + // descriptor. Otherwise it is called by the descriptor that + // becomes its parent. + // + // Puts this descriptor accessor into the attached state. This + // attaches it to the underlying structure of array nodes. It does + // not establish the parents reference to this descriptor, that is + // the job of the parent. When this function returns, + // is_attached() will return true. + // + // Not idempotent. + // + // The specified table is not allowed to be a subtable with a + // shareable spec. That is, Table::has_shared_spec() must return + // false. + // + // The specified spec must be the spec of the specified table or + // of one of its direct or indirect subtable columns. + // + // When the specified spec is the spec of the root table, the + // parent must be specified as null. When the specified spec is + // not the root spec, a proper parent must be specified. + void attach(Table*, DescriptorRef parent, Spec*) noexcept; + + // Detach accessor from underlying descriptor. Caller must ensure + // that a reference count exists upon return, for example by + // obtaining an extra reference count before the call. + // + // This function is called either by the root table if this is the + // root descriptor, or by the parent descriptor, if it is not. + // + // Puts this descriptor accessor into the detached state. This + // detaches it from the underlying structure of array nodes. It + // also calls detach_subdesc_accessors(). When this function + // returns, is_attached() will return false. + // + // Not idempotent. + void detach() noexcept; + + // Recursively detach all subtable descriptor accessors that + // exist, that is, all subtable descriptor accessors that have + // this descriptor as ancestor. + void detach_subdesc_accessors() noexcept; + + // Record the path in terms of subtable column indexes from the + // root descriptor to this descriptor. If this descriptor is a + // root descriptor, the path is empty. Returns zero if the path is + // too long to fit in the specified buffer. Otherwise the path + // indexes will be stored between `begin_2`and `end`, where + // `begin_2` is the returned pointer. + size_t* record_subdesc_path(size_t* begin, size_t* end) const noexcept; + + // Returns a pointer to the accessor of the specified + // subdescriptor if that accessor exists, otherwise this function + // return null. + DescriptorRef get_subdesc_accessor(size_t column_ndx) noexcept; + + void move_column(size_t from_ndx, size_t to_ndx); + + void adj_insert_column(size_t col_ndx) noexcept; + void adj_erase_column(size_t col_ndx) noexcept; + void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; + + friend class util::bind_ptr; + friend class util::bind_ptr; + friend class _impl::DescriptorFriend; +}; + + +// Implementation: + +inline size_t Descriptor::get_column_count() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_public_column_count(); +} + +inline StringData Descriptor::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_name(ndx); +} + +inline DataType Descriptor::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_public_column_type(ndx); +} + +inline bool Descriptor::is_nullable(size_t ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_attr(ndx) & col_attr_Nullable; +} + +inline size_t Descriptor::get_column_index(StringData name) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_column_index(name); +} + +inline size_t Descriptor::get_column_link_target(size_t column_ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec->get_opposite_link_table_ndx(column_ndx); +} + +inline size_t Descriptor::add_column(DataType type, StringData name, DescriptorRef* subdesc, bool nullable) +{ + size_t col_ndx = m_spec->get_public_column_count(); + insert_column(col_ndx, type, name, subdesc, nullable); // Throws + return col_ndx; +} + +inline void Descriptor::insert_column(size_t col_ndx, DataType type, StringData name, DescriptorRef* subdesc, + bool nullable) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx > get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(tf::is_link_type(ColumnType(type)))) + throw LogicError(LogicError::illegal_type); + + LinkTargetInfo invalid_link; + tf::insert_column(*this, col_ndx, type, name, invalid_link, nullable); // Throws + adj_insert_column(col_ndx); + if (subdesc && type == type_Table) + *subdesc = get_subdescriptor(col_ndx); +} + +inline size_t Descriptor::add_column_link(DataType type, StringData name, Table& target, LinkType link_type) +{ + size_t col_ndx = m_spec->get_public_column_count(); + insert_column_link(col_ndx, type, name, target, link_type); // Throws + return col_ndx; +} + +inline void Descriptor::insert_column_link(size_t col_ndx, DataType type, StringData name, Table& target, + LinkType link_type) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached() || !target.is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx > get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(type)))) + throw LogicError(LogicError::illegal_type); + if (REALM_UNLIKELY(!is_root())) + throw LogicError(LogicError::wrong_kind_of_descriptor); + // Both origin and target must be group-level tables, and in the same group. + Group* origin_group = tf::get_parent_group(*get_root_table()); + Group* target_group = tf::get_parent_group(target); + if (!origin_group || !target_group) + throw LogicError(LogicError::wrong_kind_of_table); + if (origin_group != target_group) + throw LogicError(LogicError::group_mismatch); + + LinkTargetInfo link(&target); + tf::insert_column(*this, col_ndx, type, name, link); // Throws + adj_insert_column(col_ndx); + + tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws +} + +inline void Descriptor::remove_column(size_t col_ndx) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + + tf::erase_column(*this, col_ndx); // Throws + adj_erase_column(col_ndx); +} + +inline void Descriptor::rename_column(size_t col_ndx, StringData name) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + + tf::rename_column(*this, col_ndx, name); // Throws +} + +inline void Descriptor::move_column(size_t from_ndx, size_t to_ndx) +{ + REALM_ASSERT(is_attached()); + typedef _impl::TableFriend tf; + tf::move_column(*this, from_ndx, to_ndx); // Throws + adj_move_column(from_ndx, to_ndx); +} + +inline void Descriptor::set_link_type(size_t col_ndx, LinkType link_type) +{ + typedef _impl::TableFriend tf; + + if (REALM_UNLIKELY(!is_attached())) + throw LogicError(LogicError::detached_accessor); + if (REALM_UNLIKELY(col_ndx >= get_column_count())) + throw LogicError(LogicError::column_index_out_of_range); + if (REALM_UNLIKELY(!tf::is_link_type(ColumnType(get_column_type(col_ndx))))) + throw LogicError(LogicError::illegal_type); + + tf::set_link_type(*get_root_table(), col_ndx, link_type); // Throws +} + +inline ConstDescriptorRef Descriptor::get_subdescriptor(size_t column_ndx) const +{ + return const_cast(this)->get_subdescriptor(column_ndx); +} + +inline DescriptorRef Descriptor::get_parent() noexcept +{ + return m_parent; +} + +inline ConstDescriptorRef Descriptor::get_parent() const noexcept +{ + return const_cast(this)->get_parent(); +} + +inline TableRef Descriptor::get_root_table() noexcept +{ + return m_root_table; +} + +inline ConstTableRef Descriptor::get_root_table() const noexcept +{ + return const_cast(this)->get_root_table(); +} + +inline TableRef Descriptor::get_link_target(size_t col_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(is_root()); + return get_root_table()->get_link_target(col_ndx); +} + +inline ConstTableRef Descriptor::get_link_target(size_t col_ndx) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(is_root()); + return get_root_table()->get_link_target(col_ndx); +} + +inline bool Descriptor::is_root() const noexcept +{ + return !m_parent; +} + +inline Descriptor::Descriptor() noexcept +{ +} + +inline void Descriptor::attach(Table* table, DescriptorRef parent, Spec* spec) noexcept +{ + REALM_ASSERT(!is_attached()); + REALM_ASSERT(!table->has_shared_type()); + m_root_table.reset(table); + m_parent = parent; + m_spec = spec; +} + +inline bool Descriptor::is_attached() const noexcept +{ + return bool(m_root_table); +} + +inline Descriptor::subdesc_entry::subdesc_entry(size_t n, DescriptorRef d) + : m_column_ndx(n) + , m_subdesc(d) +{ +} + +inline bool Descriptor::operator==(const Descriptor& d) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(d.is_attached()); + return *m_spec == *d.m_spec; +} + +inline bool Descriptor::operator!=(const Descriptor& d) const noexcept +{ + return !(*this == d); +} + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Descriptor class. +class _impl::DescriptorFriend { +public: + static DescriptorRef create() + { + return std::make_shared(Descriptor::PrivateTag()); // Throws + } + + static void attach(Descriptor& desc, Table* table, DescriptorRef parent, Spec* spec) noexcept + { + desc.attach(table, parent, spec); + } + + static void detach(Descriptor& desc) noexcept + { + desc.detach(); + } + + static Table& get_root_table(Descriptor& desc) noexcept + { + return *desc.m_root_table; + } + + static const Table& get_root_table(const Descriptor& desc) noexcept + { + return *desc.m_root_table; + } + + static Spec& get_spec(Descriptor& desc) noexcept + { + return *desc.m_spec; + } + + static const Spec& get_spec(const Descriptor& desc) noexcept + { + return *desc.m_spec; + } + + static size_t* record_subdesc_path(const Descriptor& desc, size_t* begin, size_t* end) noexcept + { + return desc.record_subdesc_path(begin, end); + } + + static DescriptorRef get_subdesc_accessor(Descriptor& desc, size_t column_ndx) noexcept + { + return desc.get_subdesc_accessor(column_ndx); + } + + static void move_column(Descriptor& desc, size_t from_ndx, size_t to_ndx) + { + return desc.move_column(from_ndx, to_ndx); + } + + static void adj_insert_column(Descriptor& desc, size_t col_ndx) noexcept + { + desc.adj_insert_column(col_ndx); + } + + static void adj_erase_column(Descriptor& desc, size_t col_ndx) noexcept + { + desc.adj_erase_column(col_ndx); + } + + static void adj_move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) noexcept + { + desc.adj_move_column(col_ndx_1, col_ndx_2); + } +}; + +} // namespace realm + +#endif // REALM_DESCRIPTOR_HPP diff --git a/Pods/Realm/include/core/realm/descriptor_fwd.hpp b/Pods/Realm/include/core/realm/descriptor_fwd.hpp new file mode 100644 index 0000000..2937724 --- /dev/null +++ b/Pods/Realm/include/core/realm/descriptor_fwd.hpp @@ -0,0 +1,33 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_DESCRIPTOR_FWD_HPP +#define REALM_DESCRIPTOR_FWD_HPP + +#include + + +namespace realm { + +class Descriptor; +typedef std::shared_ptr DescriptorRef; +typedef std::shared_ptr ConstDescriptorRef; + +} // namespace realm + +#endif // REALM_DESCRIPTOR_FWD_HPP diff --git a/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp b/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp new file mode 100644 index 0000000..f642d6f --- /dev/null +++ b/Pods/Realm/include/core/realm/disable_sync_to_disk.hpp @@ -0,0 +1,37 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_DISABLE_SYNC_TO_DISK_HPP +#define REALM_DISABLE_SYNC_TO_DISK_HPP + +#include + +namespace realm { + +/// Completely disable synchronization with storage device to speed up unit +/// testing. This is an unsafe mode of operation, and should never be used in +/// production. This function is thread safe. +void disable_sync_to_disk(); + +/// Returns true after disable_sync_to_disk() has been called. This function is +/// thread safe. +bool get_disable_sync_to_disk() noexcept; + +} // namespace realm + +#endif // REALM_DISABLE_SYNC_TO_DISK_HPP diff --git a/Pods/Realm/include/core/realm/exceptions.hpp b/Pods/Realm/include/core/realm/exceptions.hpp new file mode 100644 index 0000000..4a7bc61 --- /dev/null +++ b/Pods/Realm/include/core/realm/exceptions.hpp @@ -0,0 +1,255 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_EXCEPTIONS_HPP +#define REALM_EXCEPTIONS_HPP + +#include + +#include + +namespace realm { + +/// Thrown by various functions to indicate that a specified table does not +/// exist. +class NoSuchTable : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown by various functions to indicate that a specified table name is +/// already in use. +class TableNameInUse : public std::exception { +public: + const char* what() const noexcept override; +}; + + +// Thrown by functions that require a table to **not** be the target of link +// columns, unless those link columns are part of the table itself. +class CrossTableLinkTarget : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// Thrown by various functions to indicate that the dynamic type of a table +/// does not match a particular other table type (dynamic or static). +class DescriptorMismatch : public std::exception { +public: + const char* what() const noexcept override; +}; + + +/// The \c FileFormatUpgradeRequired exception can be thrown by the \c +/// SharedGroup constructor when opening a database that uses a deprecated file +/// format, and the user has indicated he does not want automatic upgrades to +/// be performed. This exception indicates that until an upgrade of the file +/// format is performed, the database will be unavailable for read or write +/// operations. +class FileFormatUpgradeRequired : public std::exception { +public: + const char* what() const noexcept override; +}; + +/// Thrown when memory can no longer be mapped to. When mmap/remap fails. +class AddressSpaceExhausted : public std::runtime_error { +public: + AddressSpaceExhausted(const std::string& msg); + /// runtime_error::what() returns the msg provided in the constructor. +}; + + +/// The \c LogicError exception class is intended to be thrown only when +/// applications (or bindings) violate rules that are stated (or ought to have +/// been stated) in the documentation of the public API, and only in cases +/// where the violation could have been easily and efficiently predicted by the +/// application. In other words, this exception class is for the cases where +/// the error is due to incorrect use of the public API. +/// +/// This class is not supposed to be caught by applications. It is not even +/// supposed to be considered part of the public API, and therefore the +/// documentation of the public API should **not** mention the \c LogicError +/// exception class by name. Note how this contrasts with other exception +/// classes, such as \c NoSuchTable, which are part of the public API, and are +/// supposed to be mentioned in the documentation by name. The \c LogicError +/// exception is part of Realm's private API. +/// +/// In other words, the \c LogicError class should exclusively be used in +/// replacement (or in addition to) asserts (debug or not) in order to +/// guarantee program interruption, while still allowing for complete +/// test-cases to be written and run. +/// +/// To this effect, the special `CHECK_LOGIC_ERROR()` macro is provided as a +/// test framework plugin to allow unit tests to check that the functions in +/// the public API do throw \c LogicError when rules are violated. +/// +/// The reason behind hiding this class from the public API is to prevent users +/// from getting used to the idea that "Undefined Behaviour" equates a specific +/// exception being thrown. The whole point of properly documenting "Undefined +/// Behaviour" cases is to help the user know what the limits are, without +/// constraining the database to handle every and any use-case thrown at it. +/// +/// FIXME: This exception class should probably be moved to the `_impl` +/// namespace, in order to avoid some confusion. +class LogicError : public std::exception { +public: + enum ErrorKind { + string_too_big, + binary_too_big, + table_name_too_long, + column_name_too_long, + table_index_out_of_range, + row_index_out_of_range, + column_index_out_of_range, + string_position_out_of_range, + link_index_out_of_range, + bad_version, + illegal_type, + + /// Indicates that an argument has a value that is illegal in combination + /// with another argument, or with the state of an involved object. + illegal_combination, + + /// Indicates a data type mismatch, such as when `Table::find_pkey_int()` is + /// called and the type of the primary key is not `type_Int`. + type_mismatch, + + /// Indicates that two involved tables are not in the same group. + group_mismatch, + + /// Indicates that an involved descriptor is of the wrong kind, i.e., if + /// it is a subtable descriptor, and the function requires a root table + /// descriptor. + wrong_kind_of_descriptor, + + /// Indicates that an involved table is of the wrong kind, i.e., if it + /// is a subtable, and the function requires a root table, or if it is a + /// free-standing table, and the function requires a group-level table. + wrong_kind_of_table, + + /// Indicates that an involved accessor is was detached, i.e., was not + /// attached to an underlying object. + detached_accessor, + + /// Indicates that a specified row index of a target table (a link) is + /// out of range. This is used for disambiguation in cases such as + /// Table::set_link() where one specifies both a row index of the origin + /// table, and a row index of the target table. + target_row_index_out_of_range, + + // Indicates that an involved column lacks a search index. + no_search_index, + + /// Indicates that a modification was attempted that would have produced a + /// duplicate primary value. + unique_constraint_violation, + + /// User attempted to insert null in non-nullable column + column_not_nullable, + + /// Group::open() is called on a group accessor that is already in the + /// attached state. Or Group::open() or Group::commit() is called on a + /// group accessor that is managed by a SharedGroup object. + wrong_group_state, + + /// No active transaction on a particular SharedGroup object (e.g., + /// SharedGroup::commit()), or the active transaction on the SharedGroup + /// object is of the wrong type (read/write), or an attampt was made to + /// initiate a new transaction while one is already in progress on the + /// same SharedGroup object. + wrong_transact_state, + + /// Attempted use of a continuous transaction through a SharedGroup + /// object with no history. See Replication::get_history(). + no_history, + + /// Durability setting (as passed to the SharedGroup constructor) was + /// not consistent across the session. + mixed_durability, + + /// History type (as specified by the Replication implementation passed + /// to the SharedGroup constructor) was not consistent across the + /// session. + mixed_history_type, + + /// Adding rows to a table with no columns is not supported. + table_has_no_columns + }; + + LogicError(ErrorKind message); + + const char* what() const noexcept override; + ErrorKind kind() const noexcept; + +private: + ErrorKind m_kind; +}; + + +// Implementation: + +// LCOV_EXCL_START (Wording of what() strings are not to be tested) + +inline const char* NoSuchTable::what() const noexcept +{ + return "No such table exists"; +} + +inline const char* TableNameInUse::what() const noexcept +{ + return "The specified table name is already in use"; +} + +inline const char* CrossTableLinkTarget::what() const noexcept +{ + return "Table is target of cross-table link columns"; +} + +inline const char* DescriptorMismatch::what() const noexcept +{ + return "Table descriptor mismatch"; +} + +inline const char* FileFormatUpgradeRequired::what() const noexcept +{ + return "Database upgrade required but prohibited"; +} + +// LCOV_EXCL_STOP + +inline AddressSpaceExhausted::AddressSpaceExhausted(const std::string& msg) + : std::runtime_error(msg) +{ +} + +inline LogicError::LogicError(LogicError::ErrorKind k) + : m_kind(k) +{ +} + +inline LogicError::ErrorKind LogicError::kind() const noexcept +{ + return m_kind; +} + + +} // namespace realm + +#endif // REALM_EXCEPTIONS_HPP diff --git a/Pods/Realm/include/core/realm/group.hpp b/Pods/Realm/include/core/realm/group.hpp new file mode 100644 index 0000000..490dd26 --- /dev/null +++ b/Pods/Realm/include/core/realm/group.hpp @@ -0,0 +1,1371 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_GROUP_HPP +#define REALM_GROUP_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +class SharedGroup; +namespace _impl { +class GroupFriend; +class TransactLogConvenientEncoder; +class TransactLogParser; +} + + +/// A group is a collection of named tables. +/// +/// Tables occur in the group in an unspecified order, but an order that +/// generally remains fixed. The order is guaranteed to remain fixed between two +/// points in time if no tables are added to, or removed from the group during +/// that time. When tables are added to, or removed from the group, the order +/// may change arbitrarily. +/// +/// If `table` is a table accessor attached to a group-level table, and `group` +/// is a group accessor attached to the group, then the following is guaranteed, +/// even after a change in the table order: +/// +/// \code{.cpp} +/// +/// table == group.get_table(table.get_index_in_group()) +/// +/// \endcode +/// +class Group : private Table::Parent { +public: + /// Construct a free-standing group. This group instance will be + /// in the attached state, but neither associated with a file, nor + /// with an external memory buffer. + Group(); + + enum OpenMode { + /// Open in read-only mode. Fail if the file does not already exist. + mode_ReadOnly, + /// Open in read/write mode. Create the file if it doesn't exist. + mode_ReadWrite, + /// Open in read/write mode. Fail if the file does not already exist. + mode_ReadWriteNoCreate + }; + + /// Equivalent to calling open(const std::string&, const char*, OpenMode) + /// on an unattached group accessor. + explicit Group(const std::string& file, const char* encryption_key = nullptr, OpenMode = mode_ReadOnly); + + /// Equivalent to calling open(BinaryData, bool) on an unattached + /// group accessor. Note that if this constructor throws, the + /// ownership of the memory buffer will remain with the caller, + /// regardless of whether \a take_ownership is set to `true` or + /// `false`. + explicit Group(BinaryData, bool take_ownership = true); + + struct unattached_tag { + }; + + /// Create a Group instance in its unattached state. It may then + /// be attached to a database file later by calling one of the + /// open() methods. You may test whether this instance is + /// currently in its attached state by calling + /// is_attached(). Calling any other method (except the + /// destructor) while in the unattached state has undefined + /// behavior. + Group(unattached_tag) noexcept; + + // FIXME: Implement a proper copy constructor (fairly trivial). + Group(const Group&) = delete; + + ~Group() noexcept override; + + /// Attach this Group instance to the specified database file. + /// + /// By default, the specified file is opened in read-only mode + /// (mode_ReadOnly). This allows opening a file even when the + /// caller lacks permission to write to that file. The opened + /// group may still be modified freely, but the changes cannot be + /// written back to the same file using the commit() function. An + /// attempt to do that, will cause an exception to be thrown. When + /// opening in read-only mode, it is an error if the specified + /// file does not already exist in the file system. + /// + /// Alternatively, the file can be opened in read/write mode + /// (mode_ReadWrite). This allows use of the commit() function, + /// but, of course, it also requires that the caller has + /// permission to write to the specified file. When opening in + /// read-write mode, an attempt to create the specified file will + /// be made, if it does not already exist in the file system. + /// + /// In any case, if the file already exists, it must contain a + /// valid Realm database. In many cases invalidity will be + /// detected and cause the InvalidDatabase exception to be thrown, + /// but you should not rely on it. + /// + /// Note that changes made to the database via a Group instance + /// are not automatically committed to the specified file. You + /// may, however, at any time, explicitly commit your changes by + /// calling the commit() method, provided that the specified + /// open-mode is not mode_ReadOnly. Alternatively, you may call + /// write() to write the entire database to a new file. Writing + /// the database to a new file does not end, or in any other way + /// change the association between the Group instance and the file + /// that was specified in the call to open(). + /// + /// A file that is passed to Group::open(), may not be modified by + /// a third party until after the Group object is + /// destroyed. Behavior is undefined if a file is modified by a + /// third party while any Group object is associated with it. + /// + /// Calling open() on a Group instance that is already in the + /// attached state has undefined behavior. + /// + /// Accessing a Realm database file through manual construction + /// of a Group object does not offer any level of thread safety or + /// transaction safety. When any of those kinds of safety are a + /// concern, consider using a SharedGroup instead. When accessing + /// a database file in read/write mode through a manually + /// constructed Group object, it is entirely the responsibility of + /// the application that the file is not accessed in any way by a + /// third party during the life-time of that group object. It is, + /// on the other hand, safe to concurrently access a database file + /// by multiple manually created Group objects, as long as all of + /// them are opened in read-only mode, and there is no other party + /// that modifies the file concurrently. + /// + /// Do not call this function on a group instance that is managed + /// by a shared group. Doing so will result in undefined behavior. + /// + /// Even if this function throws, it may have the side-effect of + /// creating the specified file, and the file may get left behind + /// in an invalid state. Of course, this can only happen if + /// read/write mode (mode_ReadWrite) was requested, and the file + /// did not already exist. + /// + /// \param file File system path to a Realm database file. + /// + /// \param encryption_key 32-byte key used to encrypt and decrypt + /// the database file, or nullptr to disable encryption. + /// + /// \param mode Specifying a mode that is not mode_ReadOnly + /// requires that the specified file can be opened in read/write + /// mode. In general there is no reason to open a group in + /// read/write mode unless you want to be able to call + /// Group::commit(). + /// + /// \throw util::File::AccessError If the file could not be + /// opened. If the reason corresponds to one of the exception + /// types that are derived from util::File::AccessError, the + /// derived exception type is thrown. Note that InvalidDatabase is + /// among these derived exception types. + void open(const std::string& file, const char* encryption_key = nullptr, OpenMode mode = mode_ReadOnly); + + /// Attach this Group instance to the specified memory buffer. + /// + /// This is similar to constructing a group from a file except + /// that in this case the database is assumed to be stored in the + /// specified memory buffer. + /// + /// If \a take_ownership is `true`, you pass the ownership of the + /// specified buffer to the group. In this case the buffer will + /// eventually be freed using std::free(), so the buffer you pass, + /// must have been allocated using std::malloc(). + /// + /// On the other hand, if \a take_ownership is set to `false`, it + /// is your responsibility to keep the memory buffer alive during + /// the lifetime of the group, and in case the buffer needs to be + /// deallocated afterwards, that is your responsibility too. + /// + /// If this function throws, the ownership of the memory buffer + /// will remain with the caller, regardless of whether \a + /// take_ownership is set to `true` or `false`. + /// + /// Calling open() on a Group instance that is already in the + /// attached state has undefined behavior. + /// + /// Do not call this function on a group instance that is managed + /// by a shared group. Doing so will result in undefined behavior. + /// + /// \throw InvalidDatabase If the specified buffer does not appear + /// to contain a valid database. + void open(BinaryData, bool take_ownership = true); + + /// A group may be created in the unattached state, and then later + /// attached to a file with a call to open(). Calling any method + /// other than open(), and is_attached() on an unattached instance + /// results in undefined behavior. + bool is_attached() const noexcept; + + /// Returns true if, and only if the number of tables in this + /// group is zero. + bool is_empty() const noexcept; + + /// Returns the number of tables in this group. + size_t size() const noexcept; + + /// \defgroup group_table_access Table Accessors + /// + /// has_table() returns true if, and only if this group contains a table + /// with the specified name. + /// + /// find_table() returns the index of the first table in this group with the + /// specified name, or `realm::not_found` if this group does not contain a + /// table with the specified name. + /// + /// get_table_name() returns the name of table at the specified index. + /// + /// The versions of get_table(), that accepts a \a name argument, return the + /// first table with the specified name, or null if no such table exists. + /// + /// add_table() adds a table with the specified name to this group. It + /// throws TableNameInUse if \a require_unique_name is true and \a name + /// clashes with the name of an existing table. If \a require_unique_name is + /// false, it is possible to add more than one table with the same + /// name. Whenever a table is added, the order of the preexisting tables may + /// change arbitrarily, and the new table may not end up as the last one + /// either. But know that you can always call Table::get_index_in_group() on + /// the returned table accessor to find out at which index it ends up. + /// + /// get_or_add_table() checks if a table exists in this group with the specified + /// name. If it doesn't exist, a table is created. + /// + /// get_or_insert_table() works slightly differently from get_or_add_table(), + /// in that it considers the position of the requested table as part of that + /// table's identifying "key", in addition to the name. + /// + /// remove_table() removes the specified table from this group. A table can + /// be removed only when it is not the target of a link column of a + /// different table. Whenever a table is removed, the order of the remaining + /// tables may change arbitrarily. + /// + /// rename_table() changes the name of a preexisting table. If \a + /// require_unique_name is false, it becomes possible to have more than one + /// table with a given name in a single group. + /// + /// The template functions work exactly like their non-template namesakes + /// except as follows: The template versions of get_table() and + /// get_or_add_table() throw DescriptorMismatch if the dynamic type of the + /// specified table does not match the statically specified custom table + /// type. The template versions of add_table() and get_or_add_table() set + /// the dynamic type (descriptor) to match the statically specified custom + /// table type. + /// + /// \tparam T An instance of the BasicTable class template. + /// + /// \param index Index of table in this group. + /// + /// \param name Name of table. All strings are valid table names as long as + /// they are valid UTF-8 encodings and the number of bytes does not exceed + /// `max_table_name_length`. A call to add_table() or get_or_add_table() + /// with a name that is longer than `max_table_name_length` will cause an + /// exception to be thrown. + /// + /// \param new_name New name for preexisting table. + /// + /// \param require_unique_name When set to true (the default), it becomes + /// impossible to add a table with a name that is already in use, or to + /// rename a table to a name that is already in use. + /// + /// \param was_added When specified, the boolean variable is set to true if + /// the table was added, and to false otherwise. If the function throws, the + /// boolean variable retains its original value. + /// + /// \return get_table(), add_table(), and get_or_add_table() return a table + /// accessor attached to the requested (or added) table. get_table() may + /// return null. + /// + /// \throw DescriptorMismatch Thrown by get_table() and get_or_add_table() + /// tf the dynamic table type does not match the statically specified custom + /// table type (\a T). + /// + /// \throw NoSuchTable Thrown by remove_table() and rename_table() if there + /// is no table with the specified \a name. + /// + /// \throw TableNameInUse Thrown by add_table() if \a require_unique_name is + /// true and \a name clashes with the name of a preexisting table. Thrown by + /// rename_table() if \a require_unique_name is true and \a new_name clashes + /// with the name of a preexisting table. + /// + /// \throw CrossTableLinkTarget Thrown by remove_table() if the specified + /// table is the target of a link column of a different table. + /// + //@{ + + static const size_t max_table_name_length = 63; + + bool has_table(StringData name) const noexcept; + size_t find_table(StringData name) const noexcept; + StringData get_table_name(size_t table_ndx) const; + + TableRef get_table(size_t index); + ConstTableRef get_table(size_t index) const; + + TableRef get_table(StringData name); + ConstTableRef get_table(StringData name) const; + + TableRef add_table(StringData name, bool require_unique_name = true); + TableRef insert_table(size_t index, StringData name, bool require_unique_name = true); + TableRef get_or_add_table(StringData name, bool* was_added = nullptr); + TableRef get_or_insert_table(size_t index, StringData name, bool* was_added = nullptr); + + template + BasicTableRef get_table(size_t index); + + template + BasicTableRef get_table(size_t index) const; + + template + BasicTableRef get_table(StringData name); + + template + BasicTableRef get_table(StringData name) const; + + template + BasicTableRef add_table(StringData name, bool require_unique_name = true); + + template + BasicTableRef insert_table(size_t index, StringData name, bool require_unique_name = true); + + template + BasicTableRef get_or_add_table(StringData name, bool* was_added = nullptr); + + template + BasicTableRef get_or_insert_table(size_t index, StringData name, bool* was_added = nullptr); + + void remove_table(size_t index); + void remove_table(StringData name); + + void rename_table(size_t index, StringData new_name, bool require_unique_name = true); + void rename_table(StringData name, StringData new_name, bool require_unique_name = true); + + //@} + + /// Move the table at \a from_index such that it ends up at \a + /// to_index. Other tables are shifted as necessary in such a way that their + /// order is preserved. + /// + /// Note that \a to_index is the desired final index of the moved table, + /// therefore, `move_table(1,1)` is a no-op, while `move_table(1,2)` moves + /// the table at index 1 by one position, such that it ends up at index 2. A + /// side-effect of that, is that the table, that was originally at index 2, + /// is moved to index 1. + void move_table(size_t from_index, size_t to_index); + + // Serialization + + /// Write this database to the specified output stream. + /// + /// \param out The destination output stream to write to. + /// + /// \param pad If true, the file is padded to ensure the footer is aligned + /// to the end of a page + void write(std::ostream& out, bool pad = false) const; + + /// Write this database to a new file. It is an error to specify a + /// file that already exists. This is to protect against + /// overwriting a database file that is currently open, which + /// would cause undefined behaviour. + /// + /// \param file A filesystem path. + /// + /// \param encryption_key 32-byte key used to encrypt the database file, + /// or nullptr to disable encryption. + /// + /// \throw util::File::AccessError If the file could not be + /// opened. If the reason corresponds to one of the exception + /// types that are derived from util::File::AccessError, the + /// derived exception type is thrown. In particular, + /// util::File::Exists will be thrown if the file exists already. + void write(const std::string& file, const char* encryption_key = nullptr) const; + + /// Write this database to a memory buffer. + /// + /// Ownership of the returned buffer is transferred to the + /// caller. The memory will have been allocated using + /// std::malloc(). + BinaryData write_to_mem() const; + + /// Commit changes to the attached file. This requires that the + /// attached file is opened in read/write mode. + /// + /// Calling this function on an unattached group, a free-standing + /// group, a group whose attached file is opened in read-only + /// mode, a group that is attached to a memory buffer, or a group + /// that is managed by a shared group, is an error and will result + /// in undefined behavior. + /// + /// Table accesors will remain valid across the commit. Note that + /// this is not the case when working with proper transactions. + void commit(); + + //@{ + /// Some operations on Tables in a Group can cause indirect changes to other + /// fields, including in other Tables in the same Group. Specifically, + /// removing a row will set any links to that row to null, and if it had the + /// last strong links to other rows, will remove those rows. When this + /// happens, The cascade notification handler will be called with a + /// CascadeNotification containing information about what indirect changes + /// will occur, before any changes are made. + /// + /// has_cascade_notification_handler() returns true if and only if there is + /// currently a non-null notification handler registered. + /// + /// set_cascade_notification_handler() replaces the current handler (if any) + /// with the passed in handler. Pass in nullptr to remove the current handler + /// without registering a new one. + /// + /// CascadeNotification contains a vector of rows which will be removed and + /// a vector of links which will be set to null (or removed, for entries in + /// LinkLists). + struct CascadeNotification { + struct row { + /// Non-zero iff the removal of this row is ordered + /// (Table::remove()), as opposed to ordered + /// (Table::move_last_over()). Implicit removals are always + /// unordered. + /// + /// This flag does not take part in comparisons (operator==() and + /// operator<()). + size_t is_ordered_removal : 1; + + /// Index within group of a group-level table. + size_t table_ndx : std::numeric_limits::digits - 1; + + /// Row index which will be removed. + size_t row_ndx; + + row() + : is_ordered_removal(0) + { + } + + bool operator==(const row&) const noexcept; + bool operator!=(const row&) const noexcept; + + /// Trivial lexicographic order + bool operator<(const row&) const noexcept; + }; + + struct link { + const Table* origin_table; ///< A group-level table. + size_t origin_col_ndx; ///< Link column being nullified. + size_t origin_row_ndx; ///< Row in column being nullified. + /// The target row index which is being removed. Mostly relevant for + /// LinkList (to know which entries are being removed), but also + /// valid for Link. + size_t old_target_row_ndx; + }; + + /// A sorted list of rows which will be removed by the current operation. + std::vector rows; + + /// An unordered list of links which will be nullified by the current + /// operation. + std::vector links; + }; + + bool has_cascade_notification_handler() const noexcept; + void set_cascade_notification_handler(std::function new_handler) noexcept; + + //@} + + //@{ + /// During sync operation, schema changes may happen at runtime as connected + /// clients update their schema as part of an app update. Since this is a + /// relatively rare event, no attempt is made at limiting the amount of work + /// the handler is required to do to update its information about table and + /// column indices (i.e., all table and column indices must be recalculated). + /// + /// At the time of writing, only additive schema changes may occur in that + /// scenario. + /// + /// has_schema_change_notification_handler() returns true iff there is currently + /// a non-null notification handler registered. + /// + /// set_schema_change_notification_handler() replaces the current handler (if any) + /// with the passed in handler. Pass in nullptr to remove the current handler + /// without registering a new one. + + bool has_schema_change_notification_handler() const noexcept; + void set_schema_change_notification_handler(std::function new_handler) noexcept; + + //@} + + // Conversion + template + void to_json(S& out, size_t link_depth = 0, std::map* renames = nullptr) const; + void to_string(std::ostream& out) const; + + /// Compare two groups for equality. Two groups are equal if, and + /// only if, they contain the same tables in the same order, that + /// is, for each table T at index I in one of the groups, there is + /// a table at index I in the other group that is equal to T. + /// Tables are equal if they have the same content and the same table name. + bool operator==(const Group&) const; + + /// Compare two groups for inequality. See operator==(). + bool operator!=(const Group& g) const + { + return !(*this == g); + } + + void verify() const; +#ifdef REALM_DEBUG + void print() const; + void print_free() const; + MemStats stats(); + void enable_mem_diagnostics(bool enable = true) + { + m_alloc.enable_debug(enable); + } + void to_dot(std::ostream&) const; + void to_dot() const; // To std::cerr (for GDB) + void to_dot(const char* file_path) const; +#endif + +private: + SlabAlloc m_alloc; + + /// `m_top` is the root node (or top array) of the Realm, and has the + /// following layout: + /// + ///
+    ///
+    ///   slot  value
+    ///   -----------------------
+    ///   1st   m_table_names
+    ///   2nd   m_tables
+    ///   3rd   Logical file size
+    ///   4th   GroupWriter::m_free_positions (optional)
+    ///   5th   GroupWriter::m_free_lengths   (optional)
+    ///   6th   GroupWriter::m_free_versions  (optional)
+    ///   7th   Transaction number / version  (optional)
+    ///   8th   In-Realm history type         (optional)
+    ///   9th   In-Realm history ref          (optional)
+    ///
+    /// 
+ /// + /// The 'in-Realm history type' slot stores a value of + /// Replication::HistoryType, although never + /// Replication::hist_OutOfRealm. For more information about that, see + /// Replication::get_history_type(). + /// + /// The first three entries are mandatory. In files created by + /// Group::write(), none of the optional entries are present and the size of + /// `m_top` is 3. In files updated by Group::commit(), the 4th and 5th entry + /// is present, and the size of `m_top` is 5. In files updated by way of a + /// transaction (SharedGroup::commit()), the 4th, 5th, 6th, and 7th entry is + /// present, and the size of `m_top` is 7. In files that contain a changeset + /// history, the 8th and 9th entry is present. + /// + /// When a group accessor is attached to a newly created file or an empty + /// memory buffer where there is no top array yet, `m_top`, `m_tables`, and + /// `m_table_names` with be left in the detached state until the initiation + /// of the first write transaction. In particular, they will remain in the + /// detached state during read transactions that precede the first write + /// transaction. + Array m_top; + ArrayInteger m_tables; + ArrayString m_table_names; + + typedef std::vector table_accessors; + mutable table_accessors m_table_accessors; + + bool m_attached = false; + const bool m_is_shared; + + std::function m_notify_handler; + std::function m_schema_change_handler; + + struct shared_tag { + }; + Group(shared_tag) noexcept; + + void init_array_parents() noexcept; + + /// If `top_ref` is not zero, attach this group accessor to the specified + /// underlying node structure. If `top_ref` is zero and \a + /// create_group_when_missing is true, create a new node structure that + /// represents an empty group, and attach this group accessor to it. It is + /// an error to call this function on an already attached group accessor. + void attach(ref_type top_ref, bool create_group_when_missing); + + /// Detach this group accessor from the underlying node structure. If this + /// group accessors is already in the detached state, this function does + /// nothing (idempotency). + void detach() noexcept; + + /// \param writable Must be set to true when, and only when attaching for a + /// write transaction. + void attach_shared(ref_type new_top_ref, size_t new_file_size, bool writable); + + void create_empty_group(); + + void reset_free_space_tracking(); + + void remap(size_t new_file_size); + void remap_and_update_refs(ref_type new_top_ref, size_t new_file_size); + + /// Recursively update refs stored in all cached array + /// accessors. This includes cached array accessors in any + /// currently attached table accessors. This ensures that the + /// group instance itself, as well as any attached table accessor + /// that exists across Group::commit() will remain valid. This + /// function is not appropriate for use in conjunction with + /// commits via shared group. + void update_refs(ref_type top_ref, size_t old_baseline) noexcept; + + // Overriding method in ArrayParent + void update_child_ref(size_t, ref_type) override; + + // Overriding method in ArrayParent + ref_type get_child_ref(size_t) const noexcept override; + + // Overriding method in Table::Parent + StringData get_child_name(size_t) const noexcept override; + + // Overriding method in Table::Parent + void child_accessor_destroyed(Table*) noexcept override; + + // Overriding method in Table::Parent + Group* get_parent_group() noexcept override; + + class TableWriter; + class DefaultTableWriter; + + static void write(std::ostream&, const Allocator&, TableWriter&, bool no_top_array, bool pad_for_encryption, + uint_fast64_t version_number); + + typedef void (*DescSetter)(Table&); + typedef bool (*DescMatcher)(const Spec&); + + Table* do_get_table(size_t table_ndx, DescMatcher desc_matcher); + const Table* do_get_table(size_t table_ndx, DescMatcher desc_matcher) const; + Table* do_get_table(StringData name, DescMatcher desc_matcher); + const Table* do_get_table(StringData name, DescMatcher desc_matcher) const; + Table* do_insert_table(size_t, StringData name, DescSetter desc_setter, bool require_unique_name); + Table* do_insert_table(size_t, StringData name, DescSetter desc_setter); + Table* do_get_or_add_table(StringData name, DescMatcher desc_matcher, DescSetter setter, bool* was_added); + Table* do_get_or_insert_table(size_t, StringData name, DescMatcher desc_matcher, DescSetter desc_setter, + bool* was_added); + + void create_and_insert_table(size_t new_table_ndx, StringData name); + Table* create_table_accessor(size_t table_ndx); + + void detach_table_accessors() noexcept; // Idempotent + + void mark_all_table_accessors() noexcept; + + void write(const std::string& file, const char* encryption_key, uint_fast64_t version_number) const; + void write(util::File& file, const char* encryption_key, uint_fast64_t version_number) const; + void write(std::ostream&, bool pad, uint_fast64_t version_numer) const; + + Replication* get_replication() const noexcept; + void set_replication(Replication*) noexcept; + class TransactAdvancer; + void advance_transact(ref_type new_top_ref, size_t new_file_size, _impl::NoCopyInputStream&); + void refresh_dirty_accessors(); + template + void update_table_indices(F&& map_function); + + int get_file_format_version() const noexcept; + void set_file_format_version(int) noexcept; + int get_committed_file_format_version() const noexcept; + + /// The specified history type must be a value of Replication::HistoryType. + static int get_target_file_format_version_for_session(int current_file_format_version, int history_type) noexcept; + + /// Must be called from within a write transaction + void upgrade_file_format(int target_file_format_version); + + std::pair get_to_dot_parent(size_t ndx_in_parent) const override; + + void send_cascade_notification(const CascadeNotification& notification) const; + void send_schema_change_notification() const; + + static void get_version_and_history_type(const Array& top, _impl::History::version_type& version, + int& history_type) noexcept; + static ref_type get_history_ref(const Array& top) noexcept; + void set_history_parent(Array& history_root) noexcept; + void prepare_history_parent(Array& history_root, int history_type); + + friend class Table; + friend class GroupWriter; + friend class SharedGroup; + friend class _impl::GroupFriend; + friend class _impl::TransactLogConvenientEncoder; + friend class _impl::TransactLogParser; + friend class Replication; + friend class TrivialReplication; +}; + + +// Implementation + +inline Group::Group(const std::string& file, const char* key, OpenMode mode) + : m_alloc() // Throws + , m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); + + open(file, key, mode); // Throws +} + +inline Group::Group(BinaryData buffer, bool take_ownership) + : m_alloc() // Throws + , m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); + open(buffer, take_ownership); // Throws +} + +inline Group::Group(unattached_tag) noexcept + : m_alloc() + , // Throws + m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(false) +{ + init_array_parents(); +} + +inline Group* Group::get_parent_group() noexcept +{ + return this; +} + +inline Group::Group(shared_tag) noexcept + : m_alloc() + , // Throws + m_top(m_alloc) + , m_tables(m_alloc) + , m_table_names(m_alloc) + , m_is_shared(true) +{ + init_array_parents(); +} + +inline bool Group::is_attached() const noexcept +{ + return m_attached; +} + +inline bool Group::is_empty() const noexcept +{ + if (!is_attached()) + return false; + if (m_table_names.is_attached()) + return m_table_names.is_empty(); + return true; +} + +inline size_t Group::size() const noexcept +{ + if (!is_attached()) + return 0; + if (m_table_names.is_attached()) + return m_table_names.size(); + return 0; +} + +inline StringData Group::get_table_name(size_t table_ndx) const +{ + if (table_ndx >= size()) + throw LogicError(LogicError::table_index_out_of_range); + return m_table_names.get(table_ndx); +} + +inline bool Group::has_table(StringData name) const noexcept +{ + size_t ndx = find_table(name); + return ndx != not_found; +} + +inline size_t Group::find_table(StringData name) const noexcept +{ + if (!is_attached()) + return 0; + if (m_table_names.is_attached()) + return m_table_names.find_first(name); + return not_found; +} + +inline TableRef Group::get_table(size_t table_ndx) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return TableRef(table); +} + +inline ConstTableRef Group::get_table(size_t table_ndx) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + const Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return ConstTableRef(table); +} + +inline TableRef Group::get_table(StringData name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + Table* table = do_get_table(name, desc_matcher); // Throws + return TableRef(table); +} + +inline ConstTableRef Group::get_table(StringData name) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + const Table* table = do_get_table(name, desc_matcher); // Throws + return ConstTableRef(table); +} + +inline TableRef Group::insert_table(size_t table_ndx, StringData name, bool require_unique_name) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_insert_table(table_ndx, name, desc_setter, require_unique_name); // Throws + return TableRef(table); +} + +inline TableRef Group::add_table(StringData name, bool require_unique_name) +{ + return insert_table(size(), name, require_unique_name); +} + +inline TableRef Group::get_or_insert_table(size_t table_ndx, StringData name, bool* was_added) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_added); // Throws + return TableRef(table); +} + +inline TableRef Group::get_or_add_table(StringData name, bool* was_added) +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = nullptr; // Do not check descriptor + DescSetter desc_setter = nullptr; // Do not add any columns + Table* table = do_get_or_add_table(name, desc_matcher, desc_setter, was_added); // Throws + return TableRef(table); +} + +template +inline BasicTableRef Group::get_table(size_t table_ndx) +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return BasicTableRef(static_cast(table)); +} + +template +inline BasicTableRef Group::get_table(size_t table_ndx) const +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + const Table* table = do_get_table(table_ndx, desc_matcher); // Throws + return BasicTableRef(static_cast(table)); +} + +template +inline BasicTableRef Group::get_table(StringData name) +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + Table* table = do_get_table(name, desc_matcher); // Throws + return BasicTableRef(static_cast(table)); +} + +template +inline BasicTableRef Group::get_table(StringData name) const +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + const Table* table = do_get_table(name, desc_matcher); // Throws + return BasicTableRef(static_cast(table)); +} + +template +inline BasicTableRef Group::insert_table(size_t table_ndx, StringData name, bool require_unique_name) +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescSetter desc_setter = &T::set_dynamic_type; + Table* table = do_insert_table(table_ndx, name, desc_setter, require_unique_name); // Throws + return BasicTableRef(static_cast(table)); +} + +template +inline BasicTableRef Group::add_table(StringData name, bool require_unique_name) +{ + return insert_table(size(), name, require_unique_name); +} + +template +BasicTableRef Group::get_or_insert_table(size_t table_ndx, StringData name, bool* was_added) +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + DescSetter desc_setter = &T::set_dynamic_type; + Table* table = do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_added); // Throws + return BasicTableRef(static_cast(table)); +} + +template +BasicTableRef Group::get_or_add_table(StringData name, bool* was_added) +{ + static_assert(IsBasicTable::value, "Invalid table type"); + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + DescMatcher desc_matcher = &T::matches_dynamic_type; + DescSetter desc_setter = &T::set_dynamic_type; + Table* table = do_get_or_add_table(name, desc_matcher, desc_setter, was_added); // Throws + return BasicTableRef(static_cast(table)); +} + +template +void Group::to_json(S& out, size_t link_depth, std::map* renames) const +{ + if (!is_attached()) + throw LogicError(LogicError::detached_accessor); + + std::map renames2; + renames = renames ? renames : &renames2; + + out << "{"; + + for (size_t i = 0; i < m_tables.size(); ++i) { + StringData name = m_table_names.get(i); + std::map& m = *renames; + if (m[name] != "") + name = m[name]; + + ConstTableRef table = get_table(i); + + if (i) + out << ","; + out << "\"" << name << "\""; + out << ":"; + table->to_json(out, link_depth, renames); + } + + out << "}"; +} + +inline void Group::init_array_parents() noexcept +{ + m_table_names.set_parent(&m_top, 0); + m_tables.set_parent(&m_top, 1); +} + +inline void Group::update_child_ref(size_t child_ndx, ref_type new_ref) +{ + m_tables.set(child_ndx, new_ref); +} + +inline ref_type Group::get_child_ref(size_t child_ndx) const noexcept +{ + return m_tables.get_as_ref(child_ndx); +} + +inline StringData Group::get_child_name(size_t child_ndx) const noexcept +{ + return m_table_names.get(child_ndx); +} + +inline void Group::child_accessor_destroyed(Table*) noexcept +{ + // Ignore +} + +inline bool Group::has_cascade_notification_handler() const noexcept +{ + return !!m_notify_handler; +} + +inline void +Group::set_cascade_notification_handler(std::function new_handler) noexcept +{ + m_notify_handler = std::move(new_handler); +} + +inline void Group::send_cascade_notification(const CascadeNotification& notification) const +{ + if (m_notify_handler) + m_notify_handler(notification); +} + +inline bool Group::has_schema_change_notification_handler() const noexcept +{ + return !!m_schema_change_handler; +} + +inline void Group::set_schema_change_notification_handler(std::function new_handler) noexcept +{ + m_schema_change_handler = std::move(new_handler); +} + +inline void Group::send_schema_change_notification() const +{ + if (m_schema_change_handler) + m_schema_change_handler(); +} + +inline void Group::get_version_and_history_type(const Array& top, _impl::History::version_type& version, + int& history_type) noexcept +{ + _impl::History::version_type version_2 = 0; + int history_type_2 = 0; + if (top.is_attached()) { + if (top.size() >= 6) { + REALM_ASSERT(top.size() >= 7); + version_2 = _impl::History::version_type(top.get(6) / 2); + } + if (top.size() >= 8) { + REALM_ASSERT(top.size() >= 9); + history_type_2 = int(top.get(7) / 2); + } + } + // Version 0 is not a legal initial version, so it has to be set to 1 + // instead. + if (version_2 == 0) + version_2 = 1; + version = version_2; + history_type = history_type_2; +} + +inline ref_type Group::get_history_ref(const Array& top) noexcept +{ + if (top.is_attached()) { + if (top.size() >= 8) { + REALM_ASSERT(top.size() >= 9); + return top.get_as_ref(8); + } + } + return 0; +} + +inline void Group::set_history_parent(Array& history_root) noexcept +{ + history_root.set_parent(&m_top, 8); +} + +inline void Group::prepare_history_parent(Array& history_root, int history_type) +{ + REALM_ASSERT(m_alloc.get_file_format_version() >= 4); + // Ensure that there are slots for both the history type and the history + // ref. + while (m_top.size() < 9) + m_top.add(0); // Throws + m_top.set(7, RefOrTagged::make_tagged(history_type)); // Throws + set_history_parent(history_root); +} + +class Group::TableWriter { +public: + virtual ref_type write_names(_impl::OutputStream&) = 0; + virtual ref_type write_tables(_impl::OutputStream&) = 0; + virtual ~TableWriter() noexcept + { + } +}; + +inline const Table* Group::do_get_table(size_t table_ndx, DescMatcher desc_matcher) const +{ + return const_cast(this)->do_get_table(table_ndx, desc_matcher); // Throws +} + +inline const Table* Group::do_get_table(StringData name, DescMatcher desc_matcher) const +{ + return const_cast(this)->do_get_table(name, desc_matcher); // Throws +} + +inline void Group::reset_free_space_tracking() +{ + m_alloc.reset_free_space_tracking(); // Throws +} + +inline Replication* Group::get_replication() const noexcept +{ + return m_alloc.get_replication(); +} + +inline void Group::set_replication(Replication* repl) noexcept +{ + m_alloc.set_replication(repl); +} + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Group class. +class _impl::GroupFriend { +public: + static Allocator& get_alloc(Group& group) noexcept + { + return group.m_alloc; + } + + static Table& get_table(Group& group, size_t ndx_in_group) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + Table* table = group.do_get_table(ndx_in_group, desc_matcher); // Throws + return *table; + } + + static const Table& get_table(const Group& group, size_t ndx_in_group) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + const Table* table = group.do_get_table(ndx_in_group, desc_matcher); // Throws + return *table; + } + + static Table* get_table(Group& group, StringData name) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + Table* table = group.do_get_table(name, desc_matcher); // Throws + return table; + } + + static const Table* get_table(const Group& group, StringData name) + { + Group::DescMatcher desc_matcher = 0; // Do not check descriptor + const Table* table = group.do_get_table(name, desc_matcher); // Throws + return table; + } + + static Table& insert_table(Group& group, size_t table_ndx, StringData name, bool require_unique_name) + { + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_insert_table(table_ndx, name, desc_setter, require_unique_name); + } + + static Table& add_table(Group& group, StringData name, bool require_unique_name) + { + return insert_table(group, group.size(), name, require_unique_name); + } + + static Table& get_or_insert_table(Group& group, size_t table_ndx, StringData name, bool* was_inserted) + { + Group::DescMatcher desc_matcher = nullptr; // Do not check descriptor + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_get_or_insert_table(table_ndx, name, desc_matcher, desc_setter, was_inserted); + } + + static Table& get_or_add_table(Group& group, StringData name, bool* was_inserted) + { + Group::DescMatcher desc_matcher = nullptr; // Do not check descriptor + Group::DescSetter desc_setter = nullptr; // Do not add any columns + return *group.do_get_or_add_table(name, desc_matcher, desc_setter, was_inserted); + } + + static void send_cascade_notification(const Group& group, const Group::CascadeNotification& notification) + { + group.send_cascade_notification(notification); + } + + static Replication* get_replication(const Group& group) noexcept + { + return group.get_replication(); + } + + static void set_replication(Group& group, Replication* repl) noexcept + { + group.set_replication(repl); + } + + static void detach(Group& group) noexcept + { + group.detach(); + } + + static void attach_shared(Group& group, ref_type new_top_ref, size_t new_file_size, bool writable) + { + group.attach_shared(new_top_ref, new_file_size, writable); // Throws + } + + static void reset_free_space_tracking(Group& group) + { + group.reset_free_space_tracking(); // Throws + } + + static void remap(Group& group, size_t new_file_size) + { + group.remap(new_file_size); // Throws + } + + static void remap_and_update_refs(Group& group, ref_type new_top_ref, size_t new_file_size) + { + group.remap_and_update_refs(new_top_ref, new_file_size); // Throws + } + + static void advance_transact(Group& group, ref_type new_top_ref, size_t new_file_size, + _impl::NoCopyInputStream& in) + { + group.advance_transact(new_top_ref, new_file_size, in); // Throws + } + + static void create_empty_group_when_missing(Group& group) + { + if (!group.m_top.is_attached()) + group.create_empty_group(); // Throws + } + + static void get_version_and_history_type(Allocator& alloc, ref_type top_ref, + _impl::History::version_type& version, int& history_type) noexcept + { + Array top(alloc); + if (top_ref != 0) + top.init_from_ref(top_ref); + Group::get_version_and_history_type(top, version, history_type); + } + + static ref_type get_history_ref(const Group& group) noexcept + { + return Group::get_history_ref(group.m_top); + } + + static ref_type get_history_ref(Allocator& alloc, ref_type top_ref) noexcept + { + Array top(alloc); + if (top_ref != 0) + top.init_from_ref(top_ref); + return Group::get_history_ref(top); + } + + static void set_history_parent(Group& group, Array& history_root) noexcept + { + group.set_history_parent(history_root); + } + + static void prepare_history_parent(Group& group, Array& history_root, int history_type) + { + group.prepare_history_parent(history_root, history_type); // Throws + } + + static int get_file_format_version(const Group& group) noexcept + { + return group.get_file_format_version(); + } + + static void set_file_format_version(Group& group, int file_format_version) noexcept + { + group.set_file_format_version(file_format_version); + } + + static int get_committed_file_format_version(const Group& group) noexcept + { + return group.get_committed_file_format_version(); + } + + static int get_target_file_format_version_for_session(int current_file_format_version, int history_type) noexcept + { + return Group::get_target_file_format_version_for_session(current_file_format_version, history_type); + } + + static void upgrade_file_format(Group& group, int target_file_format_version) + { + group.upgrade_file_format(target_file_format_version); // Throws + } +}; + + +struct CascadeState : Group::CascadeNotification { + /// If non-null, then no recursion will be performed for rows of that + /// table. The effect is then exactly as if all the rows of that table were + /// added to \a state.rows initially, and then removed again after the + /// explicit invocations of Table::cascade_break_backlinks_to() (one for + /// each initiating row). This is used by Table::clear() to avoid + /// reentrance. + /// + /// Must never be set concurrently with stop_on_link_list_column. + Table* stop_on_table = nullptr; + + /// If non-null, then Table::cascade_break_backlinks_to() will skip the + /// removal of reciprocal backlinks for the link list at + /// stop_on_link_list_row_ndx in this column, and no recursion will happen + /// on its behalf. This is used by LinkView::clear() to avoid reentrance. + /// + /// Must never be set concurrently with stop_on_table. + LinkListColumn* stop_on_link_list_column = nullptr; + + /// Is ignored if stop_on_link_list_column is null. + size_t stop_on_link_list_row_ndx = 0; + + /// If false, the links field is not needed, so any work done just for that + /// can be skipped. + bool track_link_nullifications = false; +}; + +inline bool Group::CascadeNotification::row::operator==(const row& r) const noexcept +{ + return table_ndx == r.table_ndx && row_ndx == r.row_ndx; +} + +inline bool Group::CascadeNotification::row::operator!=(const row& r) const noexcept +{ + return !(*this == r); +} + +inline bool Group::CascadeNotification::row::operator<(const row& r) const noexcept +{ + return table_ndx < r.table_ndx || (table_ndx == r.table_ndx && row_ndx < r.row_ndx); +} + +} // namespace realm + +#endif // REALM_GROUP_HPP diff --git a/Pods/Realm/include/core/realm/group_shared.hpp b/Pods/Realm/include/core/realm/group_shared.hpp new file mode 100644 index 0000000..6b36b97 --- /dev/null +++ b/Pods/Realm/include/core/realm/group_shared.hpp @@ -0,0 +1,1160 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_GROUP_SHARED_HPP +#define REALM_GROUP_SHARED_HPP + +#ifdef REALM_DEBUG +#include // usleep() +#endif + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +namespace _impl { +class SharedGroupFriend; +class WriteLogCollector; +} + +/// Thrown by SharedGroup::open() if the lock file is already open in another +/// process which can't share mutexes with this process +struct IncompatibleLockFile : std::runtime_error { + IncompatibleLockFile(const std::string& msg) + : std::runtime_error("Incompatible lock file. " + msg) + { + } +}; + + +/// A SharedGroup facilitates transactions. +/// +/// When multiple threads or processes need to access a database +/// concurrently, they must do so using transactions. By design, +/// Realm does not allow for multiple threads (or processes) to +/// share a single instance of SharedGroup. Instead, each concurrently +/// executing thread or process must use a separate instance of +/// SharedGroup. +/// +/// Each instance of SharedGroup manages a single transaction at a +/// time. That transaction can be either a read transaction, or a +/// write transaction. +/// +/// Utility classes ReadTransaction and WriteTransaction are provided +/// to make it safe and easy to work with transactions in a scoped +/// manner (by means of the RAII idiom). However, transactions can +/// also be explicitly started (begin_read(), begin_write()) and +/// stopped (end_read(), commit(), rollback()). +/// +/// If a transaction is active when the SharedGroup is destroyed, that +/// transaction is implicitly terminated, either by a call to +/// end_read() or rollback(). +/// +/// Two processes that want to share a database file must reside on +/// the same host. +/// +/// +/// Desired exception behavior (not yet fully implemented) +/// ------------------------------------------------------ +/// +/// - If any data access API function throws an unexpected exception during a +/// read transaction, the shared group accessor is left in state "error +/// during read". +/// +/// - If any data access API function throws an unexpected exception during a +/// write transaction, the shared group accessor is left in state "error +/// during write". +/// +/// - If SharedGroup::begin_write() or SharedGroup::begin_read() throws an +/// unexpected exception, the shared group accessor is left in state "no +/// transaction in progress". +/// +/// - SharedGroup::end_read() and SharedGroup::rollback() do not throw. +/// +/// - If SharedGroup::commit() throws an unexpected exception, the shared group +/// accessor is left in state "error during write" and the transaction was +/// not committed. +/// +/// - If SharedGroup::advance_read() or SharedGroup::promote_to_write() throws +/// an unexpected exception, the shared group accessor is left in state +/// "error during read". +/// +/// - If SharedGroup::commit_and_continue_as_read() or +/// SharedGroup::rollback_and_continue_as_read() throws an unexpected +/// exception, the shared group accessor is left in state "error during +/// write". +/// +/// It has not yet been decided exactly what an "unexpected exception" is, but +/// `std::bad_alloc` is surely one example. On the other hand, an expected +/// exception is one that is mentioned in the function specific documentation, +/// and is used to abort an operation due to a special, but expected condition. +/// +/// States +/// ------ +/// +/// - A newly created shared group accessor is in state "no transaction in +/// progress". +/// +/// - In state "error during read", almost all Realm API functions are +/// illegal on the connected group of accessors. The only valid operations +/// are destruction of the shared group, and SharedGroup::end_read(). If +/// SharedGroup::end_read() is called, the new state becomes "no transaction +/// in progress". +/// +/// - In state "error during write", almost all Realm API functions are +/// illegal on the connected group of accessors. The only valid operations +/// are destruction of the shared group, and SharedGroup::rollback(). If +/// SharedGroup::end_write() is called, the new state becomes "no transaction +/// in progress" +class SharedGroup { +public: + /// \brief Same as calling the corresponding version of open() on a instance + /// constructed in the unattached state. Exception safety note: if the + /// `upgrade_callback` throws, then the file will be closed properly and the + /// upgrade will be aborted. + explicit SharedGroup(const std::string& file, bool no_create = false, + const SharedGroupOptions options = SharedGroupOptions()); + + /// \brief Same as calling the corresponding version of open() on a instance + /// constructed in the unattached state. Exception safety note: if the + /// `upgrade_callback` throws, then the file will be closed properly and + /// the upgrade will be aborted. + explicit SharedGroup(Replication& repl, const SharedGroupOptions options = SharedGroupOptions()); + + struct unattached_tag { + }; + + /// Create a SharedGroup instance in its unattached state. It may + /// then be attached to a database file later by calling + /// open(). You may test whether this instance is currently in its + /// attached state by calling is_attached(). Calling any other + /// function (except the destructor) while in the unattached state + /// has undefined behavior. + SharedGroup(unattached_tag) noexcept; + + ~SharedGroup() noexcept; + + /// Attach this SharedGroup instance to the specified database file. + /// + /// While at least one instance of SharedGroup exists for a specific + /// database file, a "lock" file will be present too. The lock file will be + /// placed in the same directory as the database file, and its name will be + /// derived by appending ".lock" to the name of the database file. + /// + /// When multiple SharedGroup instances refer to the same file, they must + /// specify the same durability level, otherwise an exception will be + /// thrown. + /// + /// \param file Filesystem path to a Realm database file. + /// + /// \param no_create If the database file does not already exist, it will be + /// created (unless this is set to true.) When multiple threads are involved, + /// it is safe to let the first thread, that gets to it, create the file. + /// + /// \param options See SharedGroupOptions for details of each option. + /// Sensible defaults are provided if this parameter is left out. + /// + /// Calling open() on a SharedGroup instance that is already in the attached + /// state has undefined behavior. + /// + /// \throw util::File::AccessError If the file could not be opened. If the + /// reason corresponds to one of the exception types that are derived from + /// util::File::AccessError, the derived exception type is thrown. Note that + /// InvalidDatabase is among these derived exception types. + /// + /// \throw FileFormatUpgradeRequired only if \a SharedGroupOptions::allow_upgrade + /// is `false` and an upgrade is required. + void open(const std::string& file, bool no_create = false, + const SharedGroupOptions options = SharedGroupOptions()); + + /// Open this group in replication mode. The specified Replication instance + /// must remain in existence for as long as the SharedGroup. + void open(Replication&, const SharedGroupOptions options = SharedGroupOptions()); + + /// Close any open database, returning to the unattached state. + void close() noexcept; + + /// A SharedGroup may be created in the unattached state, and then + /// later attached to a file with a call to open(). Calling any + /// function other than open(), is_attached(), and ~SharedGroup() + /// on an unattached instance results in undefined behavior. + bool is_attached() const noexcept; + + /// Reserve disk space now to avoid allocation errors at a later + /// point in time, and to minimize on-disk fragmentation. In some + /// cases, less fragmentation translates into improved + /// performance. + /// + /// When supported by the system, a call to this function will + /// make the database file at least as big as the specified size, + /// and cause space on the target device to be allocated (note + /// that on many systems on-disk allocation is done lazily by + /// default). If the file is already bigger than the specified + /// size, the size will be unchanged, and on-disk allocation will + /// occur only for the initial section that corresponds to the + /// specified size. On systems that do not support preallocation, + /// this function has no effect. To know whether preallocation is + /// supported by Realm on your platform, call + /// util::File::is_prealloc_supported(). + /// + /// It is an error to call this function on an unattached shared + /// group. Doing so will result in undefined behavior. + void reserve(size_t size_in_bytes); + + /// Querying for changes: + /// + /// NOTE: + /// "changed" means that one or more commits has been made to the database + /// since the SharedGroup (on which wait_for_change() is called) last + /// started, committed, promoted or advanced a transaction. If the + /// SharedGroup has not yet begun a transaction, "changed" is undefined. + /// + /// No distinction is made between changes done by another process + /// and changes done by another thread in the same process as the caller. + /// + /// Has db been changed ? + bool has_changed(); + + /// The calling thread goes to sleep until the database is changed, or + /// until wait_for_change_release() is called. After a call to + /// wait_for_change_release() further calls to wait_for_change() will return + /// immediately. To restore the ability to wait for a change, a call to + /// enable_wait_for_change() is required. Return true if the database has + /// changed, false if it might have. + bool wait_for_change(); + + /// release any thread waiting in wait_for_change() on *this* SharedGroup. + void wait_for_change_release(); + + /// re-enable waiting for change + void enable_wait_for_change(); + // Transactions: + + using version_type = _impl::History::version_type; + using VersionID = realm::VersionID; + + /// Thrown by begin_read() if the specified version does not correspond to a + /// bound (or tethered) snapshot. + struct BadVersion; + + /// \defgroup group_shared_transactions + //@{ + + /// begin_read() initiates a new read transaction. A read transaction is + /// bound to, and provides access to a particular snapshot of the underlying + /// Realm (in general the latest snapshot, but see \a version). It cannot be + /// used to modify the Realm, and in that sense, a read transaction is not a + /// real transaction. + /// + /// begin_write() initiates a new write transaction. A write transaction + /// allows the application to both read and modify the underlying Realm + /// file. At most one write transaction can be in progress at any given time + /// for a particular underlying Realm file. If another write transaction is + /// already in progress, begin_write() will block the caller until the other + /// write transaction terminates. No guarantees are made about the order in + /// which multiple concurrent requests will be served. + /// + /// It is an error to call begin_read() or begin_write() on a SharedGroup + /// object with an active read or write transaction. + /// + /// If begin_read() or begin_write() throws, no transaction is initiated, + /// and the application may try to initiate a new read or write transaction + /// later. + /// + /// end_read() terminates the active read transaction. If no read + /// transaction is active, end_read() does nothing. It is an error to call + /// this function on a SharedGroup object with an active write + /// transaction. end_read() does not throw. + /// + /// commit() commits all changes performed in the context of the active + /// write transaction, and thereby terminates that transaction. This + /// produces a new snapshot in the underlying Realm. commit() returns the + /// version associated with the new snapshot. It is an error to call + /// commit() when there is no active write transaction. If commit() throws, + /// no changes will have been committed, and the transaction will still be + /// active, but in a bad state. In that case, the application must either + /// call rollback() to terminate the bad transaction (in which case a new + /// transaction can be initiated), call close() which also terminates the + /// bad transaction, or destroy the SharedGroup object entirely. When the + /// transaction is in a bad state, the application is not allowed to call + /// any method on the Group accessor or on any of its subordinate accessors + /// (Table, Row, Descriptor). Note that the transaction is also left in a + /// bad state when a modifying operation on any subordinate accessor throws. + /// + /// rollback() terminates the active write transaction and discards any + /// changes performed in the context of it. If no write transaction is + /// active, rollback() does nothing. It is an error to call this function in + /// a SharedGroup object with an active read transaction. rollback() does + /// not throw. + /// + /// the Group accessor and all subordinate accessors (Table, Row, + /// Descriptor) that are obtained in the context of a particular read or + /// write transaction will become detached upon termination of that + /// transaction, which means that they can no longer be used to access the + /// underlying objects. + /// + /// Subordinate accessors that were detached at the end of the previous + /// read or write transaction will not be automatically reattached when a + /// new transaction is initiated. The application must reobtain new + /// accessors during a new transaction to regain access to the underlying + /// objects. + /// + /// \param version If specified, this must be the version associated with a + /// *bound* snapshot. A snapshot is said to be bound (or tethered) if there + /// is at least one active read or write transaction bound to it. A read + /// transaction is bound to the snapshot that it provides access to. A write + /// transaction is bound to the latest snapshot available at the time of + /// initiation of the write transaction. If the specified version is not + /// associated with a bound snapshot, this function throws BadVersion. + /// + /// \throw BadVersion Thrown by begin_read() if the specified version does + /// not correspond to a bound (or tethered) snapshot. + + const Group& begin_read(VersionID version = VersionID()); + void end_read() noexcept; + Group& begin_write(); + version_type commit(); + void rollback() noexcept; + + //@} + + enum TransactStage { + transact_Ready, + transact_Reading, + transact_Writing, + }; + + /// Get the current transaction type + TransactStage get_transact_stage() const noexcept; + + /// Get a version id which may be used to request a different SharedGroup + /// to start transaction at a specific version. + VersionID get_version_of_current_transaction(); + + /// Report the number of distinct versions currently stored in the database. + /// Note: the database only cleans up versions as part of commit, so ending + /// a read transaction will not immediately release any versions. + uint_fast64_t get_number_of_versions(); + + /// Compact the database file. + /// - The method will throw if called inside a transaction. + /// - The method will throw if called in unattached state. + /// - The method will return false if other SharedGroups are accessing the + /// database in which case compaction is not done. This is not + /// necessarily an error. + /// It will return true following successful compaction. + /// While compaction is in progress, attempts by other + /// threads or processes to open the database will wait. + /// Be warned that resource requirements for compaction is proportional to + /// the amount of live data in the database. + /// Compaction works by writing the database contents to a temporary + /// database file and then replacing the database with the temporary one. + /// The name of the temporary file is formed by appending + /// ".tmp_compaction_space" to the name of the database + /// + /// FIXME: This function is not yet implemented in an exception-safe manner, + /// therefore, if it throws, the application should not attempt to + /// continue. If may not even be safe to destroy the SharedGroup object. + /// + /// WARNING / FIXME: compact() should NOT be exposed publicly on Windows + /// because it's not crash safe! It may corrupt your database if something fails + bool compact(); + +#ifdef REALM_DEBUG + void test_ringbuf(); +#endif + + /// To handover a table view, query, linkview or row accessor of type T, you + /// must wrap it into a Handover for the transfer. Wrapping and + /// unwrapping of a handover object is done by the methods + /// 'export_for_handover()' and 'import_from_handover()' declared below. + /// 'export_for_handover()' returns a Handover object, and + /// 'import_for_handover()' consumes that object, producing a new accessor + /// which is ready for use in the context of the importing SharedGroup. + /// + /// The Handover always creates a new accessor object at the importing side. + /// For TableViews, there are 3 forms of handover. + /// + /// - with payload move: the payload is handed over and ends up as a payload + /// held by the accessor at the importing side. The accessor on the + /// exporting side will rerun its query and generate a new payload, if + /// TableView::sync_if_needed() is called. If the original payload was in + /// sync at the exporting side, it will also be in sync at the importing + /// side. This is indicated to handover_export() by the argument + /// MutableSourcePayload::Move + /// + /// - with payload copy: a copy of the payload is handed over, so both the + /// accessors on the exporting side *and* the accessors created at the + /// importing side has their own payload. This is indicated to + /// handover_export() by the argument ConstSourcePayload::Copy + /// + /// - without payload: the payload stays with the accessor on the exporting + /// side. On the importing side, the new accessor is created without + /// payload. A call to TableView::sync_if_needed() will trigger generation + /// of a new payload. This form of handover is indicated to + /// handover_export() by the argument ConstSourcePayload::Stay. + /// + /// For all other (non-TableView) accessors, handover is done with payload + /// copy, since the payload is trivial. + /// + /// Handover *without* payload is useful when you want to ship a tableview + /// with its query for execution in a background thread. Handover with + /// *payload move* is useful when you want to transfer the result back. + /// + /// Handover *without* payload or with payload copy is guaranteed *not* to + /// change the accessors on the exporting side. + /// + /// Handover is *not* thread safe and should be carried out + /// by the thread that "owns" the involved accessors. + /// + /// Handover is transitive: + /// If the object being handed over depends on other views + /// (table- or link- ), those objects will be handed over as well. The mode + /// of handover (payload copy, payload move, without payload) is applied + /// recursively. Note: If you are handing over a tableview dependent upon + /// another tableview and using MutableSourcePayload::Move, + /// you are on thin ice! + /// + /// On the importing side, the top-level accessor being created during + /// import takes ownership of all other accessors (if any) being created as + /// part of the import. + + /// Type used to support handover of accessors between shared groups. + template + struct Handover; + + /// thread-safe/const export (mode is Stay or Copy) + /// during export, the following operations on the shared group is locked: + /// - advance_read(), promote_to_write(), commit_and_continue_as_read(), + /// rollback_and_continue_as_read(), close() + template + std::unique_ptr> export_for_handover(const T& accessor, ConstSourcePayload mode); + + // specialization for handover of Rows + template + std::unique_ptr>> export_for_handover(const BasicRow& accessor); + + // destructive export (mode is Move) + template + std::unique_ptr> export_for_handover(T& accessor, MutableSourcePayload mode); + + /// Import an accessor wrapped in a handover object. The import will fail + /// if the importing SharedGroup is viewing a version of the database that + /// is different from the exporting SharedGroup. The call to + /// import_from_handover is not thread-safe. + template + std::unique_ptr import_from_handover(std::unique_ptr> handover); + + // We need two cases for handling of LinkViews, because they are ref counted. + std::unique_ptr> export_linkview_for_handover(const LinkViewRef& accessor); + LinkViewRef import_linkview_from_handover(std::unique_ptr> handover); + + // likewise for Tables. + std::unique_ptr> export_table_for_handover(const TableRef& accessor); + TableRef import_table_from_handover(std::unique_ptr> handover); + + /// When doing handover to background tasks that may be run later, we + /// may want to momentarily pin the current version until the other thread + /// has retrieved it. + /// + /// Pinning can be done in both read- and write-transactions, but with different + /// semantics. When pinning during a read-transaction, the version pinned is the + /// one accessible during the read-transaction. When pinning during a write-transaction, + /// the version pinned will be the last version that was succesfully committed to the + /// realm file at the point in time, when the write-transaction was started. + /// + /// The release is not thread-safe, so it has to be done on the SharedGroup + /// associated with the thread calling unpin_version(), and the SharedGroup + /// must be attached to the realm file at the point of unpinning. + + // Pin version for handover (not thread safe) + VersionID pin_version(); + + // Release pinned version (not thread safe) + void unpin_version(VersionID version); + +private: + struct SharedInfo; + struct ReadCount; + struct ReadLockInfo { + uint_fast64_t m_version = std::numeric_limits::max(); + uint_fast32_t m_reader_idx = 0; + ref_type m_top_ref = 0; + size_t m_file_size = 0; + }; + class ReadLockUnlockGuard; + + // Member variables + Group m_group; + ReadLockInfo m_read_lock; + uint_fast32_t m_local_max_entry; + util::File m_file; + util::File::Map m_file_map; // Never remapped + util::File::Map m_reader_map; + bool m_wait_for_change_enabled; + std::string m_lockfile_path; + std::string m_lockfile_prefix; + std::string m_db_path; + std::string m_coordination_dir; + const char* m_key; + TransactStage m_transact_stage; + util::InterprocessMutex m_writemutex; +#ifdef REALM_ASYNC_DAEMON + util::InterprocessMutex m_balancemutex; +#endif + util::InterprocessMutex m_controlmutex; +#ifndef _WIN32 +#ifdef REALM_ASYNC_DAEMON + util::InterprocessCondVar m_room_to_write; + util::InterprocessCondVar m_work_to_do; + util::InterprocessCondVar m_daemon_becomes_ready; +#endif + util::InterprocessCondVar m_new_commit_available; +#endif + std::function m_upgrade_callback; + + void do_open(const std::string& file, bool no_create, bool is_backend, const SharedGroupOptions options); + + // Ring buffer management + bool ringbuf_is_empty() const noexcept; + size_t ringbuf_size() const noexcept; + size_t ringbuf_capacity() const noexcept; + bool ringbuf_is_first(size_t ndx) const noexcept; + void ringbuf_remove_first() noexcept; + size_t ringbuf_find(uint64_t version) const noexcept; + ReadCount& ringbuf_get(size_t ndx) noexcept; + ReadCount& ringbuf_get_first() noexcept; + ReadCount& ringbuf_get_last() noexcept; + void ringbuf_put(const ReadCount& v); + void ringbuf_expand(); + + /// Grab a read lock on the snapshot associated with the specified + /// version. If `version_id == VersionID()`, a read lock will be grabbed on + /// the latest available snapshot. Fails if the snapshot is no longer + /// available. + /// + /// As a side effect update memory mapping to ensure that the ringbuffer + /// entries referenced in the readlock info is accessible. + /// + /// FIXME: It needs to be made more clear exactly under which conditions + /// this function fails. Also, why is it useful to promise anything about + /// detection of bad versions? Can we really promise enough to make such a + /// promise useful to the caller? + void grab_read_lock(ReadLockInfo&, VersionID); + + // Release a specific read lock. The read lock MUST have been obtained by a + // call to grab_read_lock(). + void release_read_lock(ReadLockInfo&) noexcept; + + void do_begin_read(VersionID, bool writable); + void do_end_read() noexcept; + void do_begin_write(); + version_type do_commit(); + void do_end_write() noexcept; + + /// Returns the version of the latest snapshot. + version_type get_version_of_latest_snapshot(); + + /// Returns the version of the snapshot bound in the current read or write + /// transaction. It is an error to call this function when no transaction is + /// in progress. + version_type get_version_of_bound_snapshot() const noexcept; + + // make sure the given index is within the currently mapped area. + // if not, expand the mapped area. Returns true if the area is expanded. + bool grow_reader_mapping(uint_fast32_t index); + + // Must be called only by someone that has a lock on the write + // mutex. + void low_level_commit(uint_fast64_t new_version); + + void do_async_commits(); + + void upgrade_file_format(bool allow_file_format_upgrade, int target_file_format_version); + + //@{ + /// See LangBindHelper. + template + void advance_read(O* observer, VersionID); + template + void promote_to_write(O* observer); + version_type commit_and_continue_as_read(); + template + void rollback_and_continue_as_read(O* observer); + //@} + + /// Returns true if, and only if _impl::History::update_early_from_top_ref() + /// was called during the execution of this function. + template + bool do_advance_read(O* observer, VersionID, _impl::History&); + + /// If there is an associated \ref Replication object, then this function + /// returns `repl->get_history()` where `repl` is that Replication object, + /// otherwise this function returns null. + _impl::History* get_history(); + + int get_file_format_version() const noexcept; + + friend class _impl::SharedGroupFriend; +}; + + +class ReadTransaction { +public: + ReadTransaction(SharedGroup& sg) + : m_shared_group(sg) + { + m_shared_group.begin_read(); // Throws + } + + ~ReadTransaction() noexcept + { + m_shared_group.end_read(); + } + + bool has_table(StringData name) const noexcept + { + return get_group().has_table(name); + } + + ConstTableRef get_table(size_t table_ndx) const + { + return get_group().get_table(table_ndx); // Throws + } + + ConstTableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + template + BasicTableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + const Group& get_group() const noexcept; + + /// Get the version of the snapshot to which this read transaction is bound. + SharedGroup::version_type get_version() const noexcept; + +private: + SharedGroup& m_shared_group; +}; + + +class WriteTransaction { +public: + WriteTransaction(SharedGroup& sg) + : m_shared_group(&sg) + { + m_shared_group->begin_write(); // Throws + } + + ~WriteTransaction() noexcept + { + if (m_shared_group) + m_shared_group->rollback(); + } + + bool has_table(StringData name) const noexcept + { + return get_group().has_table(name); + } + + TableRef get_table(size_t table_ndx) const + { + return get_group().get_table(table_ndx); // Throws + } + + TableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + TableRef add_table(StringData name, bool require_unique_name = true) const + { + return get_group().add_table(name, require_unique_name); // Throws + } + + TableRef get_or_add_table(StringData name, bool* was_added = nullptr) const + { + return get_group().get_or_add_table(name, was_added); // Throws + } + + template + BasicTableRef get_table(StringData name) const + { + return get_group().get_table(name); // Throws + } + + template + BasicTableRef add_table(StringData name, bool require_unique_name = true) const + { + return get_group().add_table(name, require_unique_name); // Throws + } + + template + BasicTableRef get_or_add_table(StringData name, bool* was_added = nullptr) const + { + return get_group().get_or_add_table(name, was_added); // Throws + } + + Group& get_group() const noexcept; + + /// Get the version of the snapshot on which this write transaction is + /// based. + SharedGroup::version_type get_version() const noexcept; + + SharedGroup::version_type commit() + { + REALM_ASSERT(m_shared_group); + SharedGroup::version_type new_version = m_shared_group->commit(); + m_shared_group = nullptr; + return new_version; + } + + void rollback() noexcept + { + REALM_ASSERT(m_shared_group); + m_shared_group->rollback(); + m_shared_group = nullptr; + } + +private: + SharedGroup* m_shared_group; +}; + + +// Implementation: + +struct SharedGroup::BadVersion : std::exception { +}; + +inline SharedGroup::SharedGroup(const std::string& file, bool no_create, const SharedGroupOptions options) + : m_group(Group::shared_tag()) + , m_upgrade_callback(std::move(options.upgrade_callback)) +{ + open(file, no_create, options); // Throws +} + +inline SharedGroup::SharedGroup(unattached_tag) noexcept + : m_group(Group::shared_tag()) +{ +} + +inline SharedGroup::SharedGroup(Replication& repl, const SharedGroupOptions options) + : m_group(Group::shared_tag()) + , m_upgrade_callback(std::move(options.upgrade_callback)) +{ + open(repl, options); // Throws +} + +inline void SharedGroup::open(const std::string& path, bool no_create_file, const SharedGroupOptions options) +{ + // Exception safety: Since open() is called from constructors, if it throws, + // it must leave the file closed. + + bool is_backend = false; + do_open(path, no_create_file, is_backend, options); // Throws +} + +inline void SharedGroup::open(Replication& repl, const SharedGroupOptions options) +{ + // Exception safety: Since open() is called from constructors, if it throws, + // it must leave the file closed. + + REALM_ASSERT(!is_attached()); + + repl.initialize(*this); // Throws + + typedef _impl::GroupFriend gf; + gf::set_replication(m_group, &repl); + + std::string file = repl.get_database_path(); + bool no_create = false; + bool is_backend = false; + do_open(file, no_create, is_backend, options); // Throws +} + +inline bool SharedGroup::is_attached() const noexcept +{ + return m_file_map.is_attached(); +} + +inline SharedGroup::TransactStage SharedGroup::get_transact_stage() const noexcept +{ + return m_transact_stage; +} + +inline SharedGroup::version_type SharedGroup::get_version_of_bound_snapshot() const noexcept +{ + return m_read_lock.m_version; +} + +class SharedGroup::ReadLockUnlockGuard { +public: + ReadLockUnlockGuard(SharedGroup& shared_group, ReadLockInfo& read_lock) noexcept + : m_shared_group(shared_group) + , m_read_lock(&read_lock) + { + } + ~ReadLockUnlockGuard() noexcept + { + if (m_read_lock) + m_shared_group.release_read_lock(*m_read_lock); + } + void release() noexcept + { + m_read_lock = 0; + } + +private: + SharedGroup& m_shared_group; + ReadLockInfo* m_read_lock; +}; + + +template +struct SharedGroup::Handover { + std::unique_ptr patch; + std::unique_ptr clone; + VersionID version; +}; + +template +std::unique_ptr> SharedGroup::export_for_handover(const T& accessor, ConstSourcePayload mode) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr> result(new Handover()); + // Implementation note: + // often, the return value from clone will be T*, BUT it may be ptr to some + // base of T instead, so we must cast it to T*. This is always safe, because + // no matter the type, clone() will clone the actual accessor instance, and + // hence return an instance of the same type. + result->clone.reset(dynamic_cast(accessor.clone_for_handover(result->patch, mode).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr>> SharedGroup::export_for_handover(const BasicRow& accessor) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr>> result(new Handover>()); + // See implementation note above. + result->clone.reset(dynamic_cast*>(accessor.clone_for_handover(result->patch).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr> SharedGroup::export_for_handover(T& accessor, MutableSourcePayload mode) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + std::unique_ptr> result(new Handover()); + // see implementation note above. + result->clone.reset(dynamic_cast(accessor.clone_for_handover(result->patch, mode).release())); + result->version = get_version_of_current_transaction(); + return move(result); +} + + +template +std::unique_ptr SharedGroup::import_from_handover(std::unique_ptr> handover) +{ + if (handover->version != get_version_of_current_transaction()) { + throw BadVersion(); + } + std::unique_ptr result = move(handover->clone); + result->apply_and_consume_patch(handover->patch, m_group); + return result; +} + +template +inline void SharedGroup::advance_read(O* observer, VersionID version_id) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + + // It is an error if the new version precedes the currently bound one. + if (version_id.version < m_read_lock.m_version) + throw LogicError(LogicError::bad_version); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + do_advance_read(observer, version_id, *hist); // Throws +} + +template +inline void SharedGroup::promote_to_write(O* observer) +{ + if (m_transact_stage != transact_Reading) + throw LogicError(LogicError::wrong_transact_state); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + do_begin_write(); // Throws + try { + VersionID version = VersionID(); // Latest + bool history_updated = do_advance_read(observer, version, *hist); // Throws + + Replication* repl = m_group.get_replication(); + REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` + version_type current_version = m_read_lock.m_version; + repl->initiate_transact(current_version, history_updated); // Throws + + // If the group has no top array (top_ref == 0), create a new node + // structure for an empty group now, to be ready for modifications. See + // also Group::attach_shared(). + using gf = _impl::GroupFriend; + gf::create_empty_group_when_missing(m_group); // Throws + } + catch (...) { + do_end_write(); + throw; + } + + m_transact_stage = transact_Writing; +} + +template +inline void SharedGroup::rollback_and_continue_as_read(O* observer) +{ + if (m_transact_stage != transact_Writing) + throw LogicError(LogicError::wrong_transact_state); + + _impl::History* hist = get_history(); // Throws + if (!hist) + throw LogicError(LogicError::no_history); + + // Mark all managed space (beyond the attached file) as free. + using gf = _impl::GroupFriend; + gf::reset_free_space_tracking(m_group); // Throws + + BinaryData uncommitted_changes = hist->get_uncommitted_changes(); + + // FIXME: We are currently creating two transaction log parsers, one here, + // and one in advance_transact(). That is wasteful as the parser creation is + // expensive. + _impl::SimpleInputStream in(uncommitted_changes.data(), uncommitted_changes.size()); + _impl::TransactLogParser parser; // Throws + _impl::TransactReverser reverser; + parser.parse(in, reverser); // Throws + + if (observer && uncommitted_changes.size()) { + _impl::ReversedNoCopyInputStream reversed_in(reverser); + parser.parse(reversed_in, *observer); // Throws + observer->parse_complete(); // Throws + } + + ref_type top_ref = m_read_lock.m_top_ref; + size_t file_size = m_read_lock.m_file_size; + _impl::ReversedNoCopyInputStream reversed_in(reverser); + gf::advance_transact(m_group, top_ref, file_size, reversed_in); // Throws + + do_end_write(); + + Replication* repl = gf::get_replication(m_group); + REALM_ASSERT(repl); // Presence of `repl` follows from the presence of `hist` + repl->abort_transact(); + + m_transact_stage = transact_Reading; +} + +template +inline bool SharedGroup::do_advance_read(O* observer, VersionID version_id, _impl::History& hist) +{ + ReadLockInfo new_read_lock; + grab_read_lock(new_read_lock, version_id); // Throws + REALM_ASSERT(new_read_lock.m_version >= m_read_lock.m_version); + if (new_read_lock.m_version == m_read_lock.m_version) { + release_read_lock(new_read_lock); + // _impl::History::update_early_from_top_ref() was not called + return false; + } + + ReadLockUnlockGuard g(*this, new_read_lock); + { + version_type new_version = new_read_lock.m_version; + size_t new_file_size = new_read_lock.m_file_size; + ref_type new_top_ref = new_read_lock.m_top_ref; + hist.update_early_from_top_ref(new_version, new_file_size, new_top_ref); // Throws + } + + if (observer) { + // This has to happen in the context of the originally bound snapshot + // and while the read transaction is still in a fully functional state. + _impl::TransactLogParser parser; + version_type old_version = m_read_lock.m_version; + version_type new_version = new_read_lock.m_version; + _impl::ChangesetInputStream in(hist, old_version, new_version); + parser.parse(in, *observer); // Throws + observer->parse_complete(); // Throws + } + + // The old read lock must be retained for as long as the change history is + // accessed (until Group::advance_transact() returns). This ensures that the + // oldest needed changeset remains in the history, even when the history is + // implemented as a separate unversioned entity outside the Realm (i.e., the + // old implementation and ShortCircuitHistory in + // test_lang_Bind_helper.cpp). On the other hand, if it had been the case, + // that the history was always implemented as a versioned entity, that was + // part of the Realm state, then it would not have been necessary to retain + // the old read lock beyond this point. + + { + version_type old_version = m_read_lock.m_version; + version_type new_version = new_read_lock.m_version; + ref_type new_top_ref = new_read_lock.m_top_ref; + size_t new_file_size = new_read_lock.m_file_size; + _impl::ChangesetInputStream in(hist, old_version, new_version); + m_group.advance_transact(new_top_ref, new_file_size, in); // Throws + } + + g.release(); + release_read_lock(m_read_lock); + m_read_lock = new_read_lock; + + return true; // _impl::History::update_early_from_top_ref() was called +} + +inline _impl::History* SharedGroup::get_history() +{ + using gf = _impl::GroupFriend; + if (Replication* repl = gf::get_replication(m_group)) + return repl->get_history(); + return 0; +} + +inline int SharedGroup::get_file_format_version() const noexcept +{ + using gf = _impl::GroupFriend; + return gf::get_file_format_version(m_group); +} + + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the SharedGroup class. +class _impl::SharedGroupFriend { +public: + static Group& get_group(SharedGroup& sg) noexcept + { + return sg.m_group; + } + + template + static void advance_read(SharedGroup& sg, O* obs, SharedGroup::VersionID ver) + { + sg.advance_read(obs, ver); // Throws + } + + template + static void promote_to_write(SharedGroup& sg, O* obs) + { + sg.promote_to_write(obs); // Throws + } + + static SharedGroup::version_type commit_and_continue_as_read(SharedGroup& sg) + { + return sg.commit_and_continue_as_read(); // Throws + } + + template + static void rollback_and_continue_as_read(SharedGroup& sg, O* obs) + { + sg.rollback_and_continue_as_read(obs); // Throws + } + + static void async_daemon_open(SharedGroup& sg, const std::string& file) + { + bool no_create = true; + bool is_backend = true; + SharedGroupOptions options; + options.durability = SharedGroupOptions::Durability::Async; + options.encryption_key = nullptr; + options.allow_file_format_upgrade = false; + sg.do_open(file, no_create, is_backend, options); // Throws + } + + static int get_file_format_version(const SharedGroup& sg) noexcept + { + return sg.get_file_format_version(); + } + + static SharedGroup::version_type get_version_of_latest_snapshot(SharedGroup& sg) + { + return sg.get_version_of_latest_snapshot(); + } + + static SharedGroup::version_type get_version_of_bound_snapshot(const SharedGroup& sg) noexcept + { + return sg.get_version_of_bound_snapshot(); + } +}; + +inline const Group& ReadTransaction::get_group() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_group(m_shared_group); +} + +inline SharedGroup::version_type ReadTransaction::get_version() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_bound_snapshot(m_shared_group); +} + +inline Group& WriteTransaction::get_group() const noexcept +{ + REALM_ASSERT(m_shared_group); + using sgf = _impl::SharedGroupFriend; + return sgf::get_group(*m_shared_group); +} + +inline SharedGroup::version_type WriteTransaction::get_version() const noexcept +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_bound_snapshot(*m_shared_group); +} + +} // namespace realm + +#endif // REALM_GROUP_SHARED_HPP diff --git a/Pods/Realm/include/core/realm/group_shared_options.hpp b/Pods/Realm/include/core/realm/group_shared_options.hpp new file mode 100644 index 0000000..8430c44 --- /dev/null +++ b/Pods/Realm/include/core/realm/group_shared_options.hpp @@ -0,0 +1,96 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_GROUP_SHARED_OPTIONS_HPP +#define REALM_GROUP_SHARED_OPTIONS_HPP + +#include +#include + +namespace realm { + +struct SharedGroupOptions { + + /// The persistence level of the SharedGroup. + /// uint16_t is the type of SharedGroup::SharedInfo::durability + enum class Durability : uint16_t { + Full, + MemOnly, + Async ///< Not yet supported on windows. + }; + + explicit SharedGroupOptions(Durability level = Durability::Full, const char* key = nullptr, + bool allow_upgrade = true, + std::function file_upgrade_callback = std::function(), + std::string temp_directory = sys_tmp_dir) + : durability(level) + , encryption_key(key) + , allow_file_format_upgrade(allow_upgrade) + , upgrade_callback(file_upgrade_callback) + , temp_dir(temp_directory) + { + } + + explicit SharedGroupOptions(const char* key) + : durability(Durability::Full) + , encryption_key(key) + , allow_file_format_upgrade(true) + , upgrade_callback(std::function()) + , temp_dir(sys_tmp_dir) + { + } + + /// The persistence level of the Realm file. See Durability. + Durability durability; + + /// The key to encrypt and decrypt the Realm file with, or nullptr to + /// indicate that encryption should not be used. + const char* encryption_key; + + /// If \a allow_file_format_upgrade is set to `true`, this function will + /// automatically upgrade the file format used in the specified Realm file + /// if necessary (and if it is possible). In order to prevent this, set \a + /// allow_upgrade to `false`. + /// + /// If \a allow_upgrade is set to `false`, only two outcomes are possible: + /// + /// - the specified Realm file is already using the latest file format, and + /// can be used, or + /// + /// - the specified Realm file uses a deprecated file format, resulting a + /// the throwing of FileFormatUpgradeRequired. + bool allow_file_format_upgrade; + + /// Optionally allows a custom function to be called immediately after the + /// Realm file is upgraded. The two parameters in the function are the + /// previous version and the version just upgraded to, respectively. + /// If the callback function throws, the Realm file will safely abort the + /// upgrade (rollback the transaction) but the SharedGroup will not be opened. + std::function upgrade_callback; + + /// A path to a directory where Realm can write temporary files or pipes to. + /// This string should include a trailing slash '/'. + std::string temp_dir; + +private: + const static std::string sys_tmp_dir; +}; + +} // end namespace realm + +#endif // REALM_GROUP_SHARED_OPTIONS_HPP diff --git a/Pods/Realm/include/core/realm/group_writer.hpp b/Pods/Realm/include/core/realm/group_writer.hpp new file mode 100644 index 0000000..424f293 --- /dev/null +++ b/Pods/Realm/include/core/realm/group_writer.hpp @@ -0,0 +1,164 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_GROUP_WRITER_HPP +#define REALM_GROUP_WRITER_HPP + +#include // unint8_t etc +#include + +#include +#include +#include +#include + + +namespace realm { + +// Pre-declarations +class Group; +class SlabAlloc; + + +/// This class is not supposed to be reused for multiple write sessions. In +/// particular, do not reuse it in case any of the functions throw. +/// +/// FIXME: Move this class to namespace realm::_impl and to subdir src/realm/impl. +class GroupWriter : public _impl::ArrayWriterBase { +public: + // For groups in transactional mode (Group::m_is_shared), this constructor + // must be called while a write transaction is in progress. + // + // The constructor adds free-space tracking information to the specified + // group, if it is not already present (4th and 5th entry in + // Group::m_top). If the specified group is in transactional mode + // (Group::m_is_shared), the constructor also adds version tracking + // information to the group, if it is not already present (6th and 7th entry + // in Group::m_top). + GroupWriter(Group&); + ~GroupWriter(); + + void set_versions(uint64_t current, uint64_t read_lock) noexcept; + + /// Write all changed array nodes into free space. + /// + /// Returns the new top ref. When in full durability mode, call + /// commit() with the returned top ref. + ref_type write_group(); + + /// Flush changes to physical medium, then write the new top ref + /// to the file header, then flush again. Pass the top ref + /// returned by write_group(). + void commit(ref_type new_top_ref); + + size_t get_file_size() const noexcept; + + /// Write the specified chunk into free space. + void write(const char* data, size_t size); + + ref_type write_array(const char*, size_t, uint32_t) override; + +#ifdef REALM_DEBUG + void dump(); +#endif + +private: + class MapWindow; + Group& m_group; + SlabAlloc& m_alloc; + ArrayInteger m_free_positions; // 4th slot in Group::m_top + ArrayInteger m_free_lengths; // 5th slot in Group::m_top + ArrayInteger m_free_versions; // 6th slot in Group::m_top + uint64_t m_current_version; + uint64_t m_readlock_version; + + // Currently cached memory mappings. We keep as many as 16 1MB windows + // open for writing. The allocator will favor sequential allocation + // from a modest number of windows, depending upon fragmentation, so + // 16 windows should be more than enough. If more than 16 windows are + // needed, the least recently used is sync'ed and closed to make room + // for a new one. The windows are kept in MRU (most recently used) order. + const static int num_map_windows = 16; + std::vector m_map_windows; + + // Get a suitable memory mapping for later access: + // potentially adding it to the cache, potentially closing + // the least recently used and sync'ing it to disk + MapWindow* get_window(ref_type start_ref, size_t size); + + // Sync all cached memory mappings + void sync_all_mappings(); + + // Merge adjacent chunks + void merge_free_space(); + + /// Allocate a chunk of free space of the specified size. The + /// specified size must be 8-byte aligned. Extend the file if + /// required. The returned chunk is removed from the amount of + /// remaing free space. The returned chunk is guaranteed to be + /// within a single contiguous memory mapping. + /// + /// \return The position within the database file of the allocated + /// chunk. + size_t get_free_space(size_t size); + + /// Find a block of free space that is at least as big as the + /// specified size and which will allow an allocation that is mapped + /// inside a contiguous address range. The specified size does not + /// need to be 8-byte aligned. Extend the file if required. + /// The returned chunk is not removed from the amount of remaing + /// free space. + /// + /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` + /// is the index of a chunk whose size is at least the requestd + /// size, and `chunk_size` is the size of that chunk. + std::pair reserve_free_space(size_t size); + + /// Search only a range of the free list for a block as big as the + /// specified size. Return a pair with index and size of the found chunk. + /// \param found indicates whether a suitable block was found. + std::pair search_free_space_in_part_of_freelist(size_t size, size_t begin, size_t end, + bool& found); + + /// Extend the file to ensure that a chunk of free space of the + /// specified size is available. The specified size does not need + /// to be 8-byte aligned. This function guarantees that it will + /// add at most one entry to the free-lists. + /// + /// \return A pair (`chunk_ndx`, `chunk_size`) where `chunk_ndx` + /// is the index of a chunk whose size is at least the requestd + /// size, and `chunk_size` is the size of that chunk. + std::pair extend_free_space(size_t requested_size); + + void write_array_at(MapWindow* window, ref_type, const char* data, size_t size); + size_t split_freelist_chunk(size_t index, size_t start_pos, size_t alloc_pos, size_t chunk_size, bool is_shared); +}; + + +// Implementation: + +inline void GroupWriter::set_versions(uint64_t current, uint64_t read_lock) noexcept +{ + REALM_ASSERT(read_lock <= current); + m_current_version = current; + m_readlock_version = read_lock; +} + +} // namespace realm + +#endif // REALM_GROUP_WRITER_HPP diff --git a/Pods/Realm/include/core/realm/handover_defs.hpp b/Pods/Realm/include/core/realm/handover_defs.hpp new file mode 100644 index 0000000..6950bd5 --- /dev/null +++ b/Pods/Realm/include/core/realm/handover_defs.hpp @@ -0,0 +1,82 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_HANDOVER_DEFS +#define REALM_HANDOVER_DEFS + +#include +#include + +namespace realm { + +enum class ConstSourcePayload { Copy, Stay }; +enum class MutableSourcePayload { Move }; + +struct RowBaseHandoverPatch; +struct TableViewHandoverPatch; + +struct TableHandoverPatch { + size_t m_table_num; +}; + +struct LinkViewHandoverPatch { + std::unique_ptr m_table; + size_t m_col_num; + size_t m_row_ndx; +}; + +// Base class for handover patches for query nodes. Subclasses are declared in query_engine.hpp. +struct QueryNodeHandoverPatch { + virtual ~QueryNodeHandoverPatch() = default; +}; + +using QueryNodeHandoverPatches = std::vector>; + +struct QueryHandoverPatch { + std::unique_ptr m_table; + std::unique_ptr table_view_data; + std::unique_ptr link_view_data; + QueryNodeHandoverPatches m_node_data; +}; + +struct SortDescriptorHandoverPatch { + std::vector> columns; + std::vector ascending; +}; + +struct TableViewHandoverPatch { + std::unique_ptr m_table; + std::unique_ptr linked_row; + size_t linked_col; + bool was_in_sync; + QueryHandoverPatch query_patch; + std::unique_ptr linkview_patch; + std::unique_ptr sort_patch; + std::unique_ptr distinct_patch; +}; + + +struct RowBaseHandoverPatch { + std::unique_ptr m_table; + size_t row_ndx; +}; + + +} // end namespace Realm + +#endif diff --git a/Pods/Realm/include/core/realm/history.hpp b/Pods/Realm/include/core/realm/history.hpp new file mode 100644 index 0000000..9710d0b --- /dev/null +++ b/Pods/Realm/include/core/realm/history.hpp @@ -0,0 +1,35 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_HISTORY_HPP +#define REALM_HISTORY_HPP + +#include +#include + +#include + + +namespace realm { + +std::unique_ptr make_in_realm_history(const std::string& realm_path); + +} // namespace realm + + +#endif // REALM_HISTORY_HPP diff --git a/Pods/Realm/include/core/realm/impl/array_writer.hpp b/Pods/Realm/include/core/realm/impl/array_writer.hpp new file mode 100644 index 0000000..f039ad5 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/array_writer.hpp @@ -0,0 +1,44 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ARRAY_WRITER_HPP +#define REALM_ARRAY_WRITER_HPP + +#include + +namespace realm { +namespace _impl { + +class ArrayWriterBase { +public: + virtual ~ArrayWriterBase() + { + } + + /// Write the specified array data and its checksum into free + /// space. + /// + /// Returns the ref (position in the target stream) of the written copy of + /// the specified array data. + virtual ref_type write_array(const char* data, size_t size, uint32_t checksum) = 0; +}; + +} // namespace impl_ +} // namespace realm + +#endif // REALM_ARRAY_WRITER_HPP diff --git a/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp b/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp new file mode 100644 index 0000000..c9e43bb --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/continuous_transactions_history.hpp @@ -0,0 +1,210 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP +#define REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP + +#include +#include + +#include +#include + +namespace realm { + +class Group; + +namespace _impl { + +/// Read-only access to history of changesets as needed to enable continuous +/// transactions. +class History { +public: + using version_type = VersionID::version_type; + + /// May be called during a read transaction to gain early access to the + /// history as it appears in a new snapshot that succeeds the one bound in + /// the current read transaction. + /// + /// May also be called at other times as long as the caller owns a read lock + /// (SharedGroup::grab_read_lock()) on the Realm for the specified file size + /// and top ref, and the allocator is in a 'free space clean' state + /// (SlabAlloc::is_free_space_clean()). + /// + /// This function may cause a remapping of the Realm file + /// (SlabAlloc::remap()) if it needs to make the new snapshot fully visible + /// in memory. + /// + /// Note that this method of gaining early access to the history in a new + /// snaphot only gives read access. It does not allow for modifications of + /// the history or any other part of the new snapshot. For modifications to + /// be allowed, `Group::m_top` (the parent of the history) would first have + /// to be updated to reflect the new snapshot, but at that time we are no + /// longer in an 'early access' situation. + /// + /// This is not a problem from the point of view of this history interface, + /// as it only contains methods for reading from the history, but some + /// implementations will want to also provide for ways to modify the + /// history, but in those cases, modifications must occur only after the + /// Group accessor has been fully updated to reflect the new snapshot. + virtual void update_early_from_top_ref(version_type new_version, size_t new_file_size, ref_type new_top_ref) = 0; + + virtual void update_from_parent(version_type current_version) = 0; + + /// Get all changesets between the specified versions. References to those + /// changesets will be made availble in successive entries of `buffer`. The + /// number of retreived changesets is exactly `end_version - + /// begin_version`. If this number is greater than zero, the changeset made + /// avaialable in `buffer[0]` is the one that brought the database from + /// `begin_version` to `begin_version + 1`. + /// + /// It is an error to specify a version (for \a begin_version or \a + /// end_version) that is outside the range [V,W] where V is the version that + /// immediately precedes the first changeset available in the history as the + /// history appears in the **latest** available snapshot, and W is the + /// versionm that immediately succeeds the last changeset available in the + /// history as the history appears in the snapshot bound to the **current** + /// transaction. This restriction is necessary to allow for different kinds + /// of implementations of the history (separate standalone history or + /// history as part of versioned Realm state). + /// + /// The calee retains ownership of the memory referenced by those entries, + /// i.e., the memory referenced by `buffer[i].changeset` is **not** handed + /// over to the caller. + /// + /// This function may be called only during a transaction (prior to + /// initiation of commit operation), and only after a successfull invocation + /// of update_early_from_top_ref(). In that case, the caller may assume that + /// the memory references stay valid for the remainder of the transaction + /// (up until initiation of the commit operation). + virtual void get_changesets(version_type begin_version, version_type end_version, BinaryIterator* buffer) const + noexcept = 0; + + /// \brief Specify the version of the oldest bound snapshot. + /// + /// This function must be called by the associated SharedGroup object during + /// each successfully committed write transaction. It must be called before + /// the transaction is finalized (Replication::finalize_commit()) or aborted + /// (Replication::abort_transact()), but after the initiation of the commit + /// operation (Replication::prepare_commit()). This allows history + /// implementations to add new history entries before triming off old ones, + /// and this, in turn, guarantees that the history never becomes empty, + /// except in the initial empty Realm state. + /// + /// The caller must pass the version (\a version) of the oldest snapshot + /// that is currently (or was recently) bound via a transaction of the + /// current session. This gives the history implementation an opportunity to + /// trim off leading (early) history entries. + /// + /// Since this function must be called during a write transaction, there + /// will always be at least one snapshot that is currently bound via a + /// transaction. + /// + /// The caller must guarantee that the passed version (\a version) is less + /// than or equal to `begin_version` in all future invocations of + /// get_changesets(). + /// + /// The caller is allowed to pass a version that is less than the version + /// passed in a preceeding invocation. + /// + /// This function should be called as late as possible, to maximize the + /// trimming opportunity, but at a time where the write transaction is still + /// open for additional modifications. This is necessary because some types + /// of histories are stored inside the Realm file. + virtual void set_oldest_bound_version(version_type version) = 0; + + /// Get the list of uncommited changes accumulated so far in the current + /// write transaction. + /// + /// The callee retains ownership of the referenced memory. The ownership is + /// not handed over the the caller. + /// + /// This function may be called only during a write transaction (prior to + /// initiation of commit operation). In that case, the caller may assume that the + /// returned memory reference stays valid for the remainder of the transaction (up + /// until initiation of the commit operation). + virtual BinaryData get_uncommitted_changes() noexcept = 0; + + virtual void verify() const = 0; + + virtual ~History() noexcept + { + } +}; + + +/// This class is intended to eventually become a basis for implementing the +/// Replication API for the purpose of supporting continuous transactions. That +/// is, its purpose is to replace the current implementation in commit_log.cpp, +/// which places the history in separate files. +/// +/// By ensuring that the root node of the history is correctly configured with +/// Group::m_top as its parent, this class allows for modifications of the +/// history as long as those modifications happen after the remainder of the +/// Group accessor is updated to reflect the new snapshot (see +/// History::update_early_from_top_ref()). +class InRealmHistory : public History { +public: + void initialize(Group&); + + /// Must never be called more than once per transaction. Returns the version + /// produced by the added changeset. + version_type add_changeset(BinaryData); + + void update_early_from_top_ref(version_type, size_t, ref_type) override; + void update_from_parent(version_type) override; + void get_changesets(version_type, version_type, BinaryIterator*) const noexcept override; + void set_oldest_bound_version(version_type) override; + + void verify() const override; + +private: + Group* m_group = nullptr; + + /// Version on which the first changeset in the history is based, or if the + /// history is empty, the version associatede with currently bound + /// snapshot. In general, the version associatede with currently bound + /// snapshot is equal to `m_base_version + m_size`, but after + /// add_changeset() is called, it is equal to one minus that. + version_type m_base_version; + + /// Current number of entries in the history. A cache of + /// `m_changesets->size()`. + size_t m_size; + + /// A list of changesets, one for each entry in the history. If null, the + /// history is empty. + /// + /// FIXME: Ideally, the B+tree accessor below should have been just + /// Bptree, but Bptree seems to not allow that yet. + /// + /// FIXME: The memory-wise indirection is an unfortunate consequence of the + /// fact that it is impossible to construct a BinaryColumn without already + /// having a ref to a valid underlying node structure. This, in turn, is an + /// unfortunate consequence of the fact that a column accessor contains a + /// dynamically allocated root node accessor, and the type of the required + /// root node accessor depends on the size of the B+-tree. + std::unique_ptr m_changesets; + + void update_from_ref(ref_type, version_type); +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_CONTINUOUS_TRANSACTIONS_HISTORY_HPP diff --git a/Pods/Realm/include/core/realm/impl/destroy_guard.hpp b/Pods/Realm/include/core/realm/impl/destroy_guard.hpp new file mode 100644 index 0000000..e744141 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/destroy_guard.hpp @@ -0,0 +1,225 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_DESTROY_GUARD_HPP +#define REALM_IMPL_DESTROY_GUARD_HPP + +#include +#include + +namespace realm { +namespace _impl { + + +/// Calls `ptr->destroy()` if the guarded pointer (`ptr`) is not null +/// when the guard is destroyed. For arrays (`T` = `Array`) this means +/// that the array is destroyed in a shallow fashion. See +/// `DeepArrayDestroyGuard` for an alternative. +template +class DestroyGuard { +public: + DestroyGuard() noexcept; + + DestroyGuard(T*) noexcept; + + ~DestroyGuard() noexcept; + + void reset(T*) noexcept; + + T* get() const noexcept; + + T* release() noexcept; + +private: + T* m_ptr; +}; + +using ShallowArrayDestroyGuard = DestroyGuard; + + +/// Calls `ptr->destroy_deep()` if the guarded Array pointer (`ptr`) +/// is not null when the guard is destroyed. +class DeepArrayDestroyGuard { +public: + DeepArrayDestroyGuard() noexcept; + + DeepArrayDestroyGuard(Array*) noexcept; + + ~DeepArrayDestroyGuard() noexcept; + + void reset(Array*) noexcept; + + Array* get() const noexcept; + + Array* release() noexcept; + +private: + Array* m_ptr; +}; + + +/// Calls `Array::destroy_deep(ref, alloc)` if the guarded 'ref' +/// (`ref`) is not zero when the guard is destroyed. +class DeepArrayRefDestroyGuard { +public: + DeepArrayRefDestroyGuard(Allocator&) noexcept; + + DeepArrayRefDestroyGuard(ref_type, Allocator&) noexcept; + + ~DeepArrayRefDestroyGuard() noexcept; + + void reset(ref_type) noexcept; + + ref_type get() const noexcept; + + ref_type release() noexcept; + +private: + ref_type m_ref; + Allocator& m_alloc; +}; + + +// Implementation: + +// DestroyGuard + +template +inline DestroyGuard::DestroyGuard() noexcept + : m_ptr(nullptr) +{ +} + +template +inline DestroyGuard::DestroyGuard(T* ptr) noexcept + : m_ptr(ptr) +{ +} + +template +inline DestroyGuard::~DestroyGuard() noexcept +{ + if (m_ptr) + m_ptr->destroy(); +} + +template +inline void DestroyGuard::reset(T* ptr) noexcept +{ + if (m_ptr) + m_ptr->destroy(); + m_ptr = ptr; +} + +template +inline T* DestroyGuard::get() const noexcept +{ + return m_ptr; +} + +template +inline T* DestroyGuard::release() noexcept +{ + T* ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + + +// DeepArrayDestroyGuard + +inline DeepArrayDestroyGuard::DeepArrayDestroyGuard() noexcept + : m_ptr(nullptr) +{ +} + +inline DeepArrayDestroyGuard::DeepArrayDestroyGuard(Array* ptr) noexcept + : m_ptr(ptr) +{ +} + +inline DeepArrayDestroyGuard::~DeepArrayDestroyGuard() noexcept +{ + if (m_ptr) + m_ptr->destroy_deep(); +} + +inline void DeepArrayDestroyGuard::reset(Array* ptr) noexcept +{ + if (m_ptr) + m_ptr->destroy_deep(); + m_ptr = ptr; +} + +inline Array* DeepArrayDestroyGuard::get() const noexcept +{ + return m_ptr; +} + +inline Array* DeepArrayDestroyGuard::release() noexcept +{ + Array* ptr = m_ptr; + m_ptr = nullptr; + return ptr; +} + + +// DeepArrayRefDestroyGuard + +inline DeepArrayRefDestroyGuard::DeepArrayRefDestroyGuard(Allocator& alloc) noexcept + : m_ref(0) + , m_alloc(alloc) +{ +} + +inline DeepArrayRefDestroyGuard::DeepArrayRefDestroyGuard(ref_type ref, Allocator& alloc) noexcept + : m_ref(ref) + , m_alloc(alloc) +{ +} + +inline DeepArrayRefDestroyGuard::~DeepArrayRefDestroyGuard() noexcept +{ + if (m_ref) + Array::destroy_deep(m_ref, m_alloc); +} + +inline void DeepArrayRefDestroyGuard::reset(ref_type ref) noexcept +{ + if (m_ref) + Array::destroy_deep(m_ref, m_alloc); + m_ref = ref; +} + +inline ref_type DeepArrayRefDestroyGuard::get() const noexcept +{ + return m_ref; +} + +inline ref_type DeepArrayRefDestroyGuard::release() noexcept +{ + ref_type ref = m_ref; + m_ref = 0; + return ref; +} + + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_DESTROY_GUARD_HPP diff --git a/Pods/Realm/include/core/realm/impl/input_stream.hpp b/Pods/Realm/include/core/realm/impl/input_stream.hpp new file mode 100644 index 0000000..c973841 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/input_stream.hpp @@ -0,0 +1,256 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_INPUT_STREAM_HPP +#define REALM_IMPL_INPUT_STREAM_HPP + +#include + +#include +#include +#include + + +namespace realm { +namespace _impl { + + +class InputStream { +public: + /// Read bytes from this input stream and place them in the specified + /// buffer. The returned value is the actual number of bytes that were read, + /// and this is some number `n` such that `n <= min(size, m)` where `m` is + /// the number of bytes that could have been read from this stream before + /// reaching its end. Also, `n` cannot be zero unless `m` or `size` is + /// zero. The intention is that `size` should be non-zero, a the return + /// value used as the end-of-input indicator. + /// + /// Implementations are only allowed to block (put the calling thread to + /// sleep) up until the point in time where the first byte can be made + /// availble. + virtual size_t read(char* buffer, size_t size) = 0; + + virtual ~InputStream() noexcept + { + } +}; + + +class SimpleInputStream : public InputStream { +public: + SimpleInputStream(const char* data, size_t size) noexcept + : m_ptr(data) + , m_end(data + size) + { + } + size_t read(char* buffer, size_t size) override + { + size_t n = std::min(size, size_t(m_end - m_ptr)); + const char* begin = m_ptr; + m_ptr += n; + const char* end = m_ptr; + std::copy(begin, end, buffer); + return n; + } + +private: + const char* m_ptr; + const char* const m_end; +}; + + +class NoCopyInputStream { +public: + /// \return if any bytes was read. + /// A value of false indicates end-of-input. + /// If return value is true, \a begin and \a end are + /// updated to reflect the start and limit of a + /// contiguous memory chunk. + virtual bool next_block(const char*& begin, const char*& end) = 0; + + virtual ~NoCopyInputStream() noexcept + { + } +}; + + +class NoCopyInputStreamAdaptor : public NoCopyInputStream { +public: + NoCopyInputStreamAdaptor(InputStream& in, char* buffer, size_t buffer_size) noexcept + : m_in(in) + , m_buffer(buffer) + , m_buffer_size(buffer_size) + { + } + bool next_block(const char*& begin, const char*& end) override + { + size_t n = m_in.read(m_buffer, m_buffer_size); + begin = m_buffer; + end = m_buffer + n; + return n; + } + +private: + InputStream& m_in; + char* m_buffer; + size_t m_buffer_size; +}; + + +class SimpleNoCopyInputStream : public NoCopyInputStream { +public: + SimpleNoCopyInputStream(const char* data, size_t size) + : m_data(data) + , m_size(size) + { + } + + bool next_block(const char*& begin, const char*& end) override + { + if (m_size == 0) + return 0; + size_t size = m_size; + begin = m_data; + end = m_data + size; + m_size = 0; + return size; + } + +private: + const char* m_data; + size_t m_size; +}; + +class MultiLogNoCopyInputStream : public NoCopyInputStream { +public: + MultiLogNoCopyInputStream(const BinaryData* logs_begin, const BinaryData* logs_end) + : m_logs_begin(logs_begin) + , m_logs_end(logs_end) + { + if (m_logs_begin != m_logs_end) + m_curr_buf_remaining_size = m_logs_begin->size(); + } + + size_t read(char* buffer, size_t size) + { + if (m_logs_begin == m_logs_end) + return 0; + for (;;) { + if (m_curr_buf_remaining_size > 0) { + size_t offset = m_logs_begin->size() - m_curr_buf_remaining_size; + const char* data = m_logs_begin->data() + offset; + size_t size_2 = std::min(m_curr_buf_remaining_size, size); + m_curr_buf_remaining_size -= size_2; + // FIXME: Eliminate the need for copying by changing the API of + // Replication::InputStream such that blocks can be handed over + // without copying. This is a straight forward change, but the + // result is going to be more complicated and less conventional. + std::copy(data, data + size_2, buffer); + return size_2; + } + + ++m_logs_begin; + if (m_logs_begin == m_logs_end) + return 0; + m_curr_buf_remaining_size = m_logs_begin->size(); + } + } + + bool next_block(const char*& begin, const char*& end) override + { + while (m_logs_begin < m_logs_end) { + size_t result = m_logs_begin->size(); + const char* data = m_logs_begin->data(); + m_logs_begin++; + if (result == 0) + continue; // skip empty blocks + begin = data; + end = data + result; + return result; + } + return 0; + } + +private: + const BinaryData* m_logs_begin; + const BinaryData* m_logs_end; + size_t m_curr_buf_remaining_size; +}; + + +class ChangesetInputStream : public NoCopyInputStream { +public: + using version_type = History::version_type; + static constexpr unsigned NB_BUFFERS = 8; + + ChangesetInputStream(History& hist, version_type begin_version, version_type end_version) + : m_history(hist) + , m_begin_version(begin_version) + , m_end_version(end_version) + { + get_changeset(); + } + + bool next_block(const char*& begin, const char*& end) override + { + while (m_valid) { + BinaryData actual = m_changesets_begin->get_next(); + + if (actual.size() > 0) { + begin = actual.data(); + end = actual.data() + actual.size(); + return true; + } + + m_changesets_begin++; + + if (REALM_UNLIKELY(m_changesets_begin == m_changesets_end)) { + get_changeset(); + } + } + return false; // End of input + } + +private: + History& m_history; + version_type m_begin_version, m_end_version; + BinaryIterator m_changesets[NB_BUFFERS]; // Buffer + BinaryIterator* m_changesets_begin = nullptr; + BinaryIterator* m_changesets_end = nullptr; + bool m_valid; + + void get_changeset() + { + auto versions_to_get = m_end_version - m_begin_version; + m_valid = versions_to_get > 0; + if (m_valid) { + if (versions_to_get > NB_BUFFERS) + versions_to_get = NB_BUFFERS; + version_type end_version = m_begin_version + versions_to_get; + m_history.get_changesets(m_begin_version, end_version, m_changesets); + m_begin_version = end_version; + m_changesets_begin = m_changesets; + m_changesets_end = m_changesets_begin + versions_to_get; + } + } +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INPUT_STREAM_HPP diff --git a/Pods/Realm/include/core/realm/impl/instructions.hpp b/Pods/Realm/include/core/realm/impl/instructions.hpp new file mode 100644 index 0000000..9f54d81 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/instructions.hpp @@ -0,0 +1,380 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_INSTRUCTIONS_HPP +#define REALM_IMPL_INSTRUCTIONS_HPP + +#include // size_t +#include + +#include +#include +#include +#include +#include + +namespace realm { +namespace _impl { + +class TransactLogParser; +class TransactLogEncoder; +class InputStream; + +#define REALM_FOR_EACH_INSTRUCTION_TYPE(X) \ + X(SelectTable) \ + X(SelectDescriptor) \ + X(SelectLinkList) \ + X(InsertGroupLevelTable) \ + X(EraseGroupLevelTable) \ + X(RenameGroupLevelTable) \ + X(MoveGroupLevelTable) \ + X(InsertEmptyRows) \ + X(Remove) \ + X(MoveLastOver) \ + X(Swap) \ + X(MergeRows) \ + X(Set) \ + X(SetDefault) \ + X(SetUnique) \ + X(AddInteger) \ + X(InsertSubstring) \ + X(EraseSubstring) \ + X(ClearTable) \ + X(OptimizeTable) \ + X(InsertColumn) \ + X(EraseColumn) \ + X(RenameColumn) \ + X(MoveColumn) \ + X(AddSearchIndex) \ + X(RemoveSearchIndex) \ + X(SetLinkType) \ + X(LinkListSet) \ + X(LinkListInsert) \ + X(LinkListMove) \ + X(LinkListSwap) \ + X(LinkListErase) \ + X(LinkListClear) \ + + +enum class InstrType { +#define REALM_DEFINE_INSTRUCTION_TYPE(X) X, +REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_TYPE) +#undef REALM_DEFINE_INSTRUCTION_TYPE +}; + +struct StringBufferRange { + size_t offset, size; +}; + + +// Note: All specializations must be "POD", so that they can be +// part of a union. +template struct Instr; + +template <> struct Instr { + size_t group_level_ndx; + size_t num_pairs; + size_t pairs[2]; // FIXME: max 1 level of subtables +}; + +template <> struct Instr { + size_t num_pairs; + size_t pairs[2]; // FIXME: max 1 level of subtables +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + size_t link_target_group_level_ndx; +}; + +template <> struct Instr { + Instr() {} + size_t table_ndx; + size_t num_tables; + StringBufferRange name; +}; + +template <> struct Instr { + size_t table_ndx; + size_t num_tables; +}; + +template <> struct Instr { + size_t table_ndx; + StringBufferRange new_name; +}; + +template <> struct Instr { + size_t table_ndx_1; + size_t table_ndx_2; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_insert; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_erase; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx; + size_t num_rows_to_erase; + size_t prior_num_rows; +}; + +template <> struct Instr { + size_t row_ndx_1; + size_t row_ndx_2; +}; + +template <> struct Instr { + size_t row_ndx; + size_t new_row_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + + struct Payload { + DataType type; + + struct LinkPayload { + size_t target_row; // npos means null + size_t target_group_level_ndx; + bool implicit_nullify; + }; + + union PayloadData { + bool boolean; + int64_t integer; + float fnum; + double dnum; + StringBufferRange str; + Timestamp timestamp; + LinkPayload link; + + PayloadData() {} + PayloadData(const PayloadData&) = default; + PayloadData& operator=(const PayloadData&) = default; + }; + PayloadData data; + + bool is_null() const; + }; + + Payload payload; +}; + +template <> struct Instr { + size_t col_ndx; + size_t row_ndx; + int64_t value; +}; + +template <> struct Instr : Instr { +}; + +template <> struct Instr : Instr { + size_t prior_num_rows; +}; + +template<> struct Instr { + Instr() {} + size_t col_ndx; + size_t row_ndx; + size_t pos; + StringBufferRange value; +}; + +template<> struct Instr { + Instr() {} + size_t col_ndx; + size_t row_ndx; + size_t pos; + size_t size; +}; + +template <> struct Instr { +}; + +template <> struct Instr { +}; + +template <> struct Instr { + size_t link_ndx; + size_t value; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx; + size_t value; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx_1; + size_t link_ndx_2; +}; + +template <> struct Instr { + size_t link_ndx; + bool implicit_nullify; + size_t prior_size; +}; + +template <> struct Instr { + size_t link_ndx_1; + size_t link_ndx_2; +}; + +template <> struct Instr { + size_t num_links; +}; + +template <> struct Instr { + size_t col_ndx; + DataType type; + StringBufferRange name; + size_t link_target_table_ndx; + size_t backlink_col_ndx; + bool nullable; +}; + +template <> struct Instr { + size_t col_ndx; + size_t link_target_table_ndx; + size_t backlink_col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + StringBufferRange new_name; +}; + +template <> struct Instr { + size_t col_ndx_1; + size_t col_ndx_2; +}; + +template <> struct Instr { + size_t col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; +}; + +template <> struct Instr { + size_t col_ndx; + LinkType type; +}; + +struct AnyInstruction { + using Type = InstrType; + + AnyInstruction() {} + template + AnyInstruction(Instr instr): type(t) + { + get_as() = std::move(instr); + } + + InstrType type; + union InstrUnion { +#define REALM_DEFINE_INSTRUCTION_MEMBER(X) Instr m_ ## X; + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_INSTRUCTION_MEMBER) +#undef REALM_DEFINE_INSTRUCTION_MEMBER + + InstrUnion() { +#if defined(REALM_DEBUG) + char* mem = reinterpret_cast(this); + std::fill(mem, mem + sizeof(*this), 0xef); +#endif // REALM_DEBUG + } + + InstrUnion(const InstrUnion&) = default; + InstrUnion& operator=(const InstrUnion&) = default; + }; + InstrUnion instr; + + template + void visit(F lambda); + template + void visit(F lambda) const; + + template Instr& get_as(); + + template + const Instr& get_as() const + { + return const_cast(this)->template get_as(); + } +}; + +#define REALM_DEFINE_GETTER(X) \ + template<> inline Instr& AnyInstruction::get_as() \ + { \ + return instr.m_ ## X; \ + } + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_DEFINE_GETTER) +#undef REALM_DEFINE_GETTER + +using InstructionList = std::vector; // FIXME: Consider using std::deque + +InstructionList parse_changeset_as_instructions(_impl::TransactLogParser&, _impl::InputStream&, + util::StringBuffer&); +void encode_instructions_as_changeset(const InstructionList&, const util::StringBuffer&, + _impl::TransactLogEncoder&); + + + + +/// Implementation: + +template +void AnyInstruction::visit(F lambda) +{ + switch (type) { +#define REALM_VISIT_INSTRUCTION(X) \ + case InstrType::X: return lambda(get_as()); + REALM_FOR_EACH_INSTRUCTION_TYPE(REALM_VISIT_INSTRUCTION) +#undef REALM_VISIT_INSTRUCTION + } + REALM_UNREACHABLE(); +} + +template +void AnyInstruction::visit(F lambda) const +{ + const_cast(this)->visit(lambda); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_INSTRUCTIONS_HPP diff --git a/Pods/Realm/include/core/realm/impl/output_stream.hpp b/Pods/Realm/include/core/realm/impl/output_stream.hpp new file mode 100644 index 0000000..1d022cd --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/output_stream.hpp @@ -0,0 +1,75 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_OUTPUT_STREAM_HPP +#define REALM_IMPL_OUTPUT_STREAM_HPP + +#include +#include + +#include + +#include + +#include + +namespace realm { +namespace _impl { + + +class OutputStream : public ArrayWriterBase { +public: + OutputStream(std::ostream&); + ~OutputStream() noexcept; + + ref_type get_ref_of_next_array() const noexcept; + + void write(const char* data, size_t size); + + ref_type write_array(const char* data, size_t size, uint32_t checksum) override; + +private: + ref_type m_next_ref; + std::ostream& m_out; + + void do_write(const char* data, size_t size); +}; + + +// Implementation: + +inline OutputStream::OutputStream(std::ostream& out) + : m_next_ref(0) + , m_out(out) +{ +} + +inline OutputStream::~OutputStream() noexcept +{ +} + +inline size_t OutputStream::get_ref_of_next_array() const noexcept +{ + return m_next_ref; +} + + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_OUTPUT_STREAM_HPP diff --git a/Pods/Realm/include/core/realm/impl/sequential_getter.hpp b/Pods/Realm/include/core/realm/impl/sequential_getter.hpp new file mode 100644 index 0000000..938ae24 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/sequential_getter.hpp @@ -0,0 +1,122 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_SEQUENTIAL_GETTER_HPP +#define REALM_IMPL_SEQUENTIAL_GETTER_HPP + +namespace realm { + +class SequentialGetterBase { +public: + virtual ~SequentialGetterBase() noexcept + { + } +}; + +template +class SequentialGetter : public SequentialGetterBase { +public: + using T = typename ColType::value_type; + using ArrayType = typename ColType::LeafType; + + SequentialGetter() + { + } + + SequentialGetter(const Table& table, size_t column_ndx) + { + if (column_ndx != not_found) + m_column = static_cast(&table.get_column_base(column_ndx)); + init(m_column); + } + + SequentialGetter(const ColType* column) + { + init(column); + } + + ~SequentialGetter() noexcept override + { + } + + void init(const ColType* column) + { + m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr.reset(new (&m_leaf_accessor_storage) ArrayType(column->get_alloc())); + m_column = column; + m_leaf_end = 0; + } + + REALM_FORCEINLINE bool cache_next(size_t index) + { + // Return whether or not leaf array has changed (could be useful to know for caller) + if (index >= m_leaf_end || index < m_leaf_start) { + typename ColType::LeafInfo leaf{&m_leaf_ptr, m_array_ptr.get()}; + size_t ndx_in_leaf; + m_column->get_leaf(index, ndx_in_leaf, leaf); + m_leaf_start = index - ndx_in_leaf; + const size_t leaf_size = m_leaf_ptr->size(); + m_leaf_end = m_leaf_start + leaf_size; + return true; + } + return false; + } + + + REALM_FORCEINLINE T get_next(size_t index) + { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4800) // Disable the Microsoft warning about bool performance issue. +#endif + + cache_next(index); + T av = m_leaf_ptr->get(index - m_leaf_start); + return av; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + + size_t local_end(size_t global_end) + { + if (global_end > m_leaf_end) + return m_leaf_end - m_leaf_start; + else + return global_end - m_leaf_start; + } + + size_t m_leaf_start; + size_t m_leaf_end; + const ColType* m_column = nullptr; + + const ArrayType* m_leaf_ptr = nullptr; + +private: + // Leaf cache for when the root of the column is not a leaf. + // This dog and pony show is because Array has a reference to Allocator internally, + // but we need to be able to transfer queries between contexts, so init() reinitializes + // the leaf cache in the context of the current column. + typename std::aligned_storage::type m_leaf_accessor_storage; + std::unique_ptr m_array_ptr; +}; + +} // namespace realm + +#endif // REALM_IMPL_SEQUENTIAL_GETTER_HPP diff --git a/Pods/Realm/include/core/realm/impl/simulated_failure.hpp b/Pods/Realm/include/core/realm/impl/simulated_failure.hpp new file mode 100644 index 0000000..4843d4a --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/simulated_failure.hpp @@ -0,0 +1,213 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_SIMULATED_FAILURE_HPP +#define REALM_IMPL_SIMULATED_FAILURE_HPP + +#include +#include + +#include + +#ifdef REALM_DEBUG +#define REALM_ENABLE_SIMULATED_FAILURE +#endif + +namespace realm { +namespace _impl { + +class SimulatedFailure : public std::system_error { +public: + enum FailureType { + generic, + slab_alloc__reset_free_space_tracking, + slab_alloc__remap, + shared_group__grow_reader_mapping, + sync_client__read_head, + sync_server__read_head, + _num_failure_types + }; + + class OneShotPrimeGuard; + class RandomPrimeGuard; + + /// Prime the specified failure type on the calling thread for triggering + /// once. + static void prime_one_shot(FailureType); + + /// Prime the specified failure type on the calling thread for triggering + /// randomly \a n out of \a m times. + static void prime_random(FailureType, int n, int m, uint_fast64_t seed = 0); + + /// Unprime the specified failure type on the calling thread. + static void unprime(FailureType) noexcept; + + /// Returns true according to the mode of priming of the specified failure + /// type on the calling thread, but only if REALM_ENABLE_SIMULATED_FAILURE + /// was defined during compilation. If REALM_ENABLE_SIMULATED_FAILURE was + /// not defined, this function always return false. + static bool check_trigger(FailureType) noexcept; + + /// The specified error code is set to `make_error_code(failure_type)` if + /// check_trigger() returns true. Otherwise it is set to + /// `std::error_code()`. Returns a copy of the updated error code. + static std::error_code trigger(FailureType failure_type, std::error_code&) noexcept; + + /// Throws SimulatedFailure if check_trigger() returns true. The exception + /// will be constructed with an error code equal to + /// `make_error_code(failure_type)`. + static void trigger(FailureType failure_type); + + /// Returns true when, and only when REALM_ENABLE_SIMULATED_FAILURE was + /// defined during compilation. + static constexpr bool is_enabled(); + + SimulatedFailure(std::error_code); + +private: +#ifdef REALM_ENABLE_SIMULATED_FAILURE + static void do_prime_one_shot(FailureType); + static void do_prime_random(FailureType, int n, int m, uint_fast64_t seed); + static void do_unprime(FailureType) noexcept; + static bool do_check_trigger(FailureType) noexcept; +#endif +}; + +std::error_code make_error_code(SimulatedFailure::FailureType) noexcept; + + +class SimulatedFailure::OneShotPrimeGuard { +public: + OneShotPrimeGuard(FailureType); + ~OneShotPrimeGuard() noexcept; + +private: + const FailureType m_type; +}; + + +class SimulatedFailure::RandomPrimeGuard { +public: + RandomPrimeGuard(FailureType, int n, int m, uint_fast64_t seed = 0); + ~RandomPrimeGuard() noexcept; + +private: + const FailureType m_type; +}; + + +// Implementation + +inline void SimulatedFailure::prime_one_shot(FailureType failure_type) +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_prime_one_shot(failure_type); +#else + static_cast(failure_type); +#endif +} + +inline void SimulatedFailure::prime_random(FailureType failure_type, int n, int m, uint_fast64_t seed) +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_prime_random(failure_type, n, m, seed); +#else + static_cast(failure_type); + static_cast(n); + static_cast(m); + static_cast(seed); +#endif +} + +inline void SimulatedFailure::unprime(FailureType failure_type) noexcept +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + do_unprime(failure_type); +#else + static_cast(failure_type); +#endif +} + +inline bool SimulatedFailure::check_trigger(FailureType failure_type) noexcept +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + return do_check_trigger(failure_type); +#else + static_cast(failure_type); + return false; +#endif +} + +inline std::error_code SimulatedFailure::trigger(FailureType failure_type, std::error_code& ec) noexcept +{ + if (check_trigger(failure_type)) { + ec = make_error_code(failure_type); + } + else { + ec = std::error_code(); + } + return ec; +} + +inline void SimulatedFailure::trigger(FailureType failure_type) +{ + if (check_trigger(failure_type)) + throw SimulatedFailure(make_error_code(failure_type)); +} + +inline constexpr bool SimulatedFailure::is_enabled() +{ +#ifdef REALM_ENABLE_SIMULATED_FAILURE + return true; +#else + return false; +#endif +} + +inline SimulatedFailure::SimulatedFailure(std::error_code ec) + : std::system_error(ec) +{ +} + +inline SimulatedFailure::OneShotPrimeGuard::OneShotPrimeGuard(FailureType failure_type) + : m_type(failure_type) +{ + prime_one_shot(m_type); +} + +inline SimulatedFailure::OneShotPrimeGuard::~OneShotPrimeGuard() noexcept +{ + unprime(m_type); +} + +inline SimulatedFailure::RandomPrimeGuard::RandomPrimeGuard(FailureType failure_type, int n, int m, + uint_fast64_t seed) + : m_type(failure_type) +{ + prime_random(m_type, n, m, seed); +} + +inline SimulatedFailure::RandomPrimeGuard::~RandomPrimeGuard() noexcept +{ + unprime(m_type); +} + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_SIMULATED_FAILURE_HPP diff --git a/Pods/Realm/include/core/realm/impl/table_path.hpp b/Pods/Realm/include/core/realm/impl/table_path.hpp new file mode 100644 index 0000000..c03b6cb --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/table_path.hpp @@ -0,0 +1,88 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_IMPL_TABLE_PATH_HPP +#define REALM_IMPL_TABLE_PATH_HPP + +#include +#include +#include + +namespace realm { +namespace _impl { + +class TablePath { +public: + TablePath() + { + } + + TablePath(const size_t* begin, size_t len) : m_coords(begin, begin + len) + { + } + + TablePath(size_t group_level_ndx, size_t num_pairs, const size_t* pairs) + { + m_coords.reserve(num_pairs * 2 + 1); + m_coords.push_back(group_level_ndx); + std::copy(pairs, pairs + num_pairs * 2, std::back_inserter(m_coords)); + } + + bool operator==(const TablePath& other) const + { + if (m_coords.size() != other.m_coords.size()) { + return false; + } + for (size_t i = 0; i < m_coords.size(); ++i) { + if (m_coords[i] != other.m_coords[i]) { + return false; + } + } + return true; + } + + bool operator!=(const TablePath& other) const + { + return !((*this) == other); + } + + void push(size_t coord) + { + m_coords.push_back(coord); + } + + size_t size() const + { + return m_coords.size(); + } + + void clear() + { + m_coords.clear(); + } + + // FIXME: Should be private, but is accessed directly from sync.cpp. + std::vector m_coords; +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_TABLE_PATH_HPP diff --git a/Pods/Realm/include/core/realm/impl/transact_log.hpp b/Pods/Realm/include/core/realm/impl/transact_log.hpp new file mode 100644 index 0000000..fa748a1 --- /dev/null +++ b/Pods/Realm/include/core/realm/impl/transact_log.hpp @@ -0,0 +1,2772 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPL_TRANSACT_LOG_HPP +#define REALM_IMPL_TRANSACT_LOG_HPP + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace realm { +namespace _impl { + +/// Transaction log instruction encoding +/// NOTE: Any change to this enum is a file-format breaking change. +enum Instruction { + instr_InsertGroupLevelTable = 1, + instr_EraseGroupLevelTable = 2, // Remove columnless table from group + instr_RenameGroupLevelTable = 3, + instr_MoveGroupLevelTable = 4, + instr_SelectTable = 5, + instr_Set = 6, + instr_SetUnique = 7, + instr_SetDefault = 8, + instr_AddInteger = 9, // Add value to integer field + instr_NullifyLink = 10, // Set link to null due to target being erased + instr_InsertSubstring = 11, + instr_EraseFromString = 12, + instr_InsertEmptyRows = 13, + instr_EraseRows = 14, // Remove (multiple) rows + instr_SwapRows = 15, + instr_MergeRows = 16, // Replace links pointing to row A with links to row B + instr_ClearTable = 17, // Remove all rows in selected table + instr_OptimizeTable = 18, + instr_SelectDescriptor = 19, // Select descriptor from currently selected root table + instr_InsertColumn = + 20, // Insert new non-nullable column into to selected descriptor (nullable is instr_InsertNullableColumn) + instr_InsertLinkColumn = 21, // do, but for a link-type column + instr_InsertNullableColumn = 22, // Insert nullable column + instr_EraseColumn = 23, // Remove column from selected descriptor + instr_EraseLinkColumn = 24, // Remove link-type column from selected descriptor + instr_RenameColumn = 25, // Rename column in selected descriptor + instr_MoveColumn = 26, // Move column in selected descriptor + instr_AddSearchIndex = 27, // Add a search index to a column + instr_RemoveSearchIndex = 28, // Remove a search index from a column + instr_SetLinkType = 29, // Strong/weak + instr_SelectLinkList = 30, + instr_LinkListSet = 31, // Assign to link list entry + instr_LinkListInsert = 32, // Insert entry into link list + instr_LinkListMove = 33, // Move an entry within a link list + instr_LinkListSwap = 34, // Swap two entries within a link list + instr_LinkListErase = 35, // Remove an entry from a link list + instr_LinkListNullify = 36, // Remove an entry from a link list due to linked row being erased + instr_LinkListClear = 37, // Ramove all entries from a link list + instr_LinkListSetAll = 38, // Assign to link list entry +}; + + +class TransactLogStream { +public: + /// Ensure contiguous free space in the transaction log + /// buffer. This method must update `out_free_begin` + /// and `out_free_end` such that they refer to a chunk + /// of free space whose size is at least \a n. + /// + /// \param n The required amount of contiguous free space. Must be + /// small (probably not greater than 1024) + /// \param n Must be small (probably not greater than 1024) + virtual void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) = 0; + + /// Copy the specified data into the transaction log buffer. This + /// function should be called only when the specified data does + /// not fit inside the chunk of free space currently referred to + /// by `out_free_begin` and `out_free_end`. + /// + /// This method must update `out_begin` and + /// `out_end` such that, upon return, they still + /// refer to a (possibly empty) chunk of free space. + virtual void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) = 0; +}; + +class TransactLogBufferStream : public TransactLogStream { +public: + void transact_log_reserve(size_t size, char** out_free_begin, char** out_free_end) override; + void transact_log_append(const char* data, size_t size, char** out_free_begin, char** out_free_end) override; + + const char* transact_log_data() const; + + util::Buffer m_buffer; +}; + + +// LCOV_EXCL_START (because the NullInstructionObserver is trivial) +class NullInstructionObserver { +public: + /// The following methods are also those that TransactLogParser expects + /// to find on the `InstructionHandler`. + + // No selection needed: + bool select_table(size_t, size_t, const size_t*) + { + return true; + } + bool select_descriptor(size_t, const size_t*) + { + return true; + } + bool select_link_list(size_t, size_t, size_t) + { + return true; + } + bool insert_group_level_table(size_t, size_t, StringData) + { + return true; + } + bool erase_group_level_table(size_t, size_t) + { + return true; + } + bool rename_group_level_table(size_t, StringData) + { + return true; + } + bool move_group_level_table(size_t, size_t) + { + return true; + } + + // Must have table selected: + bool insert_empty_rows(size_t, size_t, size_t, bool) + { + return true; + } + bool erase_rows(size_t, size_t, size_t, bool) + { + return true; + } + bool swap_rows(size_t, size_t) + { + return true; + } + bool merge_rows(size_t, size_t) + { + return true; + } + bool clear_table() + { + return true; + } + bool set_int(size_t, size_t, int_fast64_t, Instruction, size_t) + { + return true; + } + bool add_int(size_t, size_t, int_fast64_t) + { + return true; + } + bool set_bool(size_t, size_t, bool, Instruction) + { + return true; + } + bool set_float(size_t, size_t, float, Instruction) + { + return true; + } + bool set_double(size_t, size_t, double, Instruction) + { + return true; + } + bool set_string(size_t, size_t, StringData, Instruction, size_t) + { + return true; + } + bool set_binary(size_t, size_t, BinaryData, Instruction) + { + return true; + } + bool set_olddatetime(size_t, size_t, OldDateTime, Instruction) + { + return true; + } + bool set_timestamp(size_t, size_t, Timestamp, Instruction) + { + return true; + } + bool set_table(size_t, size_t, Instruction) + { + return true; + } + bool set_mixed(size_t, size_t, const Mixed&, Instruction) + { + return true; + } + bool set_link(size_t, size_t, size_t, size_t, Instruction) + { + return true; + } + bool set_null(size_t, size_t, Instruction, size_t) + { + return true; + } + bool nullify_link(size_t, size_t, size_t) + { + return true; + } + bool insert_substring(size_t, size_t, size_t, StringData) + { + return true; + } + bool erase_substring(size_t, size_t, size_t, size_t) + { + return true; + } + bool optimize_table() + { + return true; + } + + // Must have descriptor selected: + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) + { + return true; + } + bool insert_column(size_t, DataType, StringData, bool) + { + return true; + } + bool erase_link_column(size_t, size_t, size_t) + { + return true; + } + bool erase_column(size_t) + { + return true; + } + bool rename_column(size_t, StringData) + { + return true; + } + bool move_column(size_t, size_t) + { + return true; + } + bool add_search_index(size_t) + { + return true; + } + bool remove_search_index(size_t) + { + return true; + } + bool set_link_type(size_t, LinkType) + { + return true; + } + + // Must have linklist selected: + bool link_list_set(size_t, size_t, size_t) + { + return true; + } + bool link_list_insert(size_t, size_t, size_t) + { + return true; + } + bool link_list_move(size_t, size_t) + { + return true; + } + bool link_list_swap(size_t, size_t) + { + return true; + } + bool link_list_erase(size_t, size_t) + { + return true; + } + bool link_list_nullify(size_t, size_t) + { + return true; + } + bool link_list_clear(size_t) + { + return true; + } + + void parse_complete() + { + } +}; +// LCOV_EXCL_STOP (NullInstructionObserver) + + +/// See TransactLogConvenientEncoder for information about the meaning of the +/// arguments of each of the functions in this class. +class TransactLogEncoder { +public: + /// The following methods are also those that TransactLogParser expects + /// to find on the `InstructionHandler`. + + // No selection needed: + bool select_table(size_t group_level_ndx, size_t levels, const size_t* path); + bool select_descriptor(size_t levels, const size_t* path); + bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx); + bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); + bool erase_group_level_table(size_t table_ndx, size_t num_tables); + bool rename_group_level_table(size_t table_ndx, StringData new_name); + bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); + + /// Must have table selected. + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered); + bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered); + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2); + bool merge_rows(size_t row_ndx, size_t new_row_ndx); + bool clear_table(); + + bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t, Instruction = instr_Set, size_t = 0); + bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t); + bool set_bool(size_t col_ndx, size_t row_ndx, bool, Instruction = instr_Set); + bool set_float(size_t col_ndx, size_t row_ndx, float, Instruction = instr_Set); + bool set_double(size_t col_ndx, size_t row_ndx, double, Instruction = instr_Set); + bool set_string(size_t col_ndx, size_t row_ndx, StringData, Instruction = instr_Set, size_t = 0); + bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData, Instruction = instr_Set); + bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime, Instruction = instr_Set); + bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp, Instruction = instr_Set); + bool set_table(size_t col_ndx, size_t row_ndx, Instruction = instr_Set); + bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed&, Instruction = instr_Set); + bool set_link(size_t col_ndx, size_t row_ndx, size_t, size_t target_group_level_ndx, Instruction = instr_Set); + bool set_null(size_t col_ndx, size_t row_ndx, Instruction = instr_Set, size_t = 0); + bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx); + bool insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData); + bool erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size); + bool optimize_table(); + + // Must have descriptor selected: + bool insert_link_column(size_t col_ndx, DataType, StringData name, size_t link_target_table_ndx, + size_t backlink_col_ndx); + bool insert_column(size_t col_ndx, DataType, StringData name, bool nullable = false); + bool erase_link_column(size_t col_ndx, size_t link_target_table_ndx, size_t backlink_col_ndx); + bool erase_column(size_t col_ndx); + bool rename_column(size_t col_ndx, StringData new_name); + bool move_column(size_t col_ndx_1, size_t col_ndx_2); + bool add_search_index(size_t col_ndx); + bool remove_search_index(size_t col_ndx); + bool set_link_type(size_t col_ndx, LinkType); + + // Must have linklist selected: + bool link_list_set(size_t link_ndx, size_t value, size_t prior_size); + bool link_list_set_all(const IntegerColumn& values); + bool link_list_insert(size_t link_ndx, size_t value, size_t prior_size); + bool link_list_move(size_t from_link_ndx, size_t to_link_ndx); + bool link_list_swap(size_t link1_ndx, size_t link2_ndx); + bool link_list_erase(size_t link_ndx, size_t prior_size); + bool link_list_nullify(size_t link_ndx, size_t prior_size); + bool link_list_clear(size_t old_list_size); + + /// End of methods expected by parser. + + + TransactLogEncoder(TransactLogStream& out_stream); + void set_buffer(char* new_free_begin, char* new_free_end); + char* write_position() const + { + return m_transact_log_free_begin; + } + +private: + // Make sure this is in agreement with the actual integer encoding + // scheme (see encode_int()). + static const int max_enc_bytes_per_int = 10; + static const int max_enc_bytes_per_double = sizeof(double); + static const int max_enc_bytes_per_num = + max_enc_bytes_per_int < max_enc_bytes_per_double ? max_enc_bytes_per_double : max_enc_bytes_per_int; + + // This value is used in Set* instructions in place of the 'type' field in + // the stream to indicate that the value of the Set* instruction is NULL, + // which doesn't have a type. + static constexpr int set_null_sentinel() + { + return -1; + } + + TransactLogStream& m_stream; + + // These two delimit a contiguous region of free space in a + // transaction log buffer following the last written data. It may + // be empty. + char* m_transact_log_free_begin = nullptr; + char* m_transact_log_free_end = nullptr; + + char* reserve(size_t size); + /// \param ptr Must be in the range [m_transact_log_free_begin, m_transact_log_free_end] + void advance(char* ptr) noexcept; + + template + void append_simple_instr(Instruction, const util::Tuple& numbers); + + template + void append_string_instr(Instruction, const util::Tuple& numbers, StringData); + + template + void append_mixed_instr(Instruction, const util::Tuple& numbers, const Mixed&); + + template + bool append_variable_size_instr(Instruction instr, const util::Tuple& numbers, I var_begin, I var_end); + + template + static char* encode_int(char*, T value); + static char* encode_bool(char*, bool value); + static char* encode_float(char*, float value); + static char* encode_double(char*, double value); + template + struct EncodeNumber; + friend class TransactLogParser; +}; + +class TransactLogConvenientEncoder { +public: + void insert_group_level_table(size_t table_ndx, size_t num_tables, StringData name); + void erase_group_level_table(size_t table_ndx, size_t num_tables); + void rename_group_level_table(size_t table_ndx, StringData new_name); + void move_group_level_table(size_t from_table_ndx, size_t to_table_ndx); + void insert_column(const Descriptor&, size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link, + bool nullable = false); + void erase_column(const Descriptor&, size_t col_ndx); + void rename_column(const Descriptor&, size_t col_ndx, StringData name); + void move_column(const Descriptor&, size_t from, size_t to); + + void set_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant = instr_Set); + void add_int(const Table*, size_t col_ndx, size_t ndx, int_fast64_t value); + void set_bool(const Table*, size_t col_ndx, size_t ndx, bool value, Instruction variant = instr_Set); + void set_float(const Table*, size_t col_ndx, size_t ndx, float value, Instruction variant = instr_Set); + void set_double(const Table*, size_t col_ndx, size_t ndx, double value, Instruction variant = instr_Set); + void set_string(const Table*, size_t col_ndx, size_t ndx, StringData value, Instruction variant = instr_Set); + void set_binary(const Table*, size_t col_ndx, size_t ndx, BinaryData value, Instruction variant = instr_Set); + void set_olddatetime(const Table*, size_t col_ndx, size_t ndx, OldDateTime value, + Instruction variant = instr_Set); + void set_timestamp(const Table*, size_t col_ndx, size_t ndx, Timestamp value, Instruction variant = instr_Set); + void set_table(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + void set_mixed(const Table*, size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant = instr_Set); + void set_link(const Table*, size_t col_ndx, size_t ndx, size_t value, Instruction variant = instr_Set); + void set_null(const Table*, size_t col_ndx, size_t ndx, Instruction variant = instr_Set); + void set_link_list(const LinkView&, const IntegerColumn& values); + void insert_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, StringData); + void erase_substring(const Table*, size_t col_ndx, size_t row_ndx, size_t pos, size_t size); + + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + void insert_empty_rows(const Table*, size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows); + + /// \param prior_num_rows The number of rows in the table prior to the + /// modification. + void erase_rows(const Table*, size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool is_move_last_over); + + void swap_rows(const Table*, size_t row_ndx_1, size_t row_ndx_2); + void merge_rows(const Table*, size_t row_ndx, size_t new_row_ndx); + void add_search_index(const Table*, size_t col_ndx); + void remove_search_index(const Table*, size_t col_ndx); + void set_link_type(const Table*, size_t col_ndx, LinkType); + void clear_table(const Table*); + void optimize_table(const Table*); + + void link_list_set(const LinkView&, size_t link_ndx, size_t value); + void link_list_insert(const LinkView&, size_t link_ndx, size_t value); + void link_list_move(const LinkView&, size_t from_link_ndx, size_t to_link_ndx); + void link_list_swap(const LinkView&, size_t link_ndx_1, size_t link_ndx_2); + void link_list_erase(const LinkView&, size_t link_ndx); + void link_list_clear(const LinkView&); + + //@{ + + /// Implicit nullifications due to removal of target row. This is redundant + /// information from the point of view of replication, as the removal of the + /// target row will reproduce the implicit nullifications in the target + /// Realm anyway. The purpose of this instruction is to allow observers + /// (reactor pattern) to be explicitly notified about the implicit + /// nullifications. + + void nullify_link(const Table*, size_t col_ndx, size_t ndx); + void link_list_nullify(const LinkView&, size_t link_ndx); + + //@} + + void on_table_destroyed(const Table*) noexcept; + void on_spec_destroyed(const Spec*) noexcept; + void on_link_list_destroyed(const LinkView&) noexcept; + +protected: + TransactLogConvenientEncoder(TransactLogStream& encoder); + + void reset_selection_caches() noexcept; + void set_buffer(char* new_free_begin, char* new_free_end) + { + m_encoder.set_buffer(new_free_begin, new_free_end); + } + char* write_position() const + { + return m_encoder.write_position(); + } + +private: + TransactLogEncoder m_encoder; + // These are mutable because they are caches. + mutable util::Buffer m_subtab_path_buf; + mutable const Table* m_selected_table; + mutable const Spec* m_selected_spec; + // Has to be atomic to support concurrent reset when a linklist + // is unselected. This can happen on a different thread. In case + // of races, setting of a new value must win. + mutable std::atomic m_selected_link_list; + + void unselect_all() noexcept; + void select_table(const Table*); // unselects descriptor and link list + void select_desc(const Descriptor&); // unselects link list + void select_link_list(const LinkView&); // unselects descriptor + + void record_subtable_path(const Table&, size_t*& out_begin, size_t*& out_end); + void do_select_table(const Table*); + void do_select_desc(const Descriptor&); + void do_select_link_list(const LinkView&); + + friend class TransactReverser; +}; + + +class TransactLogParser { +public: + class BadTransactLog; // Exception + + TransactLogParser(); + ~TransactLogParser() noexcept; + + /// See `TransactLogEncoder` for a list of methods that the `InstructionHandler` must define. + /// parse() promises that the path passed by reference to + /// InstructionHandler::select_descriptor() will remain valid + /// during subsequent calls to all descriptor modifying functions. + template + void parse(InputStream&, InstructionHandler&); + + template + void parse(NoCopyInputStream&, InstructionHandler&); + +private: + util::Buffer m_input_buffer; + + // The input stream is assumed to consist of chunks of memory organised such that + // every instruction resides in a single chunk only. + NoCopyInputStream* m_input; + // pointer into transaction log, each instruction is parsed from m_input_begin and onwards. + // Each instruction are assumed to be contiguous in memory. + const char* m_input_begin; + // pointer to one past current instruction log chunk. If m_input_begin reaches m_input_end, + // a call to next_input_buffer will move m_input_begin and m_input_end to a new chunk of + // memory. Setting m_input_end to 0 disables this check, and is used if it is already known + // that all of the instructions are in memory. + const char* m_input_end; + util::StringBuffer m_string_buffer; + static const int m_max_levels = 1024; + util::Buffer m_path; + + REALM_NORETURN void parser_error() const; + + template + void parse_one(InstructionHandler&); + bool has_next() noexcept; + + template + T read_int(); + + void read_bytes(char* data, size_t size); + BinaryData read_buffer(util::StringBuffer&, size_t size); + + bool read_bool(); + float read_float(); + double read_double(); + + StringData read_string(util::StringBuffer&); + BinaryData read_binary(util::StringBuffer&); + Timestamp read_timestamp(); + void read_mixed(Mixed*); + + // Advance m_input_begin and m_input_end to reflect the next block of instructions + // Returns false if no more input was available + bool next_input_buffer(); + + // return true if input was available + bool read_char(char&); // throws + + bool is_valid_data_type(int type); + bool is_valid_link_type(int type); +}; + + +class TransactLogParser::BadTransactLog : public std::exception { +public: + const char* what() const noexcept override + { + return "Bad transaction log"; + } +}; + + +/// Implementation: + +inline void TransactLogBufferStream::transact_log_reserve(size_t n, char** inout_new_begin, char** out_new_end) +{ + char* data = m_buffer.data(); + REALM_ASSERT(*inout_new_begin >= data); + REALM_ASSERT(*inout_new_begin <= (data + m_buffer.size())); + size_t size = *inout_new_begin - data; + m_buffer.reserve_extra(size, n); + data = m_buffer.data(); // May have changed + *inout_new_begin = data + size; + *out_new_end = data + m_buffer.size(); +} + +inline void TransactLogBufferStream::transact_log_append(const char* data, size_t size, char** out_new_begin, + char** out_new_end) +{ + transact_log_reserve(size, out_new_begin, out_new_end); + *out_new_begin = std::copy(data, data + size, *out_new_begin); +} + +inline const char* TransactLogBufferStream::transact_log_data() const +{ + return m_buffer.data(); +} + +inline TransactLogEncoder::TransactLogEncoder(TransactLogStream& stream) + : m_stream(stream) +{ +} + +inline void TransactLogEncoder::set_buffer(char* free_begin, char* free_end) +{ + REALM_ASSERT(free_begin <= free_end); + m_transact_log_free_begin = free_begin; + m_transact_log_free_end = free_end; +} + +inline void TransactLogConvenientEncoder::reset_selection_caches() noexcept +{ + unselect_all(); +} + +inline char* TransactLogEncoder::reserve(size_t n) +{ + if (size_t(m_transact_log_free_end - m_transact_log_free_begin) < n) { + m_stream.transact_log_reserve(n, &m_transact_log_free_begin, &m_transact_log_free_end); + } + return m_transact_log_free_begin; +} + +inline void TransactLogEncoder::advance(char* ptr) noexcept +{ + REALM_ASSERT_DEBUG(m_transact_log_free_begin <= ptr); + REALM_ASSERT_DEBUG(ptr <= m_transact_log_free_end); + m_transact_log_free_begin = ptr; +} + + +// The integer encoding is platform independent. Also, it does not +// depend on the type of the specified integer. Integers of any type +// can be encoded as long as the specified buffer is large enough (see +// below). The decoding does not have to use the same type. Decoding +// will fail if, and only if the encoded value falls outside the range +// of the requested destination type. +// +// The encoding uses one or more bytes. It never uses more than 8 bits +// per byte. The last byte in the sequence is the first one that has +// its 8th bit set to zero. +// +// Consider a particular non-negative value V. Let W be the number of +// bits needed to encode V using the trivial binary encoding of +// integers. The total number of bytes produced is then +// ceil((W+1)/7). The first byte holds the 7 least significant bits of +// V. The last byte holds at most 6 bits of V including the most +// significant one. The value of the first bit of the last byte is +// always 2**((N-1)*7) where N is the total number of bytes. +// +// A negative value W is encoded by setting the sign bit to one and +// then encoding the positive result of -(W+1) as described above. The +// advantage of this representation is that it converts small negative +// values to small positive values which require a small number of +// bytes. This would not have been true for 2's complements +// representation, for example. The sign bit is always stored as the +// 7th bit of the last byte. +// +// value bits value + sign max bytes +// -------------------------------------------------- +// int8_t 7 8 2 +// uint8_t 8 9 2 +// int16_t 15 16 3 +// uint16_t 16 17 3 +// int32_t 31 32 5 +// uint32_t 32 33 5 +// int64_t 63 64 10 +// uint64_t 64 65 10 +// +template +char* TransactLogEncoder::encode_int(char* ptr, T value) +{ + static_assert(std::numeric_limits::is_integer, "Integer required"); + bool negative = util::is_negative(value); + if (negative) { + // The following conversion is guaranteed by C++11 to never + // overflow (contrast this with "-value" which indeed could + // overflow). See C99+TC3 section 6.2.6.2 paragraph 2. + REALM_DIAG_PUSH(); + REALM_DIAG_IGNORE_UNSIGNED_MINUS(); + value = -(value + 1); + REALM_DIAG_POP(); + } + // At this point 'value' is always a positive number. Also, small + // negative numbers have been converted to small positive numbers. + REALM_ASSERT(!util::is_negative(value)); + // One sign bit plus number of value bits + const int num_bits = 1 + std::numeric_limits::digits; + // Only the first 7 bits are available per byte. Had it not been + // for the fact that maximum guaranteed bit width of a char is 8, + // this value could have been increased to 15 (one less than the + // number of value bits in 'unsigned'). + const int bits_per_byte = 7; + const int max_bytes = (num_bits + (bits_per_byte - 1)) / bits_per_byte; + static_assert(max_bytes <= max_enc_bytes_per_int, "Bad max_enc_bytes_per_int"); + // An explicit constant maximum number of iterations is specified + // in the hope that it will help the optimizer (to do loop + // unrolling, for example). + typedef unsigned char uchar; + for (int i = 0; i < max_bytes; ++i) { + if (value >> (bits_per_byte - 1) == 0) + break; + *reinterpret_cast(ptr) = uchar((1U << bits_per_byte) | unsigned(value & ((1U << bits_per_byte) - 1))); + ++ptr; + value >>= bits_per_byte; + } + *reinterpret_cast(ptr) = uchar(negative ? (1U << (bits_per_byte - 1)) | unsigned(value) : value); + return ++ptr; +} + +inline char* TransactLogEncoder::encode_bool(char* ptr, bool value) +{ + // A `char` is the smallest element that the encoder/decoder can process. So we encode the bool + // in a char. If we called encode_int it would end up as a char too, but we would get + // Various warnings about arithmetic on non-arithmetic type. + return encode_int(ptr, value); +} + +inline char* TransactLogEncoder::encode_float(char* ptr, float value) +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(float) * std::numeric_limits::digits == 32, + "Unsupported 'float' representation"); + const char* val_ptr = reinterpret_cast(&value); + return std::copy(val_ptr, val_ptr + sizeof value, ptr); +} + +inline char* TransactLogEncoder::encode_double(char* ptr, double value) +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(double) * std::numeric_limits::digits == 64, + "Unsupported 'double' representation"); + const char* val_ptr = reinterpret_cast(&value); + return std::copy(val_ptr, val_ptr + sizeof value, ptr); +} + +template +struct TransactLogEncoder::EncodeNumber { + void operator()(T value, char** ptr) + { + auto value_2 = value + 0; // Perform integral promotion + *ptr = encode_int(*ptr, value_2); + } +}; +template <> +struct TransactLogEncoder::EncodeNumber { + void operator()(char value, char** ptr) + { + // Write the char as-is without encoding. + **ptr = value; + ++(*ptr); + } +}; +template <> +struct TransactLogEncoder::EncodeNumber { + void operator()(bool value, char** ptr) + { + *ptr = encode_bool(*ptr, value); + } +}; +template <> +struct TransactLogEncoder::EncodeNumber { + void operator()(float value, char** ptr) + { + *ptr = encode_float(*ptr, value); + } +}; +template <> +struct TransactLogEncoder::EncodeNumber { + void operator()(double value, char** ptr) + { + *ptr = encode_double(*ptr, value); + } +}; +template <> +struct TransactLogEncoder::EncodeNumber { + void operator()(DataType type, char** ptr) + { + auto value_2 = type + 0; // Perform integral promotion + *ptr = encode_int(*ptr, value_2); + } +}; + +template +void TransactLogEncoder::append_simple_instr(Instruction instr, const util::Tuple& numbers) +{ + size_t num_numbers = util::TypeCount::value; + size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers; + char* ptr = reserve(max_required_bytes); // Throws + *ptr++ = char(instr); + util::for_each(numbers, &ptr); + advance(ptr); +} + +template +void TransactLogEncoder::append_string_instr(Instruction instr, const util::Tuple& numbers, StringData string) +{ + size_t num_numbers = util::TypeCount::value + 1; + size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers + string.size(); + char* ptr = reserve(max_required_bytes); // Throws + *ptr++ = char(instr); + util::for_each(append(numbers, string.size()), &ptr); + ptr = std::copy(string.data(), string.data() + string.size(), ptr); + advance(ptr); +} + +template +void TransactLogEncoder::append_mixed_instr(Instruction instr, const util::Tuple& numbers, const Mixed& value) +{ + DataType type = value.get_type(); + auto numbers_2 = append(numbers, type); + switch (type) { + case type_Int: + append_simple_instr(instr, append(numbers_2, value.get_int())); // Throws + return; + case type_Bool: + append_simple_instr(instr, append(numbers_2, value.get_bool())); // Throws + return; + case type_Float: + append_simple_instr(instr, append(numbers_2, value.get_float())); // Throws + return; + case type_Double: + append_simple_instr(instr, append(numbers_2, value.get_double())); // Throws + return; + case type_OldDateTime: { + auto value_2 = value.get_olddatetime().get_olddatetime(); + append_simple_instr(instr, append(numbers_2, value_2)); // Throws + return; + } + case type_String: { + append_string_instr(instr, numbers_2, value.get_string()); // Throws + return; + } + case type_Binary: { + BinaryData value_2 = value.get_binary(); + StringData value_3(value_2.data(), value_2.size()); + append_string_instr(instr, numbers_2, value_3); // Throws + return; + } + case type_Timestamp: { + Timestamp ts = value.get_timestamp(); + int64_t seconds = ts.get_seconds(); + int32_t nano_seconds = ts.get_nanoseconds(); + auto numbers_3 = append(numbers_2, seconds); + append_simple_instr(instr, append(numbers_3, nano_seconds)); // Throws + return; + } + case type_Table: + append_simple_instr(instr, numbers_2); // Throws + return; + case type_Mixed: + // Mixed in mixed is not possible + REALM_ASSERT_RELEASE(false); + case type_Link: + case type_LinkList: + // FIXME: Need to handle new link types here. + REALM_ASSERT_RELEASE(false); + } + REALM_ASSERT_RELEASE(false); +} + +template +bool TransactLogEncoder::append_variable_size_instr(Instruction instr, const util::Tuple& numbers, I var_begin, + I var_end) +{ +// Space is reserved in chunks to avoid excessive over allocation. +#ifdef REALM_DEBUG + const int max_numbers_per_chunk = 2; // Increase the chance of chunking in debug mode +#else + const int max_numbers_per_chunk = 8; +#endif + size_t num_numbers = util::TypeCount::value + max_numbers_per_chunk; + size_t max_required_bytes = 1 + max_enc_bytes_per_num * num_numbers; + char* ptr = reserve(max_required_bytes); // Throws + *ptr++ = char(instr); + util::for_each(numbers, &ptr); + I i = var_begin; + while (var_end - i > max_numbers_per_chunk) { + for (int j = 0; j < max_numbers_per_chunk; ++j) + ptr = encode_int(ptr, *i++); + advance(ptr); + size_t max_required_bytes_2 = max_enc_bytes_per_num * max_numbers_per_chunk; + ptr = reserve(max_required_bytes_2); // Throws + } + while (i != var_end) + ptr = encode_int(ptr, *i++); + advance(ptr); + return true; +} + +inline void TransactLogConvenientEncoder::unselect_all() noexcept +{ + m_selected_table = nullptr; + m_selected_spec = nullptr; + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_table(const Table* table) +{ + if (table != m_selected_table) + do_select_table(table); // Throws + m_selected_spec = nullptr; + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_desc(const Descriptor& desc) +{ + typedef _impl::DescriptorFriend df; + if (&df::get_spec(desc) != m_selected_spec) + do_select_desc(desc); // Throws + // no race with on_link_list_destroyed since both are setting to nullptr + m_selected_link_list = nullptr; +} + +inline void TransactLogConvenientEncoder::select_link_list(const LinkView& list) +{ + // A race between this and a call to on_link_list_destroyed() must + // end up with m_selected_link_list pointing to the list argument given + // here. We assume that the list given to on_link_list_destroyed() can + // *never* be the same as the list argument given here. We resolve the + // race by a) always updating m_selected_link_list in do_select_link_list() + // and b) only atomically and conditionally updating it in + // on_link_list_destroyed(). + if (&list != m_selected_link_list) { + do_select_link_list(list); // Throws + } + m_selected_spec = nullptr; +} + + +inline bool TransactLogEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, StringData name) +{ + append_string_instr(instr_InsertGroupLevelTable, util::tuple(table_ndx, prior_num_tables), name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_group_level_table(size_t table_ndx, size_t prior_num_tables, + StringData name) +{ + unselect_all(); + m_encoder.insert_group_level_table(table_ndx, prior_num_tables, name); // Throws +} + +inline bool TransactLogEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables) +{ + append_simple_instr(instr_EraseGroupLevelTable, util::tuple(table_ndx, prior_num_tables)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_group_level_table(size_t table_ndx, size_t prior_num_tables) +{ + unselect_all(); + m_encoder.erase_group_level_table(table_ndx, prior_num_tables); // Throws +} + +inline bool TransactLogEncoder::rename_group_level_table(size_t table_ndx, StringData new_name) +{ + append_string_instr(instr_RenameGroupLevelTable, util::tuple(table_ndx), new_name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_group_level_table(size_t table_ndx, StringData new_name) +{ + unselect_all(); + m_encoder.rename_group_level_table(table_ndx, new_name); // Throws +} + +inline bool TransactLogEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) +{ + REALM_ASSERT(from_table_ndx != to_table_ndx); + append_simple_instr(instr_MoveGroupLevelTable, util::tuple(from_table_ndx, to_table_ndx)); + return true; +} + +inline void TransactLogConvenientEncoder::move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) +{ + unselect_all(); + m_encoder.move_group_level_table(from_table_ndx, to_table_ndx); +} + +inline bool TransactLogEncoder::insert_column(size_t col_ndx, DataType type, StringData name, bool nullable) +{ + Instruction instr = (nullable ? instr_InsertNullableColumn : instr_InsertColumn); + append_string_instr(instr, util::tuple(col_ndx, type), name); // Throws + return true; +} + +inline bool TransactLogEncoder::insert_link_column(size_t col_ndx, DataType type, StringData name, + size_t link_target_table_ndx, size_t backlink_col_ndx) +{ + REALM_ASSERT(_impl::TableFriend::is_link_type(ColumnType(type))); + append_string_instr(instr_InsertLinkColumn, util::tuple(col_ndx, type, link_target_table_ndx, backlink_col_ndx), + name); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::insert_column(const Descriptor& desc, size_t col_ndx, DataType type, + StringData name, LinkTargetInfo& link, bool nullable) +{ + select_desc(desc); // Throws + if (link.is_valid()) { + typedef _impl::TableFriend tf; + typedef _impl::DescriptorFriend df; + size_t target_table_ndx = link.m_target_table->get_index_in_group(); + const Table& origin_table = df::get_root_table(desc); + REALM_ASSERT(origin_table.is_group_level()); + const Spec& target_spec = tf::get_spec(*(link.m_target_table)); + size_t origin_table_ndx = origin_table.get_index_in_group(); + size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx); + REALM_ASSERT_3(backlink_col_ndx, ==, link.m_backlink_col_ndx); + m_encoder.insert_link_column(col_ndx, type, name, target_table_ndx, backlink_col_ndx); // Throws + } + else { + m_encoder.insert_column(col_ndx, type, name, nullable); // Throws + } +} + +inline bool TransactLogEncoder::erase_column(size_t col_ndx) +{ + append_simple_instr(instr_EraseColumn, util::tuple(col_ndx)); // Throws + return true; +} + +inline bool TransactLogEncoder::erase_link_column(size_t col_ndx, size_t link_target_table_ndx, + size_t backlink_col_ndx) +{ + append_simple_instr(instr_EraseLinkColumn, util::tuple(col_ndx, link_target_table_ndx, + backlink_col_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_column(const Descriptor& desc, size_t col_ndx) +{ + select_desc(desc); // Throws + + DataType type = desc.get_column_type(col_ndx); + typedef _impl::TableFriend tf; + if (!tf::is_link_type(ColumnType(type))) { + m_encoder.erase_column(col_ndx); // Throws + } + else { // it's a link column: + REALM_ASSERT(desc.is_root()); + typedef _impl::DescriptorFriend df; + const Table& origin_table = df::get_root_table(desc); + REALM_ASSERT(origin_table.is_group_level()); + const Table& target_table = *tf::get_link_target_table_accessor(origin_table, col_ndx); + size_t target_table_ndx = target_table.get_index_in_group(); + const Spec& target_spec = tf::get_spec(target_table); + size_t origin_table_ndx = origin_table.get_index_in_group(); + size_t backlink_col_ndx = target_spec.find_backlink_column(origin_table_ndx, col_ndx); + m_encoder.erase_link_column(col_ndx, target_table_ndx, backlink_col_ndx); // Throws + } +} + +inline bool TransactLogEncoder::rename_column(size_t col_ndx, StringData new_name) +{ + append_string_instr(instr_RenameColumn, util::tuple(col_ndx), new_name); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::rename_column(const Descriptor& desc, size_t col_ndx, StringData name) +{ + select_desc(desc); // Throws + m_encoder.rename_column(col_ndx, name); // Throws +} + + +inline bool TransactLogEncoder::move_column(size_t from, size_t to) +{ + append_simple_instr(instr_MoveColumn, util::tuple(from, to)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::move_column(const Descriptor& desc, size_t from, size_t to) +{ + select_desc(desc); // Throws + m_encoder.move_column(from, to); +} + + +inline bool TransactLogEncoder::set_int(size_t col_ndx, size_t ndx, int_fast64_t value, Instruction variant, + size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (REALM_UNLIKELY(variant == instr_SetUnique)) + append_simple_instr(variant, util::tuple(type_Int, col_ndx, ndx, prior_num_rows, value)); // Throws + else + append_simple_instr(variant, util::tuple(type_Int, col_ndx, ndx, value)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_int(col_ndx, ndx, value, variant, prior_num_rows); // Throws +} + + +inline bool TransactLogEncoder::add_int(size_t col_ndx, size_t ndx, int_fast64_t value) +{ + append_simple_instr(instr_AddInteger, util::tuple(col_ndx, ndx, value)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::add_int(const Table* t, size_t col_ndx, size_t ndx, int_fast64_t value) +{ + select_table(t); // Throws + m_encoder.add_int(col_ndx, ndx, value); +} + +inline bool TransactLogEncoder::set_bool(size_t col_ndx, size_t ndx, bool value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, util::tuple(type_Bool, col_ndx, ndx, value)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_bool(const Table* t, size_t col_ndx, size_t ndx, bool value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_bool(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_float(size_t col_ndx, size_t ndx, float value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, util::tuple(type_Float, col_ndx, ndx, value)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_float(const Table* t, size_t col_ndx, size_t ndx, float value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_float(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_double(size_t col_ndx, size_t ndx, double value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(instr_Set, util::tuple(type_Double, col_ndx, ndx, value)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_double(const Table* t, size_t col_ndx, size_t ndx, double value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_double(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_string(size_t col_ndx, size_t ndx, StringData value, Instruction variant, + size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (value.is_null()) { + set_null(col_ndx, ndx, variant, prior_num_rows); // Throws + } + else { + if (REALM_UNLIKELY(variant == instr_SetUnique)) + append_string_instr(variant, util::tuple(type_String, col_ndx, ndx, prior_num_rows), value); // Throws + else + append_string_instr(variant, util::tuple(type_String, col_ndx, ndx), value); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_string(const Table* t, size_t col_ndx, size_t ndx, StringData value, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_string(col_ndx, ndx, value, variant, prior_num_rows); // Throws +} + +inline bool TransactLogEncoder::set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + if (value.is_null()) { + set_null(col_ndx, row_ndx, variant); // Throws + } + else { + StringData value_2(value.data(), value.size()); + append_string_instr(variant, util::tuple(type_Binary, col_ndx, row_ndx), value_2); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_binary(const Table* t, size_t col_ndx, size_t ndx, BinaryData value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_binary(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_olddatetime(size_t col_ndx, size_t ndx, OldDateTime value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, util::tuple(type_OldDateTime, col_ndx, ndx, value.get_olddatetime())); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_olddatetime(const Table* t, size_t col_ndx, size_t ndx, + OldDateTime value, Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_olddatetime(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_timestamp(size_t col_ndx, size_t ndx, Timestamp value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr( + variant, util::tuple(type_Timestamp, col_ndx, ndx, value.get_seconds(), value.get_nanoseconds())); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_timestamp(const Table* t, size_t col_ndx, size_t ndx, Timestamp value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_timestamp(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_table(size_t col_ndx, size_t ndx, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_simple_instr(variant, util::tuple(type_Table, col_ndx, ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_table(const Table* t, size_t col_ndx, size_t ndx, Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_table(col_ndx, ndx, variant); // Throws +} + +inline bool TransactLogEncoder::set_mixed(size_t col_ndx, size_t ndx, const Mixed& value, Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + append_mixed_instr(variant, util::tuple(type_Mixed, col_ndx, ndx), value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_mixed(const Table* t, size_t col_ndx, size_t ndx, const Mixed& value, + Instruction variant) +{ + select_table(t); // Throws + m_encoder.set_mixed(col_ndx, ndx, value, variant); // Throws +} + +inline bool TransactLogEncoder::set_link(size_t col_ndx, size_t ndx, size_t value, size_t target_group_level_ndx, + Instruction variant) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault, variant); + // Map `realm::npos` to zero, and `n` to `n+1`, where `n` is a target row + // index. + size_t value_2 = size_t(1) + value; + append_simple_instr(variant, util::tuple(type_Link, col_ndx, ndx, value_2, target_group_level_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link(const Table* t, size_t col_ndx, size_t ndx, size_t value, + Instruction variant) +{ + select_table(t); // Throws + size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx); + m_encoder.set_link(col_ndx, ndx, value, target_group_level_ndx, variant); // Throws +} + +inline bool TransactLogEncoder::set_null(size_t col_ndx, size_t ndx, Instruction variant, size_t prior_num_rows) +{ + REALM_ASSERT_EX(variant == instr_Set || variant == instr_SetDefault || variant == instr_SetUnique, variant); + if (REALM_UNLIKELY(variant == instr_SetUnique)) { + append_simple_instr(variant, util::tuple(set_null_sentinel(), col_ndx, ndx, prior_num_rows)); // Throws + } + else { + append_simple_instr(variant, util::tuple(set_null_sentinel(), col_ndx, ndx)); // Throws + } + return true; +} + +inline void TransactLogConvenientEncoder::set_null(const Table* t, size_t col_ndx, size_t row_ndx, + Instruction variant) +{ + select_table(t); // Throws + size_t prior_num_rows = (variant == instr_SetUnique ? t->size() : 0); + m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); // Throws +} + +inline bool TransactLogEncoder::nullify_link(size_t col_ndx, size_t ndx, size_t target_group_level_ndx) +{ + append_simple_instr(instr_NullifyLink, util::tuple(col_ndx, ndx, target_group_level_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::nullify_link(const Table* t, size_t col_ndx, size_t ndx) +{ + select_table(t); // Throws + size_t target_group_level_ndx = t->get_descriptor()->get_column_link_target(col_ndx); + m_encoder.nullify_link(col_ndx, ndx, target_group_level_ndx); // Throws +} + +inline bool TransactLogEncoder::insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData value) +{ + append_string_instr(instr_InsertSubstring, util::tuple(col_ndx, row_ndx, pos), value); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos, + StringData value) +{ + if (value.size() > 0) { + select_table(t); // Throws + m_encoder.insert_substring(col_ndx, row_ndx, pos, value); // Throws + } +} + +inline bool TransactLogEncoder::erase_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t size) +{ + append_simple_instr(instr_EraseFromString, util::tuple(col_ndx, row_ndx, pos, size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::erase_substring(const Table* t, size_t col_ndx, size_t row_ndx, size_t pos, + size_t size) +{ + if (size > 0) { + select_table(t); // Throws + m_encoder.erase_substring(col_ndx, row_ndx, pos, size); // Throws + } +} + +inline bool TransactLogEncoder::insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, + bool unordered) +{ + append_simple_instr(instr_InsertEmptyRows, + util::tuple(row_ndx, num_rows_to_insert, prior_num_rows, unordered)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::insert_empty_rows(const Table* t, size_t row_ndx, size_t num_rows_to_insert, + size_t prior_num_rows) +{ + select_table(t); // Throws + bool unordered = false; + m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered); // Throws +} + +inline bool TransactLogEncoder::erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, + bool unordered) +{ + append_simple_instr(instr_EraseRows, util::tuple(row_ndx, num_rows_to_erase, prior_num_rows, + unordered)); // Throws + return true; +} + + +inline void TransactLogConvenientEncoder::erase_rows(const Table* t, size_t row_ndx, size_t num_rows_to_erase, + size_t prior_num_rows, bool is_move_last_over) +{ + select_table(t); // Throws + bool unordered = is_move_last_over; + m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered); // Throws +} + +inline bool TransactLogEncoder::swap_rows(size_t row_ndx_1, size_t row_ndx_2) +{ + append_simple_instr(instr_SwapRows, util::tuple(row_ndx_1, row_ndx_2)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::swap_rows(const Table* t, size_t row_ndx_1, size_t row_ndx_2) +{ + REALM_ASSERT(row_ndx_1 < row_ndx_2); + select_table(t); // Throws + m_encoder.swap_rows(row_ndx_1, row_ndx_2); +} + +inline bool TransactLogEncoder::merge_rows(size_t row_ndx, size_t new_row_ndx) +{ + append_simple_instr(instr_MergeRows, util::tuple(row_ndx, new_row_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::merge_rows(const Table* t, size_t row_ndx, size_t new_row_ndx) +{ + select_table(t); // Throws + m_encoder.merge_rows(row_ndx, new_row_ndx); +} + +inline bool TransactLogEncoder::add_search_index(size_t col_ndx) +{ + append_simple_instr(instr_AddSearchIndex, util::tuple(col_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::add_search_index(const Table* t, size_t col_ndx) +{ + select_table(t); // Throws + m_encoder.add_search_index(col_ndx); // Throws +} + + +inline bool TransactLogEncoder::remove_search_index(size_t col_ndx) +{ + append_simple_instr(instr_RemoveSearchIndex, util::tuple(col_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::remove_search_index(const Table* t, size_t col_ndx) +{ + select_table(t); // Throws + m_encoder.remove_search_index(col_ndx); // Throws +} + +inline bool TransactLogEncoder::set_link_type(size_t col_ndx, LinkType link_type) +{ + append_simple_instr(instr_SetLinkType, util::tuple(col_ndx, int(link_type))); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link_type(const Table* t, size_t col_ndx, LinkType link_type) +{ + select_table(t); // Throws + m_encoder.set_link_type(col_ndx, link_type); // Throws +} + + +inline bool TransactLogEncoder::clear_table() +{ + append_simple_instr(instr_ClearTable, util::tuple()); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::clear_table(const Table* t) +{ + select_table(t); // Throws + m_encoder.clear_table(); // Throws +} + +inline bool TransactLogEncoder::optimize_table() +{ + append_simple_instr(instr_OptimizeTable, util::tuple()); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::optimize_table(const Table* t) +{ + select_table(t); // Throws + m_encoder.optimize_table(); // Throws +} + +inline bool TransactLogEncoder::link_list_set(size_t link_ndx, size_t value, size_t prior_size) +{ + append_simple_instr(instr_LinkListSet, util::tuple(link_ndx, value, prior_size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_set(const LinkView& list, size_t link_ndx, size_t value) +{ + select_link_list(list); // Throws + m_encoder.link_list_set(link_ndx, value, list.size()); // Throws +} + +inline bool TransactLogEncoder::link_list_nullify(size_t link_ndx, size_t prior_size) +{ + append_simple_instr(instr_LinkListNullify, util::tuple(link_ndx, prior_size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_nullify(const LinkView& list, size_t link_ndx) +{ + select_link_list(list); // Throws + size_t prior_size = list.size(); // Instruction is emitted before the fact. + m_encoder.link_list_nullify(link_ndx, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_set_all(const IntegerColumn& values) +{ + struct iter { + iter(const IntegerColumn& iter_values, size_t ndx) + : m_values(&iter_values) + , m_ndx(ndx) + { + } + const IntegerColumn* m_values; + size_t m_ndx; + bool operator==(const iter& i) const + { + return m_ndx == i.m_ndx; + } + bool operator!=(const iter& i) const + { + return m_ndx != i.m_ndx; + } + size_t operator-(const iter& i) const + { + return m_ndx - i.m_ndx; + } + int_fast64_t operator*() const + { + return m_values->get(m_ndx); + } + iter& operator++() + { + ++m_ndx; + return *this; + } + iter operator++(int) + { + iter i = *this; + ++m_ndx; + return i; + } + }; + size_t num_values = values.size(); + append_variable_size_instr(instr_LinkListSetAll, util::tuple(num_values), iter(values, 0), + iter(values, num_values)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::set_link_list(const LinkView& list, const IntegerColumn& values) +{ + select_link_list(list); // Throws + m_encoder.link_list_set_all(values); // Throws +} + +inline bool TransactLogEncoder::link_list_insert(size_t link_ndx, size_t value, size_t prior_size) +{ + append_simple_instr(instr_LinkListInsert, util::tuple(link_ndx, value, prior_size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_insert(const LinkView& list, size_t link_ndx, size_t value) +{ + select_link_list(list); // Throws + size_t prior_size = list.size() - 1; // The instruction is emitted after the fact. + m_encoder.link_list_insert(link_ndx, value, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_move(size_t from_link_ndx, size_t to_link_ndx) +{ + REALM_ASSERT(from_link_ndx != to_link_ndx); + append_simple_instr(instr_LinkListMove, util::tuple(from_link_ndx, to_link_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_move(const LinkView& list, size_t from_link_ndx, + size_t to_link_ndx) +{ + select_link_list(list); // Throws + m_encoder.link_list_move(from_link_ndx, to_link_ndx); // Throws +} + +inline bool TransactLogEncoder::link_list_swap(size_t link1_ndx, size_t link2_ndx) +{ + append_simple_instr(instr_LinkListSwap, util::tuple(link1_ndx, link2_ndx)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_swap(const LinkView& list, size_t link1_ndx, size_t link2_ndx) +{ + select_link_list(list); // Throws + m_encoder.link_list_swap(link1_ndx, link2_ndx); // Throws +} + +inline bool TransactLogEncoder::link_list_erase(size_t link_ndx, size_t prior_size) +{ + append_simple_instr(instr_LinkListErase, util::tuple(link_ndx, prior_size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::link_list_erase(const LinkView& list, size_t link_ndx) +{ + select_link_list(list); // Throws + size_t prior_size = list.size(); // The instruction is emitted before the fact. + m_encoder.link_list_erase(link_ndx, prior_size); // Throws +} + +inline bool TransactLogEncoder::link_list_clear(size_t old_list_size) +{ + append_simple_instr(instr_LinkListClear, util::tuple(old_list_size)); // Throws + return true; +} + +inline void TransactLogConvenientEncoder::on_table_destroyed(const Table* t) noexcept +{ + if (m_selected_table == t) + m_selected_table = nullptr; +} + +inline void TransactLogConvenientEncoder::on_spec_destroyed(const Spec* s) noexcept +{ + if (m_selected_spec == s) + m_selected_spec = nullptr; +} + + +inline void TransactLogConvenientEncoder::on_link_list_destroyed(const LinkView& list) noexcept +{ + const LinkView* lw_ptr = &list; + // atomically clear m_selected_link_list iff it already points to 'list': + // (lw_ptr will be modified if the swap fails, but we ignore that) + m_selected_link_list.compare_exchange_strong(lw_ptr, nullptr, std::memory_order_relaxed, + std::memory_order_relaxed); +} + + +inline TransactLogParser::TransactLogParser() + : m_input_buffer(1024) // Throws +{ +} + + +inline TransactLogParser::~TransactLogParser() noexcept +{ +} + + +template +void TransactLogParser::parse(NoCopyInputStream& in, InstructionHandler& handler) +{ + m_input = ∈ + m_input_begin = m_input_end = nullptr; + + while (has_next()) + parse_one(handler); // Throws +} + +template +void TransactLogParser::parse(InputStream& in, InstructionHandler& handler) +{ + NoCopyInputStreamAdaptor in_2(in, m_input_buffer.data(), m_input_buffer.size()); + parse(in_2, handler); // Throws +} + +inline bool TransactLogParser::has_next() noexcept +{ + return m_input_begin != m_input_end || next_input_buffer(); +} + +template +void TransactLogParser::parse_one(InstructionHandler& handler) +{ + char instr_ch; + if (!read_char(instr_ch)) + parser_error(); // Throws + // std::cerr << "parsing " << util::promote(instr) << " @ " << std::hex << long(m_input_begin) << std::dec << + // "\n"; + Instruction instr = Instruction(instr_ch); + switch (instr) { + case instr_SetDefault: + case instr_SetUnique: + case instr_Set: { + int type = read_int(); // Throws + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t prior_num_rows = 0; + if (REALM_UNLIKELY(instr == instr_SetUnique)) + prior_num_rows = read_int(); // Throws + + if (type == TransactLogEncoder::set_null_sentinel()) { + // Special case for set_null + if (!handler.set_null(col_ndx, row_ndx, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + + switch (DataType(type)) { + case type_Int: { + int_fast64_t value = read_int(); // Throws + if (!handler.set_int(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + case type_Bool: { + bool value = read_bool(); // Throws + if (!handler.set_bool(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Float: { + float value = read_float(); // Throws + if (!handler.set_float(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Double: { + double value = read_double(); // Throws + if (!handler.set_double(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_String: { + StringData value = read_string(m_string_buffer); // Throws + if (!handler.set_string(col_ndx, row_ndx, value, instr, prior_num_rows)) // Throws + parser_error(); + return; + } + case type_Binary: { + BinaryData value = read_binary(m_string_buffer); // Throws + if (!handler.set_binary(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_OldDateTime: { + int_fast64_t value = read_int(); // Throws + if (!handler.set_olddatetime(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Timestamp: { + int64_t seconds = read_int(); // Throws + int32_t nanoseconds = read_int(); // Throws + Timestamp value = Timestamp(seconds, nanoseconds); + if (!handler.set_timestamp(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Table: { + if (!handler.set_table(col_ndx, row_ndx, instr)) // Throws + parser_error(); + return; + } + case type_Mixed: { + Mixed value; + read_mixed(&value); // Throws + if (!handler.set_mixed(col_ndx, row_ndx, value, instr)) // Throws + parser_error(); + return; + } + case type_Link: { + size_t value = read_int(); // Throws + // Map zero to realm::npos, and `n+1` to `n`, where `n` is a target row index. + size_t target_row_ndx = size_t(value - 1); + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.set_link(col_ndx, row_ndx, target_row_ndx, target_group_level_ndx, instr)) // Throws + parser_error(); + return; + } + case type_LinkList: { + // Unsupported column type for Set. + parser_error(); + return; + } + } + parser_error(); + return; + } + case instr_AddInteger: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + int_fast64_t value = read_int(); // Throws + if (!handler.add_int(col_ndx, row_ndx, value)) // Throws + parser_error(); + return; + } + case instr_NullifyLink: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.nullify_link(col_ndx, row_ndx, target_group_level_ndx)) // Throws + parser_error(); + return; + } + case instr_InsertSubstring: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t pos = read_int(); // Throws + StringData value = read_string(m_string_buffer); // Throws + if (!handler.insert_substring(col_ndx, row_ndx, pos, value)) // Throws + parser_error(); + return; + } + case instr_EraseFromString: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t pos = read_int(); // Throws + size_t size = read_int(); // Throws + if (!handler.erase_substring(col_ndx, row_ndx, pos, size)) // Throws + parser_error(); + return; + } + case instr_InsertEmptyRows: { + size_t row_ndx = read_int(); // Throws + size_t num_rows_to_insert = read_int(); // Throws + size_t prior_num_rows = read_int(); // Throws + bool unordered = read_bool(); // Throws + if (!handler.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows, unordered)) // Throws + parser_error(); + return; + } + case instr_EraseRows: { + size_t row_ndx = read_int(); // Throws + size_t num_rows_to_erase = read_int(); // Throws + size_t prior_num_rows = read_int(); // Throws + bool unordered = read_bool(); // Throws + if (!handler.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, unordered)) // Throws + parser_error(); + return; + } + case instr_SwapRows: { + size_t row_ndx_1 = read_int(); // Throws + size_t row_ndx_2 = read_int(); // Throws + if (!handler.swap_rows(row_ndx_1, row_ndx_2)) // Throws + parser_error(); + return; + } + case instr_MergeRows: { + size_t row_ndx = read_int(); // Throws + size_t new_row_ndx = read_int(); // Throws + if (!handler.merge_rows(row_ndx, new_row_ndx)) // Throws + parser_error(); + return; + } + case instr_SelectTable: { + int levels = read_int(); // Throws + if (levels < 0 || levels > m_max_levels) + parser_error(); + m_path.reserve(0, 2 * levels); // Throws + size_t* path = m_path.data(); + size_t group_level_ndx = read_int(); // Throws + for (int i = 0; i != levels; ++i) { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + path[2 * i + 0] = col_ndx; + path[2 * i + 1] = row_ndx; + } + if (!handler.select_table(group_level_ndx, levels, path)) // Throws + parser_error(); + return; + } + case instr_ClearTable: { + if (!handler.clear_table()) // Throws + parser_error(); + return; + } + case instr_LinkListSet: { + size_t link_ndx = read_int(); // Throws + size_t value = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_set(link_ndx, value, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListSetAll: { + // todo, log that it's a SetAll we're doing + size_t size = read_int(); // Throws + for (size_t i = 0; i < size; i++) { + size_t link = read_int(); // Throws + if (!handler.link_list_set(i, link, size)) // Throws + parser_error(); + } + return; + } + case instr_LinkListInsert: { + size_t link_ndx = read_int(); // Throws + size_t value = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_insert(link_ndx, value, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListMove: { + size_t from_link_ndx = read_int(); // Throws + size_t to_link_ndx = read_int(); // Throws + if (!handler.link_list_move(from_link_ndx, to_link_ndx)) // Throws + parser_error(); + return; + } + case instr_LinkListSwap: { + size_t link1_ndx = read_int(); // Throws + size_t link2_ndx = read_int(); // Throws + if (!handler.link_list_swap(link1_ndx, link2_ndx)) // Throws + parser_error(); + return; + } + case instr_LinkListErase: { + size_t link_ndx = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_erase(link_ndx, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListNullify: { + size_t link_ndx = read_int(); // Throws + size_t prior_size = read_int(); // Throws + if (!handler.link_list_nullify(link_ndx, prior_size)) // Throws + parser_error(); + return; + } + case instr_LinkListClear: { + size_t old_list_size = read_int(); // Throws + if (!handler.link_list_clear(old_list_size)) // Throws + parser_error(); + return; + } + case instr_SelectLinkList: { + size_t col_ndx = read_int(); // Throws + size_t row_ndx = read_int(); // Throws + size_t target_group_level_ndx = read_int(); // Throws + if (!handler.select_link_list(col_ndx, row_ndx, target_group_level_ndx)) // Throws + parser_error(); + return; + } + case instr_AddSearchIndex: { + size_t col_ndx = read_int(); // Throws + if (!handler.add_search_index(col_ndx)) // Throws + parser_error(); + return; + } + case instr_RemoveSearchIndex: { + size_t col_ndx = read_int(); // Throws + if (!handler.remove_search_index(col_ndx)) // Throws + parser_error(); + return; + } + case instr_SetLinkType: { + size_t col_ndx = read_int(); // Throws + int link_type = read_int(); // Throws + if (!is_valid_link_type(link_type)) + parser_error(); + if (!handler.set_link_type(col_ndx, LinkType(link_type))) // Throws + parser_error(); + return; + } + case instr_InsertColumn: + case instr_InsertNullableColumn: { + size_t col_ndx = read_int(); // Throws + int type = read_int(); // Throws + if (!is_valid_data_type(type)) + parser_error(); + if (REALM_UNLIKELY(type == type_Link || type == type_LinkList)) + parser_error(); + StringData name = read_string(m_string_buffer); // Throws + bool nullable = (Instruction(instr) == instr_InsertNullableColumn); + if (REALM_UNLIKELY(nullable && (type == type_Table || type == type_Mixed))) { + // Nullability not supported for Table and Mixed columns. + parser_error(); + } + if (!handler.insert_column(col_ndx, DataType(type), name, nullable)) // Throws + parser_error(); + return; + } + case instr_InsertLinkColumn: { + size_t col_ndx = read_int(); // Throws + int type = read_int(); // Throws + if (!is_valid_data_type(type)) + parser_error(); + if (REALM_UNLIKELY(type != type_Link && type != type_LinkList)) + parser_error(); + size_t link_target_table_ndx = read_int(); // Throws + size_t backlink_col_ndx = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.insert_link_column(col_ndx, DataType(type), name, link_target_table_ndx, + backlink_col_ndx)) // Throws + parser_error(); + return; + } + case instr_EraseColumn: { + size_t col_ndx = read_int(); // Throws + if (!handler.erase_column(col_ndx)) // Throws + parser_error(); + return; + } + case instr_EraseLinkColumn: { + size_t col_ndx = read_int(); // Throws + size_t link_target_table_ndx = read_int(); // Throws + size_t backlink_col_ndx = read_int(); // Throws + if (!handler.erase_link_column(col_ndx, link_target_table_ndx, backlink_col_ndx)) // Throws + parser_error(); + return; + } + case instr_RenameColumn: { + size_t col_ndx = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.rename_column(col_ndx, name)) // Throws + parser_error(); + return; + } + case instr_MoveColumn: { + size_t col_ndx_1 = read_int(); // Throws + size_t col_ndx_2 = read_int(); // Throws + if (!handler.move_column(col_ndx_1, col_ndx_2)) // Throws + parser_error(); + return; + } + case instr_SelectDescriptor: { + int levels = read_int(); // Throws + if (levels < 0 || levels > m_max_levels) + parser_error(); + m_path.reserve(0, levels); // Throws + size_t* path = m_path.data(); + for (int i = 0; i != levels; ++i) { + size_t col_ndx = read_int(); // Throws + path[i] = col_ndx; + } + if (!handler.select_descriptor(levels, path)) // Throws + parser_error(); + return; + } + case instr_InsertGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + size_t num_tables = read_int(); // Throws + StringData name = read_string(m_string_buffer); // Throws + if (!handler.insert_group_level_table(table_ndx, num_tables, name)) // Throws + parser_error(); + return; + } + case instr_EraseGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + size_t prior_num_tables = read_int(); // Throws + if (!handler.erase_group_level_table(table_ndx, prior_num_tables)) // Throws + parser_error(); + return; + } + case instr_RenameGroupLevelTable: { + size_t table_ndx = read_int(); // Throws + StringData new_name = read_string(m_string_buffer); // Throws + if (!handler.rename_group_level_table(table_ndx, new_name)) // Throws + parser_error(); + return; + } + case instr_MoveGroupLevelTable: { + size_t from_table_ndx = read_int(); // Throws + size_t to_table_ndx = read_int(); // Throws + if (!handler.move_group_level_table(from_table_ndx, to_table_ndx)) // Throws + parser_error(); + return; + } + case instr_OptimizeTable: { + if (!handler.optimize_table()) // Throws + parser_error(); + return; + } + } + + throw BadTransactLog(); +} + + +template +T TransactLogParser::read_int() +{ + T value = 0; + int part = 0; + const int max_bytes = (std::numeric_limits::digits + 1 + 6) / 7; + for (int i = 0; i != max_bytes; ++i) { + char c; + if (!read_char(c)) + goto bad_transact_log; + part = static_cast(c); + if (0xFF < part) + goto bad_transact_log; // Only the first 8 bits may be used in each byte + if ((part & 0x80) == 0) { + T p = part & 0x3F; + if (util::int_shift_left_with_overflow_detect(p, i * 7)) + goto bad_transact_log; + value |= p; + break; + } + if (i == max_bytes - 1) + goto bad_transact_log; // Too many bytes + value |= T(part & 0x7F) << (i * 7); + } + if (part & 0x40) { + // The real value is negative. Because 'value' is positive at + // this point, the following negation is guaranteed by C++11 + // to never overflow. See C99+TC3 section 6.2.6.2 paragraph 2. + REALM_DIAG_PUSH(); + REALM_DIAG_IGNORE_UNSIGNED_MINUS(); + value = -value; + REALM_DIAG_POP(); + if (util::int_subtract_with_overflow_detect(value, 1)) + goto bad_transact_log; + } + return value; + +bad_transact_log: + throw BadTransactLog(); +} + + +inline void TransactLogParser::read_bytes(char* data, size_t size) +{ + for (;;) { + const size_t avail = m_input_end - m_input_begin; + if (size <= avail) + break; + const char* to = m_input_begin + avail; + std::copy(m_input_begin, to, data); + if (!next_input_buffer()) + throw BadTransactLog(); + data += avail; + size -= avail; + } + const char* to = m_input_begin + size; + std::copy(m_input_begin, to, data); + m_input_begin = to; +} + + +inline BinaryData TransactLogParser::read_buffer(util::StringBuffer& buf, size_t size) +{ + const size_t avail = m_input_end - m_input_begin; + if (avail >= size) { + m_input_begin += size; + return BinaryData(m_input_begin - size, size); + } + + buf.clear(); + buf.resize(size); // Throws + read_bytes(buf.data(), size); + return BinaryData(buf.data(), size); +} + + +inline bool TransactLogParser::read_bool() +{ + return read_int(); +} + + +inline float TransactLogParser::read_float() +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(float) * std::numeric_limits::digits == 32, + "Unsupported 'float' representation"); + float value; + read_bytes(reinterpret_cast(&value), sizeof value); // Throws + return value; +} + + +inline double TransactLogParser::read_double() +{ + static_assert(std::numeric_limits::is_iec559 && + sizeof(double) * std::numeric_limits::digits == 64, + "Unsupported 'double' representation"); + double value; + read_bytes(reinterpret_cast(&value), sizeof value); // Throws + return value; +} + + +inline StringData TransactLogParser::read_string(util::StringBuffer& buf) +{ + size_t size = read_int(); // Throws + + if (size > Table::max_string_size) + parser_error(); + + BinaryData buffer = read_buffer(buf, size); + return StringData{buffer.data(), size}; +} + +inline Timestamp TransactLogParser::read_timestamp() +{ + REALM_ASSERT(false); + return Timestamp{}; +} + + +inline BinaryData TransactLogParser::read_binary(util::StringBuffer& buf) +{ + size_t size = read_int(); // Throws + + if (size > ArrayBlob::max_binary_size) + parser_error(); + + return read_buffer(buf, size); +} + + +inline void TransactLogParser::read_mixed(Mixed* mixed) +{ + DataType type = DataType(read_int()); // Throws + switch (type) { + case type_Int: { + int_fast64_t value = read_int(); // Throws + mixed->set_int(value); + return; + } + case type_Bool: { + bool value = read_bool(); // Throws + mixed->set_bool(value); + return; + } + case type_Float: { + float value = read_float(); // Throws + mixed->set_float(value); + return; + } + case type_Double: { + double value = read_double(); // Throws + mixed->set_double(value); + return; + } + case type_OldDateTime: { + int_fast64_t value = read_int(); // Throws + mixed->set_olddatetime(value); + return; + } + case type_Timestamp: { + Timestamp value = read_timestamp(); // Throws + mixed->set_timestamp(value); + return; + } + case type_String: { + StringData value = read_string(m_string_buffer); // Throws + mixed->set_string(value); + return; + } + case type_Binary: { + BinaryData value = read_binary(m_string_buffer); // Throws + mixed->set_binary(value); + return; + } + case type_Table: { + *mixed = Mixed::subtable_tag(); + return; + } + case type_Mixed: + break; + case type_Link: + case type_LinkList: + // FIXME: Need to handle new link types here + break; + } + throw BadTransactLog(); +} + + +inline bool TransactLogParser::next_input_buffer() +{ + return m_input->next_block(m_input_begin, m_input_end); +} + + +inline bool TransactLogParser::read_char(char& c) +{ + if (m_input_begin == m_input_end && !next_input_buffer()) + return false; + c = *m_input_begin++; + return true; +} + + +inline bool TransactLogParser::is_valid_data_type(int type) +{ + switch (DataType(type)) { + case type_Int: + case type_Bool: + case type_Float: + case type_Double: + case type_String: + case type_Binary: + case type_OldDateTime: + case type_Timestamp: + case type_Table: + case type_Mixed: + case type_Link: + case type_LinkList: + return true; + } + return false; +} + + +inline bool TransactLogParser::is_valid_link_type(int type) +{ + switch (LinkType(type)) { + case link_Strong: + case link_Weak: + return true; + } + return false; +} + + +class TransactReverser { +public: + bool select_table(size_t group_level_ndx, size_t levels, const size_t* path) + { + sync_table(); + m_encoder.select_table(group_level_ndx, levels, path); + m_pending_ts_instr = get_inst(); + return true; + } + + bool select_descriptor(size_t levels, const size_t* path) + { + sync_descriptor(); + m_encoder.select_descriptor(levels, path); + m_pending_ds_instr = get_inst(); + return true; + } + + bool insert_group_level_table(size_t table_ndx, size_t num_tables, StringData) + { + sync_table(); + m_encoder.erase_group_level_table(table_ndx, num_tables + 1); + append_instruction(); + return true; + } + + bool erase_group_level_table(size_t table_ndx, size_t num_tables) + { + sync_table(); + m_encoder.insert_group_level_table(table_ndx, num_tables - 1, ""); + append_instruction(); + return true; + } + + bool rename_group_level_table(size_t, StringData) + { + sync_table(); + return true; + } + + bool move_group_level_table(size_t from_table_ndx, size_t to_table_ndx) + { + sync_table(); + m_encoder.move_group_level_table(to_table_ndx, from_table_ndx); + append_instruction(); + return true; + } + + bool optimize_table() + { + return true; // No-op + } + + bool insert_empty_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool unordered) + { + size_t num_rows_to_erase = num_rows_to_insert; + size_t prior_num_rows_2 = prior_num_rows + num_rows_to_insert; + m_encoder.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows_2, unordered); // Throws + append_instruction(); + return true; + } + + bool erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows, bool unordered) + { + size_t num_rows_to_insert = num_rows_to_erase; + // Number of rows in table after removal, but before inverse insertion + size_t prior_num_rows_2 = prior_num_rows - num_rows_to_erase; + m_encoder.insert_empty_rows(row_ndx, num_rows_to_insert, prior_num_rows_2, unordered); // Throws + append_instruction(); + return true; + } + + bool swap_rows(size_t row_ndx_1, size_t row_ndx_2) + { + m_encoder.swap_rows(row_ndx_1, row_ndx_2); + append_instruction(); + return true; + } + + bool merge_rows(size_t row_ndx, size_t new_row_ndx) + { + static_cast(row_ndx); + static_cast(new_row_ndx); + // There is no instruction we can generate here to change back. + return true; + } + + bool set_int(size_t col_ndx, size_t row_ndx, int_fast64_t value, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_int(col_ndx, row_ndx, value, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool add_int(size_t col_ndx, size_t row_ndx, int_fast64_t value) + { + m_encoder.add_int(col_ndx, row_ndx, -value); + append_instruction(); + return true; + } + + bool set_bool(size_t col_ndx, size_t row_ndx, bool value, Instruction variant) + { + m_encoder.set_bool(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_float(size_t col_ndx, size_t row_ndx, float value, Instruction variant) + { + m_encoder.set_float(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_double(size_t col_ndx, size_t row_ndx, double value, Instruction variant) + { + m_encoder.set_double(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_string(size_t col_ndx, size_t row_ndx, StringData value, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_string(col_ndx, row_ndx, value, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool set_binary(size_t col_ndx, size_t row_ndx, BinaryData value, Instruction variant) + { + m_encoder.set_binary(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_olddatetime(size_t col_ndx, size_t row_ndx, OldDateTime value, Instruction variant) + { + m_encoder.set_olddatetime(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_timestamp(size_t col_ndx, size_t row_ndx, Timestamp value, Instruction variant) + { + m_encoder.set_timestamp(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_table(size_t col_ndx, size_t row_ndx, Instruction variant) + { + m_encoder.set_table(col_ndx, row_ndx, variant); + append_instruction(); + return true; + } + + bool set_mixed(size_t col_ndx, size_t row_ndx, const Mixed& value, Instruction variant) + { + m_encoder.set_mixed(col_ndx, row_ndx, value, variant); + append_instruction(); + return true; + } + + bool set_null(size_t col_ndx, size_t row_ndx, Instruction variant, size_t prior_num_rows) + { + m_encoder.set_null(col_ndx, row_ndx, variant, prior_num_rows); + append_instruction(); + return true; + } + + bool set_link(size_t col_ndx, size_t row_ndx, size_t value, size_t target_group_level_ndx, Instruction variant) + { + m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx, variant); + append_instruction(); + return true; + } + + bool insert_substring(size_t, size_t, size_t, StringData) + { + return true; // No-op + } + + bool erase_substring(size_t, size_t, size_t, size_t) + { + return true; // No-op + } + + bool clear_table() + { + // FIXME: Add a comment on why we call insert_empty_rows() inside clear_table() + m_encoder.insert_empty_rows(0, 0, 0, true); + append_instruction(); + return true; + } + + bool add_search_index(size_t) + { + return true; // No-op + } + + bool remove_search_index(size_t) + { + return true; // No-op + } + + bool set_link_type(size_t, LinkType) + { + return true; // No-op + } + + bool insert_link_column(size_t col_idx, DataType, StringData, size_t target_table_idx, size_t backlink_col_ndx) + { + m_encoder.erase_link_column(col_idx, target_table_idx, backlink_col_ndx); + append_instruction(); + return true; + } + + bool erase_link_column(size_t col_idx, size_t target_table_idx, size_t backlink_col_idx) + { + DataType type = type_Link; // The real type of the column doesn't matter here, + // but the encoder asserts that it's actually a link type. + m_encoder.insert_link_column(col_idx, type, "", target_table_idx, backlink_col_idx); + append_instruction(); + return true; + } + + bool insert_column(size_t col_idx, DataType, StringData, bool) + { + m_encoder.erase_column(col_idx); + append_instruction(); + return true; + } + + bool erase_column(size_t col_idx) + { + m_encoder.insert_column(col_idx, DataType(), ""); + append_instruction(); + return true; + } + + bool rename_column(size_t, StringData) + { + return true; // No-op + } + + bool move_column(size_t col_ndx_1, size_t col_ndx_2) + { + m_encoder.move_column(col_ndx_2, col_ndx_1); + append_instruction(); + return true; + } + + bool select_link_list(size_t col_ndx, size_t row_ndx, size_t link_target_group_level_ndx) + { + sync_linkview(); + m_encoder.select_link_list(col_ndx, row_ndx, link_target_group_level_ndx); + m_pending_lv_instr = get_inst(); + return true; + } + + bool link_list_set(size_t row, size_t value, size_t prior_size) + { + m_encoder.link_list_set(row, value, prior_size); + append_instruction(); + return true; + } + + bool link_list_insert(size_t link_ndx, size_t, size_t prior_size) + { + m_encoder.link_list_erase(link_ndx, prior_size + 1); + append_instruction(); + return true; + } + + bool link_list_move(size_t from_link_ndx, size_t to_link_ndx) + { + m_encoder.link_list_move(from_link_ndx, to_link_ndx); + append_instruction(); + return true; + } + + bool link_list_swap(size_t link1_ndx, size_t link2_ndx) + { + m_encoder.link_list_swap(link1_ndx, link2_ndx); + append_instruction(); + return true; + } + + bool link_list_erase(size_t link_ndx, size_t prior_size) + { + m_encoder.link_list_insert(link_ndx, 0, prior_size - 1); + append_instruction(); + return true; + } + + bool link_list_clear(size_t old_list_size) + { + // Append in reverse order because the reversed log is itself applied + // in reverse, and this way it generates all back-insertions rather than + // all front-insertions + for (size_t i = old_list_size; i > 0; --i) { + m_encoder.link_list_insert(i - 1, 0, old_list_size - i); + append_instruction(); + } + return true; + } + + bool nullify_link(size_t col_ndx, size_t row_ndx, size_t target_group_level_ndx) + { + size_t value = 0; + // FIXME: Is zero this right value to pass here, or should + // TransactReverser::nullify_link() also have taken a + // `target_group_level_ndx` argument. + m_encoder.set_link(col_ndx, row_ndx, value, target_group_level_ndx); + append_instruction(); + return true; + } + + bool link_list_nullify(size_t link_ndx, size_t prior_size) + { + m_encoder.link_list_insert(link_ndx, 0, prior_size - 1); + append_instruction(); + return true; + } + +private: + _impl::TransactLogBufferStream m_buffer; + _impl::TransactLogEncoder m_encoder{m_buffer}; + struct Instr { + size_t begin; + size_t end; + }; + std::vector m_instructions; + size_t current_instr_start = 0; + Instr m_pending_ts_instr{0, 0}; + Instr m_pending_ds_instr{0, 0}; + Instr m_pending_lv_instr{0, 0}; + + Instr get_inst() + { + Instr instr; + instr.begin = current_instr_start; + current_instr_start = transact_log_size(); + instr.end = current_instr_start; + return instr; + } + + size_t transact_log_size() const + { + REALM_ASSERT_3(m_encoder.write_position(), >=, m_buffer.transact_log_data()); + return m_encoder.write_position() - m_buffer.transact_log_data(); + } + + void append_instruction() + { + m_instructions.push_back(get_inst()); + } + + void append_instruction(Instr instr) + { + m_instructions.push_back(instr); + } + + void sync_select(Instr& pending_instr) + { + if (pending_instr.begin != pending_instr.end) { + append_instruction(pending_instr); + pending_instr = {0, 0}; + } + } + + void sync_linkview() + { + sync_select(m_pending_lv_instr); + } + + void sync_descriptor() + { + sync_linkview(); + sync_select(m_pending_ds_instr); + } + + void sync_table() + { + sync_descriptor(); + sync_select(m_pending_ts_instr); + } + + friend class ReversedNoCopyInputStream; +}; + + +class ReversedNoCopyInputStream : public NoCopyInputStream { +public: + ReversedNoCopyInputStream(TransactReverser& reverser) + : m_instr_order(reverser.m_instructions) + { + // push any pending select_table or select_descriptor into the buffer + reverser.sync_table(); + + m_buffer = reverser.m_buffer.transact_log_data(); + m_current = m_instr_order.size(); + } + + bool next_block(const char*& begin, const char*& end) override + { + if (m_current != 0) { + m_current--; + begin = m_buffer + m_instr_order[m_current].begin; + end = m_buffer + m_instr_order[m_current].end; + return (end > begin); + } + return false; + } + +private: + const char* m_buffer; + std::vector& m_instr_order; + size_t m_current; +}; + +} // namespace _impl +} // namespace realm + +#endif // REALM_IMPL_TRANSACT_LOG_HPP diff --git a/Pods/Realm/include/core/realm/importer.hpp b/Pods/Realm/include/core/realm/importer.hpp new file mode 100644 index 0000000..9ef54dc --- /dev/null +++ b/Pods/Realm/include/core/realm/importer.hpp @@ -0,0 +1,131 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_IMPORTER_HPP +#define REALM_IMPORTER_HPP + +/* +Main method: import_csv(). Arguments: +--------------------------------------------------------------------------------------------------------------------- +empty_as_string_flag: + Imports a column that has occurences of empty strings as String type column. Else fields arec onverted to + false/0/0.0 + +type_detection_rows: + tells how many rows to read before analyzing data types (to see if numeric rows are really + numeric everywhere, and not strings that happen to just mostly contain numeric characters + + +This library supports: +--------------------------------------------------------------------------------------------------------------------- + * Auto detection of float vs. double, depending on number of significant digits + * Bool types can be case insensitive "true, false, 0, 1, yes, no" + * Newline inside data fields, plus auto detection of non-conforming non-quoted newlines (as in some IBM sample + files) + * Realm types String, Integer, Bool, Float and Double + * Auto detection of header and naming of Realm columns accordingly + * double-quoted and non-quoted fields, and these can be mixed arbitrarely + * double-quotes inside data field + * *nix + MacOSv9 + Windows line feed + * Scientific notation of floats/doubles (+1.23e-10) + * Comma in floats - but ONLY if field is double-quoted + * FAST FAST FAST (200 MB/s). Uses state-machine instead of traditional char-by-char loop with state checks inside + + +Problems: +--------------------------------------------------------------------------------------------------------------------- + A csv file does not tell its sheme. So we auto-detect it, based on the first N rows. However if a given column + contains 'false, false, false, hello' and we detect and create Realm table scheme using the first 3 rows, we fail + when we meet 'hello' (this error is handled with a thorough error message) + + Does not support commas in floats unless field is double-quoted + + +Design: +--------------------------------------------------------------------------------------------------------------------- + +import_csv(csv file handle, realm table) + Calls tokenize(csv file handle): + reads payload chunk and returns std::vector> with the right dimensions filled with + rows and columns of the chunk payload + Calls parse_float(), parse_bool(), etc, which tests for type and returns converted values + Calls table.add_empty_row(), table.set_float(), table.set_bool() +*/ + +#include + +// Disk read chunk size. This MUST be large enough to contain at least TWO rows of csv plaintext! It's a good idea +// to set it as low as ever possible (like 32 K) even though it's counter-intuitive with respect to performance. It +// will make the operating system read 32 K from disk and return it, and then read-ahead 32-64 K more after fread() +// has returned. This read-ahead behaviour does NOT occur if we request megabyte-sized chunks (observed on Windows 7 / +// Ubuntu) +static const size_t chunk_size = 32 * 1024; + +// Number of rows to csv-parse + insert into realm in each iteration. +static const size_t record_chunks = 100; + +// Width of each column when printing them on screen (non-Quiet mode) +const size_t print_width = 25; + +#include +#include + +namespace realm { + +class Importer { +public: + Importer(); + size_t import_csv_auto(FILE* file, Table& table, size_t type_detection_rows = 1000, + size_t import_rows = static_cast(-1)); + + size_t import_csv_manual(FILE* file, Table& table, std::vector scheme, + std::vector column_names, size_t skip_first_rows = 0, + size_t import_rows = static_cast(-1)); + + bool Quiet; // Quiet mode, only print to screen upon errors + char Separator; // csv delimitor/separator + bool Empty_as_string; // Import columns that have occurences of empty strings as String type column + +private: + size_t import_csv(FILE* file, Table& table, std::vector* import_scheme, + std::vector* column_names, size_t type_detection_rows, size_t skip_first_rows, + size_t import_rows); + template + float parse_float(const char* col, bool* success = nullptr); + template + double parse_double(const char* col, bool* success = nullptr, size_t* significants = nullptr); + template + int64_t parse_integer(const char* col, bool* success = nullptr); + template + bool parse_bool(const char* col, bool* success = nullptr); + std::vector types(std::vector v); + size_t tokenize(std::vector>& payload, size_t records); + std::vector detect_scheme(std::vector> payload, size_t begin, size_t end); + std::vector lowest_common(std::vector types1, std::vector types2); + + char src[2 * chunk_size]; // .csv input buffer + size_t m_top; // points at top of buffer + size_t m_curpos; // points at next byte to parse + FILE* m_file; // handle to .csv file + size_t m_fields; // number of fields in each row + size_t m_row; // current row in .csv file, including field-embedded line breaks. Used for err msg only +}; + +} // namespace realm + +#endif // REALM_IMPORTER_HPP diff --git a/Pods/Realm/include/core/realm/index_string.hpp b/Pods/Realm/include/core/realm/index_string.hpp new file mode 100644 index 0000000..69e749f --- /dev/null +++ b/Pods/Realm/include/core/realm/index_string.hpp @@ -0,0 +1,584 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_INDEX_STRING_HPP +#define REALM_INDEX_STRING_HPP + +#include +#include +#include + +#include +#include + +/* +The StringIndex class is used for both type_String and all integral types, such as type_Bool, type_OldDateTime and +type_Int. When used for integral types, the 64-bit integer is simply casted to a string of 8 bytes through a +pretty simple "wrapper layer" in all public methods. + +The StringIndex data structure is like an "inversed" B+ tree where the leafs contain row indexes and the non-leafs +contain 4-byte chunks of payload. Imagine a table with following strings: + + hello, kitty, kitten, foobar, kitty, foobar + +The topmost level of the index tree contains prefixes of the payload strings of length <= 4. The next level contains +prefixes of the remaining parts of the strings. Unnecessary levels of the tree are optimized away; the prefix "foob" +is shared only by rows that are identical ("foobar"), so "ar" is not needed to be stored in the tree. + + hell kitt foob + | /\ | + 0 en y {3, 5} + | \ + {1, 4} 2 + +Each non-leafs consists of two integer arrays of the same length, one containing payload and the other containing +references to the sublevel nodes. + +The leafs can be either a single value or a Column. If the reference in its parent node has its least significant +bit set, then the remaining upper bits specify the row index at which the string is stored. If the bit is clear, +it must be interpreted as a reference to a Column that stores the row indexes at which the string is stored. + +If a Column is used, then all row indexes are guaranteed to be sorted increasingly, which means you an search in it +using our binary search functions such as upper_bound() and lower_bound(). Each duplicate value will be stored in +the same Column, but Columns may contain more than just duplicates if the depth of the tree exceeds the value +`s_max_offset` This is to avoid stack overflow problems with many of our recursive functions if we have two very +long strings that have a long common prefix but differ in the last couple bytes. If a Column stores more than just +duplicates, then the list is kept sorted in ascending order by string value and within the groups of common +strings, the rows are sorted in ascending order. +*/ + +namespace realm { + +class Spec; +class Timestamp; + +class StringIndex { +public: + StringIndex(ColumnBase* target_column, Allocator&); + StringIndex(ref_type, ArrayParent*, size_t ndx_in_parent, ColumnBase* target_column, bool allow_duplicate_values, + Allocator&); + ~StringIndex() noexcept + { + } + void set_target(ColumnBase* target_column) noexcept; + + // Accessor concept: + Allocator& get_alloc() const noexcept; + void destroy() noexcept; + void detach(); + bool is_attached() const noexcept; + void set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept; + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t ndx_in_parent) noexcept; + void update_from_parent(size_t old_baseline) noexcept; + void refresh_accessor_tree(size_t, const Spec&); + ref_type get_ref() const noexcept; + + // StringIndex interface: + + // 12 is the biggest element size of any non-string/binary Realm type + static const size_t string_conversion_buffer_size = 12; + using StringConversionBuffer = std::array; + + bool is_empty() const; + + template + void insert(size_t row_ndx, T value, size_t num_rows, bool is_append); + template + void insert(size_t row_ndx, util::Optional value, size_t num_rows, bool is_append); + + template + void set(size_t row_ndx, T new_value); + template + void set(size_t row_ndx, util::Optional new_value); + + template + void erase(size_t row_ndx, bool is_last); + + template + size_t find_first(T value) const; + template + void find_all(IntegerColumn& result, T value) const; + template + FindRes find_all_no_copy(T value, InternalFindResult& result) const; + template + size_t count(T value) const; + template + void update_ref(T value, size_t old_row_ndx, size_t new_row_ndx); + + void clear(); + + void distinct(IntegerColumn& result) const; + bool has_duplicate_values() const noexcept; + + /// By default, duplicate values are allowed. + void set_allow_duplicate_values(bool) noexcept; + + void verify() const; +#ifdef REALM_DEBUG + void verify_entries(const StringColumn& column) const; + void do_dump_node_structure(std::ostream&, int) const; + void to_dot() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + + typedef int32_t key_type; + + // s_max_offset specifies the number of levels of recursive string indexes + // allowed before storing everything in lists. This is to avoid nesting + // to too deep of a level. Since every SubStringIndex stores 4 bytes, this + // means that a StringIndex is helpful for strings of a common prefix up to + // 4 times this limit (200 bytes shared). Lists are stored in sorted order, + // so strings sharing a common prefix of more than this limit will use a + // binary search of approximate complexity log2(n) from `std::lower_bound`. + static const size_t s_max_offset = 200; // max depth * s_index_key_length + static const size_t s_index_key_length = 4; + static key_type create_key(StringData) noexcept; + static key_type create_key(StringData, size_t) noexcept; + +private: + // m_array is a compact representation for storing the children of this StringIndex. + // Children can be: + // 1) a row number + // 2) a reference to a list which stores row numbers (for duplicate strings). + // 3) a reference to a sub-index + // m_array[0] is always a reference to a values array which stores the 4 byte chunk + // of payload data for quick string chunk comparisons. The array stored + // at m_array[0] lines up with the indices of values in m_array[1] so for example + // starting with an empty StringIndex: + // StringColumn::insert(target_row_ndx=42, value="test_string") would result with + // get_array_from_ref(m_array[0])[0] == create_key("test") and + // m_array[1] == 42 + // In this way, m_array which stores one child has a size of two. + // Children are type 1 (row number) if the LSB of the value is set. + // To get the actual row value, shift value down by one. + // If the LSB of the value is 0 then the value is a reference and can be either + // type 2, or type 3 (no shifting in either case). + // References point to a list if the context header flag is NOT set. + // If the header flag is set, references point to a sub-StringIndex (nesting). + std::unique_ptr m_array; + ColumnBase* m_target_column; + bool m_deny_duplicate_values; + + struct inner_node_tag { + }; + StringIndex(inner_node_tag, Allocator&); + + static Array* create_node(Allocator&, bool is_leaf); + + void insert_with_offset(size_t row_ndx, StringData value, size_t offset); + void insert_row_list(size_t ref, size_t offset, StringData value); + void insert_to_existing_list(size_t row, StringData value, IntegerColumn& list); + void insert_to_existing_list_at_lower(size_t row, StringData value, IntegerColumn& list, + const IntegerColumnIterator& lower); + key_type get_last_key() const; + + /// Add small signed \a diff to all elements that are greater than, or equal + /// to \a min_row_ndx. + void adjust_row_indexes(size_t min_row_ndx, int diff); + + struct NodeChange { + size_t ref1; + size_t ref2; + enum ChangeType { none, insert_before, insert_after, split } type; + NodeChange(ChangeType t, size_t r1 = 0, size_t r2 = 0) + : ref1(r1) + , ref2(r2) + , type(t) + { + } + NodeChange() + : ref1(0) + , ref2(0) + , type(none) + { + } + }; + + // B-Tree functions + void TreeInsert(size_t row_ndx, key_type, size_t offset, StringData value); + NodeChange do_insert(size_t ndx, key_type, size_t offset, StringData value); + /// Returns true if there is room or it can join existing entries + bool leaf_insert(size_t row_ndx, key_type, size_t offset, StringData value, bool noextend = false); + void node_insert_split(size_t ndx, size_t new_ref); + void node_insert(size_t ndx, size_t ref); + void do_delete(size_t ndx, StringData, size_t offset); + void do_update_ref(StringData value, size_t row_ndx, size_t new_row_ndx, size_t offset); + + StringData get(size_t ndx, StringConversionBuffer& buffer) const; + + void node_add_key(ref_type ref); + +#ifdef REALM_DEBUG + static void dump_node_structure(const Array& node, std::ostream&, int level); + void to_dot_2(std::ostream&, StringData title = StringData()) const; + static void array_to_dot(std::ostream&, const Array&); + static void keys_to_dot(std::ostream&, const Array&, StringData title = StringData()); +#endif +}; + + +class SortedListComparator { +public: + SortedListComparator(ColumnBase& column_values); + bool operator()(int64_t ndx, StringData needle); + bool operator()(StringData needle, int64_t ndx); + +private: + ColumnBase& values; +}; + + +// Implementation: + +template +struct GetIndexData; + +template <> +struct GetIndexData { + static StringData get_index_data(const int64_t& value, StringIndex::StringConversionBuffer& buffer) + { + const char* c = reinterpret_cast(&value); + std::copy(c, c + sizeof(int64_t), buffer.data()); + return StringData{buffer.data(), sizeof(int64_t)}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(StringData data, StringIndex::StringConversionBuffer&) + { + return data; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(null, StringIndex::StringConversionBuffer&) + { + return null{}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(const Timestamp&, StringIndex::StringConversionBuffer&); +}; + +template +struct GetIndexData> { + static StringData get_index_data(const util::Optional& value, StringIndex::StringConversionBuffer& buffer) + { + if (value) + return GetIndexData::get_index_data(*value, buffer); + return null{}; + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(float, StringIndex::StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + } +}; + +template <> +struct GetIndexData { + static StringData get_index_data(double, StringIndex::StringConversionBuffer&) + { + REALM_ASSERT_RELEASE(false); // LCOV_EXCL_LINE; Index on float not supported + } +}; + +template <> +struct GetIndexData : GetIndexData { +}; + +// to_str() is used by the integer index. The existing StringIndex is re-used for this +// by making IntegerColumn convert its integers to strings by calling to_str(). + +template +inline StringData to_str(T&& value, StringIndex::StringConversionBuffer& buffer) +{ + return GetIndexData::type>::get_index_data(value, buffer); +} + + +inline StringIndex::StringIndex(ColumnBase* target_column, Allocator& alloc) + : m_array(create_node(alloc, true)) // Throws + , m_target_column(target_column) + , m_deny_duplicate_values(false) +{ +} + +inline StringIndex::StringIndex(ref_type ref, ArrayParent* parent, size_t ndx_in_parent, ColumnBase* target_column, + bool deny_duplicate_values, Allocator& alloc) + : m_array(new Array(alloc)) + , m_target_column(target_column) + , m_deny_duplicate_values(deny_duplicate_values) +{ + REALM_ASSERT_EX(Array::get_context_flag_from_header(alloc.translate(ref)), ref, size_t(alloc.translate(ref))); + m_array->init_from_ref(ref); + set_parent(parent, ndx_in_parent); +} + +inline StringIndex::StringIndex(inner_node_tag, Allocator& alloc) + : m_array(create_node(alloc, false)) // Throws + , m_target_column(nullptr) + , m_deny_duplicate_values(false) +{ +} + +inline void StringIndex::set_allow_duplicate_values(bool allow) noexcept +{ + m_deny_duplicate_values = !allow; +} + +// Byte order of the key is *reversed*, so that for the integer index, the least significant +// byte comes first, so that it fits little-endian machines. That way we can perform fast +// range-lookups and iterate in order, etc, as future features. This, however, makes the same +// features slower for string indexes. Todo, we should reverse the order conditionally, depending +// on the column type. +inline StringIndex::key_type StringIndex::create_key(StringData str) noexcept +{ + key_type key = 0; + + if (str.size() >= 4) + goto four; + if (str.size() < 2) { + if (str.size() == 0) + goto none; + goto one; + } + if (str.size() == 2) + goto two; + goto three; + +// Create 4 byte index key +// (encoded like this to allow literal comparisons +// independently of endianness) +four: + key |= (key_type(static_cast(str[3])) << 0); +three: + key |= (key_type(static_cast(str[2])) << 8); +two: + key |= (key_type(static_cast(str[1])) << 16); +one: + key |= (key_type(static_cast(str[0])) << 24); +none: + return key; +} + +// Index works as follows: All non-NULL values are stored as if they had appended an 'X' character at the end. So +// "foo" is stored as if it was "fooX", and "" (empty string) is stored as "X". And NULLs are stored as empty strings. +inline StringIndex::key_type StringIndex::create_key(StringData str, size_t offset) noexcept +{ + if (str.is_null()) + return 0; + + if (offset > str.size()) + return 0; + + // for very short strings + size_t tail = str.size() - offset; + if (tail <= sizeof(key_type) - 1) { + char buf[sizeof(key_type)]; + memset(buf, 0, sizeof(key_type)); + buf[tail] = 'X'; + memcpy(buf, str.data() + offset, tail); + return create_key(StringData(buf, tail + 1)); + } + // else fallback + return create_key(str.substr(offset)); +} + +template +void StringIndex::insert(size_t row_ndx, T value, size_t num_rows, bool is_append) +{ + REALM_ASSERT_3(row_ndx, !=, npos); + + // If the new row is inserted after the last row in the table, we don't need + // to adjust any row indexes. + if (!is_append) { + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx + i; + adjust_row_indexes(row_ndx_2, 1); // Throws + } + } + + StringConversionBuffer buffer; + + for (size_t i = 0; i < num_rows; ++i) { + size_t row_ndx_2 = row_ndx + i; + size_t offset = 0; // First key from beginning of string + insert_with_offset(row_ndx_2, to_str(value, buffer), offset); // Throws + } +} + +template +void StringIndex::insert(size_t row_ndx, util::Optional value, size_t num_rows, bool is_append) +{ + if (value) { + insert(row_ndx, *value, num_rows, is_append); + } + else { + insert(row_ndx, null{}, num_rows, is_append); + } +} + +template +void StringIndex::set(size_t row_ndx, T new_value) +{ + StringConversionBuffer buffer; + StringConversionBuffer buffer2; + StringData old_value = get(row_ndx, buffer); + StringData new_value2 = to_str(new_value, buffer2); + + // Note that insert_with_offset() throws UniqueConstraintViolation. + + if (REALM_LIKELY(new_value2 != old_value)) { + // We must erase this row first because erase uses find_first which + // might find the duplicate if we insert before erasing. + bool is_last = true; // To avoid updating refs + erase(row_ndx, is_last); // Throws + + size_t offset = 0; // First key from beginning of string + insert_with_offset(row_ndx, new_value2, offset); // Throws + } +} + +template +void StringIndex::set(size_t row_ndx, util::Optional new_value) +{ + if (new_value) { + set(row_ndx, *new_value); + } + else { + set(row_ndx, null{}); + } +} + +template +void StringIndex::erase(size_t row_ndx, bool is_last) +{ + StringConversionBuffer buffer; + StringData value = get(row_ndx, buffer); + + do_delete(row_ndx, value, 0); + + // Collapse top nodes with single item + while (m_array->is_inner_bptree_node()) { + REALM_ASSERT(m_array->size() > 1); // node cannot be empty + if (m_array->size() > 2) + break; + + ref_type ref = m_array->get_as_ref(1); + m_array->set(1, 1); // avoid destruction of the extracted ref + m_array->destroy_deep(); + m_array->init_from_ref(ref); + m_array->update_parent(); + } + + // If it is last item in column, we don't have to update refs + if (!is_last) + adjust_row_indexes(row_ndx, -1); +} + +template +size_t StringIndex::find_first(T value) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_first(to_str(value, buffer), m_target_column); +} + +template +void StringIndex::find_all(IntegerColumn& result, T value) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_all(result, to_str(value, buffer), m_target_column); +} + +template +FindRes StringIndex::find_all_no_copy(T value, InternalFindResult& result) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_find_all_no_copy(to_str(value, buffer), m_target_column, result); +} + +template +size_t StringIndex::count(T value) const +{ + // Use direct access method + StringConversionBuffer buffer; + return m_array->index_string_count(to_str(value, buffer), m_target_column); +} + +template +void StringIndex::update_ref(T value, size_t old_row_ndx, size_t new_row_ndx) +{ + StringConversionBuffer buffer; + do_update_ref(to_str(value, buffer), old_row_ndx, new_row_ndx, 0); +} + +inline void StringIndex::destroy() noexcept +{ + return m_array->destroy_deep(); +} + +inline bool StringIndex::is_attached() const noexcept +{ + return m_array->is_attached(); +} + +inline void StringIndex::refresh_accessor_tree(size_t, const Spec&) +{ + m_array->init_from_parent(); +} + +inline ref_type StringIndex::get_ref() const noexcept +{ + return m_array->get_ref(); +} + +inline void StringIndex::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_array->set_parent(parent, ndx_in_parent); +} + +inline size_t StringIndex::get_ndx_in_parent() const noexcept +{ + return m_array->get_ndx_in_parent(); +} + +inline void StringIndex::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + m_array->set_ndx_in_parent(ndx_in_parent); +} + +inline void StringIndex::update_from_parent(size_t old_baseline) noexcept +{ + m_array->update_from_parent(old_baseline); +} + +} // namespace realm + +#endif // REALM_INDEX_STRING_HPP diff --git a/Pods/Realm/include/core/realm/lang_bind_helper.hpp b/Pods/Realm/include/core/realm/lang_bind_helper.hpp new file mode 100644 index 0000000..9e93c77 --- /dev/null +++ b/Pods/Realm/include/core/realm/lang_bind_helper.hpp @@ -0,0 +1,380 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_LANG_BIND_HELPER_HPP +#define REALM_LANG_BIND_HELPER_HPP + +#include + +#include +#include +#include +#include +#include + +#include + +namespace realm { + + +/// These functions are only to be used by language bindings to gain +/// access to certain memebers that are othewise private. +/// +/// \note Applications are not supposed to call any of these functions +/// directly. +/// +/// All of the get_subtable_ptr() functions bind the table accessor pointer +/// before it is returned (bind_table_ptr()). The caller is then responsible for +/// making the corresponding call to unbind_table_ptr(). +class LangBindHelper { +public: + /// Increment the reference counter of the specified table accessor. This is + /// done automatically by all of the functions in this class that return + /// table accessor pointers, but if the binding/application makes a copy of + /// such a pointer, and the copy needs to have an "independent life", then + /// the binding/application must bind that copy using this function. + static void bind_table_ptr(const Table*) noexcept; + + /// Decrement the reference counter of the specified table accessor. The + /// binding/application must call this function for every bound table + /// accessor pointer object, when that pointer object ends its life. + static void unbind_table_ptr(const Table*) noexcept; + + /// Construct a new freestanding table. The table accessor pointer is bound + /// by the callee before it is returned (bind_table_ptr()). + static Table* new_table(); + + /// Construct a new freestanding table as a copy of the specified one. The + /// table accessor pointer is bound by the callee before it is returned + /// (bind_table_ptr()). + static Table* copy_table(const Table&); + + //@{ + + /// These functions are like their namesakes in Group, but these bypass the + /// construction of a smart-pointer object (TableRef). The table accessor + /// pointer is bound by the callee before it is returned (bind_table_ptr()). + + static Table* get_table(Group&, size_t index_in_group); + static const Table* get_table(const Group&, size_t index_in_group); + + static Table* get_table(Group&, StringData name); + static const Table* get_table(const Group&, StringData name); + + static Table* add_table(Group&, StringData name, bool require_unique_name = true); + static Table* get_or_add_table(Group&, StringData name, bool* was_added = nullptr); + + //@} + + static Table* get_subtable_ptr(Table*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const Table*, size_t column_ndx, size_t row_ndx); + + // FIXME: This is an 'oddball', do we really need it? If we do, + // please provide a comment that explains why it is needed! + static Table* get_subtable_ptr_during_insert(Table*, size_t col_ndx, size_t row_ndx); + + static Table* get_subtable_ptr(TableView*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const TableView*, size_t column_ndx, size_t row_ndx); + static const Table* get_subtable_ptr(const ConstTableView*, size_t column_ndx, size_t row_ndx); + + /// Calls parent.set_mixed_subtable(col_ndx, row_ndx, &source). Note + /// that the source table must have a descriptor that is + /// compatible with the target subtable column. + static void set_mixed_subtable(Table& parent, size_t col_ndx, size_t row_ndx, const Table& source); + + static const LinkViewRef& get_linklist_ptr(Row&, size_t col_ndx); + static void unbind_linklist_ptr(const LinkViewRef&); + + using VersionID = SharedGroup::VersionID; + + /// \defgroup lang_bind_helper_transactions Continuous Transactions + /// + /// advance_read() is equivalent to terminating the current read transaction + /// (SharedGroup::end_read()), and initiating a new one + /// (SharedGroup::begin_read()), except that all subordinate accessors + /// (Table, Row, Descriptor) will remain attached to the underlying objects, + /// unless those objects were removed in the target snapshot. By default, + /// the read transaction is advanced to the latest available snapshot, but + /// see SharedGroup::begin_read() for information about \a version. + /// + /// promote_to_write() is equivalent to terminating the current read + /// transaction (SharedGroup::end_read()), and initiating a new write + /// transaction (SharedGroup::begin_write()), except that all subordinate + /// accessors (Table, Row, Descriptor) will remain attached to the + /// underlying objects, unless those objects were removed in the target + /// snapshot. + /// + /// commit_and_continue_as_read() is equivalent to committing the current + /// write transaction (SharedGroup::commit()) and initiating a new read + /// transaction, which is bound to the snapshot produced by the write + /// transaction (SharedGroup::begin_read()), except that all subordinate + /// accessors (Table, Row, Descriptor) will remain attached to the + /// underlying objects. commit_and_continue_as_read() returns the version + /// produced by the committed transaction. + /// + /// rollback_and_continue_as_read() is equivalent to rolling back the + /// current write transaction (SharedGroup::rollback()) and initiating a new + /// read transaction, which is bound to the snapshot, that the write + /// transaction was based on (SharedGroup::begin_read()), except that all + /// subordinate accessors (Table, Row, Descriptor) will remain attached to + /// the underlying objects, unless they were attached to object that were + /// added during the rolled back transaction. + /// + /// If advance_read(), promote_to_write(), commit_and_continue_as_read(), or + /// rollback_and_continue_as_read() throws, the associated group accessor + /// and all of its subordinate accessors are left in a state that may not be + /// fully consistent. Only minimal consistency is guaranteed (see + /// AccessorConsistencyLevels). In this case, the application is required to + /// either destroy the SharedGroup object, forcing all associated accessors + /// to become detached, or take some other equivalent action that involves a + /// complete accessor detachment, such as terminating the transaction in + /// progress. Until then it is an error, and unsafe if the application + /// attempts to access any of those accessors. + /// + /// The application must use SharedGroup::end_read() if it wants to + /// terminate the transaction after advance_read() or promote_to_write() has + /// thrown an exception. Likewise, it must use SharedGroup::rollback() if it + /// wants to terminate the transaction after commit_and_continue_as_read() + /// or rollback_and_continue_as_read() has thrown an exception. + /// + /// \param observer An optional custom replication instruction handler. The + /// application may pass such a handler to observe the sequence of + /// modifications that advances (or rolls back) the state of the Realm. + /// + /// \throw SharedGroup::BadVersion Thrown by advance_read() if the specified + /// version does not correspond to a bound (or tethered) snapshot. + /// + //@{ + + static void advance_read(SharedGroup&, VersionID = VersionID()); + template + static void advance_read(SharedGroup&, O&& observer, VersionID = VersionID()); + static void promote_to_write(SharedGroup&); + template + static void promote_to_write(SharedGroup&, O&& observer); + static SharedGroup::version_type commit_and_continue_as_read(SharedGroup&); + static void rollback_and_continue_as_read(SharedGroup&); + template + static void rollback_and_continue_as_read(SharedGroup&, O&& observer); + + //@} + + /// Returns the name of the specified data type. Examples: + /// + ///
+    ///
+    ///   type_Int          ->  "int"
+    ///   type_Bool         ->  "bool"
+    ///   type_Float        ->  "float"
+    ///   ...
+    ///
+    /// 
+ static const char* get_data_type_name(DataType) noexcept; + + static SharedGroup::version_type get_version_of_latest_snapshot(SharedGroup&); +}; + + +// Implementation: + +inline Table* LangBindHelper::new_table() +{ + typedef _impl::TableFriend tf; + Allocator& alloc = Allocator::get_default(); + size_t ref = tf::create_empty_table(alloc); // Throws + Table::Parent* parent = nullptr; + size_t ndx_in_parent = 0; + Table* table = tf::create_accessor(alloc, ref, parent, ndx_in_parent); // Throws + bind_table_ptr(table); + return table; +} + +inline Table* LangBindHelper::copy_table(const Table& table) +{ + typedef _impl::TableFriend tf; + Allocator& alloc = Allocator::get_default(); + size_t ref = tf::clone(table, alloc); // Throws + Table::Parent* parent = nullptr; + size_t ndx_in_parent = 0; + Table* copy_of_table = tf::create_accessor(alloc, ref, parent, ndx_in_parent); // Throws + bind_table_ptr(copy_of_table); + return copy_of_table; +} + +inline Table* LangBindHelper::get_subtable_ptr(Table* t, size_t column_ndx, size_t row_ndx) +{ + Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws + subtab->bind_ptr(); + return subtab; +} + +inline const Table* LangBindHelper::get_subtable_ptr(const Table* t, size_t column_ndx, size_t row_ndx) +{ + const Table* subtab = t->get_subtable_ptr(column_ndx, row_ndx); // Throws + subtab->bind_ptr(); + return subtab; +} + +inline Table* LangBindHelper::get_subtable_ptr(TableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline const Table* LangBindHelper::get_subtable_ptr(const TableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline const Table* LangBindHelper::get_subtable_ptr(const ConstTableView* tv, size_t column_ndx, size_t row_ndx) +{ + return get_subtable_ptr(&tv->get_parent(), column_ndx, tv->get_source_ndx(row_ndx)); +} + +inline Table* LangBindHelper::get_table(Group& group, size_t index_in_group) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::get_table(group, index_in_group); // Throws + table->bind_ptr(); + return table; +} + +inline const Table* LangBindHelper::get_table(const Group& group, size_t index_in_group) +{ + typedef _impl::GroupFriend gf; + const Table* table = &gf::get_table(group, index_in_group); // Throws + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::get_table(Group& group, StringData name) +{ + typedef _impl::GroupFriend gf; + Table* table = gf::get_table(group, name); // Throws + if (table) + table->bind_ptr(); + return table; +} + +inline const Table* LangBindHelper::get_table(const Group& group, StringData name) +{ + typedef _impl::GroupFriend gf; + const Table* table = gf::get_table(group, name); // Throws + if (table) + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::add_table(Group& group, StringData name, bool require_unique_name) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::add_table(group, name, require_unique_name); // Throws + table->bind_ptr(); + return table; +} + +inline Table* LangBindHelper::get_or_add_table(Group& group, StringData name, bool* was_added) +{ + typedef _impl::GroupFriend gf; + Table* table = &gf::get_or_add_table(group, name, was_added); // Throws + table->bind_ptr(); + return table; +} + +inline void LangBindHelper::unbind_table_ptr(const Table* t) noexcept +{ + t->unbind_ptr(); +} + +inline void LangBindHelper::bind_table_ptr(const Table* t) noexcept +{ + t->bind_ptr(); +} + +inline void LangBindHelper::set_mixed_subtable(Table& parent, size_t col_ndx, size_t row_ndx, const Table& source) +{ + parent.set_mixed_subtable(col_ndx, row_ndx, &source); +} + +inline const LinkViewRef& LangBindHelper::get_linklist_ptr(Row& row, size_t col_ndx) +{ + LinkViewRef* link_view = new LinkViewRef(row.get_linklist(col_ndx)); + return *link_view; +} + +inline void LangBindHelper::unbind_linklist_ptr(const LinkViewRef& link_view) +{ + delete (&link_view); +} + +inline void LangBindHelper::advance_read(SharedGroup& sg, VersionID version) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::advance_read(sg, observer, version); // Throws +} + +template +inline void LangBindHelper::advance_read(SharedGroup& sg, O&& observer, VersionID version) +{ + using sgf = _impl::SharedGroupFriend; + sgf::advance_read(sg, &observer, version); // Throws +} + +inline void LangBindHelper::promote_to_write(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::promote_to_write(sg, observer); // Throws +} + +template +inline void LangBindHelper::promote_to_write(SharedGroup& sg, O&& observer) +{ + using sgf = _impl::SharedGroupFriend; + sgf::promote_to_write(sg, &observer); // Throws +} + +inline SharedGroup::version_type LangBindHelper::commit_and_continue_as_read(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + return sgf::commit_and_continue_as_read(sg); // Throws +} + +inline void LangBindHelper::rollback_and_continue_as_read(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + _impl::NullInstructionObserver* observer = nullptr; + sgf::rollback_and_continue_as_read(sg, observer); // Throws +} + +template +inline void LangBindHelper::rollback_and_continue_as_read(SharedGroup& sg, O&& observer) +{ + using sgf = _impl::SharedGroupFriend; + sgf::rollback_and_continue_as_read(sg, &observer); // Throws +} + +inline SharedGroup::version_type LangBindHelper::get_version_of_latest_snapshot(SharedGroup& sg) +{ + using sgf = _impl::SharedGroupFriend; + return sgf::get_version_of_latest_snapshot(sg); // Throws +} + +} // namespace realm + +#endif // REALM_LANG_BIND_HELPER_HPP diff --git a/Pods/Realm/include/core/realm/link_view.hpp b/Pods/Realm/include/core/realm/link_view.hpp new file mode 100644 index 0000000..92b9297 --- /dev/null +++ b/Pods/Realm/include/core/realm/link_view.hpp @@ -0,0 +1,383 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_LINK_VIEW_HPP +#define REALM_LINK_VIEW_HPP + +#include +#include +#include +#include + +namespace realm { + +class LinkListColumn; + +namespace _impl { +class LinkListFriend; +class TransactLogConvenientEncoder; +} + + +/// The effect of calling most of the link list functions on a detached accessor +/// is unspecified and may lead to general corruption, or even a crash. The +/// exceptions are is_attached() and the destructor. +/// +/// FIXME: Rename this class to `LinkList`. +class LinkView : public RowIndexes, public std::enable_shared_from_this { +public: + ~LinkView() noexcept; + bool is_attached() const noexcept; + + /// This method will return true if the LinkView is detached (no assert). + bool is_empty() const noexcept; + + /// This method will return 0 if the LinkView is detached (no assert). + size_t size() const noexcept override; + + bool operator==(const LinkView&) const noexcept; + bool operator!=(const LinkView&) const noexcept; + + // Getting links + Table::ConstRowExpr operator[](size_t link_ndx) const noexcept; + Table::RowExpr operator[](size_t link_ndx) noexcept; + Table::ConstRowExpr get(size_t link_ndx) const noexcept; + Table::RowExpr get(size_t link_ndx) noexcept; + + // Modifiers + void add(size_t target_row_ndx); + void insert(size_t link_ndx, size_t target_row_ndx); + void set(size_t link_ndx, size_t target_row_ndx); + /// Move the link at \a from_ndx such that it ends up at \a to_ndx. Other + /// links are shifted as necessary in such a way that their order is + /// preserved. + /// + /// Note that \a to_ndx is the desired final index of the moved link, + /// therefore, `move(1,1)` is a no-op, while `move(1,2)` moves the link at + /// index 1 by one position, such that it ends up at index 2. A side-effect + /// of that, is that the link, that was originally at index 2, is moved to + /// index 1. + void move(size_t from_ndx, size_t to_ndx); + void swap(size_t link1_ndx, size_t link2_ndx); + void remove(size_t link_ndx); + void clear(); + + void sort(size_t column, bool ascending = true); + void sort(const SortDescriptor& order); + + TableView get_sorted_view(size_t column_index, bool ascending = true) const; + TableView get_sorted_view(SortDescriptor order) const; + + /// Remove the target row of the specified link from the target table. This + /// also removes the specified link from this link list, and any other link + /// pointing to that row. This is merely a shorthand for + /// `get_target_table.move_last_over(get(link_ndx))`. + void remove_target_row(size_t link_ndx); + + /// Remove all target rows pointed to by links in this link list, and clear + /// this link list. + void remove_all_target_rows(); + + /// Search this list for a link to the specified target table row (specified + /// by its index in the target table). If found, the index of the link to + /// that row within this list is returned, otherwise `realm::not_found` is + /// returned. + size_t find(size_t target_row_ndx, size_t start = 0) const noexcept; + + const ColumnBase& get_column_base(size_t index) + const override; // FIXME: `ColumnBase` is not part of the public API, so this function must be made private. + const Table& get_origin_table() const noexcept; + Table& get_origin_table() noexcept; + + size_t get_origin_row_index() const noexcept; + + const Table& get_target_table() const noexcept; + Table& get_target_table() noexcept; + + // No-op because LinkViews are always kept in sync. + uint_fast64_t sync_if_needed() const override; + bool is_in_sync() const override + { + return true; + } + +private: + struct ctor_cookie { + }; + + TableRef m_origin_table; + LinkListColumn& m_origin_column; + + using HandoverPatch = LinkViewHandoverPatch; + static void generate_patch(const ConstLinkViewRef& ref, std::unique_ptr& patch); + static LinkViewRef create_from_and_consume_patch(std::unique_ptr& patch, Group& group); + + void detach(); + void set_origin_row_index(size_t row_ndx) noexcept; + + void do_insert(size_t link_ndx, size_t target_row_ndx); + size_t do_set(size_t link_ndx, size_t target_row_ndx); + size_t do_remove(size_t link_ndx); + void do_clear(bool broken_reciprocal_backlinks); + + void do_nullify_link(size_t old_target_row_ndx); + void do_update_link(size_t old_target_row_ndx, size_t new_target_row_ndx); + void do_swap_link(size_t target_row_ndx_1, size_t target_row_ndx_2); + + void refresh_accessor_tree(size_t new_row_ndx) noexcept; + + void update_from_parent(size_t old_baseline) noexcept; + + Replication* get_repl() noexcept; + void repl_unselect() noexcept; + friend class _impl::TransactLogConvenientEncoder; + +#ifdef REALM_DEBUG + void verify(size_t row_ndx) const; +#endif + // allocate using make_shared: + static std::shared_ptr create(Table* origin_table, LinkListColumn&, size_t row_ndx); + + friend class _impl::LinkListFriend; + friend class LinkListColumn; + friend class LangBindHelper; + friend class SharedGroup; + friend class Query; + friend class TableViewBase; + + // must be public for use by make_shared, but cannot be called from outside, + // because ctor_cookie is private +public: + LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn&, size_t row_ndx); +}; + + +// Implementation + +inline LinkView::LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn& column, size_t row_ndx) + : RowIndexes(IntegerColumn::unattached_root_tag(), column.get_alloc()) // Throws + , m_origin_table(origin_table->get_table_ref()) + , m_origin_column(column) +{ + Array& root = *m_row_indexes.get_root_array(); + root.set_parent(&column, row_ndx); + if (ref_type ref = root.get_ref_from_parent()) + root.init_from_ref(ref); +} + +inline std::shared_ptr LinkView::create(Table* origin_table, LinkListColumn& column, size_t row_ndx) +{ + return std::make_shared(ctor_cookie(), origin_table, column, row_ndx); +} + +inline LinkView::~LinkView() noexcept +{ + if (is_attached()) { + repl_unselect(); + m_origin_column.unregister_linkview(); + } +} + +inline void LinkView::detach() +{ + REALM_ASSERT(is_attached()); + repl_unselect(); + m_origin_table.reset(); + m_row_indexes.detach(); +} + +inline bool LinkView::is_attached() const noexcept +{ + return static_cast(m_origin_table); +} + +inline bool LinkView::is_empty() const noexcept +{ + if (!is_attached()) + return true; + + if (!m_row_indexes.is_attached()) + return true; + + return m_row_indexes.is_empty(); +} + +inline size_t LinkView::size() const noexcept +{ + if (!is_attached()) + return 0; + + if (!m_row_indexes.is_attached()) + return 0; + + return m_row_indexes.size(); +} + +inline bool LinkView::operator==(const LinkView& link_list) const noexcept +{ + Table& target_table_1 = m_origin_column.get_target_table(); + Table& target_table_2 = link_list.m_origin_column.get_target_table(); + if (target_table_1.get_index_in_group() != target_table_2.get_index_in_group()) + return false; + if (!m_row_indexes.is_attached() || m_row_indexes.is_empty()) { + return !link_list.m_row_indexes.is_attached() || link_list.m_row_indexes.is_empty(); + } + return link_list.m_row_indexes.is_attached() && m_row_indexes.compare(link_list.m_row_indexes); +} + +inline bool LinkView::operator!=(const LinkView& link_list) const noexcept +{ + return !(*this == link_list); +} + +inline Table::ConstRowExpr LinkView::get(size_t link_ndx) const noexcept +{ + return const_cast(this)->get(link_ndx); +} + +inline Table::RowExpr LinkView::get(size_t link_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT(m_row_indexes.is_attached()); + REALM_ASSERT_3(link_ndx, <, m_row_indexes.size()); + + Table& target_table = m_origin_column.get_target_table(); + size_t target_row_ndx = to_size_t(m_row_indexes.get(link_ndx)); + return target_table[target_row_ndx]; +} + +inline Table::ConstRowExpr LinkView::operator[](size_t link_ndx) const noexcept +{ + return get(link_ndx); +} + +inline Table::RowExpr LinkView::operator[](size_t link_ndx) noexcept +{ + return get(link_ndx); +} + +inline void LinkView::add(size_t target_row_ndx) +{ + REALM_ASSERT(is_attached()); + size_t ins_pos = (m_row_indexes.is_attached()) ? m_row_indexes.size() : 0; + insert(ins_pos, target_row_ndx); +} + +inline size_t LinkView::find(size_t target_row_ndx, size_t start) const noexcept +{ + REALM_ASSERT(is_attached()); + REALM_ASSERT_3(target_row_ndx, <, m_origin_column.get_target_table().size()); + REALM_ASSERT_3(start, <=, size()); + + if (!m_row_indexes.is_attached()) + return not_found; + + return m_row_indexes.find_first(target_row_ndx, start); +} + +inline const ColumnBase& LinkView::get_column_base(size_t index) const +{ + return get_target_table().get_column_base(index); +} + +inline const Table& LinkView::get_origin_table() const noexcept +{ + return *m_origin_table; +} + +inline Table& LinkView::get_origin_table() noexcept +{ + return *m_origin_table; +} + +inline size_t LinkView::get_origin_row_index() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_row_indexes.get_root_array()->get_ndx_in_parent(); +} + +inline void LinkView::set_origin_row_index(size_t row_ndx) noexcept +{ + REALM_ASSERT(is_attached()); + m_row_indexes.get_root_array()->set_ndx_in_parent(row_ndx); +} + +inline const Table& LinkView::get_target_table() const noexcept +{ + return m_origin_column.get_target_table(); +} + +inline Table& LinkView::get_target_table() noexcept +{ + return m_origin_column.get_target_table(); +} + +inline void LinkView::refresh_accessor_tree(size_t new_row_ndx) noexcept +{ + Array& root = *m_row_indexes.get_root_array(); + root.set_ndx_in_parent(new_row_ndx); + if (ref_type ref = root.get_ref_from_parent()) { + root.init_from_ref(ref); + } + else { + root.detach(); + } +} + +inline void LinkView::update_from_parent(size_t old_baseline) noexcept +{ + if (m_row_indexes.is_attached()) + m_row_indexes.update_from_parent(old_baseline); +} + +inline Replication* LinkView::get_repl() noexcept +{ + typedef _impl::TableFriend tf; + return tf::get_repl(*m_origin_table); +} + + +// The purpose of this class is to give internal access to some, but not all of +// the non-public parts of LinkView. +class _impl::LinkListFriend { +public: + static void do_set(LinkView& list, size_t link_ndx, size_t target_row_ndx) + { + list.do_set(link_ndx, target_row_ndx); + } + + static void do_remove(LinkView& list, size_t link_ndx) + { + list.do_remove(link_ndx); + } + + static void do_clear(LinkView& list) + { + bool broken_reciprocal_backlinks = false; + list.do_clear(broken_reciprocal_backlinks); + } + + static void do_insert(LinkView& list, size_t link_ndx, size_t target_row_ndx) + { + list.do_insert(link_ndx, target_row_ndx); + } +}; + +} // namespace realm + +#endif // REALM_LINK_VIEW_HPP diff --git a/Pods/Realm/include/core/realm/link_view_fwd.hpp b/Pods/Realm/include/core/realm/link_view_fwd.hpp new file mode 100644 index 0000000..cba8801 --- /dev/null +++ b/Pods/Realm/include/core/realm/link_view_fwd.hpp @@ -0,0 +1,32 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_LINK_VIEW_FWD_HPP +#define REALM_LINK_VIEW_FWD_HPP + +#include + +namespace realm { + +class LinkView; +using LinkViewRef = std::shared_ptr; +using ConstLinkViewRef = std::shared_ptr; + +} // namespace realm + +#endif // REALM_LINK_VIEW_FWD_HPP diff --git a/Pods/Realm/include/core/realm/mixed.hpp b/Pods/Realm/include/core/realm/mixed.hpp new file mode 100644 index 0000000..8f07f4f --- /dev/null +++ b/Pods/Realm/include/core/realm/mixed.hpp @@ -0,0 +1,657 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_MIXED_HPP +#define REALM_MIXED_HPP + +#include // int64_t - not part of C++03, not even required by C++11 (see C++11 section 18.4.1) + +#include // size_t +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +/// This class represents a polymorphic Realm value. +/// +/// At any particular moment an instance of this class stores a +/// definite value of a definite type. If, for instance, that is an +/// integer value, you may call get_int() to extract that value. You +/// may call get_type() to discover what type of value is currently +/// stored. Calling get_int() on an instance that does not store an +/// integer, has undefined behavior, and likewise for all the other +/// types that can be stored. +/// +/// It is crucial to understand that the act of extracting a value of +/// a particular type requires definite knowledge about the stored +/// type. Calling a getter method for any particular type, that is not +/// the same type as the stored value, has undefined behavior. +/// +/// While values of numeric types are contained directly in a Mixed +/// instance, character and binary data are merely referenced. A Mixed +/// instance never owns the referenced data, nor does it in any other +/// way attempt to manage its lifetime. +/// +/// For compatibility with C style strings, when a string (character +/// data) is stored in a Realm database, it is always followed by a +/// terminating null character. This is also true when strings are +/// stored in a mixed type column. This means that in the following +/// code, if the 'mixed' value of the 8th row stores a string, then \c +/// c_str will always point to a null-terminated string: +/// +/// \code{.cpp} +/// +/// const char* c_str = my_table[7].mixed.data(); // Always null-terminated +/// +/// \endcode +/// +/// Note that this assumption does not hold in general for strings in +/// instances of Mixed. Indeed there is nothing stopping you from +/// constructing a new Mixed instance that refers to a string without +/// a terminating null character. +/// +/// At the present time no soultion has been found that would allow +/// for a Mixed instance to directly store a reference to a table. The +/// problem is roughly as follows: From most points of view, the +/// desirable thing to do, would be to store the table reference in a +/// Mixed instance as a plain pointer without any ownership +/// semantics. This would have no negative impact on the performance +/// of copying and destroying Mixed instances, and it would serve just +/// fine for passing a table as argument when setting the value of an +/// entry in a mixed column. In that case a copy of the referenced +/// table would be inserted into the mixed column. +/// +/// On the other hand, when retrieving a table reference from a mixed +/// column, storing it as a plain pointer in a Mixed instance is no +/// longer an acceptable option. The complex rules for managing the +/// lifetime of a Table instance, that represents a subtable, +/// necessitates the use of a "smart pointer" such as +/// TableRef. Enhancing the Mixed class to be able to act as a +/// TableRef would be possible, but would also lead to several new +/// problems. One problem is the risk of a Mixed instance outliving a +/// stack allocated Table instance that it references. This would be a +/// fatal error. Another problem is the impact that the nontrivial +/// table reference has on the performance of copying and destroying +/// Mixed instances. +/// +/// \sa StringData +class Mixed { +public: + Mixed() noexcept; + + Mixed(bool) noexcept; + Mixed(int64_t) noexcept; + Mixed(float) noexcept; + Mixed(double) noexcept; + Mixed(StringData) noexcept; + Mixed(BinaryData) noexcept; + Mixed(OldDateTime) noexcept; + Mixed(Timestamp) noexcept; + + // These are shortcuts for Mixed(StringData(c_str)), and are + // needed to avoid unwanted implicit conversion of char* to bool. + Mixed(char* c_str) noexcept + { + set_string(c_str); + } + Mixed(const char* c_str) noexcept + { + set_string(c_str); + } + + struct subtable_tag { + }; + Mixed(subtable_tag) noexcept + : m_type(type_Table) + { + } + + ~Mixed() noexcept + { + } + + DataType get_type() const noexcept + { + return m_type; + } + + int64_t get_int() const noexcept; + bool get_bool() const noexcept; + float get_float() const noexcept; + double get_double() const noexcept; + StringData get_string() const noexcept; + BinaryData get_binary() const noexcept; + OldDateTime get_olddatetime() const noexcept; + Timestamp get_timestamp() const noexcept; + + void set_int(int64_t) noexcept; + void set_bool(bool) noexcept; + void set_float(float) noexcept; + void set_double(double) noexcept; + void set_string(StringData) noexcept; + void set_binary(BinaryData) noexcept; + void set_binary(const char* data, size_t size) noexcept; + void set_olddatetime(OldDateTime) noexcept; + void set_timestamp(Timestamp) noexcept; + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const Mixed&); + +private: + DataType m_type; + union { + int64_t m_int; + bool m_bool; + float m_float; + double m_double; + const char* m_data; + int_fast64_t m_date; + Timestamp m_timestamp; + }; + size_t m_size = 0; +}; + +// Note: We cannot compare two mixed values, since when the type of +// both is type_Table, we would have to compare the two tables, but +// the mixed values do not provide access to those tables. + +// Note: The mixed values are specified as Wrap. If they were +// not, these operators would apply to simple comparisons, such as int +// vs int64_t, and cause ambiguity. This is because the constructors +// of Mixed are not explicit. + +// Compare mixed with integer +template +bool operator==(Wrap, const T&) noexcept; +template +bool operator!=(Wrap, const T&) noexcept; +template +bool operator==(const T&, Wrap) noexcept; +template +bool operator!=(const T&, Wrap) noexcept; + +// Compare mixed with boolean +bool operator==(Wrap, bool) noexcept; +bool operator!=(Wrap, bool) noexcept; +bool operator==(bool, Wrap) noexcept; +bool operator!=(bool, Wrap) noexcept; + +// Compare mixed with float +bool operator==(Wrap, float); +bool operator!=(Wrap, float); +bool operator==(float, Wrap); +bool operator!=(float, Wrap); + +// Compare mixed with double +bool operator==(Wrap, double); +bool operator!=(Wrap, double); +bool operator==(double, Wrap); +bool operator!=(double, Wrap); + +// Compare mixed with string +bool operator==(Wrap, StringData) noexcept; +bool operator!=(Wrap, StringData) noexcept; +bool operator==(StringData, Wrap) noexcept; +bool operator!=(StringData, Wrap) noexcept; +bool operator==(Wrap, const char* c_str) noexcept; +bool operator!=(Wrap, const char* c_str) noexcept; +bool operator==(const char* c_str, Wrap) noexcept; +bool operator!=(const char* c_str, Wrap) noexcept; +bool operator==(Wrap, char* c_str) noexcept; +bool operator!=(Wrap, char* c_str) noexcept; +bool operator==(char* c_str, Wrap) noexcept; +bool operator!=(char* c_str, Wrap) noexcept; + +// Compare mixed with binary data +bool operator==(Wrap, BinaryData) noexcept; +bool operator!=(Wrap, BinaryData) noexcept; +bool operator==(BinaryData, Wrap) noexcept; +bool operator!=(BinaryData, Wrap) noexcept; + +// Compare mixed with date +bool operator==(Wrap, OldDateTime) noexcept; +bool operator!=(Wrap, OldDateTime) noexcept; +bool operator==(OldDateTime, Wrap) noexcept; +bool operator!=(OldDateTime, Wrap) noexcept; + + +// Implementation: + +inline Mixed::Mixed() noexcept +{ + m_type = type_Int; + m_int = 0; +} + +inline Mixed::Mixed(int64_t v) noexcept +{ + m_type = type_Int; + m_int = v; +} + +inline Mixed::Mixed(bool v) noexcept +{ + m_type = type_Bool; + m_bool = v; +} + +inline Mixed::Mixed(float v) noexcept +{ + m_type = type_Float; + m_float = v; +} + +inline Mixed::Mixed(double v) noexcept +{ + m_type = type_Double; + m_double = v; +} + +inline Mixed::Mixed(StringData v) noexcept +{ + m_type = type_String; + m_data = v.data(); + m_size = v.size(); +} + +inline Mixed::Mixed(BinaryData v) noexcept +{ + m_type = type_Binary; + m_data = v.data(); + m_size = v.size(); +} + +inline Mixed::Mixed(OldDateTime v) noexcept +{ + m_type = type_OldDateTime; + m_date = v.get_olddatetime(); +} + +inline Mixed::Mixed(Timestamp v) noexcept +{ + m_type = type_Timestamp; + m_timestamp = v; +} + +inline int64_t Mixed::get_int() const noexcept +{ + REALM_ASSERT(m_type == type_Int); + return m_int; +} + +inline bool Mixed::get_bool() const noexcept +{ + REALM_ASSERT(m_type == type_Bool); + return m_bool; +} + +inline float Mixed::get_float() const noexcept +{ + REALM_ASSERT(m_type == type_Float); + return m_float; +} + +inline double Mixed::get_double() const noexcept +{ + REALM_ASSERT(m_type == type_Double); + return m_double; +} + +inline StringData Mixed::get_string() const noexcept +{ + REALM_ASSERT(m_type == type_String); + return StringData(m_data, m_size); +} + +inline BinaryData Mixed::get_binary() const noexcept +{ + REALM_ASSERT(m_type == type_Binary); + return BinaryData(m_data, m_size); +} + +inline OldDateTime Mixed::get_olddatetime() const noexcept +{ + REALM_ASSERT(m_type == type_OldDateTime); + return m_date; +} + +inline Timestamp Mixed::get_timestamp() const noexcept +{ + REALM_ASSERT(m_type == type_Timestamp); + return m_timestamp; +} + +inline void Mixed::set_int(int64_t v) noexcept +{ + m_type = type_Int; + m_int = v; +} + +inline void Mixed::set_bool(bool v) noexcept +{ + m_type = type_Bool; + m_bool = v; +} + +inline void Mixed::set_float(float v) noexcept +{ + m_type = type_Float; + m_float = v; +} + +inline void Mixed::set_double(double v) noexcept +{ + m_type = type_Double; + m_double = v; +} + +inline void Mixed::set_string(StringData v) noexcept +{ + m_type = type_String; + m_data = v.data(); + m_size = v.size(); +} + +inline void Mixed::set_binary(BinaryData v) noexcept +{ + set_binary(v.data(), v.size()); +} + +inline void Mixed::set_binary(const char* data, size_t size) noexcept +{ + m_type = type_Binary; + m_data = data; + m_size = size; +} + +inline void Mixed::set_olddatetime(OldDateTime v) noexcept +{ + m_type = type_OldDateTime; + m_date = v.get_olddatetime(); +} + +// LCOV_EXCL_START +inline void Mixed::set_timestamp(Timestamp v) noexcept +{ + REALM_ASSERT(false && "not yet implemented"); + m_type = type_Timestamp; + m_timestamp = v; +} +// LCOV_EXCL_STOP + +// LCOV_EXCL_START +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const Mixed& m) +{ + out << "Mixed("; + switch (m.m_type) { + case type_Int: + out << m.m_int; + break; + case type_Bool: + out << m.m_bool; + break; + case type_Float: + out << m.m_float; + break; + case type_Double: + out << m.m_double; + break; + case type_String: + out << StringData(m.m_data, m.m_size); + break; + case type_Binary: + out << BinaryData(m.m_data, m.m_size); + break; + case type_OldDateTime: + out << OldDateTime(m.m_date); + break; + case type_Timestamp: + out << Timestamp(m.m_timestamp); + break; + case type_Table: + out << "subtable"; + break; + case type_Mixed: + case type_Link: + case type_LinkList: + REALM_ASSERT(false); + } + out << ")"; + return out; +} +// LCOV_EXCL_STOP + + +// Compare mixed with integer + +template +inline bool operator==(Wrap a, const T& b) noexcept +{ + return Mixed(a).get_type() == type_Int && Mixed(a).get_int() == b; +} + +template +inline bool operator!=(Wrap a, const T& b) noexcept +{ + return Mixed(a).get_type() != type_Int || Mixed(a).get_int() != b; +} + +template +inline bool operator==(const T& a, Wrap b) noexcept +{ + return type_Int == Mixed(b).get_type() && a == Mixed(b).get_int(); +} + +template +inline bool operator!=(const T& a, Wrap b) noexcept +{ + return type_Int != Mixed(b).get_type() || a != Mixed(b).get_int(); +} + + +// Compare mixed with boolean + +inline bool operator==(Wrap a, bool b) noexcept +{ + return Mixed(a).get_type() == type_Bool && Mixed(a).get_bool() == b; +} + +inline bool operator!=(Wrap a, bool b) noexcept +{ + return Mixed(a).get_type() != type_Bool || Mixed(a).get_bool() != b; +} + +inline bool operator==(bool a, Wrap b) noexcept +{ + return type_Bool == Mixed(b).get_type() && a == Mixed(b).get_bool(); +} + +inline bool operator!=(bool a, Wrap b) noexcept +{ + return type_Bool != Mixed(b).get_type() || a != Mixed(b).get_bool(); +} + + +// Compare mixed with float + +inline bool operator==(Wrap a, float b) +{ + return Mixed(a).get_type() == type_Float && Mixed(a).get_float() == b; +} + +inline bool operator!=(Wrap a, float b) +{ + return Mixed(a).get_type() != type_Float || Mixed(a).get_float() != b; +} + +inline bool operator==(float a, Wrap b) +{ + return type_Float == Mixed(b).get_type() && a == Mixed(b).get_float(); +} + +inline bool operator!=(float a, Wrap b) +{ + return type_Float != Mixed(b).get_type() || a != Mixed(b).get_float(); +} + + +// Compare mixed with double + +inline bool operator==(Wrap a, double b) +{ + return Mixed(a).get_type() == type_Double && Mixed(a).get_double() == b; +} + +inline bool operator!=(Wrap a, double b) +{ + return Mixed(a).get_type() != type_Double || Mixed(a).get_double() != b; +} + +inline bool operator==(double a, Wrap b) +{ + return type_Double == Mixed(b).get_type() && a == Mixed(b).get_double(); +} + +inline bool operator!=(double a, Wrap b) +{ + return type_Double != Mixed(b).get_type() || a != Mixed(b).get_double(); +} + + +// Compare mixed with string + +inline bool operator==(Wrap a, StringData b) noexcept +{ + return Mixed(a).get_type() == type_String && Mixed(a).get_string() == b; +} + +inline bool operator!=(Wrap a, StringData b) noexcept +{ + return Mixed(a).get_type() != type_String || Mixed(a).get_string() != b; +} + +inline bool operator==(StringData a, Wrap b) noexcept +{ + return type_String == Mixed(b).get_type() && a == Mixed(b).get_string(); +} + +inline bool operator!=(StringData a, Wrap b) noexcept +{ + return type_String != Mixed(b).get_type() || a != Mixed(b).get_string(); +} + +inline bool operator==(Wrap a, const char* b) noexcept +{ + return a == StringData(b); +} + +inline bool operator!=(Wrap a, const char* b) noexcept +{ + return a != StringData(b); +} + +inline bool operator==(const char* a, Wrap b) noexcept +{ + return StringData(a) == b; +} + +inline bool operator!=(const char* a, Wrap b) noexcept +{ + return StringData(a) != b; +} + +inline bool operator==(Wrap a, char* b) noexcept +{ + return a == StringData(b); +} + +inline bool operator!=(Wrap a, char* b) noexcept +{ + return a != StringData(b); +} + +inline bool operator==(char* a, Wrap b) noexcept +{ + return StringData(a) == b; +} + +inline bool operator!=(char* a, Wrap b) noexcept +{ + return StringData(a) != b; +} + + +// Compare mixed with binary data + +inline bool operator==(Wrap a, BinaryData b) noexcept +{ + return Mixed(a).get_type() == type_Binary && Mixed(a).get_binary() == b; +} + +inline bool operator!=(Wrap a, BinaryData b) noexcept +{ + return Mixed(a).get_type() != type_Binary || Mixed(a).get_binary() != b; +} + +inline bool operator==(BinaryData a, Wrap b) noexcept +{ + return type_Binary == Mixed(b).get_type() && a == Mixed(b).get_binary(); +} + +inline bool operator!=(BinaryData a, Wrap b) noexcept +{ + return type_Binary != Mixed(b).get_type() || a != Mixed(b).get_binary(); +} + + +// Compare mixed with date + +inline bool operator==(Wrap a, OldDateTime b) noexcept +{ + return Mixed(a).get_type() == type_OldDateTime && OldDateTime(Mixed(a).get_olddatetime()) == b; +} + +inline bool operator!=(Wrap a, OldDateTime b) noexcept +{ + return Mixed(a).get_type() != type_OldDateTime || OldDateTime(Mixed(a).get_olddatetime()) != b; +} + +inline bool operator==(OldDateTime a, Wrap b) noexcept +{ + return type_OldDateTime == Mixed(b).get_type() && a == OldDateTime(Mixed(b).get_olddatetime()); +} + +inline bool operator!=(OldDateTime a, Wrap b) noexcept +{ + return type_OldDateTime != Mixed(b).get_type() || a != OldDateTime(Mixed(b).get_olddatetime()); +} + + +} // namespace realm + +#endif // REALM_MIXED_HPP diff --git a/Pods/Realm/include/core/realm/null.hpp b/Pods/Realm/include/core/realm/null.hpp new file mode 100644 index 0000000..92c8d68 --- /dev/null +++ b/Pods/Realm/include/core/realm/null.hpp @@ -0,0 +1,164 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_NULL_HPP +#define REALM_NULL_HPP + +#include + +#include +#include +#include +#include + +namespace realm { + +/* +Represents null in Query, find(), get(), set(), etc. + +Float/Double: Realm can both store user-given NaNs and null. Any user-given signaling NaN is converted to +0x7fa00000 (if float) or 0x7ff4000000000000 (if double). Any user-given quiet NaN is converted to +0x7fc00000 (if float) or 0x7ff8000000000000 (if double). So Realm does not preserve the optional bits in +user-given NaNs. + +However, since both clang and gcc on x64 and ARM, and also Java on x64, return these bit patterns when +requesting NaNs, these will actually seem to roundtrip bit-exact for the end-user in most cases. + +If set_null() is called, a null is stored in form of the bit pattern 0xffffffff (if float) or +0xffffffffffffffff (if double). These are quiet NaNs. + +Executing a query that involves a float/double column that contains NaNs gives an undefined result. If +it contains signaling NaNs, it may throw an exception. + +Notes on IEEE: + +A NaN float is any bit pattern `s 11111111 S xxxxxxxxxxxxxxxxxxxxxx` where `s` and `x` are arbitrary, but at +least 1 `x` must be 1. If `S` is 1, it's a quiet NaN, else it's a signaling NaN. + +A NaN doubule is the same as above, but for `s eeeeeeeeeee S xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` + +The `S` bit is at position 22 (float) or 51 (double). +*/ + +struct null { + null() + { + } + operator int64_t() + { + throw(LogicError::type_mismatch); + } + template + operator util::Optional() + { + return util::none; + } + + template + bool operator==(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator!=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator>(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator>=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator<=(const T&) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator<(const T&) const + { + REALM_ASSERT(false); + return false; + } + + /// Returns whether `v` bitwise equals the null bit-pattern + template + static bool is_null_float(T v) + { + T i = null::get_null_float(); + return std::memcmp(&i, &v, sizeof(T)) == 0; + } + + /// Returns the quiet NaNs that represent null for floats/doubles in Realm in stored payload. + template + static T get_null_float() + { + typename std::conditional::value, uint32_t, uint64_t>::type i; + int64_t double_nan = 0x7ff80000000000aa; + i = std::is_same::value ? 0x7fc000aa : static_cast(double_nan); + T d = type_punning(i); + REALM_ASSERT_DEBUG(std::isnan(static_cast(d))); + REALM_ASSERT_DEBUG(!is_signaling(d)); + return d; + } + + /// Takes a NaN as argument and returns whether or not it's signaling + template + static bool is_signaling(T v) + { + REALM_ASSERT(std::isnan(static_cast(v))); + typename std::conditional::value, uint32_t, uint64_t>::type i; + size_t signal_bit = std::is_same::value ? 22 : 51; // If this bit is set, it's quiet + i = type_punning(v); + return !(i & (1ull << signal_bit)); + } + + /// Converts any signaling or quiet NaN to their their respective bit patterns that are used on x64 gcc+clang, + /// ARM clang and x64 Java. + template + static T to_realm(T v) + { + if (std::isnan(static_cast(v))) { + typename std::conditional::value, uint32_t, uint64_t>::type i; + if (std::is_same::value) { + i = is_signaling(v) ? 0x7fa00000 : 0x7fc00000; + } + else { + i = static_cast(is_signaling(v) ? 0x7ff4000000000000 : 0x7ff8000000000000); + } + return type_punning(i); + } + else { + return v; + } + } +}; + +} // namespace realm + +#endif // REALM_NULL_HPP diff --git a/Pods/Realm/include/core/realm/olddatetime.hpp b/Pods/Realm/include/core/realm/olddatetime.hpp new file mode 100644 index 0000000..b662899 --- /dev/null +++ b/Pods/Realm/include/core/realm/olddatetime.hpp @@ -0,0 +1,157 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_DATETIME_HPP +#define REALM_DATETIME_HPP + +#include +#include + +namespace realm { + + +class OldDateTime { +public: + OldDateTime() noexcept + : m_time(0) + { + } + + /// Construct from the number of seconds since Jan 1 00:00:00 UTC + /// 1970. + /// FIXME: See if we can make this private again. Required by query_expression.hpp + OldDateTime(int_fast64_t d) noexcept + : m_time(d) + { + } + + /// Return the time as seconds since Jan 1 00:00:00 UTC 1970. + int_fast64_t get_olddatetime() const noexcept + { + return m_time; + } + + friend bool operator==(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator!=(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator<(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator<=(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator>(const OldDateTime&, const OldDateTime&) noexcept; + friend bool operator>=(const OldDateTime&, const OldDateTime&) noexcept; + + /// Construct from broken down local time. + /// + /// \note This constructor uses std::mktime() to convert the + /// specified local time to seconds since the Epoch, that is, the + /// result depends on the current globally specified time zone + /// setting. + /// + /// \param year The year (the minimum valid value is 1970). + /// + /// \param month The month in the range [1, 12]. + /// + /// \param day The day of the month in the range [1, 31]. + /// + /// \param hours Hours since midnight in the range [0, 23]. + /// + /// \param minutes Minutes after the hour in the range [0, 59]. + /// + /// \param seconds Seconds after the minute in the range [0, + /// 60]. Note that the range allows for leap seconds. + OldDateTime(int year, int month, int day, int hours = 0, int minutes = 0, int seconds = 0); + + template + friend std::basic_ostream& operator<<(std::basic_ostream& out, const OldDateTime&); + + // This is used by query_expression.hpp to generalize its templates and simplify the code *alot*; it is needed + // because OldDateTime is internally stored in an int64_t column. + operator int_fast64_t() noexcept; + +private: + int_fast64_t m_time; // Seconds since Jan 1 00:00:00 UTC 1970. + static std::time_t assemble(int year, int month, int day, int hours, int minutes, int seconds); + template + friend class Value; +}; + + +// Implementation: + +inline bool operator==(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time == b.m_time; +} + +inline bool operator!=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time != b.m_time; +} + +inline bool operator<(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time < b.m_time; +} + +inline bool operator<=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time <= b.m_time; +} + +inline bool operator>(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time > b.m_time; +} + +inline bool operator>=(const OldDateTime& a, const OldDateTime& b) noexcept +{ + return a.m_time >= b.m_time; +} + +inline OldDateTime::operator int_fast64_t() noexcept +{ + return m_time; +} + +inline OldDateTime::OldDateTime(int year, int month, int day, int hours, int minutes, int seconds) + : m_time(assemble(year, month, day, hours, minutes, seconds)) +{ +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const OldDateTime& d) +{ + out << "OldDateTime(" << d.m_time << ")"; + return out; +} + +inline std::time_t OldDateTime::assemble(int year, int month, int day, int hours, int minutes, int seconds) +{ + std::tm local_time; + local_time.tm_year = year - 1900; + local_time.tm_mon = month - 1; + local_time.tm_mday = day; + local_time.tm_hour = hours; + local_time.tm_min = minutes; + local_time.tm_sec = seconds; + local_time.tm_isdst = -1; + return std::mktime(&local_time); +} + + +} // namespace realm + +#endif // REALM_DATETIME_HPP diff --git a/Pods/Realm/include/core/realm/owned_data.hpp b/Pods/Realm/include/core/realm/owned_data.hpp new file mode 100644 index 0000000..039ccc8 --- /dev/null +++ b/Pods/Realm/include/core/realm/owned_data.hpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_OWNED_DATA_HPP +#define REALM_OWNED_DATA_HPP + +#include +#include + +namespace realm { + +/// A chunk of owned data. +class OwnedData { +public: + /// Construct a null reference. + OwnedData() noexcept + { + } + + /// If \a data_to_copy is 'null', \a data_size must be zero. + OwnedData(const char* data_to_copy, size_t data_size) + : m_size(data_size) + { + REALM_ASSERT_DEBUG(data_to_copy || data_size == 0); + if (data_to_copy) { + m_data = std::unique_ptr(new char[data_size]); + memcpy(m_data.get(), data_to_copy, data_size); + } + } + + /// If \a unique_data is 'null', \a data_size must be zero. + OwnedData(std::unique_ptr unique_data, size_t data_size) noexcept + : m_data(std::move(unique_data)) + , m_size(data_size) + { + REALM_ASSERT_DEBUG(m_data || m_size == 0); + } + + OwnedData(const OwnedData& other) + : OwnedData(other.m_data.get(), other.m_size) + { + } + OwnedData& operator=(const OwnedData& other); + + OwnedData(OwnedData&&) = default; + OwnedData& operator=(OwnedData&&) = default; + + const char* data() const + { + return m_data.get(); + } + size_t size() const + { + return m_size; + } + +private: + std::unique_ptr m_data; + size_t m_size = 0; +}; + +inline OwnedData& OwnedData::operator=(const OwnedData& other) +{ + if (this != &other) { + if (other.m_data) { + m_data = std::unique_ptr(new char[other.m_size]); + memcpy(m_data.get(), other.m_data.get(), other.m_size); + } + else { + m_data = nullptr; + } + m_size = other.m_size; + } + return *this; +} + +} // namespace realm + +#endif // REALM_OWNED_DATA_HPP diff --git a/Pods/Realm/include/core/realm/query.hpp b/Pods/Realm/include/core/realm/query.hpp new file mode 100644 index 0000000..d62a1df --- /dev/null +++ b/Pods/Realm/include/core/realm/query.hpp @@ -0,0 +1,457 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_QUERY_HPP +#define REALM_QUERY_HPP + +#include +#include +#include +#include +#include +#include + +#define REALM_MULTITHREAD_QUERY 0 + +#if REALM_MULTITHREAD_QUERY +// FIXME: Use our C++ thread abstraction API since it provides a much +// higher level of encapsulation and safety. +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + + +// Pre-declarations +class ParentNode; +class Table; +class TableView; +class TableViewBase; +class ConstTableView; +class Array; +class Expression; +class SequentialGetterBase; +class Group; + +struct QueryGroup { + enum class State { + Default, + OrCondition, + OrConditionChildren, + }; + + QueryGroup() = default; + + QueryGroup(const QueryGroup&); + QueryGroup& operator=(const QueryGroup&); + + QueryGroup(QueryGroup&&) = default; + QueryGroup& operator=(QueryGroup&&) = default; + + QueryGroup(const QueryGroup&, QueryNodeHandoverPatches&); + + std::unique_ptr m_root_node; + + bool m_pending_not = false; + size_t m_subtable_column = not_found; + State m_state = State::Default; +}; + +class Query final { +public: + Query(const Table& table, TableViewBase* tv = nullptr); + Query(const Table& table, std::unique_ptr); + Query(const Table& table, const LinkViewRef& lv); + Query(); + Query(std::unique_ptr); + ~Query() noexcept; + + Query(const Query& copy); + Query& operator=(const Query& source); + + Query(Query&&); + Query& operator=(Query&&); + + // Find links that point to a specific target row + Query& links_to(size_t column_ndx, const ConstRow& target_row); + + // Conditions: null + Query& equal(size_t column_ndx, null); + Query& not_equal(size_t column_ndx, null); + + // Conditions: int64_t + Query& equal(size_t column_ndx, int64_t value); + Query& not_equal(size_t column_ndx, int64_t value); + Query& greater(size_t column_ndx, int64_t value); + Query& greater_equal(size_t column_ndx, int64_t value); + Query& less(size_t column_ndx, int64_t value); + Query& less_equal(size_t column_ndx, int64_t value); + Query& between(size_t column_ndx, int64_t from, int64_t to); + + // Conditions: int (we need those because conversion from '1234' is ambiguous with float/double) + Query& equal(size_t column_ndx, int value); + Query& not_equal(size_t column_ndx, int value); + Query& greater(size_t column_ndx, int value); + Query& greater_equal(size_t column_ndx, int value); + Query& less(size_t column_ndx, int value); + Query& less_equal(size_t column_ndx, int value); + Query& between(size_t column_ndx, int from, int to); + + // Conditions: 2 int columns + Query& equal_int(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_int(size_t column_ndx1, size_t column_ndx2); + Query& greater_int(size_t column_ndx1, size_t column_ndx2); + Query& less_int(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_int(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_int(size_t column_ndx1, size_t column_ndx2); + + // Conditions: float + Query& equal(size_t column_ndx, float value); + Query& not_equal(size_t column_ndx, float value); + Query& greater(size_t column_ndx, float value); + Query& greater_equal(size_t column_ndx, float value); + Query& less(size_t column_ndx, float value); + Query& less_equal(size_t column_ndx, float value); + Query& between(size_t column_ndx, float from, float to); + + // Conditions: 2 float columns + Query& equal_float(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_float(size_t column_ndx1, size_t column_ndx2); + Query& greater_float(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_float(size_t column_ndx1, size_t column_ndx2); + Query& less_float(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_float(size_t column_ndx1, size_t column_ndx2); + + // Conditions: double + Query& equal(size_t column_ndx, double value); + Query& not_equal(size_t column_ndx, double value); + Query& greater(size_t column_ndx, double value); + Query& greater_equal(size_t column_ndx, double value); + Query& less(size_t column_ndx, double value); + Query& less_equal(size_t column_ndx, double value); + Query& between(size_t column_ndx, double from, double to); + + // Conditions: 2 double columns + Query& equal_double(size_t column_ndx1, size_t column_ndx2); + Query& not_equal_double(size_t column_ndx1, size_t column_ndx2); + Query& greater_double(size_t column_ndx1, size_t column_ndx2); + Query& greater_equal_double(size_t column_ndx1, size_t column_ndx2); + Query& less_double(size_t column_ndx1, size_t column_ndx2); + Query& less_equal_double(size_t column_ndx1, size_t column_ndx2); + + // Conditions: timestamp + Query& equal(size_t column_ndx, Timestamp value); + Query& not_equal(size_t column_ndx, Timestamp value); + Query& greater(size_t column_ndx, Timestamp value); + Query& greater_equal(size_t column_ndx, Timestamp value); + Query& less_equal(size_t column_ndx, Timestamp value); + Query& less(size_t column_ndx, Timestamp value); + + // Conditions: bool + Query& equal(size_t column_ndx, bool value); + + // Conditions: date + Query& equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& not_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return not_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& greater_olddatetime(size_t column_ndx, OldDateTime value) + { + return greater(column_ndx, int64_t(value.get_olddatetime())); + } + Query& greater_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return greater_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& less_olddatetime(size_t column_ndx, OldDateTime value) + { + return less(column_ndx, int64_t(value.get_olddatetime())); + } + Query& less_equal_olddatetime(size_t column_ndx, OldDateTime value) + { + return less_equal(column_ndx, int64_t(value.get_olddatetime())); + } + Query& between_olddatetime(size_t column_ndx, OldDateTime from, OldDateTime to) + { + return between(column_ndx, int64_t(from.get_olddatetime()), int64_t(to.get_olddatetime())); + } + + // Conditions: strings + Query& equal(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& not_equal(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& begins_with(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& ends_with(size_t column_ndx, StringData value, bool case_sensitive = true); + Query& contains(size_t column_ndx, StringData value, bool case_sensitive = true); + + // These are shortcuts for equal(StringData(c_str)) and + // not_equal(StringData(c_str)), and are needed to avoid unwanted + // implicit conversion of char* to bool. + Query& equal(size_t column_ndx, const char* c_str, bool case_sensitive = true); + Query& not_equal(size_t column_ndx, const char* c_str, bool case_sensitive = true); + + // Conditions: binary data + Query& equal(size_t column_ndx, BinaryData value); + Query& not_equal(size_t column_ndx, BinaryData value); + Query& begins_with(size_t column_ndx, BinaryData value); + Query& ends_with(size_t column_ndx, BinaryData value); + Query& contains(size_t column_ndx, BinaryData value); + + // Negation + Query& Not(); + + // Grouping + Query& group(); + Query& end_group(); + Query& subtable(size_t column); + Query& end_subtable(); + Query& Or(); + + Query& and_query(const Query& q); + Query& and_query(Query&& q); + Query operator||(const Query& q); + Query operator&&(const Query& q); + Query operator!(); + + + // Searching + size_t find(size_t begin_at_table_row = size_t(0)); + TableView find_all(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)); + ConstTableView find_all(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + + // Aggregates + size_t count(size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + + int64_t sum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + int64_t maximum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + int64_t minimum_int(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double sum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + float maximum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + float minimum_float(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double sum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double average_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + double maximum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + double minimum_double(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1), size_t* return_ndx = nullptr) const; + + OldDateTime maximum_olddatetime(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, + size_t end = size_t(-1), size_t limit = size_t(-1), + size_t* return_ndx = nullptr) const; + + OldDateTime minimum_olddatetime(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, + size_t end = size_t(-1), size_t limit = size_t(-1), + size_t* return_ndx = nullptr) const; + + Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)); + + Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)); + + // Deletion + size_t remove(); + +#if REALM_MULTITHREAD_QUERY + // Multi-threading + TableView find_all_multi(size_t start = 0, size_t end = size_t(-1)); + ConstTableView find_all_multi(size_t start = 0, size_t end = size_t(-1)) const; + int set_threads(unsigned int threadcount); +#endif + + const TableRef& get_table() + { + return m_table; + } + + // True if matching rows are guaranteed to be returned in table order. + bool produces_results_in_table_order() const + { + return !m_view; + } + + // Calls sync_if_needed on the restricting view, if present. + // Returns the current version of the table(s) this query depends on, + // or util::none if the query is not associated with a table. + util::Optional sync_view_if_needed() const; + + std::string validate(); + +private: + Query(Table& table, TableViewBase* tv = nullptr); + void create(); + + void init() const; + size_t find_internal(size_t start = 0, size_t end = size_t(-1)) const; + size_t peek_tablerow(size_t row) const; + void handle_pending_not(); + void set_table(TableRef tr); + + static bool comp(const std::pair& a, const std::pair& b); + +public: + using HandoverPatch = QueryHandoverPatch; + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, ConstSourcePayload mode) const + { + patch.reset(new HandoverPatch); + return std::make_unique(*this, *patch, mode); + } + + std::unique_ptr clone_for_handover(std::unique_ptr& patch, MutableSourcePayload mode) + { + patch.reset(new HandoverPatch); + return std::make_unique(*this, *patch, mode); + } + + void apply_and_consume_patch(std::unique_ptr& patch, Group& dest_group) + { + apply_patch(*patch, dest_group); + patch.reset(); + } + + void apply_patch(HandoverPatch& patch, Group& dest_group); + Query(const Query& source, HandoverPatch& patch, ConstSourcePayload mode); + Query(Query& source, HandoverPatch& patch, MutableSourcePayload mode); + +private: + void fetch_descriptor(); + + void add_expression_node(std::unique_ptr); + + template + Query& equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& less(size_t column_ndx1, size_t column_ndx2); + + template + Query& less_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& greater(size_t column_ndx1, size_t column_ndx2); + + template + Query& greater_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& not_equal(size_t column_ndx1, size_t column_ndx2); + + template + Query& add_condition(size_t column_ndx, T value); + + template + double average(size_t column_ndx, size_t* resultcount = nullptr, size_t start = 0, size_t end = size_t(-1), + size_t limit = size_t(-1)) const; + + template + R aggregate(R (ColClass::*method)(size_t, size_t, size_t, size_t*) const, size_t column_ndx, size_t* resultcount, + size_t start, size_t end, size_t limit, size_t* return_ndx = nullptr) const; + + void aggregate_internal(Action TAction, DataType TSourceColumn, bool nullable, ParentNode* pn, QueryStateBase* st, + size_t start, size_t end, SequentialGetterBase* source_column) const; + + void find_all(TableViewBase& tv, size_t start = 0, size_t end = size_t(-1), size_t limit = size_t(-1)) const; + void delete_nodes() noexcept; + + bool has_conditions() const + { + return m_groups.size() > 0 && m_groups[0].m_root_node; + } + ParentNode* root_node() const + { + REALM_ASSERT(m_groups.size()); + return m_groups[0].m_root_node.get(); + } + + void add_node(std::unique_ptr); + + friend class Table; + friend class TableViewBase; + + std::string error_code; + + std::vector m_groups; + + // Used to access schema while building query: + std::vector m_subtable_path; + + ConstDescriptorRef m_current_descriptor; + TableRef m_table; + + // points to the base class of the restricting view. If the restricting + // view is a link view, m_source_link_view is non-zero. If it is a table view, + // m_source_table_view is non-zero. + RowIndexes* m_view = nullptr; + + // At most one of these can be non-zero, and if so the non-zero one indicates the restricting view. + LinkViewRef m_source_link_view; // link views are refcounted and shared. + TableViewBase* m_source_table_view = nullptr; // table views are not refcounted, and not owned by the query. + std::unique_ptr m_owned_source_table_view; // <--- except when indicated here +}; + +// Implementation: + +inline Query& Query::equal(size_t column_ndx, const char* c_str, bool case_sensitive) +{ + return equal(column_ndx, StringData(c_str), case_sensitive); +} + +inline Query& Query::not_equal(size_t column_ndx, const char* c_str, bool case_sensitive) +{ + return not_equal(column_ndx, StringData(c_str), case_sensitive); +} + +} // namespace realm + +#endif // REALM_QUERY_HPP diff --git a/Pods/Realm/include/core/realm/query_conditions.hpp b/Pods/Realm/include/core/realm/query_conditions.hpp new file mode 100644 index 0000000..ef1ef6d --- /dev/null +++ b/Pods/Realm/include/core/realm/query_conditions.hpp @@ -0,0 +1,614 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_QUERY_CONDITIONS_HPP +#define REALM_QUERY_CONDITIONS_HPP + +#include +#include + +#include +#include +#include + +namespace realm { + +// Array::VTable only uses the first 4 conditions (enums) in an array of function pointers +enum { cond_Equal, cond_NotEqual, cond_Greater, cond_Less, cond_VTABLE_FINDER_COUNT, cond_None, cond_LeftNotNull }; + +// Quick hack to make "Queries with Integer null columns" able to compile in Visual Studio 2015 which doesn't full +// support sfinae +// (real cause hasn't been investigated yet, cannot exclude that we don't obey c++11 standard) +struct HackClass { + template + bool can_match(A, B, C) + { + REALM_ASSERT(false); + return false; + } + template + bool will_match(A, B, C) + { + REALM_ASSERT(false); + return false; + } +}; + +// Does v2 contain v1? +struct Contains : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.contains(v1); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 begin with v1? +struct BeginsWith : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.begins_with(v1); + } + + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 end with v1? +struct EndsWith : public HackClass { + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v2.ends_with(v1); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct Equal { + static const int avx = 0x00; // _CMP_EQ_OQ + // bool operator()(const bool v1, const bool v2, bool v1null = false, bool v2null = false) const { return v1 == + // v2; } + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v1 == v2; + } + bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const + { + return v1 == v2; + } + + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + return (v1null && v2null) || (!v1null && !v2null && v1 == v2); + } + static const int condition = cond_Equal; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v >= lbound && v <= ubound); + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v == 0 && ubound == 0 && lbound == 0); + } +}; + +struct NotEqual { + static const int avx = 0x0B; // _CMP_FALSE_OQ + bool operator()(StringData v1, const char*, const char*, StringData v2, bool = false, bool = false) const + { + return v1 != v2; + } + // bool operator()(BinaryData v1, BinaryData v2, bool = false, bool = false) const { return v1 != v2; } + + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (!v1null && !v2null) + return v1 != v2; + + if (v1null && v2null) + return false; + + return true; + } + + static const int condition = cond_NotEqual; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + return !(v == 0 && ubound == 0 && lbound == 0); + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + return (v > ubound || v < lbound); + } + + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } +}; + +// Does v2 contain v1? +struct ContainsIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() == 0 && !v2.is_null()) + return true; + + return search_case_fold(v2, v1_upper, v1_lower, v1.size()) != v2.size(); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() == 0 && !v2.is_null()) + return true; + + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return search_case_fold(v2, v1_upper.c_str(), v1_lower.c_str(), v1.size()) != v2.size(); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 begin with v1? +struct BeginsWithIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + return v1.size() <= v2.size() && equal_case_fold(v2.prefix(v1.size()), v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() > v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2.prefix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +// Does v2 end with v1? +struct EndsWithIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + return v1.size() <= v2.size() && equal_case_fold(v2.suffix(v1.size()), v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v2.is_null() && !v1.is_null()) + return false; + + if (v1.size() > v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2.suffix(v1.size()), v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct EqualIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v1.is_null() != v2.is_null()) + return false; + + return v1.size() == v2.size() && equal_case_fold(v2, v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v1.is_null() != v2.is_null()) + return false; + + if (v1.size() != v2.size()) + return false; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool operator()(int64_t, int64_t, bool, bool) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct NotEqualIns : public HackClass { + bool operator()(StringData v1, const char* v1_upper, const char* v1_lower, StringData v2, bool = false, + bool = false) const + { + if (v1.is_null() != v2.is_null()) + return true; + return v1.size() != v2.size() || !equal_case_fold(v2, v1_upper, v1_lower); + } + + // Slow version, used if caller hasn't stored an upper and lower case version + bool operator()(StringData v1, StringData v2, bool = false, bool = false) const + { + if (v1.is_null() != v2.is_null()) + return true; + + if (v1.size() != v2.size()) + return true; + std::string v1_upper = case_map(v1, true, IgnoreErrors); + std::string v1_lower = case_map(v1, false, IgnoreErrors); + return !equal_case_fold(v2, v1_upper.c_str(), v1_lower.c_str()); + } + + template + bool operator()(A, B) const + { + REALM_ASSERT(false); + return false; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + static const int condition = -1; +}; + +struct Greater { + static const int avx = 0x1E; // _CMP_GT_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null || v2null) + return false; + + return v1 > v2; + } + static const int condition = cond_Greater; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + return ubound > v; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(ubound); + return lbound > v; + } +}; + +struct None { + template + bool operator()(const T&, const T&, bool = false, bool = false) const + { + return true; + } + static const int condition = cond_None; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } +}; + +struct NotNull { + template + bool operator()(const T&, const T&, bool v = false, bool = false) const + { + return !v; + } + static const int condition = cond_LeftNotNull; + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + static_cast(ubound); + static_cast(v); + return true; + } +}; + + +struct Less { + static const int avx = 0x11; // _CMP_LT_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null || v2null) + return false; + + return v1 < v2; + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = cond_Less; + bool can_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(ubound); + return lbound < v; + } + bool will_match(int64_t v, int64_t lbound, int64_t ubound) + { + static_cast(lbound); + return ubound < v; + } +}; + +struct LessEqual : public HackClass { + static const int avx = 0x12; // _CMP_LE_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null && v2null) + return true; + + return (!v1null && !v2null && v1 <= v2); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = -1; +}; + +struct GreaterEqual : public HackClass { + static const int avx = 0x1D; // _CMP_GE_OQ + template + bool operator()(const T& v1, const T& v2, bool v1null = false, bool v2null = false) const + { + if (v1null && v2null) + return true; + + return (!v1null && !v2null && v1 >= v2); + } + template + bool operator()(A, B, C, D) const + { + REALM_ASSERT(false); + return false; + } + static const int condition = -1; +}; + + +// CompareLess is a temporary hack to have a generalized way to compare any realm types. Todo, enable correct < +// operator of StringData (currently gives circular header dependency with utf8.hpp) +template +struct CompareLess { + static bool compare(T v1, T v2, bool = false, bool = false) + { + return v1 < v2; + } +}; +template <> +struct CompareLess { + static bool compare(StringData v1, StringData v2, bool = false, bool = false) + { + bool ret = utf8_compare(v1.data(), v2.data()); + return ret; + } +}; + +} // namespace realm + +#endif // REALM_QUERY_CONDITIONS_HPP diff --git a/Pods/Realm/include/core/realm/query_engine.hpp b/Pods/Realm/include/core/realm/query_engine.hpp new file mode 100644 index 0000000..0bcb874 --- /dev/null +++ b/Pods/Realm/include/core/realm/query_engine.hpp @@ -0,0 +1,1844 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +/* +A query consists of node objects, one for each query condition. Each node contains pointers to all other nodes: + +node1 node2 node3 +------ ----- ----- +node2* node1* node1* +node3* node3* node2* + +The construction of all this takes part in query.cpp. Each node has two important functions: + + aggregate(start, end) + aggregate_local(start, end) + +The aggregate() function executes the aggregate of a query. You can call the method on any of the nodes +(except children nodes of OrNode and SubtableNode) - it has the same behaviour. The function contains +scheduling that calls aggregate_local(start, end) on different nodes with different start/end ranges, +depending on what it finds is most optimal. + +The aggregate_local() function contains a tight loop that tests the condition of its own node, and upon match +it tests all other conditions at that index to report a full match or not. It will remain in the tight loop +after a full match. + +So a call stack with 2 and 9 being local matches of a node could look like this: + +aggregate(0, 10) + node1->aggregate_local(0, 3) + node2->find_first_local(2, 3) + node3->find_first_local(2, 3) + node3->aggregate_local(3, 10) + node1->find_first_local(4, 5) + node2->find_first_local(4, 5) + node1->find_first_local(7, 8) + node2->find_first_local(7, 8) + +find_first_local(n, n + 1) is a function that can be used to test a single row of another condition. Note that +this is very simplified. There are other statistical arguments to the methods, and also, find_first_local() can be +called from a callback function called by an integer Array. + + +Template arguments in methods: +---------------------------------------------------------------------------------------------------- + +TConditionFunction: Each node has a condition from query_conditions.c such as Equal, GreaterEqual, etc + +TConditionValue: Type of values in condition column. That is, int64_t, float, int, bool, etc + +TAction: What to do with each search result, from the enums act_ReturnFirst, act_Count, act_Sum, etc + +TResult: Type of result of actions - float, double, int64_t, etc. Special notes: For act_Count it's + int64_t, for RLM_FIND_ALL it's int64_t which points at destination array. + +TSourceColumn: Type of source column used in actions, or *ignored* if no source column is used (like for + act_Count, act_ReturnFirst) + + +There are two important classes used in queries: +---------------------------------------------------------------------------------------------------- +SequentialGetter Column iterator used to get successive values with leaf caching. Used both for condition columns + and aggregate source column + +AggregateState State of the aggregate - contains a state variable that stores intermediate sum, max, min, + etc, etc. + +*/ + +#ifndef REALM_QUERY_ENGINE_HPP +#define REALM_QUERY_ENGINE_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 +#include +#endif + +namespace realm { + +// Number of matches to find in best condition loop before breaking out to probe other conditions. Too low value gives +// too many constant time overheads everywhere in the query engine. Too high value makes it adapt less rapidly to +// changes in match frequencies. +const size_t findlocals = 64; + +// Average match distance in linear searches where further increase in distance no longer increases query speed +// (because time spent on handling each match becomes insignificant compared to time spent on the search). +const size_t bestdist = 512; + +// Minimum number of matches required in a certain condition before it can be used to compute statistics. Too high +// value can spent too much time in a bad node (with high match frequency). Too low value gives inaccurate statistics. +const size_t probe_matches = 4; + +const size_t bitwidth_time_unit = 64; + +typedef bool (*CallbackDummy)(int64_t); + + +class ParentNode { + typedef ParentNode ThisType; + +public: + ParentNode() = default; + virtual ~ParentNode() = default; + + void gather_children(std::vector& v) + { + m_children.clear(); + size_t i = v.size(); + v.push_back(this); + + if (m_child) + m_child->gather_children(v); + + m_children = v; + m_children.erase(m_children.begin() + i); + m_children.insert(m_children.begin(), this); + } + + double cost() const + { + return 8 * bitwidth_time_unit / m_dD + + m_dT; // dt = 1/64 to 1. Match dist is 8 times more important than bitwidth + } + + size_t find_first(size_t start, size_t end); + + virtual void init() + { + if (m_child) + m_child->init(); + m_column_action_specializer = nullptr; + } + + void set_table(const Table& table) + { + if (&table == m_table) + return; + + m_table.reset(&table); + if (m_child) + m_child->set_table(table); + table_changed(); + } + + virtual size_t find_first_local(size_t start, size_t end) = 0; + + virtual void aggregate_local_prepare(Action TAction, DataType col_id, bool nullable); + + template + bool column_action_specialization(QueryStateBase* st, SequentialGetterBase* source_column, size_t r) + { + // TResult: type of query result + // TSourceValue: type of aggregate source + using TSourceValue = typename TSourceColumn::value_type; + using TResult = typename ColumnTypeTraitsSum::sum_type; + + // Sum of float column must accumulate in double + static_assert(!(TAction == act_Sum && + (std::is_same::value && !std::is_same::value)), + ""); + + TSourceValue av{}; + // uses_val test because compiler cannot see that IntegerColumn::get has no side effect and result is + // discarded + if (static_cast*>(st)->template uses_val() && source_column != nullptr) { + REALM_ASSERT_DEBUG(dynamic_cast*>(source_column) != nullptr); + av = static_cast*>(source_column)->get_next(r); + } + REALM_ASSERT_DEBUG(dynamic_cast*>(st) != nullptr); + bool cont = static_cast*>(st)->template match(r, 0, av); + return cont; + } + + virtual size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column); + + + virtual std::string validate() + { + if (error_code != "") + return error_code; + if (m_child == nullptr) + return ""; + else + return m_child->validate(); + } + + ParentNode(const ParentNode& from) + : ParentNode(from, nullptr) + { + } + + ParentNode(const ParentNode& from, QueryNodeHandoverPatches* patches) + : m_child(from.m_child ? from.m_child->clone(patches) : nullptr) + , m_condition_column_idx(from.m_condition_column_idx) + , m_dD(from.m_dD) + , m_dT(from.m_dT) + , m_probes(from.m_probes) + , m_matches(from.m_matches) + , m_table(from.m_table) + { + } + + void add_child(std::unique_ptr child) + { + if (m_child) + m_child->add_child(std::move(child)); + else + m_child = std::move(child); + } + + virtual std::unique_ptr clone(QueryNodeHandoverPatches* = nullptr) const = 0; + + virtual void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) + { + if (m_child) + m_child->apply_handover_patch(patches, group); + } + + std::unique_ptr m_child; + std::vector m_children; + size_t m_condition_column_idx = npos; // Column of search criteria + + double m_dD; // Average row distance between each local match at current position + double m_dT = 0.0; // Time overhead of testing index i + 1 if we have just tested index i. > 1 for linear scans, 0 + // for index/tableview + + size_t m_probes = 0; + size_t m_matches = 0; + +protected: + typedef bool (ParentNode::*Column_action_specialized)(QueryStateBase*, SequentialGetterBase*, size_t); + Column_action_specialized m_column_action_specializer; + ConstTableRef m_table; + std::string error_code; + + const ColumnBase& get_column_base(size_t ndx) + { + return m_table->get_column_base(ndx); + } + + template + const ColType& get_column(size_t ndx) + { + auto& col = m_table->get_column_base(ndx); + REALM_ASSERT_DEBUG(dynamic_cast(&col)); + return static_cast(col); + } + + ColumnType get_real_column_type(size_t ndx) + { + return m_table->get_real_column_type(ndx); + } + + template + void copy_getter(SequentialGetter& dst, size_t& dst_idx, const SequentialGetter& src, + const QueryNodeHandoverPatches* patches) + { + if (src.m_column) { + if (patches) { + dst_idx = src.m_column->get_column_index(); + REALM_ASSERT_DEBUG(dst_idx < m_table->get_column_count()); + } + else + dst.init(src.m_column); + } + } + +private: + virtual void table_changed() = 0; +}; + +// For conditions on a subtable (encapsulated in subtable()...end_subtable()). These return the parent row as match if +// and +// only if one or more subtable rows match the condition. +class SubtableNode : public ParentNode { +public: + SubtableNode(size_t column, std::unique_ptr condition) + : m_condition(std::move(condition)) + { + m_dT = 100.0; + m_condition_column_idx = column; + } + + void init() override + { + m_dD = 10.0; + + // m_condition is first node in condition of subtable query. + if (m_condition) { + // Can't call init() here as usual since the subtable can be degenerate + // m_condition->init(table); + std::vector v; + m_condition->gather_children(v); + } + + // m_child is next node of parent query + if (m_child) + m_child->init(); + } + + void table_changed() override + { + m_col_type = m_table->get_real_column_type(m_condition_column_idx); + REALM_ASSERT(m_col_type == col_type_Table || m_col_type == col_type_Mixed); + if (m_col_type == col_type_Table) + m_column = &m_table->get_column_table(m_condition_column_idx); + else // Mixed + m_column = &m_table->get_column_mixed(m_condition_column_idx); + } + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_condition == nullptr) + return "Unbalanced subtable/end_subtable block"; + else + return m_condition->validate(); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(m_table); + REALM_ASSERT(m_condition); + + for (size_t s = start; s < end; ++s) { + const Table* subtable; + if (m_col_type == col_type_Table) + subtable = static_cast(m_column)->get_subtable_ptr(s); + else { + subtable = static_cast(m_column)->get_subtable_ptr(s); + if (!subtable) + continue; + } + + if (subtable->is_degenerate()) + return not_found; + + m_condition->set_table(*subtable); + m_condition->init(); + const size_t subsize = subtable->size(); + const size_t sub = m_condition->find_first(0, subsize); + + if (sub != not_found) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new SubtableNode(*this, patches)); + } + + SubtableNode(const SubtableNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_condition(from.m_condition ? from.m_condition->clone(patches) : nullptr) + , m_column(from.m_column) + , m_col_type(from.m_col_type) + { + if (m_column && patches) + m_condition_column_idx = m_column->get_column_index(); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_condition->apply_handover_patch(patches, group); + ParentNode::apply_handover_patch(patches, group); + } + + std::unique_ptr m_condition; + const ColumnBase* m_column = nullptr; + ColumnType m_col_type; +}; + +namespace _impl { + +template +struct CostHeuristic; + +template <> +struct CostHeuristic { + static constexpr double dD() + { + return 100.0; + } + static constexpr double dT() + { + return 1.0 / 4.0; + } +}; + +template <> +struct CostHeuristic { + static constexpr double dD() + { + return 100.0; + } + static constexpr double dT() + { + return 1.0 / 4.0; + } +}; + +// FIXME: Add AdaptiveStringColumn, BasicColumn, etc. +} + +class ColumnNodeBase : public ParentNode { +protected: + ColumnNodeBase(size_t column_idx) + { + m_condition_column_idx = column_idx; + } + + ColumnNodeBase(const ColumnNodeBase& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_last_local_match(from.m_last_local_match) + , m_local_matches(from.m_local_matches) + , m_local_limit(from.m_local_limit) + , m_fastmode_disabled(from.m_fastmode_disabled) + , m_action(from.m_action) + , m_state(from.m_state) + , m_source_column(from.m_source_column) + { + } + + template + bool match_callback(int64_t v) + { + using TSourceValue = typename ColType::value_type; + using QueryStateType = typename ColumnTypeTraitsSum::sum_type; + + size_t i = to_size_t(v); + m_last_local_match = i; + m_local_matches++; + + auto state = static_cast*>(m_state); + auto source_column = static_cast*>(m_source_column); + + // Test remaining sub conditions of this node. m_children[0] is the node that called match_callback(), so skip + // it + for (size_t c = 1; c < m_children.size(); c++) { + m_children[c]->m_probes++; + size_t m = m_children[c]->find_first_local(i, i + 1); + if (m != i) + return true; + } + + bool b; + if (state->template uses_val()) { // Compiler cannot see that IntegerColumn::Get has no side effect + // and result is discarded + TSourceValue av = source_column->get_next(i); + b = state->template match(i, 0, av); + } + else { + b = state->template match(i, 0, TSourceValue{}); + } + + return b; + } + + // Aggregate bookkeeping + size_t m_last_local_match = npos; + size_t m_local_matches = 0; + size_t m_local_limit = 0; + bool m_fastmode_disabled = false; + Action m_action; + QueryStateBase* m_state = nullptr; + SequentialGetterBase* m_source_column = + nullptr; // Column of values used in aggregate (act_FindAll, actReturnFirst, act_Sum, etc) +}; + +template +class IntegerNodeBase : public ColumnNodeBase { + using ThisType = IntegerNodeBase; + +public: + using TConditionValue = typename ColType::value_type; + static const bool nullable = ColType::nullable; + + template + bool find_callback_specialization(size_t s, size_t end_in_leaf) + { + using AggregateColumnType = typename GetColumnType::type; + bool cont; + size_t start_in_leaf = s - this->m_leaf_start; + cont = this->m_leaf_ptr->template find( + m_value, start_in_leaf, end_in_leaf, this->m_leaf_start, nullptr, + std::bind(std::mem_fn(&ThisType::template match_callback), this, + std::placeholders::_1)); + return cont; + } + +protected: + using LeafType = typename ColType::LeafType; + using LeafInfo = typename ColType::LeafInfo; + + size_t aggregate_local_impl(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column, int c) + { + REALM_ASSERT(m_children.size() > 0); + m_local_matches = 0; + m_local_limit = local_limit; + m_last_local_match = start - 1; + m_state = st; + + // If there are no other nodes than us (m_children.size() == 1) AND the column used for our condition is + // the same as the column used for the aggregate action, then the entire query can run within scope of that + // column only, with no references to other columns: + bool fastmode = should_run_in_fastmode(source_column); + for (size_t s = start; s < end;) { + cache_leaf(s); + + size_t end_in_leaf; + if (end > m_leaf_end) + end_in_leaf = m_leaf_end - m_leaf_start; + else + end_in_leaf = end - m_leaf_start; + + if (fastmode) { + bool cont; + size_t start_in_leaf = s - m_leaf_start; + cont = m_leaf_ptr->find(c, m_action, m_value, start_in_leaf, end_in_leaf, m_leaf_start, + static_cast*>(st)); + if (!cont) + return not_found; + } + // Else, for each match in this node, call our IntegerNodeBase::match_callback to test remaining nodes + // and/or extract + // aggregate payload from aggregate column: + else { + m_source_column = source_column; + bool cont = (this->*m_find_callback_specialized)(s, end_in_leaf); + if (!cont) + return not_found; + } + + if (m_local_matches == m_local_limit) + break; + + s = end_in_leaf + m_leaf_start; + } + + if (m_local_matches == m_local_limit) { + m_dD = (m_last_local_match + 1 - start) / (m_local_matches + 1.0); + return m_last_local_match + 1; + } + else { + m_dD = (end - start) / (m_local_matches + 1.0); + return end; + } + } + + IntegerNodeBase(TConditionValue value, size_t column_idx) + : ColumnNodeBase(column_idx) + , m_value(std::move(value)) + { + } + + IntegerNodeBase(const ThisType& from, QueryNodeHandoverPatches* patches) + : ColumnNodeBase(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_find_callback_specialized(from.m_find_callback_specialized) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void init() override + { + ColumnNodeBase::init(); + + m_dT = _impl::CostHeuristic::dT(); + m_dD = _impl::CostHeuristic::dD(); + + // Clear leaf cache + m_leaf_end = 0; + m_array_ptr.reset(); // Explicitly destroy the old one first, because we're reusing the memory. + m_array_ptr.reset(new (&m_leaf_cache_storage) LeafType(m_table->get_alloc())); + + if (m_child) + m_child->init(); + } + + void get_leaf(const ColType& col, size_t ndx) + { + size_t ndx_in_leaf; + LeafInfo leaf_info{&m_leaf_ptr, m_array_ptr.get()}; + col.get_leaf(ndx, ndx_in_leaf, leaf_info); + m_leaf_start = ndx - ndx_in_leaf; + m_leaf_end = m_leaf_start + m_leaf_ptr->size(); + } + + void cache_leaf(size_t s) + { + if (s >= m_leaf_end || s < m_leaf_start) { + get_leaf(*m_condition_column, s); + size_t w = m_leaf_ptr->get_width(); + m_dT = (w == 0 ? 1.0 / REALM_MAX_BPNODE_SIZE : w / float(bitwidth_time_unit)); + } + } + + bool should_run_in_fastmode(SequentialGetterBase* source_column) const + { + return (m_children.size() == 1 && + (source_column == nullptr || + (!m_fastmode_disabled && + static_cast*>(source_column)->m_column == m_condition_column))); + } + + // Search value: + TConditionValue m_value; + + // Column on which search criteria are applied + const ColType* m_condition_column = nullptr; + + // Leaf cache + using LeafCacheStorage = typename std::aligned_storage::type; + LeafCacheStorage m_leaf_cache_storage; + std::unique_ptr m_array_ptr; + const LeafType* m_leaf_ptr = nullptr; + size_t m_leaf_start = npos; + size_t m_leaf_end = 0; + size_t m_local_end; + + // Aggregate optimization + using TFind_callback_specialized = bool (ThisType::*)(size_t, size_t); + TFind_callback_specialized m_find_callback_specialized = nullptr; +}; + +// FIXME: Add specialization that uses index for TConditionFunction = Equal +template +class IntegerNode : public IntegerNodeBase { + using BaseType = IntegerNodeBase; + using ThisType = IntegerNode; + +public: + static const bool special_null_node = false; + using TConditionValue = typename BaseType::TConditionValue; + + IntegerNode(TConditionValue value, size_t column_ndx) + : BaseType(value, column_ndx) + { + } + IntegerNode(const IntegerNode& from, QueryNodeHandoverPatches* patches) + : BaseType(from, patches) + { + } + + void aggregate_local_prepare(Action action, DataType col_id, bool nullable) override + { + this->m_fastmode_disabled = (col_id == type_Float || col_id == type_Double); + this->m_action = action; + this->m_find_callback_specialized = get_specialized_callback(action, col_id, nullable); + } + + size_t aggregate_local(QueryStateBase* st, size_t start, size_t end, size_t local_limit, + SequentialGetterBase* source_column) override + { + constexpr int cond = TConditionFunction::condition; + return this->aggregate_local_impl(st, start, end, local_limit, source_column, cond); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(this->m_table); + + while (start < end) { + + // Cache internal leaves + if (start >= this->m_leaf_end || start < this->m_leaf_start) { + this->get_leaf(*this->m_condition_column, start); + } + + // FIXME: Create a fast bypass when you just need to check 1 row, which is used alot from within core. + // It should just call array::get and save the initial overhead of find_first() which has become quite + // big. Do this when we have cleaned up core a bit more. + + size_t end2; + if (end > this->m_leaf_end) + end2 = this->m_leaf_end - this->m_leaf_start; + else + end2 = end - this->m_leaf_start; + + size_t s; + s = this->m_leaf_ptr->template find_first(this->m_value, start - this->m_leaf_start, + end2); + + if (s == not_found) { + start = this->m_leaf_end; + continue; + } + else + return s + this->m_leaf_start; + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new IntegerNode(*this, patches)); + } + +protected: + using TFind_callback_specialized = typename BaseType::TFind_callback_specialized; + + static TFind_callback_specialized get_specialized_callback(Action action, DataType col_id, bool nullable) + { + switch (action) { + case act_Count: + return get_specialized_callback_2_int(col_id, nullable); + case act_Sum: + return get_specialized_callback_2(col_id, nullable); + case act_Max: + return get_specialized_callback_2(col_id, nullable); + case act_Min: + return get_specialized_callback_2(col_id, nullable); + case act_FindAll: + return get_specialized_callback_2_int(col_id, nullable); + case act_CallbackIdx: + return get_specialized_callback_2_int(col_id, nullable); + default: + break; + } + REALM_ASSERT(false); // Invalid aggregate function + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_2(DataType col_id, bool nullable) + { + switch (col_id) { + case type_Int: + return get_specialized_callback_3(nullable); + case type_Float: + return get_specialized_callback_3(nullable); + case type_Double: + return get_specialized_callback_3(nullable); + default: + break; + } + REALM_ASSERT(false); // Invalid aggregate source column + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_2_int(DataType col_id, bool nullable) + { + if (col_id == type_Int) { + return get_specialized_callback_3(nullable); + } + REALM_ASSERT(false); // Invalid aggregate source column + return nullptr; + } + + template + static TFind_callback_specialized get_specialized_callback_3(bool nullable) + { + if (nullable) { + return &BaseType::template find_callback_specialization; + } + else { + return &BaseType::template find_callback_specialization; + } + } +}; + +// This node is currently used for floats and doubles only +template +class FloatDoubleNode : public ParentNode { +public: + using TConditionValue = typename ColType::value_type; + static const bool special_null_node = false; + + FloatDoubleNode(TConditionValue v, size_t column_ndx) + : m_value(v) + { + m_condition_column_idx = column_ndx; + m_dT = 1.0; + } + FloatDoubleNode(null, size_t column_ndx) + : m_value(null::get_null_float()) + { + m_condition_column_idx = column_ndx; + m_dT = 1.0; + } + + void table_changed() override + { + m_condition_column.init(&get_column(m_condition_column_idx)); + } + + void init() override + { + ParentNode::init(); + m_dD = 100.0; + } + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction cond; + + auto find = [&](bool nullability) { + bool m_value_nan = nullability ? null::is_null_float(m_value) : false; + for (size_t s = start; s < end; ++s) { + TConditionValue v = m_condition_column.get_next(s); + REALM_ASSERT(!(null::is_null_float(v) && !nullability)); + if (cond(v, m_value, nullability ? null::is_null_float(v) : false, m_value_nan)) + return s; + } + return not_found; + }; + + // This will inline the second case but no the first. Todo, use templated lambda when switching to c++14 + if (m_table->is_nullable(m_condition_column_idx)) + return find(true); + else + return find(false); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new FloatDoubleNode(*this, patches)); + } + + FloatDoubleNode(const FloatDoubleNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + { + copy_getter(m_condition_column, m_condition_column_idx, from.m_condition_column, patches); + } + +protected: + TConditionValue m_value; + SequentialGetter m_condition_column; +}; + + +template +class BinaryNode : public ParentNode { +public: + using TConditionValue = BinaryData; + static const bool special_null_node = false; + + BinaryNode(BinaryData v, size_t column) + : m_value(v) + { + m_dT = 100.0; + m_condition_column_idx = column; + } + + BinaryNode(null, size_t column) + : BinaryNode(BinaryData{}, column) + { + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void init() override + { + m_dD = 100.0; + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction condition; + for (size_t s = start; s < end; ++s) { + BinaryData value = m_condition_column->get(s); + if (condition(m_value.get(), value)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new BinaryNode(*this, patches)); + } + + BinaryNode(const BinaryNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + OwnedBinaryData m_value; + const BinaryColumn* m_condition_column; +}; + + +template +class TimestampNode : public ParentNode { +public: + using TConditionValue = Timestamp; + static const bool special_null_node = false; + + TimestampNode(Timestamp v, size_t column) + : m_value(v) + { + m_condition_column_idx = column; + } + + TimestampNode(null, size_t column) + : TimestampNode(Timestamp{}, column) + { + } + + void table_changed() override + { + m_condition_column = &get_column(m_condition_column_idx); + } + + void init() override + { + m_dD = 100.0; + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override + { + size_t ret = m_condition_column->find(m_value, start, end); + return ret; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new TimestampNode(*this, patches)); + } + + TimestampNode(const TimestampNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +private: + Timestamp m_value; + const TimestampColumn* m_condition_column; +}; + +class StringNodeBase : public ParentNode { +public: + using TConditionValue = StringData; + static const bool special_null_node = true; + + StringNodeBase(StringData v, size_t column) + : m_value(v.is_null() ? util::none : util::make_optional(std::string(v))) + { + m_condition_column_idx = column; + } + + void table_changed() override + { + m_condition_column = &get_column_base(m_condition_column_idx); + m_column_type = get_real_column_type(m_condition_column_idx); + } + + void init() override + { + m_dT = 10.0; + m_probes = 0; + m_matches = 0; + m_end_s = 0; + m_leaf_start = 0; + m_leaf_end = 0; + } + + void clear_leaf_state() + { + m_leaf.reset(nullptr); + } + + StringNodeBase(const StringNodeBase& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_column_type(from.m_column_type) + , m_leaf_type(from.m_leaf_type) + { + if (m_condition_column && patches) + m_condition_column_idx = m_condition_column->get_column_index(); + } + +protected: + util::Optional m_value; + + const ColumnBase* m_condition_column = nullptr; + ColumnType m_column_type; + + // Used for linear scan through short/long-string + std::unique_ptr m_leaf; + StringColumn::LeafType m_leaf_type; + size_t m_end_s = 0; + size_t m_leaf_start = 0; + size_t m_leaf_end = 0; +}; + +// Conditions for strings. Note that Equal is specialized later in this file! +template +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, size_t column) + : StringNodeBase(v, column) + { + auto upper = case_map(v, true); + auto lower = case_map(v, false); + if (!upper || !lower) { + error_code = "Malformed UTF-8: " + std::string(v); + } + else { + m_ucase = std::move(*upper); + m_lcase = std::move(*lower); + } + } + + void init() override + { + clear_leaf_state(); + + m_dD = 100.0; + + StringNodeBase::init(); + + if (m_child) + m_child->init(); + } + + + size_t find_first_local(size_t start, size_t end) override + { + TConditionFunction cond; + + for (size_t s = start; s < end; ++s) { + StringData t; + + if (m_column_type == col_type_StringEnum) { + // enum + t = static_cast(m_condition_column)->get(s); + } + else { + // short or long + const StringColumn* asc = static_cast(m_condition_column); + REALM_ASSERT_3(s, <, asc->size()); + if (s >= m_end_s || s < m_leaf_start) { + // we exceeded current leaf's range + clear_leaf_state(); + size_t ndx_in_leaf; + m_leaf = asc->get_leaf(s, ndx_in_leaf, m_leaf_type); + m_leaf_start = s - ndx_in_leaf; + + if (m_leaf_type == StringColumn::leaf_type_Small) + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + else + m_end_s = m_leaf_start + static_cast(*m_leaf).size(); + } + + if (m_leaf_type == StringColumn::leaf_type_Small) + t = static_cast(*m_leaf).get(s - m_leaf_start); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + t = static_cast(*m_leaf).get(s - m_leaf_start); + else + t = static_cast(*m_leaf).get_string(s - m_leaf_start); + } + if (cond(StringData(m_value), m_ucase.data(), m_lcase.data(), t)) + return s; + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + , m_ucase(from.m_ucase) + , m_lcase(from.m_lcase) + { + } + +protected: + std::string m_ucase; + std::string m_lcase; +}; + + +// Specialization for Equal condition on Strings - we specialize because we can utilize indexes (if they exist) for +// Equal. +// Future optimization: make specialization for greater, notequal, etc +template <> +class StringNode : public StringNodeBase { +public: + StringNode(StringData v, size_t column) + : StringNodeBase(v, column) + { + } + ~StringNode() noexcept override + { + deallocate(); + } + + void deallocate() noexcept + { + // Must be called after each query execution too free temporary resources used by the execution. Run in + // destructor, but also in Init because a user could define a query once and execute it multiple times. + clear_leaf_state(); + + if (m_index_matches_destroy) + m_index_matches->destroy(); + + m_index_matches_destroy = false; + m_index_matches.reset(); + m_index_getter.reset(); + } + + void init() override + { + deallocate(); + m_dD = 10.0; + StringNodeBase::init(); + + if (m_column_type == col_type_StringEnum) { + m_dT = 1.0; + m_key_ndx = static_cast(m_condition_column)->get_key_ndx(m_value); + } + else if (m_condition_column->has_search_index()) { + m_dT = 0.0; + } + else { + m_dT = 10.0; + } + + if (m_condition_column->has_search_index()) { + FindRes fr; + InternalFindResult res; + + if (m_column_type == col_type_StringEnum) { + fr = static_cast(m_condition_column)->find_all_no_copy(m_value, res); + } + else { + fr = static_cast(m_condition_column)->find_all_no_copy(m_value, res); + } + + m_index_matches_destroy = false; + m_last_start = size_t(-1); + + switch (fr) { + case FindRes_single: + m_index_matches.reset( + new IntegerColumn(IntegerColumn::unattached_root_tag(), Allocator::get_default())); // Throws + m_index_matches->get_root_array()->create(Array::type_Normal); // Throws + m_index_matches->add(res.payload); + m_index_matches_destroy = true; // we own m_index_matches, so we must destroy it + m_results_start = 0; + m_results_end = 1; + break; + case FindRes_column: + // todo: Apparently we can't use m_index.get_alloc() because it uses default allocator which + // simply makes + // translate(x) = x. Shouldn't it inherit owner column's allocator?! + m_index_matches.reset(new IntegerColumn(IntegerColumn::unattached_root_tag(), + m_condition_column->get_alloc())); // Throws + m_index_matches->get_root_array()->init_from_ref(res.payload); + m_results_start = res.start_ndx; + m_results_end = res.end_ndx; + + // FIXME: handle start and end of find_result! + break; + case FindRes_not_found: + m_index_matches.reset(); + m_index_getter.reset(); + break; + } + + if (m_index_matches) { + m_index_getter.reset(new SequentialGetter(m_index_matches.get())); + } + } + else if (m_column_type != col_type_String) { + REALM_ASSERT_DEBUG(dynamic_cast(m_condition_column)); + m_cse.init(static_cast(m_condition_column)); + } + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(m_table); + + if (m_condition_column->has_search_index()) { + // Indexed string column + if (!m_index_getter) + return not_found; // no matches in the index + + if (m_last_start > start) + m_last_indexed = m_results_start; + m_last_start = start; + + while (m_last_indexed < m_results_end) { + m_index_getter->cache_next(m_last_indexed); + size_t f = m_index_getter->m_leaf_ptr->find_gte(start, m_last_indexed - m_index_getter->m_leaf_start, + m_results_end - m_index_getter->m_leaf_start); + + if (f == not_found) { + // Not found in this leaf - move on to next + m_last_indexed = m_index_getter->m_leaf_end; + } + else if (f >= (m_results_end - m_index_getter->m_leaf_start)) { + // Found outside valid range + return not_found; + } + else { + size_t found_index = to_size_t(m_index_getter->m_leaf_ptr->get(f)); + if (found_index >= end) + return not_found; + else { + m_last_indexed = f + m_index_getter->m_leaf_start; + return found_index; + } + } + } + return not_found; + } + + if (m_column_type != col_type_String) { + // Enum string column + if (m_key_ndx == not_found) + return not_found; // not in key set + + for (size_t s = start; s < end; ++s) { + m_cse.cache_next(s); + s = m_cse.m_leaf_ptr->find_first(m_key_ndx, s - m_cse.m_leaf_start, m_cse.local_end(end)); + if (s == not_found) + s = m_cse.m_leaf_end - 1; + else + return s + m_cse.m_leaf_start; + } + + return not_found; + } + + // Normal string column, with long or short leaf + for (size_t s = start; s < end; ++s) { + const StringColumn* asc = static_cast(m_condition_column); + if (s >= m_leaf_end || s < m_leaf_start) { + clear_leaf_state(); + size_t ndx_in_leaf; + m_leaf = asc->get_leaf(s, ndx_in_leaf, m_leaf_type); + m_leaf_start = s - ndx_in_leaf; + if (m_leaf_type == StringColumn::leaf_type_Small) + m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); + else + m_leaf_end = m_leaf_start + static_cast(*m_leaf).size(); + REALM_ASSERT(m_leaf); + } + size_t end2 = (end > m_leaf_end ? m_leaf_end - m_leaf_start : end - m_leaf_start); + + if (m_leaf_type == StringColumn::leaf_type_Small) + s = static_cast(*m_leaf).find_first(m_value, s - m_leaf_start, end2); + else if (m_leaf_type == StringColumn::leaf_type_Medium) + s = static_cast(*m_leaf).find_first(m_value, s - m_leaf_start, end2); + else + s = static_cast(*m_leaf).find_first(str_to_bin(m_value), true, s - m_leaf_start, + end2); + + if (s == not_found) + s = m_leaf_end - 1; + else + return s + m_leaf_start; + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new StringNode(*this, patches)); + } + + StringNode(const StringNode& from, QueryNodeHandoverPatches* patches) + : StringNodeBase(from, patches) + { + } + +private: + inline BinaryData str_to_bin(const StringData& s) noexcept + { + return BinaryData(s.data(), s.size()); + } + + size_t m_key_ndx = not_found; + size_t m_last_indexed; + + // Used for linear scan through enum-string + SequentialGetter m_cse; + + // Used for index lookup + std::unique_ptr m_index_matches; + bool m_index_matches_destroy = false; + std::unique_ptr> m_index_getter; + size_t m_results_start; + size_t m_results_end; + size_t m_last_start; +}; + +// OR node contains at least two node pointers: Two or more conditions to OR +// together in m_conditions, and the next AND condition (if any) in m_child. +// +// For 'second.equal(23).begin_group().first.equal(111).Or().first.equal(222).end_group().third().equal(555)', this +// will first set m_conditions[0] = left-hand-side through constructor, and then later, when .first.equal(222) is +// invoked, +// invocation will set m_conditions[1] = right-hand-side through Query& Query::Or() (see query.cpp). In there, m_child +// is +// also set to next AND condition (if any exists) following the OR. +class OrNode : public ParentNode { +public: + OrNode(std::unique_ptr condition) + { + m_dT = 50.0; + if (condition) + m_conditions.emplace_back(std::move(condition)); + } + + OrNode(const OrNode& other, QueryNodeHandoverPatches* patches) + : ParentNode(other, patches) + { + for (const auto& condition : other.m_conditions) { + m_conditions.emplace_back(condition->clone(patches)); + } + } + + void table_changed() override + { + for (auto& condition : m_conditions) { + condition->set_table(*m_table); + } + } + + void init() override + { + m_dD = 10.0; + + m_start.clear(); + m_start.resize(m_conditions.size(), 0); + + m_last.clear(); + m_last.resize(m_conditions.size(), 0); + + m_was_match.clear(); + m_was_match.resize(m_conditions.size(), false); + + std::vector v; + for (auto& condition : m_conditions) { + condition->init(); + v.clear(); + condition->gather_children(v); + } + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override + { + if (start >= end) + return not_found; + + size_t index = not_found; + + for (size_t c = 0; c < m_conditions.size(); ++c) { + // out of order search; have to discard cached results + if (start < m_start[c]) { + m_last[c] = 0; + m_was_match[c] = false; + } + // already searched this range and didn't match + else if (m_last[c] >= end) + continue; + // already search this range and *did* match + else if (m_was_match[c] && m_last[c] >= start) { + if (index > m_last[c]) + index = m_last[c]; + continue; + } + + m_start[c] = start; + size_t fmax = std::max(m_last[c], start); + size_t f = m_conditions[c]->find_first(fmax, end); + m_was_match[c] = f != not_found; + m_last[c] = f == not_found ? end : f; + if (f != not_found && index > m_last[c]) + index = m_last[c]; + } + + return index; + } + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_conditions.size() == 0) + return "Missing left-hand side of OR"; + if (m_conditions.size() == 1) + return "Missing right-hand side of OR"; + std::string s; + if (m_child != 0) + s = m_child->validate(); + if (s != "") + return s; + for (size_t i = 0; i < m_conditions.size(); ++i) { + s = m_conditions[i]->validate(); + if (s != "") + return s; + } + return ""; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new OrNode(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + for (auto it = m_conditions.rbegin(); it != m_conditions.rend(); ++it) + (*it)->apply_handover_patch(patches, group); + + ParentNode::apply_handover_patch(patches, group); + } + + std::vector> m_conditions; + +private: + // start index of the last find for each cond + std::vector m_start; + // last looked at index of the lasft find for each cond + // is a matching index if m_was_match is true + std::vector m_last; + std::vector m_was_match; +}; + + +class NotNode : public ParentNode { +public: + NotNode(std::unique_ptr condition) + : m_condition(std::move(condition)) + { + m_dT = 50.0; + } + + void table_changed() override + { + m_condition->set_table(*m_table); + } + + void init() override + { + m_dD = 10.0; + + std::vector v; + + m_condition->init(); + v.clear(); + m_condition->gather_children(v); + + // Heuristics bookkeeping: + m_known_range_start = 0; + m_known_range_end = 0; + m_first_in_known_range = not_found; + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override; + + std::string validate() override + { + if (error_code != "") + return error_code; + if (m_condition == 0) + return "Missing argument to Not"; + std::string s; + if (m_child != 0) + s = m_child->validate(); + if (s != "") + return s; + s = m_condition->validate(); + if (s != "") + return s; + return ""; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new NotNode(*this, patches)); + } + + NotNode(const NotNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_condition(from.m_condition ? from.m_condition->clone(patches) : nullptr) + , m_known_range_start(from.m_known_range_start) + , m_known_range_end(from.m_known_range_end) + , m_first_in_known_range(from.m_first_in_known_range) + { + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_condition->apply_handover_patch(patches, group); + ParentNode::apply_handover_patch(patches, group); + } + + std::unique_ptr m_condition; + +private: + // FIXME This heuristic might as well be reused for all condition nodes. + size_t m_known_range_start; + size_t m_known_range_end; + size_t m_first_in_known_range; + + bool evaluate_at(size_t rowndx); + void update_known(size_t start, size_t end, size_t first); + size_t find_first_loop(size_t start, size_t end); + size_t find_first_covers_known(size_t start, size_t end); + size_t find_first_covered_by_known(size_t start, size_t end); + size_t find_first_overlap_lower(size_t start, size_t end); + size_t find_first_overlap_upper(size_t start, size_t end); + size_t find_first_no_overlap(size_t start, size_t end); +}; + + +// Compare two columns with eachother row-by-row +template +class TwoColumnsNode : public ParentNode { +public: + using TConditionValue = typename ColType::value_type; + + TwoColumnsNode(size_t column1, size_t column2) + { + m_dT = 100.0; + m_condition_column_idx1 = column1; + m_condition_column_idx2 = column2; + } + + ~TwoColumnsNode() noexcept override + { + delete[] m_value.data(); + } + + void table_changed() override + { + m_getter1.init(&get_column(m_condition_column_idx1)); + m_getter2.init(&get_column(m_condition_column_idx2)); + } + + void init() override + { + m_dD = 100.0; + + if (m_child) + m_child->init(); + } + + size_t find_first_local(size_t start, size_t end) override + { + size_t s = start; + + while (s < end) { + if (std::is_same::value) { + // For int64_t we've created an array intrinsics named compare_leafs which template expands bitwidths + // of boths arrays to make Get faster. + m_getter1.cache_next(s); + m_getter2.cache_next(s); + + QueryState qs; + bool resume = m_getter1.m_leaf_ptr->template compare_leafs( + m_getter2.m_leaf_ptr, s - m_getter1.m_leaf_start, m_getter1.local_end(end), 0, &qs, + CallbackDummy()); + + if (resume) + s = m_getter1.m_leaf_end; + else + return to_size_t(qs.m_state) + m_getter1.m_leaf_start; + } + else { +// This is for float and double. + +#if 0 && defined(REALM_COMPILER_AVX) +// AVX has been disabled because of array alignment (see https://app.asana.com/0/search/8836174089724/5763107052506) +// +// For AVX you can call things like if (sseavx<1>()) to test for AVX, and then utilize _mm256_movemask_ps (VC) +// or movemask_cmp_ps (gcc/clang) +// +// See https://github.com/rrrlasse/realm/tree/AVX for an example of utilizing AVX for a two-column search which has +// been benchmarked to: floats: 288 ms vs 552 by using AVX compared to 2-level-unrolled FPU loop. doubles: 415 ms vs +// 475 (more bandwidth bound). Tests against SSE have not been performed; AVX may not pay off. Please benchmark +#endif + + TConditionValue v1 = m_getter1.get_next(s); + TConditionValue v2 = m_getter2.get_next(s); + TConditionFunction C; + + if (C(v1, v2)) + return s; + else + s++; + } + } + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new TwoColumnsNode(*this, patches)); + } + + TwoColumnsNode(const TwoColumnsNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_value(from.m_value) + , m_condition_column(from.m_condition_column) + , m_column_type(from.m_column_type) + { + if (m_condition_column) + m_condition_column_idx = m_condition_column->get_column_index(); + copy_getter(m_getter1, m_condition_column_idx1, from.m_getter1, patches); + copy_getter(m_getter2, m_condition_column_idx2, from.m_getter2, patches); + } + +private: + BinaryData m_value; + const BinaryColumn* m_condition_column = nullptr; + ColumnType m_column_type; + + size_t m_condition_column_idx1 = not_found; + size_t m_condition_column_idx2 = not_found; + + SequentialGetter m_getter1; + SequentialGetter m_getter2; +}; + + +// For Next-Generation expressions like col1 / col2 + 123 > col4 * 100. +class ExpressionNode : public ParentNode { + +public: + ExpressionNode(std::unique_ptr expression) + : m_expression(std::move(expression)) + { + m_dD = 10.0; + m_dT = 50.0; + } + + void table_changed() override + { + m_expression->set_base_table(m_table.get()); + } + + size_t find_first_local(size_t start, size_t end) override + { + return m_expression->find_first(start, end); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new ExpressionNode(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_expression->apply_handover_patch(patches, group); + ParentNode::apply_handover_patch(patches, group); + } + +private: + ExpressionNode(const ExpressionNode& from, QueryNodeHandoverPatches* patches) + : ParentNode(from, patches) + , m_expression(from.m_expression->clone(patches)) + { + } + + std::unique_ptr m_expression; +}; + + +struct LinksToNodeHandoverPatch : public QueryNodeHandoverPatch { + std::unique_ptr m_target_row; + size_t m_origin_column; +}; + +class LinksToNode : public ParentNode { +public: + LinksToNode(size_t origin_column_index, const ConstRow& target_row) + : m_origin_column(origin_column_index) + , m_target_row(target_row) + { + m_dD = 10.0; + m_dT = 50.0; + } + + void table_changed() override + { + m_column_type = m_table->get_column_type(m_origin_column); + m_column = &const_cast(m_table.get())->get_column_link_base(m_origin_column); + REALM_ASSERT(m_column_type == type_Link || m_column_type == type_LinkList); + } + + size_t find_first_local(size_t start, size_t end) override + { + REALM_ASSERT(m_column); + if (!m_target_row.is_attached()) + return not_found; + + if (m_column_type == type_Link) { + LinkColumn& cl = static_cast(*m_column); + return cl.find_first(m_target_row.get_index() + 1, start, + end); // LinkColumn stores link to row N as the integer N + 1 + } + else if (m_column_type == type_LinkList) { + LinkListColumn& cll = static_cast(*m_column); + + for (size_t i = start; i < end; i++) { + LinkViewRef lv = cll.get(i); + if (lv->find(m_target_row.get_index()) != not_found) + return i; + } + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(patches ? new LinksToNode(*this, patches) : new LinksToNode(*this)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_origin_column = patch->m_origin_column; + m_target_row.apply_and_consume_patch(patch->m_target_row, group); + + ParentNode::apply_handover_patch(patches, group); + } + +private: + size_t m_origin_column = npos; + ConstRow m_target_row; + LinkColumnBase* m_column = nullptr; + DataType m_column_type; + + LinksToNode(const LinksToNode& source, QueryNodeHandoverPatches* patches) + : ParentNode(source, patches) + { + auto patch = std::make_unique(); + patch->m_origin_column = source.m_column->get_column_index(); + ConstRow::generate_patch(source.m_target_row, patch->m_target_row); + patches->push_back(std::move(patch)); + } +}; + +} // namespace realm + +#endif // REALM_QUERY_ENGINE_HPP diff --git a/Pods/Realm/include/core/realm/query_expression.hpp b/Pods/Realm/include/core/realm/query_expression.hpp new file mode 100644 index 0000000..1a29aed --- /dev/null +++ b/Pods/Realm/include/core/realm/query_expression.hpp @@ -0,0 +1,3092 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +/* +This file lets you write queries in C++ syntax like: Expression* e = (first + 1 / second >= third + 12.3); + +Type conversion/promotion semantics is the same as in the C++ expressions, e.g float + int > double == float + +(float)int > double. + + +Grammar: +----------------------------------------------------------------------------------------------------------------------- + Expression: Subexpr2 Compare Subexpr2 + operator! Expression + + Subexpr2: Value + Columns + Subexpr2 Operator Subexpr2 + power(Subexpr2) // power(x) = x * x, as example of unary operator + + Value: T + + Operator>: +, -, *, / + + Compare: ==, !=, >=, <=, >, < + + T: bool, int, int64_t, float, double, StringData + + +Class diagram +----------------------------------------------------------------------------------------------------------------------- +Subexpr2 + void evaluate(size_t i, ValueBase* destination) + +Compare: public Subexpr2 + size_t find_first(size_t start, size_t end) // main method that executes query + + unique_ptr m_left; // left expression subtree + unique_ptr m_right; // right expression subtree + +Operator: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + unique_ptr m_left; // left expression subtree + unique_ptr m_right; // right expression subtree + +Value: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + T m_v[8]; + +Columns: public Subexpr2 + void evaluate(size_t i, ValueBase* destination) + SequentialGetter sg; // class bound to a column, lets you read values in a fast way + Table* m_table; + +class ColumnAccessor<>: public Columns + + +Call diagram: +----------------------------------------------------------------------------------------------------------------------- +Example of 'table.first > 34.6 + table.second': + +size_t Compare::find_first()-------------+ + | | + | | + | | + +--> Columns::evaluate() +--------> Operator::evaluate() + | | + Value::evaluate() Columns::evaluate() + +Operator, Value and Columns have an evaluate(size_t i, ValueBase* destination) method which returns a Value +containing 8 values representing table rows i...i + 7. + +So Value contains 8 concecutive values and all operations are based on these chunks. This is +to save overhead by virtual calls needed for evaluating a query that has been dynamically constructed at runtime. + + +Memory allocation: +----------------------------------------------------------------------------------------------------------------------- +Subexpressions created by the end-user are stack allocated. They are cloned to the heap when passed to UnaryOperator, +Operator, and Compare. Those types own the clones and deallocate them when destroyed. + + +Caveats, notes and todos +----------------------------------------------------------------------------------------------------------------------- + * Perhaps disallow columns from two different tables in same expression + * The name Columns (with s) an be confusing because we also have Column (without s) + * We have Columns::m_table, Query::m_table and ColumnAccessorBase::m_table that point at the same thing, even with + ColumnAccessor<> extending Columns. So m_table is redundant, but this is in order to keep class dependencies and + entanglement low so that the design is flexible (if you perhaps later want a Columns class that is not dependent + on ColumnAccessor) + +Nulls +----------------------------------------------------------------------------------------------------------------------- +First note that at array level, nulls are distinguished between non-null in different ways: +String: + m_data == 0 && m_size == 0 + +Integer, Bool, OldDateTime stored in ArrayIntNull: + value == get(0) (entry 0 determins a magic value that represents nulls) + +Float/double: + null::is_null(value) which tests if value bit-matches one specific bit pattern reserved for null + +The Columns class encapsulates all this into a simple class that, for any type T has + evaluate(size_t index) that reads values from a column, taking nulls in count + get(index) + set(index) + is_null(index) + set_null(index) +*/ + +#ifndef REALM_QUERY_EXPRESSION_HPP +#define REALM_QUERY_EXPRESSION_HPP + +#include +#include +#include + +#include + +// Normally, if a next-generation-syntax condition is supported by the old query_engine.hpp, a query_engine node is +// created because it's faster (by a factor of 5 - 10). Because many of our existing next-generation-syntax unit +// unit tests are indeed simple enough to fallback to old query_engine, query_expression gets low test coverage. Undef +// flag to get higher query_expression test coverage. This is a good idea to try out each time you develop on/modify +// query_expression. + +#define REALM_OLDQUERY_FALLBACK + +namespace realm { + +template +T minimum(T a, T b) +{ + return a < b ? a : b; +} + +// FIXME, this needs to exist elsewhere +typedef int64_t Int; +typedef bool Bool; +typedef realm::OldDateTime OldDateTime; +typedef float Float; +typedef double Double; +typedef realm::StringData String; +typedef realm::BinaryData Binary; + +#ifdef REALM_OLDQUERY_FALLBACK +// Hack to avoid template instantiation errors. See create(). Todo, see if we can simplify only_numeric somehow +namespace { +template +T only_numeric(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +int only_numeric(const StringData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +int only_numeric(const BinaryData&) +{ + REALM_ASSERT(false); + return 0; +} + +template +StringData only_string(T in) +{ + REALM_ASSERT(false); + static_cast(in); + return StringData(); +} + +StringData only_string(StringData in) +{ + return in; +} + +template +T no_timestamp(U in) +{ + return static_cast(util::unwrap(in)); +} + +template +int no_timestamp(const Timestamp&) +{ + REALM_ASSERT(false); + return 0; +} +#endif // REALM_OLDQUERY_FALLBACK + +} // anonymous namespace + +template +struct Plus { + T operator()(T v1, T v2) const + { + return v1 + v2; + } + typedef T type; +}; + +template +struct Minus { + T operator()(T v1, T v2) const + { + return v1 - v2; + } + typedef T type; +}; + +template +struct Div { + T operator()(T v1, T v2) const + { + return v1 / v2; + } + typedef T type; +}; + +template +struct Mul { + T operator()(T v1, T v2) const + { + return v1 * v2; + } + typedef T type; +}; + +// Unary operator +template +struct Pow { + T operator()(T v) const + { + return v * v; + } + typedef T type; +}; + +// Finds a common type for T1 and T2 according to C++ conversion/promotion in arithmetic (float + int => float, etc) +template ::is_integer || std::is_same::value, + bool T2_is_int = std::numeric_limits::is_integer || std::is_same::value, + bool T1_is_widest = (sizeof(T1) > sizeof(T2) || std::is_same::value)> +struct Common; +template +struct Common { + typedef T1 type; +}; +template +struct Common { + typedef T2 type; +}; +template +struct Common { + typedef T1 type; +}; +template +struct Common { + typedef T2 type; +}; + + +struct RowIndex { + enum DetachedTag { + Detached, + }; + + explicit RowIndex() + : m_row_index(npos) + { + } + explicit RowIndex(size_t row_index) + : m_row_index(row_index) + { + } + RowIndex(DetachedTag) + : m_row_index() + { + } + + bool is_attached() const + { + return bool(m_row_index); + } + bool is_null() const + { + return is_attached() && *m_row_index == npos; + } + + bool operator==(const RowIndex& other) const + { + // Row indexes that are detached are never equal to any other row index. + if (!is_attached() || !other.is_attached()) + return false; + return m_row_index == other.m_row_index; + } + bool operator!=(const RowIndex& other) const + { + return !(*this == other); + } + +private: + util::Optional m_row_index; +}; + + +struct ValueBase { + static const size_t default_size = 8; + virtual void export_bool(ValueBase& destination) const = 0; + virtual void export_Timestamp(ValueBase& destination) const = 0; + virtual void export_int(ValueBase& destination) const = 0; + virtual void export_float(ValueBase& destination) const = 0; + virtual void export_int64_t(ValueBase& destination) const = 0; + virtual void export_double(ValueBase& destination) const = 0; + virtual void export_StringData(ValueBase& destination) const = 0; + virtual void export_BinaryData(ValueBase& destination) const = 0; + virtual void export_RowIndex(ValueBase& destination) const = 0; + virtual void export_null(ValueBase& destination) const = 0; + virtual void import(const ValueBase& destination) = 0; + + // If true, all values in the class come from a link list of a single field in the parent table (m_table). If + // false, then values come from successive rows of m_table (query operations are operated on in bulks for speed) + bool m_from_link_list; + + // Number of values stored in the class. + size_t m_values; +}; + +class Expression { +public: + Expression() + { + } + virtual ~Expression() + { + } + + virtual size_t find_first(size_t start, size_t end) const = 0; + virtual void set_base_table(const Table* table) = 0; + virtual const Table* get_base_table() const = 0; + + virtual std::unique_ptr clone(QueryNodeHandoverPatches*) const = 0; + virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&) + { + } +}; + +template +std::unique_ptr make_expression(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +class Subexpr { +public: + virtual ~Subexpr() + { + } + + virtual std::unique_ptr clone(QueryNodeHandoverPatches* = nullptr) const = 0; + virtual void apply_handover_patch(QueryNodeHandoverPatches&, Group&) + { + } + + // When the user constructs a query, it always "belongs" to one single base/parent table (regardless of + // any links or not and regardless of any queries assembled with || or &&). When you do a Query::find(), + // then Query::m_table is set to this table, and set_base_table() is called on all Columns and LinkMaps in + // the query expression tree so that they can set/update their internals as required. + // + // During thread-handover of a Query, set_base_table() is also called to make objects point at the new table + // instead of the old one from the old thread. + virtual void set_base_table(const Table*) + { + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + virtual const Table* get_base_table() const + { + return nullptr; + } + + virtual void evaluate(size_t index, ValueBase& destination) = 0; +}; + +template +std::unique_ptr make_subexpr(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +class Columns; +template +class Value; +class ConstantStringValue; +template +class Subexpr2; +template +class Operator; +template +class UnaryOperator; +template +class Compare; +template +class UnaryLinkCompare; +class ColumnAccessorBase; + + +// Handle cases where left side is a constant (int, float, int64_t, double, StringData) +template +Query create(L left, const Subexpr2& right) +{ +// Purpose of below code is to intercept the creation of a condition and test if it's supported by the old +// query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a +// query_expression.hpp node. +// +// This method intercepts only Value Subexpr2. Interception of Subexpr2 Subexpr is elsewhere. + +#ifdef REALM_OLDQUERY_FALLBACK // if not defined, then never fallback to query_engine.hpp; always use query_expression + const Columns* column = dynamic_cast*>(&right); + + if (column && ((std::numeric_limits::is_integer && std::numeric_limits::is_integer) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value) || + (std::is_same::value && std::is_same::value)) && + !column->links_exist()) { + const Table* t = column->get_base_table(); + Query q = Query(*t); + + if (std::is_same::value) + q.greater(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.less(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_ndx(), left); + else if (std::is_same::value) + q.not_equal(column->column_ndx(), left); + else if (std::is_same::value) + q.greater_equal(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.less_equal(column->column_ndx(), only_numeric(left)); + else if (std::is_same::value) + q.equal(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.not_equal(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.begins_with(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.begins_with(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.ends_with(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.ends_with(column->column_ndx(), only_string(left), false); + else if (std::is_same::value) + q.contains(column->column_ndx(), only_string(left)); + else if (std::is_same::value) + q.contains(column->column_ndx(), only_string(left), false); + else { + // query_engine.hpp does not support this Cond. Please either add support for it in query_engine.hpp or + // fallback to using use 'return new Compare<>' instead. + REALM_ASSERT(false); + } + // Return query_engine.hpp node + return q; + } + else +#endif + { + // Return query_expression.hpp node + using CommonType = typename Common::type; + using ValueType = + typename std::conditional::value, ConstantStringValue, Value>::type; + return make_expression>(make_subexpr(left), right.clone()); + } +} + + +// All overloads where left-hand-side is Subexpr2: +// +// left-hand-side operator right-hand-side +// Subexpr2 +, -, *, /, <, >, ==, !=, <=, >= R, Subexpr2 +// +// For L = R = {int, int64_t, float, double, StringData, Timestamp}: +template +class Overloads { + typedef typename Common::type CommonType; + + std::unique_ptr clone_subexpr() const + { + return static_cast&>(*this).clone(); + } + +public: + // Arithmetic, right side constant + Operator> operator+(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator-(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator*(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + Operator> operator/(R right) const + { + return {clone_subexpr(), make_subexpr>(right)}; + } + + // Arithmetic, right side subexpression + Operator> operator+(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator-(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator*(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + Operator> operator/(const Subexpr2& right) const + { + return {clone_subexpr(), right.clone()}; + } + + // Compare, right side constant + Query operator>(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator<(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator>=(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator<=(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator==(R right) + { + return create(right, static_cast&>(*this)); + } + Query operator!=(R right) + { + return create(right, static_cast&>(*this)); + } + + // Purpose of this method is to intercept the creation of a condition and test if it's supported by the old + // query_engine.hpp which is faster. If it's supported, create a query_engine.hpp node, otherwise create a + // query_expression.hpp node. + // + // This method intercepts Subexpr2 Subexpr2 only. Value Subexpr2 is intercepted elsewhere. + template + Query create2(const Subexpr2& right) + { +#ifdef REALM_OLDQUERY_FALLBACK // if not defined, never fallback query_engine; always use query_expression + // Test if expressions are of type Columns. Other possibilities are Value and Operator. + const Columns* left_col = dynamic_cast*>(static_cast*>(this)); + const Columns* right_col = dynamic_cast*>(&right); + + // query_engine supports 'T-column ' for T = {int64_t, float, double}, op = {<, >, ==, !=, <=, + // >=}, + // but only if both columns are non-nullable, and aren't in linked tables. + if (left_col && right_col && std::is_same::value && !left_col->is_nullable() && + !right_col->is_nullable() && !left_col->links_exist() && !right_col->links_exist() && + !std::is_same::value) { + const Table* t = left_col->get_base_table(); + Query q = Query(*t); + + if (std::numeric_limits::is_integer || std::is_same::value) { + if (std::is_same::value) + q.less_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_int(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_float(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else if (std::is_same::value) { + if (std::is_same::value) + q.less_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.not_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.less_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else if (std::is_same::value) + q.greater_equal_double(left_col->column_ndx(), right_col->column_ndx()); + else { + REALM_ASSERT(false); + } + } + else { + REALM_ASSERT(false); + } + // Return query_engine.hpp node + return q; + } + else +#endif + { + // Return query_expression.hpp node + return make_expression::type>>(clone_subexpr(), right.clone()); + } + } + + // Compare, right side subexpression + Query operator==(const Subexpr2& right) + { + return create2(right); + } + Query operator!=(const Subexpr2& right) + { + return create2(right); + } + Query operator>(const Subexpr2& right) + { + return create2(right); + } + Query operator<(const Subexpr2& right) + { + return create2(right); + } + Query operator>=(const Subexpr2& right) + { + return create2(right); + } + Query operator<=(const Subexpr2& right) + { + return create2(right); + } +}; + +// With this wrapper class we can define just 20 overloads inside Overloads instead of 5 * 20 = 100. Todo: We +// can +// consider if it's simpler/better to remove this class completely and just list all 100 overloads manually anyway. +template +class Subexpr2 : public Subexpr, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads, + public Overloads { +public: + virtual ~Subexpr2() + { + } + +#define RLM_U2(t, o) using Overloads::operator o; +#define RLM_U(o) \ + RLM_U2(int, o) \ + RLM_U2(float, o) \ + RLM_U2(double, o) \ + RLM_U2(int64_t, o) \ + RLM_U2(StringData, o) RLM_U2(bool, o) RLM_U2(OldDateTime, o) RLM_U2(Timestamp, o) RLM_U2(null, o) + RLM_U(+) RLM_U(-) RLM_U(*) RLM_U(/) RLM_U(>) RLM_U(<) RLM_U(==) RLM_U(!=) RLM_U(>=) RLM_U(<=) +}; + +// Subexpr2 only provides equality comparisons. Their implementations can be found later in this file. +template <> +class Subexpr2 : public Subexpr { +}; + + +/* +This class is used to store N values of type T = {int64_t, bool, OldDateTime or StringData}, and allows an entry +to be null too. It's used by the Value class for internal storage. + +To indicate nulls, we could have chosen a separate bool vector or some other bitmask construction. But for +performance, we customize indication of nulls to match the same indication that is used in the persisted database +file + +Queries in query_expression.hpp execute by processing chunks of 8 rows at a time. Assume you have a column: + + price (int) = {1, 2, 3, null, 1, 6, 6, 9, 5, 2, null} + +And perform a query: + + Query q = (price + 2 == 5); + +query_expression.hpp will then create a NullableVector = {5, 5, 5, 5, 5, 5, 5, 5} and then read values +NullableVector = {1, 2, 3, null, 1, 6, 6, 9} from the column, and then perform `+` and `==` on these chunks. + +See the top of this file for more information on all this. + +Assume the user specifies the null constant in a query: + +Query q = (price == null) + +The query system will then construct a NullableVector of type `null` (NullableVector). This allows compile +time optimizations for these cases. +*/ + +template +struct NullableVector { + using Underlying = typename util::RemoveOptional::type; + using t_storage = + typename std::conditional::value || std::is_same::value, + int64_t, Underlying>::type; + + NullableVector() + { + } + + NullableVector& operator=(const NullableVector& other) + { + if (this != &other) { + init(other.m_size); + std::copy(other.m_first, other.m_first + other.m_size, m_first); + m_null = other.m_null; + } + return *this; + } + + NullableVector(const NullableVector& other) + { + init(other.m_size); + std::copy(other.m_first, other.m_first + other.m_size, m_first); + m_null = other.m_null; + } + + ~NullableVector() + { + dealloc(); + } + + T operator[](size_t index) const + { + REALM_ASSERT_3(index, <, m_size); + return static_cast(m_first[index]); + } + + inline bool is_null(size_t index) const + { + REALM_ASSERT((std::is_same::value)); + return m_first[index] == m_null; + } + + inline void set_null(size_t index) + { + REALM_ASSERT((std::is_same::value)); + m_first[index] = m_null; + } + + template + typename std::enable_if::value, void>::type set(size_t index, t_storage value) + { + REALM_ASSERT((std::is_same::value)); + + // If value collides with magic null value, then switch to a new unique representation for null + if (REALM_UNLIKELY(value == m_null)) { + // adding a prime will generate 2^64 unique values. Todo: Only works on 2's complement architecture + uint64_t candidate = static_cast(m_null) + 0xfffffffbULL; + while (std::find(m_first, m_first + m_size, static_cast(candidate)) != m_first + m_size) + candidate += 0xfffffffbULL; + std::replace(m_first, m_first + m_size, m_null, static_cast(candidate)); + } + m_first[index] = value; + } + + template + typename std::enable_if< + realm::is_any::value, + void>::type + set(size_t index, t_storage value) + { + m_first[index] = value; + } + + + inline util::Optional get(size_t index) const + { + if (is_null(index)) + return util::none; + + return util::make_optional((*this)[index]); + } + + inline void set(size_t index, util::Optional value) + { + if (value) { + Underlying v = *value; + set(index, v); + } + else { + set_null(index); + } + } + + void fill(T value) + { + for (size_t t = 0; t < m_size; t++) { + if (std::is_same::value) + set_null(t); + else + set(t, value); + } + } + + void init(size_t size) + { + if (size == m_size) + return; + + dealloc(); + m_size = size; + if (m_size > 0) { + if (m_size > prealloc) + m_first = reinterpret_cast(new t_storage[m_size]); + else + m_first = m_cache; + } + } + + void init(size_t size, T values) + { + init(size); + fill(values); + } + + void dealloc() + { + if (m_first) { + if (m_size > prealloc) + delete[] m_first; + m_first = nullptr; + } + } + + t_storage m_cache[prealloc]; + t_storage* m_first = &m_cache[0]; + size_t m_size = 0; + + int64_t m_null = reinterpret_cast(&m_null); // choose magic value to represent nulls +}; + +// Double +// NOTE: fails in gcc 4.8 without `inline`. Do not remove. Same applies for all methods below. +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return null::is_null_float(m_first[index]); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = null::get_null_float(); +} + +// Float +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return null::is_null_float(m_first[index]); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = null::get_null_float(); +} + + +// Null +template <> +inline void NullableVector::set_null(size_t) +{ + return; +} +template <> +inline bool NullableVector::is_null(size_t) const +{ + return true; +} + + +// OldDateTime +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].get_olddatetime() == m_null; +} + + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = m_null; +} + +// StringData + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = StringData(); +} + +// BinaryData + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = BinaryData(); +} + +// RowIndex +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = RowIndex(); +} + + +// Timestamp + +template <> +inline bool NullableVector::is_null(size_t index) const +{ + return m_first[index].is_null(); +} + +template <> +inline void NullableVector::set_null(size_t index) +{ + m_first[index] = Timestamp{}; +} + + +template +struct OperatorOptionalAdapter { + template + util::Optional operator()(const util::Optional& left, const util::Optional& right) + { + if (!left || !right) + return util::none; + return Operator()(*left, *right); + } + + template + util::Optional operator()(const util::Optional& arg) + { + if (!arg) + return util::none; + return Operator()(*arg); + } +}; + +// Stores N values of type T. Can also exchange data with other ValueBase of different types +template +class Value : public ValueBase, public Subexpr2 { +public: + Value() + { + init(false, ValueBase::default_size, T()); + } + Value(T v) + { + init(false, ValueBase::default_size, v); + } + + Value(bool from_link_list, size_t values) + { + init(from_link_list, values, T()); + } + + Value(bool from_link_list, size_t values, T v) + { + init(from_link_list, values, v); + } + + Value(const Value&) = default; + Value& operator=(const Value&) = default; + + void init(bool from_link_list, size_t values, T v) + { + m_storage.init(values, v); + ValueBase::m_from_link_list = from_link_list; + ValueBase::m_values = values; + } + + void init(bool from_link_list, size_t values) + { + m_storage.init(values); + ValueBase::m_from_link_list = from_link_list; + ValueBase::m_values = values; + } + + void evaluate(size_t, ValueBase& destination) override + { + destination.import(*this); + } + + + template + REALM_FORCEINLINE void fun(const Value* left, const Value* right) + { + OperatorOptionalAdapter o; + + if (!left->m_from_link_list && !right->m_from_link_list) { + // Operate on values one-by-one (one value is one row; no links) + size_t min = std::min(left->m_values, right->m_values); + init(false, min); + + for (size_t i = 0; i < min; i++) { + m_storage.set(i, o(left->m_storage.get(i), right->m_storage.get(i))); + } + } + else if (left->m_from_link_list && right->m_from_link_list) { + // FIXME: Many-to-many links not supported yet. Need to specify behaviour + REALM_ASSERT_DEBUG(false); + } + else if (!left->m_from_link_list && right->m_from_link_list) { + // Right values come from link. Left must come from single row. + REALM_ASSERT_DEBUG(left->m_values > 0); + init(true, right->m_values); + + auto left_value = left->m_storage.get(0); + for (size_t i = 0; i < right->m_values; i++) { + m_storage.set(i, o(left_value, right->m_storage.get(i))); + } + } + else if (left->m_from_link_list && !right->m_from_link_list) { + // Same as above, but with left values coming from links + REALM_ASSERT_DEBUG(right->m_values > 0); + init(true, left->m_values); + + auto right_value = right->m_storage.get(0); + for (size_t i = 0; i < left->m_values; i++) { + m_storage.set(i, o(left->m_storage.get(i), right_value)); + } + } + } + + template + REALM_FORCEINLINE void fun(const Value* value) + { + init(value->m_from_link_list, value->m_values); + + OperatorOptionalAdapter o; + for (size_t i = 0; i < value->m_values; i++) { + m_storage.set(i, o(value->m_storage.get(i))); + } + } + + + // Below import and export methods are for type conversion between float, double, int64_t, etc. + template + typename std::enable_if::value>::type REALM_FORCEINLINE + export2(ValueBase& destination) const + { + Value& d = static_cast&>(destination); + d.init(ValueBase::m_from_link_list, ValueBase::m_values, D()); + for (size_t t = 0; t < ValueBase::m_values; t++) { + if (m_storage.is_null(t)) + d.m_storage.set_null(t); + else { + d.m_storage.set(t, static_cast(m_storage[t])); + } + } + } + + template + typename std::enable_if::value>::type REALM_FORCEINLINE export2(ValueBase&) const + { + // export2 is instantiated for impossible conversions like T=StringData, D=int64_t. These are never + // performed at runtime but would result in a compiler error if we did not provide this implementation. + REALM_ASSERT_DEBUG(false); + } + + REALM_FORCEINLINE void export_Timestamp(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_bool(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_int64_t(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_float(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_int(ValueBase& destination) const override + { + export2(destination); + } + + REALM_FORCEINLINE void export_double(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_StringData(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_BinaryData(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_RowIndex(ValueBase& destination) const override + { + export2(destination); + } + REALM_FORCEINLINE void export_null(ValueBase& destination) const override + { + Value& d = static_cast&>(destination); + d.init(m_from_link_list, m_values); + } + + REALM_FORCEINLINE void import(const ValueBase& source) override + { + if (std::is_same::value) + source.export_int(*this); + else if (std::is_same::value) + source.export_Timestamp(*this); + else if (std::is_same::value) + source.export_bool(*this); + else if (std::is_same::value) + source.export_float(*this); + else if (std::is_same::value) + source.export_double(*this); + else if (std::is_same::value || std::is_same::value || + std::is_same::value) + source.export_int64_t(*this); + else if (std::is_same::value) + source.export_StringData(*this); + else if (std::is_same::value) + source.export_BinaryData(*this); + else if (std::is_same::value) + source.export_RowIndex(*this); + else if (std::is_same::value) + source.export_null(*this); + else + REALM_ASSERT_DEBUG(false); + } + + // Given a TCond (==, !=, >, <, >=, <=) and two Value, return index of first match + template + REALM_FORCEINLINE static size_t compare(Value* left, Value* right) + { + TCond c; + + if (!left->m_from_link_list && !right->m_from_link_list) { + // Compare values one-by-one (one value is one row; no link lists) + size_t min = minimum(left->ValueBase::m_values, right->ValueBase::m_values); + for (size_t m = 0; m < min; m++) { + + if (c(left->m_storage[m], right->m_storage[m], left->m_storage.is_null(m), + right->m_storage.is_null(m))) + return m; + } + } + else if (left->m_from_link_list && right->m_from_link_list) { + // FIXME: Many-to-many links not supported yet. Need to specify behaviour + REALM_ASSERT_DEBUG(false); + } + else if (!left->m_from_link_list && right->m_from_link_list) { + // Right values come from link list. Left must come from single row. Semantics: Match if at least 1 + // linked-to-value fulfills the condition + REALM_ASSERT_DEBUG(left->m_values > 0); + for (size_t r = 0; r < right->m_values; r++) { + if (c(left->m_storage[0], right->m_storage[r], left->m_storage.is_null(0), + right->m_storage.is_null(r))) + return 0; + } + } + else if (left->m_from_link_list && !right->m_from_link_list) { + // Same as above, but with left values coming from link list. + REALM_ASSERT_DEBUG(right->m_values > 0); + for (size_t l = 0; l < left->m_values; l++) { + if (c(left->m_storage[l], right->m_storage[0], left->m_storage.is_null(l), + right->m_storage.is_null(0))) + return 0; + } + } + + return not_found; // no match + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return make_subexpr>(*this); + } + + NullableVector m_storage; +}; + +class ConstantStringValue : public Value { +public: + ConstantStringValue(const StringData& string) + : Value() + , m_string(string.is_null() ? util::none : util::make_optional(std::string(string))) + { + init(false, ValueBase::default_size, m_string); + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return std::unique_ptr(new ConstantStringValue(*this)); + } + +private: + ConstantStringValue(const ConstantStringValue& other) + : Value() + , m_string(other.m_string) + { + init(other.m_from_link_list, other.m_values, m_string); + } + + util::Optional m_string; +}; + +// All overloads where left-hand-side is L: +// +// left-hand-side operator right-hand-side +// L +, -, *, /, <, >, ==, !=, <=, >= Subexpr2 +// +// For L = R = {int, int64_t, float, double, Timestamp}: +// Compare numeric values +template +Query operator>(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} + +template +Query operator<(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator==(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator>=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator<=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(double left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(float left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(int left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(int64_t left, const Subexpr2& right) +{ + return create(left, right); +} +template +Query operator!=(Timestamp left, const Subexpr2& right) +{ + return create(left, right); +} + +// Arithmetic +template +Operator::type>> operator+(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator+(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator-(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator*(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(double left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(float left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(int left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} +template +Operator::type>> operator/(int64_t left, const Subexpr2& right) +{ + return {make_subexpr>(left), right.clone()}; +} + +// Unary operators +template +UnaryOperator> power(const Subexpr2& left) +{ + return {left.clone()}; +} + + +// Classes used for LinkMap (see below). +struct LinkMapFunction { + // Your consume() method is given row index of the linked-to table as argument, and you must return whether or + // not you want the LinkMapFunction to exit (return false) or continue (return true) harvesting the link tree + // for the current main table row index (it will be a link tree if you have multiple type_LinkList columns + // in a link()->link() query. + virtual bool consume(size_t row_index) = 0; +}; + +struct FindNullLinks : public LinkMapFunction { + bool consume(size_t row_index) override + { + static_cast(row_index); + m_has_link = true; + return false; // we've found a row index, so this can't be a null-link, so exit link harvesting + } + + bool m_has_link = false; +}; + +struct MakeLinkVector : public LinkMapFunction { + MakeLinkVector(std::vector& result) + : m_links(result) + { + } + + bool consume(size_t row_index) override + { + m_links.push_back(row_index); + return true; // continue evaluation + } + std::vector& m_links; +}; + +struct CountLinks : public LinkMapFunction { + bool consume(size_t) override + { + m_link_count++; + return true; + } + + size_t result() const + { + return m_link_count; + } + + size_t m_link_count = 0; +}; + + +/* +The LinkMap and LinkMapFunction classes are used for query conditions on links themselves (contrary to conditions on +the value payload they point at). + +MapLink::map_links() takes a row index of the link column as argument and follows any link chain stated in the query +(through the link()->link() methods) until the final payload table is reached, and then applies LinkMapFunction on +the linked-to row index(es). + +If all link columns are type_Link, then LinkMapFunction is only invoked for a single row index. If one or more +columns are type_LinkList, then it may result in multiple row indexes. + +The reason we use this map pattern is that we can exit the link-tree-traversal as early as possible, e.g. when we've +found the first link that points to row '5'. Other solutions could be a std::vector harvest_all_links(), or an +iterator pattern. First solution can't exit, second solution requires internal state. +*/ +class LinkMap { +public: + LinkMap() = default; + LinkMap(const Table* table, std::vector columns) + : m_link_column_indexes(std::move(columns)) + { + set_base_table(table); + } + + LinkMap(LinkMap const& other, QueryNodeHandoverPatches* patches) + : LinkMap(other) + { + if (!patches || m_link_column_indexes.empty()) + return; + + m_link_column_indexes.clear(); + const Table* table = m_base_table; + for (auto column : m_link_columns) { + m_link_column_indexes.push_back(column->get_column_index()); + if (table->get_real_column_type(m_link_column_indexes.back()) == col_type_BackLink) + table = &static_cast(column)->get_origin_table(); + else + table = &static_cast(column)->get_target_table(); + } + } + + void set_base_table(const Table* table) + { + if (table == m_base_table) + return; + + m_base_table = table; + m_link_columns.clear(); + m_link_types.clear(); + m_only_unary_links = true; + + for (size_t link_column_index : m_link_column_indexes) { + // Link column can be either LinkList or single Link + ColumnType type = table->get_real_column_type(link_column_index); + REALM_ASSERT(Table::is_link_type(type) || type == col_type_BackLink); + m_link_types.push_back(type); + + if (type == col_type_LinkList) { + const LinkListColumn& cll = table->get_column_link_list(link_column_index); + m_link_columns.push_back(&cll); + m_only_unary_links = false; + table = &cll.get_target_table(); + } + else if (type == col_type_Link) { + const LinkColumn& cl = table->get_column_link(link_column_index); + m_link_columns.push_back(&cl); + table = &cl.get_target_table(); + } + else if (type == col_type_BackLink) { + const BacklinkColumn& bl = table->get_column_backlink(link_column_index); + m_link_columns.push_back(&bl); + m_only_unary_links = false; + table = &bl.get_origin_table(); + } + } + + m_target_table = table; + } + + std::vector get_links(size_t index) + { + std::vector res; + get_links(index, res); + return res; + } + + size_t count_links(size_t row) + { + CountLinks counter; + map_links(row, counter); + return counter.result(); + } + + void map_links(size_t row, LinkMapFunction& lm) + { + map_links(0, row, lm); + } + + bool only_unary_links() const + { + return m_only_unary_links; + } + + const Table* base_table() const + { + return m_base_table; + } + + const Table* target_table() const + { + return m_target_table; + } + + std::vector m_link_columns; + +private: + void map_links(size_t column, size_t row, LinkMapFunction& lm) + { + bool last = (column + 1 == m_link_columns.size()); + ColumnType type = m_link_types[column]; + if (type == col_type_Link) { + const LinkColumn& cl = *static_cast(m_link_columns[column]); + size_t r = to_size_t(cl.get(row)); + if (r == 0) + return; + r--; // LinkColumn stores link to row N as N + 1 + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + else if (type == col_type_LinkList) { + const LinkListColumn& cll = *static_cast(m_link_columns[column]); + ConstLinkViewRef lvr = cll.get(row); + for (size_t t = 0; t < lvr->size(); t++) { + size_t r = lvr->get(t).get_index(); + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + } + else if (type == col_type_BackLink) { + const BacklinkColumn& bl = *static_cast(m_link_columns[column]); + size_t count = bl.get_backlink_count(row); + for (size_t i = 0; i < count; ++i) { + size_t r = bl.get_backlink(row, i); + if (last) { + bool continue2 = lm.consume(r); + if (!continue2) + return; + } + else + map_links(column + 1, r, lm); + } + } + } + + + void get_links(size_t row, std::vector& result) + { + MakeLinkVector mlv = MakeLinkVector(result); + map_links(row, mlv); + } + + std::vector m_link_column_indexes; + std::vector m_link_types; + const Table* m_base_table = nullptr; + const Table* m_target_table = nullptr; + bool m_only_unary_links = true; + + template + friend Query compare(const Subexpr2&, const ConstRow&); +}; + +template +Query string_compare(const Columns& left, T right, bool case_insensitive); +template +Query string_compare(const Columns& left, const Columns& right, bool case_insensitive); + +template +Value make_value_for_link(bool only_unary_links, size_t size) +{ + Value value; + if (only_unary_links) { + REALM_ASSERT(size <= 1); + value.init(false, 1); + value.m_storage.set_null(0); + } + else { + value.init(true, size); + } + return value; +} + + +// If we add a new Realm type T and quickly want Query support for it, then simply inherit from it like +// `template <> class Columns : public SimpleQuerySupport` and you're done. Any operators of the set +// { ==, >=, <=, !=, >, < } that are supported by T will be supported by the "query expression syntax" +// automatically. NOTE: This method of Query support will be slow because it goes through Table::get. +// To get faster Query support, either add SequentialGetter support (faster) or create a query_engine.hpp +// node for it (super fast). + +template +class SimpleQuerySupport : public Subexpr2 { +public: + SimpleQuerySupport(size_t column, const Table* table, std::vector links = {}) + : m_column_ndx(column) + , m_link_map(table, std::move(links)) + { + m_column = &m_link_map.target_table()->get_column_base(m_column_ndx); + } + + bool is_nullable() const noexcept + { + return m_link_map.base_table()->is_nullable(m_column->get_column_index()); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + if (table != get_base_table()) { + m_link_map.set_base_table(table); + m_column = &m_link_map.target_table()->get_column_base(m_column_ndx); + } + } + + void evaluate(size_t index, ValueBase& destination) override + { + Value& d = static_cast&>(destination); + size_t col = column_ndx(); + + if (links_exist()) { + std::vector links = m_link_map.get_links(index); + Value v = make_value_for_link(m_link_map.only_unary_links(), links.size()); + + for (size_t t = 0; t < links.size(); t++) { + size_t link_to = links[t]; + v.m_storage.set(t, m_link_map.target_table()->template get(col, link_to)); + } + destination.import(v); + } + else { + // Not a link column + const Table* target_table = m_link_map.target_table(); + for (size_t t = 0; t < destination.m_values && index + t < target_table->size(); t++) { + d.m_storage.set(t, target_table->get(col, index + t)); + } + } + } + + bool links_exist() const + { + return m_link_map.m_link_columns.size() > 0; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches = nullptr) const override + { + return make_subexpr>(static_cast&>(*this), patches); + } + + SimpleQuerySupport(SimpleQuerySupport const& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_column_ndx(other.m_column_ndx) + , m_column(other.m_column) + , m_link_map(other.m_link_map, patches) + { + if (patches && m_column) { + m_column_ndx = column_ndx(); + m_column = nullptr; + } + } + + size_t column_ndx() const + { + return m_column->get_column_index(); + } + +private: + // Column index of payload column of m_table + mutable size_t m_column_ndx; + const ColumnBase* m_column; + LinkMap m_link_map; +}; + + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + +template <> +class Columns : public SimpleQuerySupport { + using SimpleQuerySupport::SimpleQuerySupport; +}; + + +template <> +class Columns : public SimpleQuerySupport { +public: + using SimpleQuerySupport::SimpleQuerySupport; + + Query equal(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query equal(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query not_equal(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query not_equal(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query begins_with(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query begins_with(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query ends_with(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query ends_with(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } + + Query contains(StringData sd, bool case_sensitive = true) + { + return string_compare(*this, sd, case_sensitive); + } + + Query contains(const Columns& col, bool case_sensitive = true) + { + return string_compare(*this, col, case_sensitive); + } +}; + + +template +Query string_compare(const Columns& left, T right, bool case_sensitive) +{ + StringData sd(right); + if (case_sensitive) + return create(sd, left); + else + return create(sd, left); +} + +template +Query string_compare(const Columns& left, const Columns& right, bool case_sensitive) +{ + if (case_sensitive) + return make_expression>(right.clone(), left.clone()); + else + return make_expression>(right.clone(), left.clone()); +} + +// Columns == Columns +inline Query operator==(const Columns& left, const Columns& right) +{ + return string_compare(left, right, true); +} + +// Columns != Columns +inline Query operator!=(const Columns& left, const Columns& right) +{ + return string_compare(left, right, true); +} + +// String == Columns +template +Query operator==(T left, const Columns& right) +{ + return operator==(right, left); +} + +// String != Columns +template +Query operator!=(T left, const Columns& right) +{ + return operator!=(right, left); +} + +// Columns == String +template +Query operator==(const Columns& left, T right) +{ + return string_compare(left, right, true); +} + +// Columns != String +template +Query operator!=(const Columns& left, T right) +{ + return string_compare(left, right, true); +} + + +inline Query operator==(const Columns& left, BinaryData right) +{ + return create(right, left); +} + +inline Query operator==(BinaryData left, const Columns& right) +{ + return create(left, right); +} + +inline Query operator!=(const Columns& left, BinaryData right) +{ + return create(right, left); +} + +inline Query operator!=(BinaryData left, const Columns& right) +{ + return create(left, right); +} + + +// This class is intended to perform queries on the *pointers* of links, contrary to performing queries on *payload* +// in linked-to tables. Queries can be "find first link that points at row X" or "find first null-link". Currently +// only "find first null link" and "find first non-null link" is supported. More will be added later. When we add +// more, I propose to remove the template argument from this class and instead template it by +// a criteria-class (like the FindNullLinks class below in find_first()) in some generalized fashion. +template +class UnaryLinkCompare : public Expression { +public: + UnaryLinkCompare(LinkMap lm) + : m_link_map(std::move(lm)) + { + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + // Return main table of query (table on which table->where()... is invoked). Note that this is not the same as + // any linked-to payload tables + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + size_t find_first(size_t start, size_t end) const override + { + for (; start < end;) { + std::vector l = m_link_map.get_links(start); + // We have found a Link which is NULL, or LinkList with 0 entries. Return it as match. + + FindNullLinks fnl; + m_link_map.map_links(start, fnl); + if (fnl.m_has_link == has_links) + return start; + + start++; + } + + return not_found; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new UnaryLinkCompare(*this, patches)); + } + +private: + UnaryLinkCompare(const UnaryLinkCompare& other, QueryNodeHandoverPatches* patches = nullptr) + : Expression(other) + , m_link_map(other.m_link_map, patches) + { + } + + mutable LinkMap m_link_map; +}; + +class LinkCount : public Subexpr2 { +public: + LinkCount(LinkMap link_map) + : m_link_map(std::move(link_map)) + { + } + LinkCount(LinkCount const& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void evaluate(size_t index, ValueBase& destination) override + { + size_t count = m_link_map.count_links(index); + destination.import(Value(false, 1, count)); + } + +private: + LinkMap m_link_map; +}; + +struct ConstantRowValueHandoverPatch : public QueryNodeHandoverPatch { + std::unique_ptr row_patch; +}; + +class ConstantRowValue : public Subexpr2 { +public: + ConstantRowValue(const ConstRow& row) + : m_row(row) + { + } + + void set_base_table(const Table*) override + { + } + const Table* get_base_table() const override + { + return nullptr; + } + + void evaluate(size_t, ValueBase& destination) override + { + if (m_row.is_attached()) { + Value v(RowIndex(m_row.get_index())); + destination.import(v); + } + else { + Value v(RowIndex::Detached); + destination.import(v); + } + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new ConstantRowValue(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_row.apply_and_consume_patch(patch->row_patch, group); + } + +private: + ConstantRowValue(const ConstantRowValue& source, QueryNodeHandoverPatches* patches) + : m_row(patches ? ConstRow() : source.m_row) + { + if (!patches) + return; + + std::unique_ptr patch(new ConstantRowValueHandoverPatch); + ConstRow::generate_patch(source.m_row, patch->row_patch); + patches->emplace_back(patch.release()); + } + + ConstRow m_row; +}; + +template +class SubColumns; + +// This is for LinkList and BackLink too since they're declared as typedefs of Link. +template <> +class Columns : public Subexpr2 { +public: + Query is_null() + { + if (m_link_map.m_link_columns.size() > 1) + throw std::runtime_error("Combining link() and is_null() is currently not supported"); + // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour + return make_expression>(m_link_map); + } + + Query is_not_null() + { + if (m_link_map.m_link_columns.size() > 1) + throw std::runtime_error("Combining link() and is_not_null() is currently not supported"); + // Todo, it may be useful to support the above, but we would need to figure out an intuitive behaviour + return make_expression>(m_link_map); + } + + LinkCount count() const + { + return LinkCount(m_link_map); + } + + template + SubColumns column(size_t column_ndx) const + { + return SubColumns(Columns(column_ndx, m_link_map.target_table()), m_link_map); + } + + const LinkMap& link_map() const + { + return m_link_map; + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr>(*this, patches); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + Value v = make_value_for_link(m_link_map.only_unary_links(), links.size()); + + for (size_t t = 0; t < links.size(); t++) { + v.m_storage.set(t, RowIndex(links[t])); + } + destination.import(v); + } + + Columns(const Columns& other, QueryNodeHandoverPatches* patches) + : Subexpr2(other) + , m_link_map(other.m_link_map, patches) + { + } + +private: + Columns(size_t column_ndx, const Table* table, const std::vector& links = {}) + : m_link_map(table, links) + { + static_cast(column_ndx); + } + + LinkMap m_link_map; + friend class Table; +}; + + +template +Query compare(const Subexpr2& left, const ConstRow& row) +{ + static_assert(std::is_same::value || std::is_same::value, + "Links can only be compared for equality."); + const Columns* column = dynamic_cast*>(&left); + if (column) { + const LinkMap& link_map = column->link_map(); + REALM_ASSERT(link_map.target_table() == row.get_table() || !row.is_attached()); +#ifdef REALM_OLDQUERY_FALLBACK + if (link_map.m_link_columns.size() == 1) { + // We can fall back to Query::links_to for != and == operations on links, but only + // for == on link lists. This is because negating query.links_to() is equivalent to + // to "ALL linklist != row" rather than the "ANY linklist != row" semantics we're after. + if (link_map.m_link_types[0] == col_type_Link || + (link_map.m_link_types[0] == col_type_LinkList && std::is_same::value)) { + const Table* t = column->get_base_table(); + Query query(*t); + + if (std::is_same::value) { + // Negate the following `links_to`. + query.Not(); + } + query.links_to(link_map.m_link_column_indexes[0], row); + return query; + } + } +#endif + } + return make_expression>(left.clone(), make_subexpr(row)); +} + +inline Query operator==(const Subexpr2& left, const ConstRow& row) +{ + return compare(left, row); +} +inline Query operator!=(const Subexpr2& left, const ConstRow& row) +{ + return compare(left, row); +} +inline Query operator==(const ConstRow& row, const Subexpr2& right) +{ + return compare(right, row); +} +inline Query operator!=(const ConstRow& row, const Subexpr2& right) +{ + return compare(right, row); +} + +template +Query compare(const Subexpr2& left, null) +{ + static_assert(std::is_same::value || std::is_same::value, + "Links can only be compared for equality."); + return make_expression>(left.clone(), make_subexpr>()); +} + +inline Query operator==(const Subexpr2& left, null) +{ + return compare(left, null()); +} +inline Query operator!=(const Subexpr2& left, null) +{ + return compare(left, null()); +} +inline Query operator==(null, const Subexpr2& right) +{ + return compare(right, null()); +} +inline Query operator!=(null, const Subexpr2& right) +{ + return compare(right, null()); +} + + +template +class Columns : public Subexpr2 { +public: + using ColType = typename ColumnTypeTraits::column_type; + + Columns(size_t column, const Table* table, std::vector links = {}) + : m_link_map(table, std::move(links)) + , m_column(column) + , m_nullable(m_link_map.target_table()->is_nullable(m_column)) + { + } + + Columns(const Columns& other, QueryNodeHandoverPatches* patches = nullptr) + : m_link_map(other.m_link_map, patches) + , m_column(other.m_column) + , m_nullable(other.m_nullable) + { + if (!other.m_sg) + return; + + if (patches) { + m_column = other.get_column_base().get_column_index(); + } + else { + if (m_nullable && std::is_same::value) { + init(&other.get_column_base()); + } + else { + init(&other.get_column_base()); + } + } + } + + Columns& operator=(const Columns& other) + { + if (this != &other) { + m_link_map = other.m_link_map; + m_sg.reset(); + m_column = other.m_column; + m_nullable = other.m_nullable; + } + return *this; + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr>(*this, patches); + } + + // See comment in base class + void set_base_table(const Table* table) override + { + if (m_sg && table == get_base_table()) + return; + + m_link_map.set_base_table(table); + m_nullable = m_link_map.target_table()->is_nullable(m_column); + + const ColumnBase* c = &m_link_map.target_table()->get_column_base(m_column); + if (m_nullable && std::is_same::value) { + init(c); + } + else { + init(c); + } + } + + template + void init(const ColumnBase* c) + { + REALM_ASSERT_DEBUG(dynamic_cast(c)); + if (m_sg == nullptr) { + m_sg.reset(new SequentialGetter()); + } + static_cast&>(*m_sg).init(static_cast(c)); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + template + void evaluate_internal(size_t index, ValueBase& destination) + { + using U = typename ColType2::value_type; + auto sgc = static_cast*>(m_sg.get()); + REALM_ASSERT_DEBUG(dynamic_cast*>(m_sg.get())); + REALM_ASSERT_DEBUG(sgc->m_column); + + if (links_exist()) { + // LinkList with more than 0 values. Create Value with payload for all fields + + std::vector links = m_link_map.get_links(index); + auto v = make_value_for_link::type>(m_link_map.only_unary_links(), + links.size()); + + for (size_t t = 0; t < links.size(); t++) { + size_t link_to = links[t]; + sgc->cache_next(link_to); + + if (sgc->m_column->is_null(link_to)) + v.m_storage.set_null(t); + else + v.m_storage.set(t, sgc->get_next(link_to)); + } + destination.import(v); + } + else { + // Not a Link column + // make sequential getter load the respective leaf to access data at column row 'index' + sgc->cache_next(index); + size_t colsize = sgc->m_column->size(); + + // Now load `ValueBase::default_size` rows from from the leaf into m_storage. If it's an integer + // leaf, then it contains the method get_chunk() which copies these values in a super fast way (first + // case of the `if` below. Otherwise, copy the values one by one in a for-loop (the `else` case). + if (std::is_same::value && index + ValueBase::default_size <= sgc->m_leaf_end) { + Value v; + + // If you want to modify 'default_size' then update Array::get_chunk() + REALM_ASSERT_3(ValueBase::default_size, ==, 8); + + auto sgc_2 = static_cast*>(m_sg.get()); + sgc_2->m_leaf_ptr->get_chunk( + index - sgc->m_leaf_start, + static_cast*>(static_cast(&v))->m_storage.m_first); + + destination.import(v); + } + else { + size_t rows = colsize - index; + if (rows > ValueBase::default_size) + rows = ValueBase::default_size; + Value::type> v(false, rows); + + for (size_t t = 0; t < rows; t++) + v.m_storage.set(t, sgc->get_next(index + t)); + + destination.import(v); + } + } + } + + // Load values from Column into destination + void evaluate(size_t index, ValueBase& destination) override + { + if (m_nullable && std::is_same::value) { + evaluate_internal(index, destination); + } + else { + evaluate_internal(index, destination); + } + } + + bool links_exist() const + { + return m_link_map.m_link_columns.size() > 0; + } + + bool is_nullable() const + { + return m_nullable; + } + + size_t column_ndx() const noexcept + { + return m_sg ? get_column_base().get_column_index() : m_column; + } + +private: + LinkMap m_link_map; + + // Fast (leaf caching) value getter for payload column (column in table on which query condition is executed) + std::unique_ptr m_sg; + + // Column index of payload column of m_table + size_t m_column; + + // set to false by default for stand-alone Columns declaration that are not yet associated with any table + // or oclumn. Call init() to update it or use a constructor that takes table + column index as argument. + bool m_nullable = false; + + const ColumnBase& get_column_base() const noexcept + { + if (m_nullable && std::is_same::value) + return *static_cast&>(*m_sg).m_column; + else + return *static_cast&>(*m_sg).m_column; + } +}; + +template +class SubColumnAggregate; +namespace aggregate_operations { +template +class Minimum; +template +class Maximum; +template +class Sum; +template +class Average; +} + +template +class SubColumns : public Subexpr { +public: + SubColumns(Columns column, LinkMap link_map) + : m_column(std::move(column)) + , m_link_map(std::move(link_map)) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches*) const override + { + return make_subexpr>(*this); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.target_table()); + } + + void evaluate(size_t, ValueBase&) override + { + // SubColumns can only be used in an expression in conjunction with its aggregate methods. + REALM_ASSERT(false); + } + + SubColumnAggregate> min() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> max() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> sum() const + { + return {m_column, m_link_map}; + } + + SubColumnAggregate> average() const + { + return {m_column, m_link_map}; + } + +private: + Columns m_column; + LinkMap m_link_map; +}; + +template +class SubColumnAggregate : public Subexpr2 { +public: + SubColumnAggregate(Columns column, LinkMap link_map) + : m_column(std::move(column)) + , m_link_map(std::move(link_map)) + { + } + SubColumnAggregate(SubColumnAggregate const& other, QueryNodeHandoverPatches* patches) + : m_column(other.m_column, patches) + , m_link_map(other.m_link_map, patches) + { + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + m_column.set_base_table(m_link_map.target_table()); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + std::sort(links.begin(), links.end()); + + Operation op; + for (size_t link_index = 0; link_index < links.size();) { + Value value; + size_t link = links[link_index]; + m_column.evaluate(link, value); + + // Columns::evaluate fetches values in chunks of ValueBase::default_size. Process all values + // within the chunk that came from rows that we link to. + const auto& value_storage = value.m_storage; + for (size_t value_index = 0; value_index < value.m_values;) { + if (!value_storage.is_null(value_index)) { + op.accumulate(value_storage[value_index]); + } + if (++link_index >= links.size()) { + break; + } + + size_t previous_link = link; + link = links[link_index]; + value_index += link - previous_link; + } + } + if (op.is_null()) { + destination.import(Value(false, 1, null())); + } + else { + destination.import(Value(false, 1, op.result())); + } + } + +private: + Columns m_column; + LinkMap m_link_map; +}; + +struct SubQueryCountHandoverPatch : QueryNodeHandoverPatch { + QueryHandoverPatch m_query; +}; + +class SubQueryCount : public Subexpr2 { +public: + SubQueryCount(Query q, LinkMap link_map) + : m_query(std::move(q)) + , m_link_map(std::move(link_map)) + { + } + + const Table* get_base_table() const override + { + return m_link_map.base_table(); + } + + void set_base_table(const Table* table) override + { + m_link_map.set_base_table(table); + } + + void evaluate(size_t index, ValueBase& destination) override + { + std::vector links = m_link_map.get_links(index); + std::sort(links.begin(), links.end()); + + size_t count = std::accumulate(links.begin(), links.end(), 0, [this](size_t running_count, size_t link) { + return running_count + m_query.count(link, link + 1, 1); + }); + + destination.import(Value(false, 1, count)); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + if (patches) + return std::unique_ptr(new SubQueryCount(*this, patches)); + + return make_subexpr(*this); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + REALM_ASSERT(patches.size()); + std::unique_ptr abstract_patch = std::move(patches.back()); + patches.pop_back(); + + auto patch = dynamic_cast(abstract_patch.get()); + REALM_ASSERT(patch); + + m_query.apply_patch(patch->m_query, group); + } + +private: + SubQueryCount(const SubQueryCount& other, QueryNodeHandoverPatches* patches) + : m_link_map(other.m_link_map, patches) + { + std::unique_ptr patch(new SubQueryCountHandoverPatch); + m_query = Query(other.m_query, patch->m_query, ConstSourcePayload::Copy); + patches->emplace_back(patch.release()); + } + + Query m_query; + LinkMap m_link_map; +}; + +// The unused template parameter is a hack to avoid a circular dependency between table.hpp and query_expression.hpp. +template +class SubQuery { +public: + SubQuery(Columns link_column, Query query) + : m_query(std::move(query)) + , m_link_map(link_column.link_map()) + { + REALM_ASSERT(m_link_map.target_table() == m_query.get_table()); + } + + SubQueryCount count() const + { + return SubQueryCount(m_query, m_link_map); + } + +private: + Query m_query; + LinkMap m_link_map; +}; + +namespace aggregate_operations { +template +class BaseAggregateOperation { + static_assert(std::is_same::value || std::is_same::value || std::is_same::value, + "Numeric aggregates can only be used with subcolumns of numeric types"); + +public: + using ResultType = R; + + void accumulate(T value) + { + m_count++; + m_result = Derived::apply(m_result, value); + } + + bool is_null() const + { + return m_count == 0; + } + ResultType result() const + { + return m_result; + } + +protected: + size_t m_count = 0; + ResultType m_result = Derived::initial_value(); +}; + +template +class Minimum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return std::numeric_limits::max(); + } + static T apply(T a, T b) + { + return std::min(a, b); + } +}; + +template +class Maximum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return std::numeric_limits::min(); + } + static T apply(T a, T b) + { + return std::max(a, b); + } +}; + +template +class Sum : public BaseAggregateOperation> { +public: + static T initial_value() + { + return T(); + } + static T apply(T a, T b) + { + return a + b; + } + bool is_null() const + { + return false; + } +}; + +template +class Average : public BaseAggregateOperation, double> { + using Base = BaseAggregateOperation, double>; + +public: + static double initial_value() + { + return 0; + } + static double apply(double a, T b) + { + return a + b; + } + double result() const + { + return Base::m_result / Base::m_count; + } +}; +} + +template +class UnaryOperator : public Subexpr2 { +public: + UnaryOperator(std::unique_ptr left) + : m_left(std::move(left)) + { + } + + UnaryOperator(const UnaryOperator& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + { + } + + UnaryOperator& operator=(const UnaryOperator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + } + return *this; + } + + UnaryOperator(UnaryOperator&&) = default; + UnaryOperator& operator=(UnaryOperator&&) = default; + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + const Table* get_base_table() const override + { + return m_left->get_base_table(); + } + + // destination = operator(left) + void evaluate(size_t index, ValueBase& destination) override + { + Value result; + Value left; + m_left->evaluate(index, left); + result.template fun(&left); + destination.import(result); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_left->apply_handover_patch(patches, group); + } + +private: + typedef typename oper::type T; + std::unique_ptr m_left; +}; + + +template +class Operator : public Subexpr2 { +public: + Operator(std::unique_ptr left, std::unique_ptr right) + : m_left(std::move(left)) + , m_right(std::move(right)) + { + } + + Operator(const Operator& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + , m_right(other.m_right->clone(patches)) + { + } + + Operator& operator=(const Operator& other) + { + if (this != &other) { + m_left = other.m_left->clone(); + m_right = other.m_right->clone(); + } + return *this; + } + + Operator(Operator&&) = default; + Operator& operator=(Operator&&) = default; + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + const Table* get_base_table() const override + { + const Table* l = m_left->get_base_table(); + const Table* r = m_right->get_base_table(); + + // Queries do not support multiple different tables; all tables must be the same. + REALM_ASSERT(l == nullptr || r == nullptr || l == r); + + // nullptr pointer means expression which isn't yet associated with any table, or is a Value + return l ? l : r; + } + + // destination = operator(left, right) + void evaluate(size_t index, ValueBase& destination) override + { + Value result; + Value left; + Value right; + m_left->evaluate(index, left); + m_right->evaluate(index, right); + result.template fun(&left, &right); + destination.import(result); + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return make_subexpr(*this, patches); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_right->apply_handover_patch(patches, group); + m_left->apply_handover_patch(patches, group); + } + +private: + typedef typename oper::type T; + std::unique_ptr m_left; + std::unique_ptr m_right; +}; + + +template +class Compare : public Expression { +public: + Compare(std::unique_ptr left, std::unique_ptr right) + : m_left(std::move(left)) + , m_right(std::move(right)) + { + } + + // See comment in base class + void set_base_table(const Table* table) override + { + m_left->set_base_table(table); + m_right->set_base_table(table); + } + + // Recursively fetch tables of columns in expression tree. Used when user first builds a stand-alone expression + // and + // binds it to a Query at a later time + const Table* get_base_table() const override + { + const Table* l = m_left->get_base_table(); + const Table* r = m_right->get_base_table(); + + // All main tables in each subexpression of a query (table.columns() or table.link()) must be the same. + REALM_ASSERT(l == nullptr || r == nullptr || l == r); + + // nullptr pointer means expression which isn't yet associated with any table, or is a Value + return l ? l : r; + } + + size_t find_first(size_t start, size_t end) const override + { + size_t match; + Value right; + Value left; + + for (; start < end;) { + m_left->evaluate(start, left); + m_right->evaluate(start, right); + match = Value::template compare(&left, &right); + + if (match != not_found && match + start < end) + return start + match; + + size_t rows = + (left.m_from_link_list || right.m_from_link_list) ? 1 : minimum(right.m_values, left.m_values); + start += rows; + } + + return not_found; // no match + } + + std::unique_ptr clone(QueryNodeHandoverPatches* patches) const override + { + return std::unique_ptr(new Compare(*this, patches)); + } + + void apply_handover_patch(QueryNodeHandoverPatches& patches, Group& group) override + { + m_right->apply_handover_patch(patches, group); + m_left->apply_handover_patch(patches, group); + } + +private: + Compare(const Compare& other, QueryNodeHandoverPatches* patches) + : m_left(other.m_left->clone(patches)) + , m_right(other.m_right->clone(patches)) + { + } + + std::unique_ptr m_left; + std::unique_ptr m_right; +}; +} +#endif // REALM_QUERY_EXPRESSION_HPP diff --git a/Pods/Realm/include/core/realm/realm_nmmintrin.h b/Pods/Realm/include/core/realm/realm_nmmintrin.h new file mode 100644 index 0000000..8144da6 --- /dev/null +++ b/Pods/Realm/include/core/realm/realm_nmmintrin.h @@ -0,0 +1,182 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_NMMINTRIN_H +#define REALM_NMMINTRIN_H + +/* + We must support runtime detection of CPU support of SSE when distributing Realm as a closed source library. + + This is a problem on gcc and llvm: To use SSE intrinsics we need to pass -msse on the command line (to get offered + __builtin_ accessors used by intrinsics functions). However, the -msse flag allows gcc to emit SSE instructions + in its code generation/optimization. This is unwanted because the binary would crash on non-SSE CPUs. + + Since there exists no flag in gcc that enables intrinsics but probits SSE in code generation, we define our + own intrinsics to be assembled by the back end assembler and omit passing -msse to gcc. +*/ + +#ifndef _MSC_VER + +#ifdef REALM_COMPILER_SSE +#include // SSE2 (using __m128i) +#endif + +namespace realm { + +#if 0 +#ifdef REALM_COMPILER_AVX +typedef float __m256 __attribute__((__vector_size__(32), __may_alias__)); +typedef double __m256d __attribute__((__vector_size__(32), __may_alias__)); + +const int _CMP_EQ_OQ = 0x00; // Equal (ordered, non-signaling) +const int _CMP_NEQ_OQ = 0x0c; // Not-equal (ordered, non-signaling) +const int _CMP_LT_OQ = 0x11; // Less-than (ordered, non-signaling) +const int _CMP_LE_OQ = 0x12; // Less-than-or-equal (ordered, non-signaling) +const int _CMP_GE_OQ = 0x1d; // Greater-than-or-equal (ordered, non-signaling) +const int _CMP_GT_OQ = 0x1e; // Greater-than (ordered, non-signaling) + + +template +static int movemask_cmp_ps(__m256* y1, __m256* y2) +{ + int ret; + __asm__("vmovaps %0, %%ymm0" : : "m"(*y1) : "%xmm0" ); + __asm__("vmovaps %0, %%ymm1" : : "m"(*y2) : "%xmm1" ); + __asm__("vcmpps %0, %%ymm0, %%ymm1, %%ymm0" : : "I"(op) : "%xmm0" ); + __asm__("vmovmskps %%ymm0, %0" : "=r"(ret) : : ); + return ret; +} + +template +static inline int movemask_cmp_pd(__m256d* y1, __m256d* y2) +{ + int ret; + __asm__("vmovapd %0, %%ymm0" : : "m"(*y1) : "%xmm0" ); + __asm__("vmovapd %0, %%ymm1" : : "m"(*y2) : "%xmm1" ); + __asm__("vcmppd %0, %%ymm0, %%ymm1, %%ymm0" : : "I"(op) : "%xmm0" ); + __asm__("vmovmskpd %%ymm0, %0" : "=r"(ret) : : ); + return ret; +} + + + +static inline int movemask_cmp_ps(__m256* y1, __m256* y2, int op) +{ + // todo, use constexpr; + if (op == _CMP_EQ_OQ) + return movemask_cmp_ps<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_NEQ_OQ) + return movemask_cmp_ps<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_LT_OQ) + return movemask_cmp_ps<_CMP_LT_OQ>(y1, y2); + else if (op == _CMP_LE_OQ) + return movemask_cmp_ps<_CMP_LE_OQ>(y1, y2); + else if (op == _CMP_GE_OQ) + return movemask_cmp_ps<_CMP_GE_OQ>(y1, y2); + else if (op == _CMP_GT_OQ) + return movemask_cmp_ps<_CMP_GT_OQ>(y1, y2); + + REALM_ASSERT(false); + return 0; +} + +static inline int movemask_cmp_pd(__m256d* y1, __m256d* y2, int op) +{ + // todo, use constexpr; + if (op == _CMP_EQ_OQ) + return movemask_cmp_pd<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_NEQ_OQ) + return movemask_cmp_pd<_CMP_NEQ_OQ>(y1, y2); + else if (op == _CMP_LT_OQ) + return movemask_cmp_pd<_CMP_LT_OQ>(y1, y2); + else if (op == _CMP_LE_OQ) + return movemask_cmp_pd<_CMP_LE_OQ>(y1, y2); + else if (op == _CMP_GE_OQ) + return movemask_cmp_pd<_CMP_GE_OQ>(y1, y2); + else if (op == _CMP_GT_OQ) + return movemask_cmp_pd<_CMP_GT_OQ>(y1, y2); + + REALM_ASSERT(false); + return 0; +} + + +#endif +#endif + +// Instructions introduced by SSE 3 and 4.2 +static inline __m128i _mm_cmpgt_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpgtq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i _mm_cmpeq_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpeqq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi8_epi16(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxbw %1, %0" : "=x" (xmm1) : "xm" (xmm2) : "xmm1"); + return xmm1; +} +static inline __m128i __attribute__((always_inline)) _mm_cvtepi16_epi32(__m128i xmm2) +{ + __m128i xmm1; + asm("pmovsxwd %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi32_epi64(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxdq %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +} // namespace realm + +#endif +#endif diff --git a/Pods/Realm/include/core/realm/replication.hpp b/Pods/Realm/include/core/realm/replication.hpp new file mode 100644 index 0000000..c31e5ca --- /dev/null +++ b/Pods/Realm/include/core/realm/replication.hpp @@ -0,0 +1,529 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_REPLICATION_HPP +#define REALM_REPLICATION_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace realm { +namespace util { +class Logger; +} + +// FIXME: Be careful about the possibility of one modification function being called by another where both do +// transaction logging. + +// FIXME: The current table/subtable selection scheme assumes that a TableRef of a subtable is not accessed after any +// modification of one of its ancestor tables. + +// FIXME: Checking on same Table* requires that ~Table checks and nullifies on match. Another option would be to store +// m_selected_table as a TableRef. Yet another option would be to assign unique identifiers to each Table instance via +// Allocator. Yet another option would be to explicitely invalidate subtables recursively when parent is modified. + +/// Replication is enabled by passing an instance of an implementation of this +/// class to the SharedGroup constructor. +class Replication : public _impl::TransactLogConvenientEncoder, protected _impl::TransactLogStream { +public: + // Be sure to keep this type aligned with what is actually used in + // SharedGroup. + using version_type = _impl::History::version_type; + using InputStream = _impl::NoCopyInputStream; + class TransactLogApplier; + class Interrupted; // Exception + class SimpleIndexTranslator; + + virtual std::string get_database_path() = 0; + + /// Called during construction of the associated SharedGroup object. + /// + /// \param shared_group The assocoated SharedGroup object. + virtual void initialize(SharedGroup& shared_group) = 0; + + /// Called by the associated SharedGroup object when a session is + /// initiated. A *session* is a sequence of of temporally overlapping + /// accesses to a specific Realm file, where each access consists of a + /// SharedGroup object through which the Realm file is open. Session + /// initiation occurs during the first opening of the Realm file within such + /// a session. + /// + /// Session initiation fails if this function throws. + /// + /// \param version The current version of the associated Realm. Out-of-Realm + /// history implementation can use this to trim off history entries that + /// were successfully added to the history, but for which the corresponding + /// subsequent commits on the Realm file failed. + /// + /// The default implementation does nothing. + virtual void initiate_session(version_type version) = 0; + + /// Called by the associated SharedGroup object when a session is + /// terminated. See initiate_session() for the definition of a + /// session. Session termination occurs upon closing the Realm through the + /// last SharedGroup object within the session. + /// + /// The default implementation does nothing. + virtual void terminate_session() noexcept = 0; + + /// \defgroup replication_transactions + //@{ + + /// From the point of view of the Replication class, a transaction is + /// initiated when, and only when the associated SharedGroup object calls + /// initiate_transact() and the call is successful. The associated + /// SharedGroup object must terminate every initiated transaction either by + /// calling finalize_commit() or by calling abort_transact(). It may only + /// call finalize_commit(), however, after calling prepare_commit(), and + /// only when prepare_commit() succeeds. If prepare_commit() fails (i.e., + /// throws) abort_transact() must still be called. + /// + /// The associated SharedGroup object is supposed to terminate a transaction + /// as soon as possible, and is required to terminate it before attempting + /// to initiate a new one. + /// + /// initiate_transact() is called by the associated SharedGroup object as + /// part of the initiation of a transaction, and at a time where the caller + /// has acquired exclusive write access to the local Realm. The Replication + /// implementation is allowed to perform "precursor transactions" on the + /// local Realm at this time. During the initiated transaction, the + /// associated SharedGroup object must inform the Replication object of all + /// modifying operations by calling set_value() and friends. + /// + /// FIXME: There is currently no way for implementations to perform + /// precursor transactions, since a regular transaction would cause a dead + /// lock when it tries to acquire a write lock. Consider giving access to + /// special non-locking precursor transactions via an extra argument to this + /// function. + /// + /// prepare_commit() serves as the first phase of a two-phase commit. This + /// function is called by the associated SharedGroup object immediately + /// before the commit operation on the local Realm. The associated + /// SharedGroup object will then, as the second phase, either call + /// finalize_commit() or abort_transact() depending on whether the commit + /// operation succeeded or not. The Replication implementation is allowed to + /// modify the Realm via the associated SharedGroup object at this time + /// (important to in-Realm histories). + /// + /// initiate_transact() and prepare_commit() are allowed to block the + /// calling thread if, for example, they need to communicate over the + /// network. If a calling thread is blocked in one of these functions, it + /// must be possible to interrupt the blocking operation by having another + /// thread call interrupt(). The contract is as follows: When interrupt() is + /// called, then any execution of initiate_transact() or prepare_commit(), + /// initiated before the interruption, must complete without blocking, or + /// the execution must be aborted by throwing an Interrupted exception. If + /// initiate_transact() or prepare_commit() throws Interrupted, it counts as + /// a failed operation. + /// + /// finalize_commit() is called by the associated SharedGroup object + /// immediately after a successful commit operation on the local Realm. This + /// happens at a time where modification of the Realm is no longer possible + /// via the associated SharedGroup object. In the case of in-Realm + /// histories, the changes are automatically finalized as part of the commit + /// operation performed by the caller prior to the invocation of + /// finalize_commit(), so in that case, finalize_commit() might not need to + /// do anything. + /// + /// abort_transact() is called by the associated SharedGroup object to + /// terminate a transaction without committing. That is, any transaction + /// that is not terminated by finalize_commit() is terminated by + /// abort_transact(). This could be due to an explicit rollback, or due to a + /// failed commit attempt. + /// + /// Note that finalize_commit() and abort_transact() are not allowed to + /// throw. + /// + /// \param current_version The version of the snapshot that the current + /// transaction is based on. + /// + /// \param history_updated Pass true only when the history has already been + /// updated to reflect the currently bound snapshot, such as when + /// _impl::History::update_early_from_top_ref() was called during the + /// transition from a read transaction to the current write transaction. + /// + /// \return prepare_commit() returns the version of the new snapshot + /// produced by the transaction. + /// + /// \throw Interrupted Thrown by initiate_transact() and prepare_commit() if + /// a blocking operation was interrupted. + + void initiate_transact(version_type current_version, bool history_updated); + version_type prepare_commit(version_type current_version); + void finalize_commit() noexcept; + void abort_transact() noexcept; + + //@} + + + /// Interrupt any blocking call to a function in this class. This function + /// may be called asyncronously from any thread, but it may not be called + /// from a system signal handler. + /// + /// Some of the public function members of this class may block, but only + /// when it it is explicitely stated in the documention for those functions. + /// + /// FIXME: Currently we do not state blocking behaviour for all the + /// functions that can block. + /// + /// After any function has returned with an interruption indication, the + /// only functions that may safely be called are abort_transact() and the + /// destructor. If a client, after having received an interruption + /// indication, calls abort_transact() and then clear_interrupt(), it may + /// resume normal operation through this Replication object. + void interrupt() noexcept; + + /// May be called by a client to reset this Replication object after an + /// interrupted transaction. It is not an error to call this function in a + /// situation where no interruption has occured. + void clear_interrupt() noexcept; + + /// Apply a changeset to the specified group. + /// + /// \param changeset The changes to be applied. + /// + /// \param group The destination group to apply the changeset to. + /// + /// \param logger If specified, and the library was compiled in debug mode, + /// then a line describing each individual operation is writted to the + /// specified logger. + /// + /// \throw BadTransactLog If the changeset could not be successfully parsed, + /// or ended prematurely. + static void apply_changeset(InputStream& changeset, Group& group, util::Logger* logger = nullptr); + + enum HistoryType { + /// No history available. No support for either continuous transactions + /// or inter-client synchronization. + hist_None = 0, + + /// Out-of-Realm history supporting continuous transactions. + hist_OutOfRealm = 1, + + /// In-Realm history supporting continuous transactions + /// (_impl::InRealmHistory). + hist_InRealm = 2, + + /// In-Realm history supporting continuous transactions and inter-client + /// synchronization (_impl::SyncHistory). + hist_Sync = 3 + }; + + /// Returns the type of history maintained by this Replication + /// implementation, or \ref hist_None if no history is maintained by it. + /// + /// This type is used to ensure that all session participants agree on + /// history type, and that the Realm file contains a compatible type of + /// history, at the beginning of a new session. + /// + /// As a special case, if there is no top array (Group::m_top) at the + /// beginning of a new session, then all history types (as returned by + /// get_history_type()) are allowed during that session. Note that this is + /// only possible if there was no preceding session, or if no transaction + /// was sucessfully comitted during any of the preceding sessions. As soon + /// as a transaction is successfully committed, the Realm contains at least + /// a top array, and from that point on, the history type is generally + /// fixed, although still subject to certain allowed changes (as mentioned + /// below). + /// + /// For the sake of backwards compatibility with older Realm files that does + /// not store any history type, the following rule shall apply: + /// + /// - If the top array of a Realm file (Group::m_top) does not contain a + /// history type, because it is too short, it shall be understood as + /// implicitely storing the type \ref hist_None. + /// + /// Note: In what follows, the meaning of *preceding session* is: The last + /// preceding session that modified the Realm by sucessfully committing a + /// new snapshot. + /// + /// Older Realm files do not store any history type, even when they were + /// last used with a history of type \ref hist_OutOfRealm. Howewver, since + /// such histories (\ref hist_OutOfRealm) are placed outside the Realm file, + /// and are transient (recreated at the beginning of each new session), a + /// new session is not obliged to use the same type of history (\ref + /// hist_OutOfRealm). For this reason, and to achieve further backwards + /// compatibility, the following rules are adopted: + /// + /// - At the beginning of a new session, if there is no stored history + /// type (no top array), or if the stored history type is \ref + /// hist_None, assume that the history type used during the preceding + /// session was \ref hist_None or \ref hist_OutOfRealm, or that there + /// was no preceding session. In all other cases, assume that the stored + /// history type is the type used during the preceding session. + /// + /// - When storing the history type, store \ref hist_None if the history + /// type used in the current session is \ref hist_None or \ref + /// hist_OutOfRealm. In all other cases, store the actual history type + /// used. + /// + /// It shall be allowed to switch to a \ref hist_InRealm history if the + /// stored history type is either \ref hist_None or \ref + /// hist_OutOfRealm. Fortunately, this can be done simply by adding a + /// history to the Realm file (of type \ref hist_InRealm), and that is + /// possible because a \ref hist_InRealm history is independent of any + /// history used in a previous session (as long as it was session-confined), + /// or whether any history was used at all. Conversely, if a \ref + /// hist_OutOfRealm history was used in the previous session, then the + /// contents of that history becomes obsolete at the end of the previous + /// session. + /// + /// On the other hand, as soon as a history of type \ref hist_InRealm is + /// added to a Realm file, that history type is binding for all subsequent + /// sessions. In theory, this constraint is not necessary, and a later + /// switch to \ref hist_None or \ref hist_OutOfRealm would be possible + /// because of the fact that the contents of the history becomes obsolete at + /// the end of the session, however, because the \ref hist_InRealm history + /// remains in the Realm file, there are practical complications, and for + /// that reason, such switching shall not be supported. + /// + /// The \ref hist_Sync history type can only be used if the stored history + /// type is also \ref hist_Sync, or when there is no top array + /// yet. Additionally, when the stored history type is \ref hist_Sync, then + /// all subsequent sesssions must have the same type. These restrictions + /// apply because such a history needs to be maintained persistently across + /// sessions. That is, the contents of such a history is not obsolete at the + /// end of the session, and is in general needed during subsequent sessions. + /// + /// In general, if there is no stored history type (no top array) at the + /// beginning of a new session, or if the stored type disagrees with what is + /// returned by get_history_type() (which is possible due to particular + /// allowed changes of history type), the actual history type (as returned + /// by get_history_type()) used during that session, must be stored in the + /// Realm during the first successfully committed transaction of that + /// session, if any are sucessfully committed. But note that there is still + /// no need to expand the top array to store the history type \ref + /// hist_None, due to the rule mentioned above. + /// + /// Due to the rules listed above, a new history type only actually needs to + /// be stored when the history type of the session (get_history_type()) is + /// neither \ref hist_None nor \ref hist_OutOfRealm, and only when that + /// differs from the stored history type, or if there is no top array at the + /// beginning of the session. + /// + /// Summary of session-to-session history type change constraints: + /// + /// If there is no top array at the beginning of a new session, then all + /// history types (as returned by get_history_type()) are possible during + /// that session. Otherwise there must have been a preceding session (at + /// least one that adds the top array), and the following rules then apply: + /// + ///
+    ///
+    ///                      Type stored in
+    ///   Type used during   Realm file at
+    ///   preceding          beginning of     Possible history types (as returned by
+    ///   session            new session      get_history_type()) during new session
+    ///   ----------------------------------------------------------------------------
+    ///   hist_None          hist_None        hist_None, hist_OutOfRealm, hist_InRealm
+    ///   hist_OutOfRealm    hist_None        hist_None, hist_OutOfRealm, hist_InRealm
+    ///   hist_InRealm       hist_InRealm     hist_InRealm
+    ///   hist_Sync          hist_Sync        hist_Sync
+    ///
+    /// 
+ /// + /// This function must return \ref hist_None when, and only when + /// get_history() returns null. + virtual HistoryType get_history_type() const noexcept = 0; + + /// Returns an object that gives access to the history of changesets in a + /// way that allows for continuous transactions to work + /// (Group::advance_transact() in particular). + /// + /// This function must return null when, and only when get_history_type() + /// returns \ref hist_None. + virtual _impl::History* get_history() = 0; + + virtual ~Replication() noexcept + { + } + +protected: + Replication(); + + + //@{ + + /// do_initiate_transact() is called by initiate_transact(), and likewise + /// for do_prepare_commit), do_finalize_commit(), and do_abort_transact(). + /// + /// With respect to exception safety, the Replication implementation has two + /// options: It can prepare to accept the accumulated changeset in + /// do_prepapre_commit() by allocating all required resources, and delay the + /// actual acceptance to do_finalize_commit(), which requires that the final + /// acceptance can be done without any risk of failure. Alternatively, the + /// Replication implementation can fully accept the changeset in + /// do_prepapre_commit() (allowing for failure), and then discard that + /// changeset during the next invocation of do_initiate_transact() if + /// `current_version` indicates that the previous transaction failed. + + virtual void do_initiate_transact(version_type current_version, bool history_updated) = 0; + virtual version_type do_prepare_commit(version_type orig_version) = 0; + virtual void do_finalize_commit() noexcept = 0; + virtual void do_abort_transact() noexcept = 0; + + //@} + + + virtual void do_interrupt() noexcept = 0; + + virtual void do_clear_interrupt() noexcept = 0; + + friend class _impl::TransactReverser; +}; + + +class Replication::Interrupted : public std::exception { +public: + const char* what() const noexcept override + { + return "Interrupted"; + } +}; + + +class TrivialReplication : public Replication { +public: + ~TrivialReplication() noexcept + { + } + +protected: + typedef Replication::version_type version_type; + + TrivialReplication(const std::string& database_file); + + virtual version_type prepare_changeset(const char* data, size_t size, version_type orig_version) = 0; + virtual void finalize_changeset() noexcept = 0; + + static void apply_changeset(const char* data, size_t size, SharedGroup& target, util::Logger* logger = nullptr); + + bool is_history_updated() const noexcept; + + BinaryData get_uncommitted_changes() const noexcept; + + std::string get_database_path() override; + void initialize(SharedGroup&) override; + void do_initiate_transact(version_type, bool) override; + version_type do_prepare_commit(version_type orig_version) override; + void do_finalize_commit() noexcept override; + void do_abort_transact() noexcept override; + void do_interrupt() noexcept override; + void do_clear_interrupt() noexcept override; + void transact_log_reserve(size_t n, char** new_begin, char** new_end) override; + void transact_log_append(const char* data, size_t size, char** new_begin, char** new_end) override; + +private: + const std::string m_database_file; + util::Buffer m_transact_log_buffer; + bool m_history_updated; + void internal_transact_log_reserve(size_t, char** new_begin, char** new_end); + + size_t transact_log_size(); +}; + + +// Implementation: + +inline Replication::Replication() + : _impl::TransactLogConvenientEncoder(static_cast<_impl::TransactLogStream&>(*this)) +{ +} + +inline void Replication::initiate_transact(version_type current_version, bool history_updated) +{ + do_initiate_transact(current_version, history_updated); + reset_selection_caches(); +} + +inline Replication::version_type Replication::prepare_commit(version_type orig_version) +{ + return do_prepare_commit(orig_version); +} + +inline void Replication::finalize_commit() noexcept +{ + do_finalize_commit(); +} + +inline void Replication::abort_transact() noexcept +{ + do_abort_transact(); +} + +inline void Replication::interrupt() noexcept +{ + do_interrupt(); +} + +inline void Replication::clear_interrupt() noexcept +{ + do_clear_interrupt(); +} + +inline TrivialReplication::TrivialReplication(const std::string& database_file) + : m_database_file(database_file) +{ +} + +inline bool TrivialReplication::is_history_updated() const noexcept +{ + return m_history_updated; +} + +inline BinaryData TrivialReplication::get_uncommitted_changes() const noexcept +{ + const char* data = m_transact_log_buffer.data(); + size_t size = write_position() - data; + return BinaryData(data, size); +} + +inline size_t TrivialReplication::transact_log_size() +{ + return write_position() - m_transact_log_buffer.data(); +} + +inline void TrivialReplication::transact_log_reserve(size_t n, char** new_begin, char** new_end) +{ + internal_transact_log_reserve(n, new_begin, new_end); +} + +inline void TrivialReplication::internal_transact_log_reserve(size_t n, char** new_begin, char** new_end) +{ + char* data = m_transact_log_buffer.data(); + size_t size = write_position() - data; + m_transact_log_buffer.reserve_extra(size, n); + data = m_transact_log_buffer.data(); // May have changed + *new_begin = data + size; + *new_end = data + m_transact_log_buffer.size(); +} + +} // namespace realm + +#endif // REALM_REPLICATION_HPP diff --git a/Pods/Realm/include/core/realm/row.hpp b/Pods/Realm/include/core/realm/row.hpp new file mode 100644 index 0000000..f79794b --- /dev/null +++ b/Pods/Realm/include/core/realm/row.hpp @@ -0,0 +1,818 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_ROW_HPP +#define REALM_ROW_HPP + +#include + +#include +#include +#include +#include +#include + +namespace realm { + +template +class BasicRow; + + +/// This class is a "mixin" and contains the common set of functions for several +/// distinct row-like classes. +/// +/// There is a direct and natural correspondance between the functions in this +/// class and functions in Table of the same name. For example: +/// +/// table[i].get_int(j) == table.get_int(i,j) +/// +/// The effect of calling most of the row accessor functions on a detached +/// accessor is unspecified and may lead to general corruption, and/or a +/// crash. The exceptions are is_attached(), detach(), get_table(), get_index(), +/// and the destructor. Note however, that get_index() will still return an +/// unspecified value for a deatched accessor. +/// +/// When a row accessor is evaluated in a boolean context, it evaluates to true +/// if, and only if it is attached. +/// +/// \tparam T A const or non-const table type (currently either `Table` or +/// `const Table`). +/// +/// \tparam R A specific row accessor class (BasicRow or BasicRowExpr) providing +/// members `T* impl_get_table() const`, `size_t impl_get_row_ndx() +/// const`, and `void impl_detach()`. Neither are allowed to throw. +/// +/// \sa Table +/// \sa BasicRow +template +class RowFuncs { +public: + typedef T table_type; + + typedef BasicTableRef ConstTableRef; + typedef BasicTableRef TableRef; // Same as ConstTableRef if `T` is 'const' + + typedef typename util::CopyConst::type L; + using ConstLinkViewRef = std::shared_ptr; + using LinkViewRef = std::shared_ptr; // Same as ConstLinkViewRef if `T` is 'const' + + int_fast64_t get_int(size_t col_ndx) const noexcept; + bool get_bool(size_t col_ndx) const noexcept; + float get_float(size_t col_ndx) const noexcept; + double get_double(size_t col_ndx) const noexcept; + StringData get_string(size_t col_ndx) const noexcept; + BinaryData get_binary(size_t col_ndx) const noexcept; + OldDateTime get_olddatetime(size_t col_ndx) const noexcept; + Timestamp get_timestamp(size_t col_ndx) const noexcept; + ConstTableRef get_subtable(size_t col_ndx) const; + TableRef get_subtable(size_t col_ndx); + size_t get_subtable_size(size_t col_ndx) const noexcept; + size_t get_link(size_t col_ndx) const noexcept; + bool is_null_link(size_t col_ndx) const noexcept; + bool is_null(size_t col_ndx) const noexcept; + ConstLinkViewRef get_linklist(size_t col_ndx) const; + LinkViewRef get_linklist(size_t col_ndx); + bool linklist_is_empty(size_t col_ndx) const noexcept; + size_t get_link_count(size_t col_ndx) const noexcept; + Mixed get_mixed(size_t col_ndx) const noexcept; + DataType get_mixed_type(size_t col_ndx) const noexcept; + template + U get(size_t col_ndx) const noexcept; + + void set_int(size_t col_ndx, int_fast64_t value); + void set_int_unique(size_t col_ndx, int_fast64_t value); + void set_bool(size_t col_ndx, bool value); + void set_float(size_t col_ndx, float value); + void set_double(size_t col_ndx, double value); + void set_string(size_t col_ndx, StringData value); + void set_string_unique(size_t col_ndx, StringData value); + void set_binary(size_t col_ndx, BinaryData value); + void set_olddatetime(size_t col_ndx, OldDateTime value); + void set_timestamp(size_t col_ndx, Timestamp value); + void set_subtable(size_t col_ndx, const Table* value); + void set_link(size_t col_ndx, size_t value); + void nullify_link(size_t col_ndx); + void set_mixed(size_t col_ndx, Mixed value); + void set_mixed_subtable(size_t col_ndx, const Table* value); + void set_null(size_t col_ndx); + void set_null_unique(size_t col_ndx); + + void insert_substring(size_t col_ndx, size_t pos, StringData); + void remove_substring(size_t col_ndx, size_t pos, size_t size); + + //@{ + /// Note that these operations will cause the row accessor to be detached. + void remove(); + void move_last_over(); + //@} + + size_t get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept; + size_t get_backlink(const Table& src_table, size_t src_col_ndx, size_t backlink_ndx) const noexcept; + + size_t get_column_count() const noexcept; + DataType get_column_type(size_t col_ndx) const noexcept; + StringData get_column_name(size_t col_ndx) const noexcept; + size_t get_column_index(StringData name) const noexcept; + + /// Returns true if, and only if this accessor is currently attached to a + /// row. + /// + /// A row accesor may get detached from the underlying row for various + /// reasons (see below). When it does, it no longer refers to anything, and + /// can no longer be used, except for calling is_attached(), detach(), + /// get_table(), get_index(), and the destructor. The consequences of + /// calling other methods on a detached row accessor are unspecified. There + /// are a few Realm functions (Table::find_pkey_int()) that return a + /// detached row accessor to indicate a 'null' result. In all other cases, + /// however, row accessors obtained by calling functions in the Realm API + /// are always in the 'attached' state immediately upon return from those + /// functions. + /// + /// A row accessor becomes detached if the underlying row is removed, if the + /// associated table accessor becomes detached, or if the detach() method is + /// called. A row accessor does not become detached for any other reason. + bool is_attached() const noexcept; + + /// Detach this accessor from the row it was attached to. This function has + /// no effect if the accessor was already detached (idempotency). + void detach() noexcept; + + /// The table containing the row to which this accessor is currently + /// bound. For a detached accessor, the returned value is null. + const table_type* get_table() const noexcept; + table_type* get_table() noexcept; + + /// The index of the row to which this accessor is currently bound. For a + /// detached accessor, the returned value is unspecified. + size_t get_index() const noexcept; + + explicit operator bool() const noexcept; + +private: + const T* table() const noexcept; + T* table() noexcept; + size_t row_ndx() const noexcept; +}; + + +/// This class is a special kind of row accessor. It differes from a real row +/// accessor (BasicRow) by having a trivial and fast copy constructor and +/// descructor. It is supposed to be used as the return type of functions such +/// as Table::operator[](), and then to be used as a basis for constructing a +/// real row accessor. Objects of this class are intended to only ever exist as +/// temporaries. +/// +/// In contrast to a real row accessor (`BasicRow`), objects of this class do +/// not keep the parent table "alive", nor are they maintained (adjusted) across +/// row insertions and row removals like real row accessors are. +/// +/// \sa BasicRow +template +class BasicRowExpr : public RowFuncs> { +public: + BasicRowExpr() noexcept; + + template + BasicRowExpr(const BasicRowExpr&) noexcept; + +private: + T* m_table; // nullptr if detached. + size_t m_row_ndx; // Undefined if detached. + + BasicRowExpr(T*, size_t init_row_ndx) noexcept; + + T* impl_get_table() const noexcept; + size_t impl_get_row_ndx() const noexcept; + void impl_detach() noexcept; + + // Make impl_get_table(), impl_get_row_ndx(), and impl_detach() accessible + // from RowFuncs. + friend class RowFuncs>; + + // Make m_table and m_col_ndx accessible from BasicRowExpr(const + // BasicRowExpr&) for any U. + template + friend class BasicRowExpr; + + // Make m_table and m_col_ndx accessible from + // BasicRow::BaicRow(BasicRowExpr) for any U. + template + friend class BasicRow; + + // Make BasicRowExpr(T*, size_t) accessible from Table. + friend class Table; +}; + +// fwd decl +class Group; + +class RowBase { +protected: + TableRef m_table; // nullptr if detached. + size_t m_row_ndx; // Undefined if detached. + + void attach(Table*, size_t row_ndx) noexcept; + void reattach(Table*, size_t row_ndx) noexcept; + void impl_detach() noexcept; + + RowBase() + { + } + + RowBase(const RowBase&) = delete; + using HandoverPatch = RowBaseHandoverPatch; + + RowBase(const RowBase& source, HandoverPatch& patch); + +public: + static void generate_patch(const RowBase& source, HandoverPatch& patch); + void apply_patch(HandoverPatch& patch, Group& group); + +private: + RowBase* m_prev = nullptr; // nullptr if first, undefined if detached. + RowBase* m_next = nullptr; // nullptr if last, undefined if detached. + + // Table needs to be able to modify m_table and m_row_ndx. + friend class Table; +}; + + +/// An accessor class for table rows (a.k.a. a "row accessor"). +/// +/// For as long as it remains attached, a row accessor will keep the parent +/// table accessor alive. In case the lifetime of the parent table is not +/// managed by reference counting (such as when the table is an automatic +/// variable on the stack), the destruction of the table will cause all +/// remaining row accessors to be detached. +/// +/// While attached, a row accessor is bound to a particular row of the parent +/// table. If that row is removed, the accesssor becomes detached. If rows are +/// inserted or removed before it (at lower row index), then the accessor is +/// automatically adjusted to account for the change in index of the row to +/// which the accessor is bound. In other words, a row accessor is bound to the +/// contents of a row, not to a row index. See also is_attached(). +/// +/// Row accessors are created and used as follows: +/// +/// Row row = table[7]; // 8th row of `table` +/// ConstRow crow = ctable[2]; // 3rd row of const `ctable` +/// Row first_row = table.front(); +/// Row last_row = table.back(); +/// +/// float v = row.get_float(1); // Get the float in the 2nd column +/// row.set_string(0, "foo"); // Update the string in the 1st column +/// +/// Table* t = row.get_table(); // The parent table +/// size_t i = row.get_index(); // The current row index +/// +/// \sa RowFuncs +template +class BasicRow : private RowBase, public RowFuncs> { +public: + BasicRow() noexcept; + + template + BasicRow(BasicRowExpr) noexcept; + + BasicRow(const BasicRow&) noexcept; + + template + BasicRow(const BasicRow&) noexcept; + + template + BasicRow& operator=(BasicRowExpr) noexcept; + + template + BasicRow& operator=(BasicRow) noexcept; + + BasicRow& operator=(const BasicRow&) noexcept; + + ~BasicRow() noexcept; + +private: + T* impl_get_table() const noexcept; + size_t impl_get_row_ndx() const noexcept; + + // Make impl_get_table(), impl_get_row_ndx(), and impl_detach() accessible + // from RowFuncs. + friend class RowFuncs>; + + // Make m_table and m_col_ndx accessible from BasicRow(const BasicRow&) + // for any U. + template + friend class BasicRow; + +public: + std::unique_ptr> clone_for_handover(std::unique_ptr& patch) const + { + patch.reset(new HandoverPatch); + std::unique_ptr> retval(new BasicRow(*this, *patch)); + return retval; + } + + static void generate_patch(const BasicRow& row, std::unique_ptr& patch) + { + patch.reset(new HandoverPatch); + RowBase::generate_patch(row, *patch); + } + + void apply_and_consume_patch(std::unique_ptr& patch, Group& group) + { + apply_patch(*patch, group); + patch.reset(); + } + + void apply_patch(HandoverPatch& patch, Group& group) + { + RowBase::apply_patch(patch, group); + } + +private: + BasicRow(const BasicRow& source, HandoverPatch& patch) + : RowBase(source, patch) + { + } + friend class SharedGroup; +}; + +typedef BasicRow Row; +typedef BasicRow ConstRow; + + +// Implementation + +template +inline int_fast64_t RowFuncs::get_int(size_t col_ndx) const noexcept +{ + return table()->get_int(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::get_bool(size_t col_ndx) const noexcept +{ + return table()->get_bool(col_ndx, row_ndx()); +} + +template +inline float RowFuncs::get_float(size_t col_ndx) const noexcept +{ + return table()->get_float(col_ndx, row_ndx()); +} + +template +inline double RowFuncs::get_double(size_t col_ndx) const noexcept +{ + return table()->get_double(col_ndx, row_ndx()); +} + +template +inline StringData RowFuncs::get_string(size_t col_ndx) const noexcept +{ + return table()->get_string(col_ndx, row_ndx()); +} + +template +inline BinaryData RowFuncs::get_binary(size_t col_ndx) const noexcept +{ + return table()->get_binary(col_ndx, row_ndx()); +} + +template +inline OldDateTime RowFuncs::get_olddatetime(size_t col_ndx) const noexcept +{ + return table()->get_olddatetime(col_ndx, row_ndx()); +} + +template +inline Timestamp RowFuncs::get_timestamp(size_t col_ndx) const noexcept +{ + return table()->get_timestamp(col_ndx, row_ndx()); +} + +template +inline typename RowFuncs::ConstTableRef RowFuncs::get_subtable(size_t col_ndx) const +{ + return table()->get_subtable(col_ndx, row_ndx()); // Throws +} + +template +inline typename RowFuncs::TableRef RowFuncs::get_subtable(size_t col_ndx) +{ + return table()->get_subtable(col_ndx, row_ndx()); // Throws +} + +template +inline size_t RowFuncs::get_subtable_size(size_t col_ndx) const noexcept +{ + return table()->get_subtable_size(col_ndx, row_ndx()); +} + +template +inline size_t RowFuncs::get_link(size_t col_ndx) const noexcept +{ + return table()->get_link(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::is_null_link(size_t col_ndx) const noexcept +{ + return table()->is_null_link(col_ndx, row_ndx()); +} + +template +inline bool RowFuncs::is_null(size_t col_ndx) const noexcept +{ + return table()->is_null(col_ndx, row_ndx()); +} + +template +inline typename RowFuncs::ConstLinkViewRef RowFuncs::get_linklist(size_t col_ndx) const +{ + return table()->get_linklist(col_ndx, row_ndx()); // Throws +} + +template +inline typename RowFuncs::LinkViewRef RowFuncs::get_linklist(size_t col_ndx) +{ + return table()->get_linklist(col_ndx, row_ndx()); // Throws +} + +template +inline bool RowFuncs::linklist_is_empty(size_t col_ndx) const noexcept +{ + return table()->linklist_is_empty(col_ndx, row_ndx()); +} + +template +inline size_t RowFuncs::get_link_count(size_t col_ndx) const noexcept +{ + return table()->get_link_count(col_ndx, row_ndx()); +} + +template +inline Mixed RowFuncs::get_mixed(size_t col_ndx) const noexcept +{ + return table()->get_mixed(col_ndx, row_ndx()); +} + +template +inline DataType RowFuncs::get_mixed_type(size_t col_ndx) const noexcept +{ + return table()->get_mixed_type(col_ndx, row_ndx()); +} + +template +template +inline U RowFuncs::get(size_t col_ndx) const noexcept +{ + return table()->template get(col_ndx, row_ndx()); +} + +template +inline void RowFuncs::set_int(size_t col_ndx, int_fast64_t value) +{ + table()->set_int(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_int_unique(size_t col_ndx, int_fast64_t value) +{ + table()->set_int_unique(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_bool(size_t col_ndx, bool value) +{ + table()->set_bool(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_float(size_t col_ndx, float value) +{ + table()->set_float(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_double(size_t col_ndx, double value) +{ + table()->set_double(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_string(size_t col_ndx, StringData value) +{ + table()->set_string(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_string_unique(size_t col_ndx, StringData value) +{ + table()->set_string_unique(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_binary(size_t col_ndx, BinaryData value) +{ + table()->set_binary(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_olddatetime(size_t col_ndx, OldDateTime value) +{ + table()->set_olddatetime(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_timestamp(size_t col_ndx, Timestamp value) +{ + table()->set_timestamp(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_subtable(size_t col_ndx, const Table* value) +{ + table()->set_subtable(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_link(size_t col_ndx, size_t value) +{ + table()->set_link(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::nullify_link(size_t col_ndx) +{ + table()->nullify_link(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::set_mixed(size_t col_ndx, Mixed value) +{ + table()->set_mixed(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_mixed_subtable(size_t col_ndx, const Table* value) +{ + table()->set_mixed_subtable(col_ndx, row_ndx(), value); // Throws +} + +template +inline void RowFuncs::set_null(size_t col_ndx) +{ + table()->set_null(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::set_null_unique(size_t col_ndx) +{ + table()->set_null_unique(col_ndx, row_ndx()); // Throws +} + +template +inline void RowFuncs::insert_substring(size_t col_ndx, size_t pos, StringData value) +{ + table()->insert_substring(col_ndx, row_ndx(), pos, value); // Throws +} + +template +inline void RowFuncs::remove_substring(size_t col_ndx, size_t pos, size_t size) +{ + table()->remove_substring(col_ndx, row_ndx(), pos, size); // Throws +} + +template +inline void RowFuncs::remove() +{ + table()->remove(row_ndx()); // Throws +} + +template +inline void RowFuncs::move_last_over() +{ + table()->move_last_over(row_ndx()); // Throws +} + +template +inline size_t RowFuncs::get_backlink_count(const Table& src_table, size_t src_col_ndx) const noexcept +{ + return table()->get_backlink_count(row_ndx(), src_table, src_col_ndx); +} + +template +inline size_t RowFuncs::get_backlink(const Table& src_table, size_t src_col_ndx, size_t backlink_ndx) const + noexcept +{ + return table()->get_backlink(row_ndx(), src_table, src_col_ndx, backlink_ndx); +} + +template +inline size_t RowFuncs::get_column_count() const noexcept +{ + return table()->get_column_count(); +} + +template +inline DataType RowFuncs::get_column_type(size_t col_ndx) const noexcept +{ + return table()->get_column_type(col_ndx); +} + +template +inline StringData RowFuncs::get_column_name(size_t col_ndx) const noexcept +{ + return table()->get_column_name(col_ndx); +} + +template +inline size_t RowFuncs::get_column_index(StringData name) const noexcept +{ + return table()->get_column_index(name); +} + +template +inline bool RowFuncs::is_attached() const noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline void RowFuncs::detach() noexcept +{ + static_cast(this)->impl_detach(); +} + +template +inline const T* RowFuncs::get_table() const noexcept +{ + return table(); +} + +template +inline T* RowFuncs::get_table() noexcept +{ + return table(); +} + +template +inline size_t RowFuncs::get_index() const noexcept +{ + return row_ndx(); +} + +template +inline RowFuncs::operator bool() const noexcept +{ + return is_attached(); +} + +template +inline const T* RowFuncs::table() const noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline T* RowFuncs::table() noexcept +{ + return static_cast(this)->impl_get_table(); +} + +template +inline size_t RowFuncs::row_ndx() const noexcept +{ + return static_cast(this)->impl_get_row_ndx(); +} + + +template +inline BasicRowExpr::BasicRowExpr() noexcept + : m_table(0) + , m_row_ndx(0) +{ +} + +template +template +inline BasicRowExpr::BasicRowExpr(const BasicRowExpr& expr) noexcept + : m_table(expr.m_table) + , m_row_ndx(expr.m_row_ndx) +{ +} + +template +inline BasicRowExpr::BasicRowExpr(T* init_table, size_t init_row_ndx) noexcept + : m_table(init_table) + , m_row_ndx(init_row_ndx) +{ +} + +template +inline T* BasicRowExpr::impl_get_table() const noexcept +{ + return m_table; +} + +template +inline size_t BasicRowExpr::impl_get_row_ndx() const noexcept +{ + return m_row_ndx; +} + +template +inline void BasicRowExpr::impl_detach() noexcept +{ + m_table = nullptr; +} + + +template +inline BasicRow::BasicRow() noexcept +{ +} + +template +inline BasicRow::BasicRow(const BasicRow& row) noexcept + : RowBase() +{ + attach(const_cast(row.m_table.get()), row.m_row_ndx); +} + +template +template +inline BasicRow::BasicRow(BasicRowExpr expr) noexcept +{ + T* expr_table = expr.m_table; // Check that pointer types are compatible + attach(const_cast(expr_table), expr.m_row_ndx); +} + +template +template +inline BasicRow::BasicRow(const BasicRow& row) noexcept +{ + T* row_table = row.m_table.get(); // Check that pointer types are compatible + attach(const_cast(row_table), row.m_row_ndx); +} + +template +template +inline BasicRow& BasicRow::operator=(BasicRowExpr expr) noexcept +{ + T* expr_table = expr.m_table; // Check that pointer types are compatible + reattach(const_cast(expr_table), expr.m_row_ndx); + return *this; +} + +template +template +inline BasicRow& BasicRow::operator=(BasicRow row) noexcept +{ + T* row_table = row.m_table.get(); // Check that pointer types are compatible + reattach(const_cast(row_table), row.m_row_ndx); + return *this; +} + +template +inline BasicRow& BasicRow::operator=(const BasicRow& row) noexcept +{ + reattach(const_cast(row.m_table.get()), row.m_row_ndx); + return *this; +} + +template +inline BasicRow::~BasicRow() noexcept +{ + RowBase::impl_detach(); +} + +template +inline T* BasicRow::impl_get_table() const noexcept +{ + return m_table.get(); +} + +template +inline size_t BasicRow::impl_get_row_ndx() const noexcept +{ + return m_row_ndx; +} + +} // namespace realm + +#endif // REALM_ROW_HPP diff --git a/Pods/Realm/include/core/realm/spec.hpp b/Pods/Realm/include/core/realm/spec.hpp new file mode 100644 index 0000000..78448c9 --- /dev/null +++ b/Pods/Realm/include/core/realm/spec.hpp @@ -0,0 +1,476 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_SPEC_HPP +#define REALM_SPEC_HPP + +#include +#include +#include +#include +#include +#include + +namespace realm { + +class Table; +class SubspecRef; +class ConstSubspecRef; + +class Spec { +public: + Spec(SubspecRef) noexcept; + ~Spec() noexcept; + + Allocator& get_alloc() const noexcept; + + bool has_strong_link_columns() noexcept; + + void insert_column(size_t column_ndx, ColumnType type, StringData name, ColumnAttr attr = col_attr_None); + void rename_column(size_t column_ndx, StringData new_name); + void move_column(size_t from, size_t to); + + /// Erase the column at the specified index, and move columns at + /// succeeding indexes to the next lower index. + /// + /// This function is guaranteed to *never* throw if the spec is + /// used in a non-transactional context, or if the spec has + /// already been successfully modified within the current write + /// transaction. + void erase_column(size_t column_ndx); + + //@{ + // If a new Spec is constructed from the returned subspec + // reference, it is the responsibility of the application that the + // parent Spec object (this) is kept alive for at least as long as + // the new Spec object. + SubspecRef get_subtable_spec(size_t column_ndx) noexcept; + ConstSubspecRef get_subtable_spec(size_t column_ndx) const noexcept; + //@} + + // Column info + size_t get_column_count() const noexcept; + size_t get_public_column_count() const noexcept; + DataType get_public_column_type(size_t column_ndx) const noexcept; + ColumnType get_column_type(size_t column_ndx) const noexcept; + StringData get_column_name(size_t column_ndx) const noexcept; + + /// Returns size_t(-1) if the specified column is not found. + size_t get_column_index(StringData name) const noexcept; + + // Column Attributes + ColumnAttr get_column_attr(size_t column_ndx) const noexcept; + + size_t get_subspec_ndx(size_t column_ndx) const noexcept; + ref_type get_subspec_ref(size_t subspec_ndx) const noexcept; + SubspecRef get_subspec_by_ndx(size_t subspec_ndx) noexcept; + ConstSubspecRef get_subspec_by_ndx(size_t subspec_ndx) const noexcept; + + // Auto Enumerated string columns + void upgrade_string_to_enum(size_t column_ndx, ref_type keys_ref, ArrayParent*& keys_parent, size_t& keys_ndx); + size_t get_enumkeys_ndx(size_t column_ndx) const noexcept; + ref_type get_enumkeys_ref(size_t column_ndx, ArrayParent** keys_parent = nullptr, + size_t* keys_ndx = nullptr) noexcept; + + // Links + size_t get_opposite_link_table_ndx(size_t column_ndx) const noexcept; + void set_opposite_link_table_ndx(size_t column_ndx, size_t table_ndx); + bool has_backlinks() const noexcept; + void set_backlink_origin_column(size_t backlink_col_ndx, size_t origin_col_ndx); + size_t get_origin_column_ndx(size_t backlink_col_ndx) const noexcept; + size_t find_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx) const noexcept; + + /// Get position in `Table::m_columns` of the specified column. It may be + /// different from the specified logical column index due to the presence of + /// search indexes, since their top refs are stored in Table::m_columns as + /// well. + size_t get_column_ndx_in_parent(size_t column_ndx) const; + + //@{ + /// Compare two table specs for equality. + bool operator==(const Spec&) const noexcept; + bool operator!=(const Spec&) const noexcept; + //@} + + void destroy() noexcept; + + size_t get_ndx_in_parent() const noexcept; + void set_ndx_in_parent(size_t) noexcept; + +#ifdef REALM_DEBUG + void verify() const; + void to_dot(std::ostream&, StringData title = StringData()) const; +#endif + +private: + // Underlying array structure. + // + // `m_subspecs` contains one entry for each subtable column, one entry for + // each link or link list columns, two entries for each backlink column, and + // zero entries for all other column types. For subtable columns the entry + // is a ref pointing to the subtable spec, for link and link list columns it + // is the group-level table index of the target table, and for backlink + // columns the first entry is the group-level table index of the origin + // table, and the second entry is the index of the origin column in the + // origin table. + Array m_top; + ArrayInteger m_types; // 1st slot in m_top + ArrayString m_names; // 2nd slot in m_top + ArrayInteger m_attr; // 3rd slot in m_top + Array m_subspecs; // 4th slot in m_top (optional) + Array m_enumkeys; // 5th slot in m_top (optional) + bool m_has_strong_link_columns; + + Spec(Allocator&) noexcept; // Unattached + + void init(ref_type) noexcept; + void init(MemRef) noexcept; + void init(SubspecRef) noexcept; + void update_has_strong_link_columns() noexcept; + + // Similar in function to Array::init_from_parent(). + void init_from_parent() noexcept; + + ref_type get_ref() const noexcept; + + /// Called in the context of Group::commit() to ensure that + /// attached table accessors stay valid across a commit. Please + /// note that this works only for non-transactional commits. Table + /// accessors obtained during a transaction are always detached + /// when the transaction ends. + void update_from_parent(size_t old_baseline) noexcept; + + void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept; + + void set_column_type(size_t column_ndx, ColumnType type); + void set_column_attr(size_t column_ndx, ColumnAttr attr); + + /// Construct an empty spec and return just the reference to the + /// underlying memory. + static MemRef create_empty_spec(Allocator&); + + struct ColumnInfo { + size_t m_column_ref_ndx = 0; ///< Index within Table::m_columns + bool m_has_search_index = false; + }; + + ColumnInfo get_column_info(size_t column_ndx) const noexcept; + + size_t get_subspec_ndx_after(size_t column_ndx, size_t skip_column_ndx) const noexcept; + size_t get_subspec_entries_for_col_type(ColumnType type) const noexcept; + bool has_subspec() const noexcept; + + // Returns false if the spec has no columns, otherwise it returns + // true and sets `type` to the type of the first column. + static bool get_first_column_type_from_ref(ref_type, Allocator&, ColumnType& type) noexcept; + + friend class Replication; + friend class Group; + friend class Table; +}; + + +class SubspecRef { +public: + struct const_cast_tag { + }; + SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept; + ~SubspecRef() noexcept + { + } + Allocator& get_alloc() const noexcept + { + return m_parent->get_alloc(); + } + +private: + Array* const m_parent; + size_t const m_ndx_in_parent; + + SubspecRef(Array* parent, size_t ndx_in_parent) noexcept; + + friend class Spec; + friend class ConstSubspecRef; +}; + +class ConstSubspecRef { +public: + ConstSubspecRef(SubspecRef r) noexcept; + ~ConstSubspecRef() noexcept + { + } + Allocator& get_alloc() const noexcept + { + return m_parent->get_alloc(); + } + +private: + const Array* const m_parent; + size_t const m_ndx_in_parent; + + ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept; + + friend class Spec; + friend class SubspecRef; +}; + + +// Implementation: + +inline Allocator& Spec::get_alloc() const noexcept +{ + return m_top.get_alloc(); +} + +inline bool Spec::has_strong_link_columns() noexcept +{ + return m_has_strong_link_columns; +} + +inline ref_type Spec::get_subspec_ref(size_t subspec_ndx) const noexcept +{ + REALM_ASSERT(subspec_ndx < m_subspecs.size()); + + // Note that this addresses subspecs directly, indexing + // by number of sub-table columns + return m_subspecs.get_as_ref(subspec_ndx); +} + +inline Spec::Spec(SubspecRef r) noexcept + : m_top(r.m_parent->get_alloc()) + , m_types(r.m_parent->get_alloc()) + , m_names(r.m_parent->get_alloc()) + , m_attr(r.m_parent->get_alloc()) + , m_subspecs(r.m_parent->get_alloc()) + , m_enumkeys(r.m_parent->get_alloc()) +{ + init(r); +} + +// Uninitialized Spec (call init() to init) +inline Spec::Spec(Allocator& alloc) noexcept + : m_top(alloc) + , m_types(alloc) + , m_names(alloc) + , m_attr(alloc) + , m_subspecs(alloc) + , m_enumkeys(alloc) +{ +} + +inline SubspecRef Spec::get_subtable_spec(size_t column_ndx) noexcept +{ + REALM_ASSERT(column_ndx < get_column_count()); + REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); + size_t subspec_ndx = get_subspec_ndx(column_ndx); + return SubspecRef(&m_subspecs, subspec_ndx); +} + +inline ConstSubspecRef Spec::get_subtable_spec(size_t column_ndx) const noexcept +{ + REALM_ASSERT(column_ndx < get_column_count()); + REALM_ASSERT(get_column_type(column_ndx) == col_type_Table); + size_t subspec_ndx = get_subspec_ndx(column_ndx); + return ConstSubspecRef(&m_subspecs, subspec_ndx); +} + +inline SubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) noexcept +{ + return SubspecRef(&m_subspecs, subspec_ndx); +} + +inline ConstSubspecRef Spec::get_subspec_by_ndx(size_t subspec_ndx) const noexcept +{ + return const_cast(this)->get_subspec_by_ndx(subspec_ndx); +} + +inline void Spec::init(ref_type ref) noexcept +{ + MemRef mem(ref, get_alloc()); + init(mem); +} + +inline void Spec::init(SubspecRef r) noexcept +{ + m_top.set_parent(r.m_parent, r.m_ndx_in_parent); + ref_type ref = r.m_parent->get_as_ref(r.m_ndx_in_parent); + init(ref); +} + +inline void Spec::init_from_parent() noexcept +{ + ref_type ref = m_top.get_ref_from_parent(); + init(ref); +} + +inline void Spec::destroy() noexcept +{ + m_top.destroy_deep(); +} + +inline size_t Spec::get_ndx_in_parent() const noexcept +{ + return m_top.get_ndx_in_parent(); +} + +inline void Spec::set_ndx_in_parent(size_t ndx) noexcept +{ + m_top.set_ndx_in_parent(ndx); +} + +inline ref_type Spec::get_ref() const noexcept +{ + return m_top.get_ref(); +} + +inline void Spec::set_parent(ArrayParent* parent, size_t ndx_in_parent) noexcept +{ + m_top.set_parent(parent, ndx_in_parent); +} + +inline void Spec::rename_column(size_t column_ndx, StringData new_name) +{ + REALM_ASSERT(column_ndx < m_types.size()); + m_names.set(column_ndx, new_name); +} + +inline size_t Spec::get_column_count() const noexcept +{ + // This is the total count of columns, including backlinks (not public) + return m_types.size(); +} + +inline size_t Spec::get_public_column_count() const noexcept +{ + // Backlinks are the last columns, and do not have names, so getting + // the number of names gives us the count of user facing columns + return m_names.size(); +} + +inline ColumnType Spec::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return ColumnType(m_types.get(ndx)); +} + +inline void Spec::set_column_type(size_t column_ndx, ColumnType type) +{ + REALM_ASSERT(column_ndx < get_column_count()); + + // At this point we only support upgrading to string enum + REALM_ASSERT(ColumnType(m_types.get(column_ndx)) == col_type_String); + REALM_ASSERT(type == col_type_StringEnum); + + m_types.set(column_ndx, type); // Throws + + update_has_strong_link_columns(); +} + +inline ColumnAttr Spec::get_column_attr(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return ColumnAttr(m_attr.get(ndx)); +} + +inline void Spec::set_column_attr(size_t column_ndx, ColumnAttr attr) +{ + REALM_ASSERT(column_ndx < get_column_count()); + + // At this point we only allow one attr at a time + // so setting it will overwrite existing. In the future + // we will allow combinations. + m_attr.set(column_ndx, attr); + + update_has_strong_link_columns(); +} + +inline StringData Spec::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT(ndx < get_column_count()); + return m_names.get(ndx); +} + +inline size_t Spec::get_column_index(StringData name) const noexcept +{ + return m_names.find_first(name); +} + +inline bool Spec::get_first_column_type_from_ref(ref_type top_ref, Allocator& alloc, ColumnType& type) noexcept +{ + const char* top_header = alloc.translate(top_ref); + ref_type types_ref = to_ref(Array::get(top_header, 0)); + const char* types_header = alloc.translate(types_ref); + if (Array::get_size_from_header(types_header) == 0) + return false; + type = ColumnType(Array::get(types_header, 0)); + return true; +} + +inline bool Spec::has_backlinks() const noexcept +{ + // backlinks are always last and do not have names. + return m_names.size() < m_types.size(); + + // Fixme: It's bad design that backlinks are stored and recognized like this. Backlink columns + // should be a column type like any other, and we should find another way to hide them away from + // the user. +} + +// Spec will have a subspec when it contains a column which is one of: +// link, linklist, backlink, or subtable. It is possible for m_top.size() +// to contain an entry for m_subspecs (at index 3) but this reference +// may be empty if the spec contains enumkeys (at index 4) but no subspec types. +inline bool Spec::has_subspec() const noexcept +{ + return (m_top.size() >= 4) && (m_top.get_as_ref(3) != 0); +} + +inline bool Spec::operator!=(const Spec& s) const noexcept +{ + return !(*this == s); +} + + +inline SubspecRef::SubspecRef(Array* parent, size_t ndx_in_parent) noexcept + : m_parent(parent) + , m_ndx_in_parent(ndx_in_parent) +{ +} + +inline SubspecRef::SubspecRef(const_cast_tag, ConstSubspecRef r) noexcept + : m_parent(const_cast(r.m_parent)) + , m_ndx_in_parent(r.m_ndx_in_parent) +{ +} + +inline ConstSubspecRef::ConstSubspecRef(const Array* parent, size_t ndx_in_parent) noexcept + : m_parent(parent) + , m_ndx_in_parent(ndx_in_parent) +{ +} + +inline ConstSubspecRef::ConstSubspecRef(SubspecRef r) noexcept + : m_parent(r.m_parent) + , m_ndx_in_parent(r.m_ndx_in_parent) +{ +} + + +} // namespace realm + +#endif // REALM_SPEC_HPP diff --git a/Pods/Realm/include/core/realm/string_data.hpp b/Pods/Realm/include/core/realm/string_data.hpp new file mode 100644 index 0000000..e1234cf --- /dev/null +++ b/Pods/Realm/include/core/realm/string_data.hpp @@ -0,0 +1,323 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_STRING_HPP +#define REALM_STRING_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace realm { + +/// A reference to a chunk of character data. +/// +/// An instance of this class can be thought of as a type tag on a region of +/// memory. It does not own the referenced memory, nor does it in any other way +/// attempt to manage the lifetime of it. +/// +/// A null character inside the referenced region is considered a part of the +/// string by Realm. +/// +/// For compatibility with C-style strings, when a string is stored in a Realm +/// database, it is always followed by a terminating null character, regardless +/// of whether the string itself has internal null characters. This means that +/// when a StringData object is extracted from Realm, the referenced region is +/// guaranteed to be followed immediately by an extra null character, but that +/// null character is not inside the referenced region. Therefore, all of the +/// following forms are guaranteed to return a pointer to a null-terminated +/// string: +/// +/// \code{.cpp} +/// +/// group.get_table_name(...).data() +/// table.get_column_name().data() +/// table.get_string(...).data() +/// table.get_mixed(...).get_string().data() +/// +/// \endcode +/// +/// Note that in general, no assumptions can be made about what follows a string +/// that is referenced by a StringData object, or whether anything follows it at +/// all. In particular, the receiver of a StringData object cannot assume that +/// the referenced string is followed by a null character unless there is an +/// externally provided guarantee. +/// +/// This class makes it possible to distinguish between a 'null' reference and a +/// reference to the empty string (see is_null()). +/// +/// \sa BinaryData +/// \sa Mixed +class StringData { +public: + /// Construct a null reference. + StringData() noexcept; + + /// If \a external_data is 'null', \a data_size must be zero. + StringData(const char* external_data, size_t data_size) noexcept; + + template + StringData(const std::basic_string&); + + template + operator std::basic_string() const; + + // StringData does not store data, callers must manage their own strings. + template + StringData(std::basic_string&&) = delete; + + template + StringData(const util::Optional>&); + + StringData(const null&) noexcept; + + /// Initialize from a zero terminated C style string. Pass null to construct + /// a null reference. + StringData(const char* c_str) noexcept; + + char operator[](size_t i) const noexcept; + + const char* data() const noexcept; + size_t size() const noexcept; + + /// Is this a null reference? + /// + /// An instance of StringData is a null reference when, and only when the + /// stored size is zero (size()) and the stored pointer is the null pointer + /// (data()). + /// + /// In the case of the empty string, the stored size is still zero, but the + /// stored pointer is **not** the null pointer. It could for example point + /// to the empty string literal. Note that the actual value of the pointer + /// is immaterial in this case (as long as it is not zero), because when the + /// size is zero, it is an error to dereference the pointer. + /// + /// Conversion of a StringData object to `bool` yields the logical negation + /// of the result of calling this function. In other words, a StringData + /// object is converted to true if it is not the null reference, otherwise + /// it is converted to false. + bool is_null() const noexcept; + + friend bool operator==(const StringData&, const StringData&) noexcept; + friend bool operator!=(const StringData&, const StringData&) noexcept; + + //@{ + /// Trivial bytewise lexicographical comparison. + friend bool operator<(const StringData&, const StringData&) noexcept; + friend bool operator>(const StringData&, const StringData&) noexcept; + friend bool operator<=(const StringData&, const StringData&) noexcept; + friend bool operator>=(const StringData&, const StringData&) noexcept; + //@} + + bool begins_with(StringData) const noexcept; + bool ends_with(StringData) const noexcept; + bool contains(StringData) const noexcept; + + //@{ + /// Undefined behavior if \a n, \a i, or i+n is greater than + /// size(). + StringData prefix(size_t n) const noexcept; + StringData suffix(size_t n) const noexcept; + StringData substr(size_t i, size_t n) const noexcept; + StringData substr(size_t i) const noexcept; + //@} + + template + friend std::basic_ostream& operator<<(std::basic_ostream&, const StringData&); + + explicit operator bool() const noexcept; + +private: + const char* m_data; + size_t m_size; +}; + + +// Implementation: + +inline StringData::StringData() noexcept + : m_data(nullptr) + , m_size(0) +{ +} + +inline StringData::StringData(const char* external_data, size_t data_size) noexcept + : m_data(external_data) + , m_size(data_size) +{ + REALM_ASSERT_DEBUG(external_data || data_size == 0); +} + +template +inline StringData::StringData(const std::basic_string& s) + : m_data(s.data()) + , m_size(s.size()) +{ +} + +template +inline StringData::operator std::basic_string() const +{ + return std::basic_string(m_data, m_size); +} + +template +inline StringData::StringData(const util::Optional>& s) + : m_data(s ? s->data() : nullptr) + , m_size(s ? s->size() : 0) +{ +} + +inline StringData::StringData(const null&) noexcept + : m_data(nullptr) + , m_size(0) +{ +} + +inline StringData::StringData(const char* c_str) noexcept + : m_data(c_str) + , m_size(0) +{ + if (c_str) + m_size = std::char_traits::length(c_str); +} + +inline char StringData::operator[](size_t i) const noexcept +{ + return m_data[i]; +} + +inline const char* StringData::data() const noexcept +{ + return m_data; +} + +inline size_t StringData::size() const noexcept +{ + return m_size; +} + +inline bool StringData::is_null() const noexcept +{ + return !m_data; +} + +inline bool operator==(const StringData& a, const StringData& b) noexcept +{ + return a.m_size == b.m_size && a.is_null() == b.is_null() && safe_equal(a.m_data, a.m_data + a.m_size, b.m_data); +} + +inline bool operator!=(const StringData& a, const StringData& b) noexcept +{ + return !(a == b); +} + +inline bool operator<(const StringData& a, const StringData& b) noexcept +{ + if (a.is_null() && !b.is_null()) { + // Null strings are smaller than all other strings, and not + // equal to empty strings. + return true; + } + return std::lexicographical_compare(a.m_data, a.m_data + a.m_size, b.m_data, b.m_data + b.m_size); +} + +inline bool operator>(const StringData& a, const StringData& b) noexcept +{ + return b < a; +} + +inline bool operator<=(const StringData& a, const StringData& b) noexcept +{ + return !(b < a); +} + +inline bool operator>=(const StringData& a, const StringData& b) noexcept +{ + return !(a < b); +} + +inline bool StringData::begins_with(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + return d.m_size <= m_size && safe_equal(m_data, m_data + d.m_size, d.m_data); +} + +inline bool StringData::ends_with(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + return d.m_size <= m_size && safe_equal(m_data + m_size - d.m_size, m_data + m_size, d.m_data); +} + +inline bool StringData::contains(StringData d) const noexcept +{ + if (is_null() && !d.is_null()) + return false; + + return d.m_size == 0 || std::search(m_data, m_data + m_size, d.m_data, d.m_data + d.m_size) != m_data + m_size; +} + +inline StringData StringData::prefix(size_t n) const noexcept +{ + return substr(0, n); +} + +inline StringData StringData::suffix(size_t n) const noexcept +{ + return substr(m_size - n); +} + +inline StringData StringData::substr(size_t i, size_t n) const noexcept +{ + return StringData(m_data + i, n); +} + +inline StringData StringData::substr(size_t i) const noexcept +{ + return substr(i, m_size - i); +} + +template +inline std::basic_ostream& operator<<(std::basic_ostream& out, const StringData& d) +{ + for (const char* i = d.m_data; i != d.m_data + d.m_size; ++i) + out << *i; + return out; +} + +inline StringData::operator bool() const noexcept +{ + return !is_null(); +} + +} // namespace realm + +#endif // REALM_STRING_HPP diff --git a/Pods/Realm/include/core/realm/sync/client.hpp b/Pods/Realm/include/core/realm/sync/client.hpp new file mode 100644 index 0000000..d5907e7 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/client.hpp @@ -0,0 +1,420 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_CLIENT_HPP +#define REALM_SYNC_CLIENT_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace realm { +namespace sync { + +class Client { +public: + enum class Reconnect { + /// This is the mode that should always be used in production. In this + /// mode the client uses a scheme for determining a reconnect delay that + /// prevents it from creating too many connection requests in a short + /// amount of time. + normal, + + /// Never delay reconnect attempts. For testing purposes only. + immediately + }; + + struct Config { + Config() {} + + /// The maximum number of Realm files that will be kept open + /// concurrently by this client. The client keeps a cache of open Realm + /// files for efficiency reasons. + long max_open_files = 256; + + /// An optional logger to be used by the client. If no logger is + /// specified, the client will use an instance of util::StderrLogger + /// with the log level threshold set to util::Logger::Level::info. The + /// client does not require a thread-safe logger, and it guarantees that + /// all logging happens on behalf of the thread that executes run(). + util::Logger* logger = nullptr; + + /// verify_servers_ssl_certificate controls whether the server certificate + /// is verified for SSL connections. It should always be true in production. + /// + /// A server certificate is verified by first checking that the + /// certificate has a valid signature chain back to a trust/anchor certificate, + /// and secondly checking that the host name of the Realm URL matches + /// a host name contained in the certificate. + /// The host name of the certificate is stored in either Common Name or + /// the Alternative Subject Name (DNS section). + /// + /// From the point of view of OpenSSL, setting verify_servers_ssl_certificate + /// to false means calling `SSL_set_verify()` with `SSL_VERIFY_NONE`. + /// Setting verify_servers_ssl_certificate to true means calling `SSL_set_verify()` + /// with `SSL_VERIFY_PEER`, and setting the host name using the function + /// X509_VERIFY_PARAM_set1_host() (OpenSSL version 1.0.2 or newer). + /// For other platforms, an equivalent procedure is followed. + bool verify_servers_ssl_certificate = true; + + /// ssl_trust_certificate_path is the path of a trust/anchor certificate + /// used by the client to verify the server certificate. + /// If ssl_trust_certificate_path is None (default), the default device + /// trust/anchor store is used. + util::Optional ssl_trust_certificate_path; // default None + + /// Use ports 80 and 443 by default instead of 7800 and 7801 + /// respectively. Ideally, these default ports should have been made + /// available via a different URI scheme instead (http/https or ws/wss). + bool enable_default_port_hack = true; + + /// For testing purposes only. + Reconnect reconnect = Reconnect::normal; + + /// Create a separate connection for each session. For testing purposes + /// only. + bool one_connection_per_session = false; + + /// Do not access the local file system. Sessions will act as if + /// initiated on behalf of an empty (or nonexisting) local Realm + /// file. Received DOWNLOAD messages will be accepted, but otherwise + /// ignored. No UPLOAD messages will be generated. For testing purposes + /// only. + bool dry_run = false; + }; + + /// \throw util::EventLoop::Implementation::NotAvailable if no event loop + /// implementation was specified, and + /// util::EventLoop::Implementation::get_default() throws it. + Client(Config = Config()); + Client(Client&&) noexcept; + ~Client() noexcept; + + using ErrorHandler = void(int error_code, std::string message); + + /// \brief Set a function to be called when the server reports a + /// connection-level error. + /// + /// Only connection-level errors are reported through this callback. See + /// also Session::set_error_handler(). See \ref Error (or `protocol.md`) for + /// a list of errors and their categorization. + /// + /// The callback function will always be called by the thread that executes + /// run(). If the callback function throws an exception, that exception will + /// "travel" out through run(). + /// + /// Note: Any call to this function must have returned before run() is + /// called. If this function is called multiple times, each call overrides + /// the previous setting. + /// + /// Note: This function is **not thread-safe**. + void set_error_handler(std::function); + + /// Run the internal event-loop of the client. At most one thread may + /// execute run() at any given time. The call will not return until somebody + /// calls stop(). + void run(); + + // Thread-safe + void stop() noexcept; + + /// Technically thread-safe, but the returned value is not accurate until + /// after run() has returned. + /// + /// For testing purposes. + uint_fast64_t errors_seen() const noexcept; + +private: + class Impl; + std::unique_ptr m_impl; + friend class Session; +}; + + +/// Supported protocols: +/// +/// Protocol URL scheme Default port +/// ----------------------------------------------------------------------------------- +/// realm "realm:" 7800 (80 if Client::Config::enable_default_port_hack) +/// realm_ssl "realms:" 7801 (443 if Client::Config::enable_default_port_hack) +/// +enum class Protocol { + realm, + realm_ssl +}; + + +class BadServerUrl; // Exception + + +/// Session objects must be destroyed before the Client object with which they +/// are assocoated is destroyed. +/// +/// It is an error to create two Session objects for a particular Realm file if +/// those Session objects overlap in time, or if they are associated with two +/// different Client objects that overlap in time. +/// +/// Thread-safety: It is safe for multiple threads to construct, use (with some +/// exceptions), and destroy session objects concurrently, regardless of whether +/// those session objects are associated with the same, or with different Client +/// objects. Please note that some of the public member functions are fully +/// thread-safe, while others are not. +class Session { +public: + using port_type = util::network::endpoint::port_type; + using version_type = _impl::History::version_type; + using SyncTransactCallback = void(VersionID old_version, VersionID new_version); + using ErrorHandler = Client::ErrorHandler; + + /// \brief Start a new session for the specified client-side Realm. + /// + /// Note that the session is not fully activated until you call bind(). Also + /// note that if you call set_sync_transact_callback(), it must be done + /// before calling bind(). + /// + /// \param realm_path The file-system path of a local client-side Realm + /// file. + Session(Client&, std::string realm_path); + + Session(Session&&) noexcept; + + ~Session() noexcept; + + /// \brief Set a function to be called when the local Realm has changed due + /// to integration of a downloaded changeset. + /// + /// Specify the callback function that will be called when one or more + /// transactions are performed to integrate downloaded changesets into the + /// client-side Realm, that is associated with this session. + /// + /// The callback function will always be called by the thread that executes + /// the event loop (Client::run()), but not until bind() is called. If the + /// callback function throws an exception, that exception will "travel" out + /// through Client::run(). + /// + /// Note: Any call to this function must have returned before bind() is + /// called. If this function is called multiple times, each call overrides + /// the previous setting. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// CAUTION: The specified callback function may be called before the call + /// to bind() returns, and it may be called (or continue to execute) after + /// the session object is destroyed. The application must pass a handler + /// that can be safely called, and can safely continue to execute from the + /// point in time where bind() starts executing, and up until the point in + /// time where the last invocation of `clint.run()` returns. Here, `client` + /// refers to the associated Client object. + void set_sync_transact_callback(std::function); + + /// \brief Set a function to be called when an error is reported by the + /// server to have occurred in this session. + /// + /// Only session-level errors are reported through the callback. See also + /// Client::set_error_handler(). See \ref Error (or `protocol.md`) for a + /// list of errors and their categorization. + /// + /// The callback function will always be called by the thread that executes + /// the event loop (Client::run()), but not until bind() is called. If the + /// callback function throws an exception, that exception will "travel" out + /// through Client::run(). + /// + /// Note: Any call to this function must have returned before bind() is + /// called. If this function is called multiple times, each call overrides + /// the previous setting. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// CAUTION: The specified callback function may be called before the call + /// to bind() returns, and it may be called (or continue to execute) after + /// the session object is destroyed. The application must pass a handler + /// that can be safely called, and can safely continue to execute from the + /// point in time where bind() starts executing, and up until the point in + /// time where the last invocation of `clint.run()` returns. Here, `client` + /// refers to the associated Client object. + void set_error_handler(std::function); + + /// @{ \brief Bind this session to the specified server side Realm. + /// + /// No communication takes place on behalf of this session before the + /// session is bound, but as soon as the session becomes bound, the server + /// will start to push changes to the client, and vice versa. + /// + /// If a callback function was set using set_sync_transact_callback(), then + /// that callback function will start to be called as changesets are + /// downloaded and integrated locally. It is important to understand that + /// callback functions are executed by the event loop thread (Client::run()) + /// and the callback function may therfore be called before bind() returns. + /// + /// Note: It is an error if this function is called more than once per + /// Session object. + /// + /// Note: This function is **not thread-safe**. That is, it is an error if + /// it is called while another thread is executing any member function on + /// the same Session object. + /// + /// \param server_url For example "realm://sync.realm.io/test". See \a + /// server_address, \a server_path, and \a server_port for information about + /// the individual components of the URL. See \ref Protocol for the list of + /// available URL schemes and the associated default ports. The 2-argument + /// version has exactly the same affect as the 5-argument version. + /// + /// \param server_address The fully qualified host name, or IP address of + /// the server. + /// + /// \param server_path The virtual path by which the server identifies the + /// Realm. This path must always be an absolute path, and must therefore + /// always contain a leading slash (`/`). Further more, each segment of the + /// virtual path must consist of one or more characters that are either + /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to + /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or + /// `.realm.management`. These rules are necessary because the server + /// currently reserves the right to use the specified path as part of the + /// file system path of a Realm file. It is expected that these rules will + /// be significantly relaxed in the future by completely decoupling the + /// virtual paths from actual file system paths. + /// + /// \param signed_user_token A cryptographically signed token describing the + /// identity and access rights of the current user. See \ref Protocol. + /// + /// \param server_port If zero, use the default port for the specified + /// protocol. See \ref Protocol for information on default ports. + /// + /// \param protocol See \ref Protocol. + /// + /// \throw BadServerUrl if the specified server URL is malformed. + void bind(std::string server_url, std::string signed_user_token); + void bind(std::string server_address, std::string server_path, + std::string signed_user_token, port_type server_port = 0, + Protocol protocol = Protocol::realm); + /// @} + + /// \brief Refresh the user token associated with this session. + /// + /// This causes the REFRESH protocol message to be sent to the server. See + /// \ref Protocol. + /// + /// In an on-going session a client may expect its access token to expire at + /// a certain time and schedule acquisition of a fresh access token (using a + /// refresh token or by other means) in due time to provide a better user + /// experience. Without refreshing the token, the client will be notified + /// that the session is terminated due to insufficient privileges and must + /// reacquire a fresh token, which is a potentially disruptive process. + /// + /// It is an error if the user token used with this message represents a + /// different user identity than a previously used user token. The server + /// will detect this scenario and report an error. + /// + /// It is an error to call this function before calling `Client::bind()`. + /// + /// Note: This function is thread-safe. + /// + /// \param signed_user_token A cryptographically signed token describing the + /// identity and access rights of the current user. See \ref Protocol. + void refresh(std::string signed_user_token); + + /// \brief Inform the synchronization agent about changes of local origin. + /// + /// This function must be called by the application after a transaction + /// performed on its behlaf, that is, after a transaction that is not + /// performed to integrate a changeset that was downloaded from the server. + /// + /// It is an error to call this function before bind() has been called, and + /// has returned. + /// + /// Note: This function is fully thread-safe. That is, it may be called by + /// any thread, and by multiple threads concurrently. + void nonsync_transact_notify(version_type new_version); + + /// \brief Wait for upload to complete + /// + /// This function waits for all currently outstanding client-side changesets + /// to be uploaded to, and acknowledged by the server. More specifically, it + /// waits until all changesets that must be uploaded, and that precede a + /// certain client version threshold have been uploaded. The threshold is + /// the highest client version passed to nonsync_transact_notify() prior to + /// the invocation of wait_for_upload_complete_or_client_stopped(). + /// + /// If nonsync_transact_notify() is called while + /// wait_for_upload_complete_or_client_stopped() executes, and with a + /// version that greater than the currently applying threashold, then that + /// may, or may not cause the threshold to be pushed forward and the wait to + /// be extended. + /// + /// It is an error to call this function before bind() has been called, and + /// has returned. + /// + /// Note: This function is fully thread-safe. That is, it may be called by + /// any thread, and by multiple threads concurrently. + void wait_for_upload_complete_or_client_stopped(); + + /// \brief Wait for download to complete + /// + /// This function waits for all currently outstanding server-side changesets + /// to be downloaded. More specifically, it waits until all changesets that + /// must be downloaded, and that precede a certain server version threshold + /// have been downloaded (FIXME: Shouldn't it have been downloaded and + /// integrated?). The threshold is the currently latest server version at + /// the time where wait_for_download_complete_or_client_stopped() is called, + /// or shortly thereafter. + /// + /// If wait_for_download_complete_or_client_stopped() is called by one + /// thread while it is being executed by another thread, then the later call + /// may, or may not push forward the threshold that applies the the earlier + /// call, and cause its wait to be correspondingly extended. + /// + /// It is an error to call this function before bind() has been called, and + /// has returned. + /// + /// Note: This function is fully thread-safe. That is, it may be called by + /// any thread, and by multiple threads concurrently. + void wait_for_download_complete_or_client_stopped(); + +private: + class Impl; + Impl* m_impl; +}; + + + +// Implementation + +class BadServerUrl: public std::exception { +public: + const char* what() const noexcept override + { + return "Bad server URL"; + } +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CLIENT_HPP diff --git a/Pods/Realm/include/core/realm/sync/crypto.hpp b/Pods/Realm/include/core/realm/sync/crypto.hpp new file mode 100644 index 0000000..016543e --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/crypto.hpp @@ -0,0 +1,46 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CRYPTO_HPP +#define REALM_SYNC_CRYPTO_HPP + +#include +#include + +#include +#include + +namespace realm { +namespace sync { +namespace crypto { + +/// sha1() calculates the sha1 hash value of \param in_buffer of size \param +/// in_buffer_size. The value is placed in \param out_buffer. The value has +/// size 20, and the caller must ensure that \param out_buffer has size at +/// least 20. +/// sha1() throws an exception if the underlying openssl implementation +/// fails, which should just happen in case of memory allocation failure. +void sha1(const char* in_buffer, size_t in_buffer_size, char* out_buffer); + +} // namespace crypto +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CRYPTO_HPP diff --git a/Pods/Realm/include/core/realm/sync/crypto_server.hpp b/Pods/Realm/include/core/realm/sync/crypto_server.hpp new file mode 100644 index 0000000..f2ac3f0 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/crypto_server.hpp @@ -0,0 +1,86 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_CRYPTO_SERVER_HPP +#define REALM_SYNC_CRYPTO_SERVER_HPP + +#include +#include + +#include +#include + +namespace realm { +namespace sync { + +struct CryptoError: std::runtime_error { + CryptoError(std::string message) : std::runtime_error(std::move(message)) {} +}; + +/// This class represents a public/private keypair, or more commonly a single public +/// key used for verifying signatures. +/// +/// Only RSA keys are supported for now. +/// +/// Its methods correspond roughly to the EVP_PKEY_* set of functionality found in +/// the OpenSSL library. +class PKey { +public: + PKey(PKey&&); + PKey& operator=(PKey&&); + ~PKey(); + + /// Load RSA public key from \a pemfile. + static PKey load_public(const std::string& pemfile); + /// Load RSA public/private keypair from \a pemfile. + static PKey load_private(const std::string& pemfile); + + /// Whether or not the key can be used for signing. + /// + /// True if the private part is loaded. + bool can_sign() const noexcept; + + /// Whether or not the key can be used for verifying. + /// + /// Always true for RSA keys. + bool can_verify() const noexcept; + + /// Sign \a message with the loaded key, if the private part is + /// loaded. Store the signed message as binary data in \a signature. + /// + /// If a private key is not loaded, throws an exception of type CryptoError. + void sign(BinaryData message, util::Buffer& signature) const; + + /// Verify that \a signature is a valid digest of \a message. + /// + /// Returns true if the signature is valid, otherwise false. If an error occurs while + /// attempting verification, an exception of type CryptoError is thrown. + bool verify(BinaryData message, BinaryData signature) const; + +private: + PKey(); + struct Impl; + std::unique_ptr m_impl; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_CRYPTO_SERVER_HPP diff --git a/Pods/Realm/include/core/realm/sync/history.hpp b/Pods/Realm/include/core/realm/sync/history.hpp new file mode 100644 index 0000000..4bee7da --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/history.hpp @@ -0,0 +1,218 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#include +#include + +#include +#include + +#ifndef REALM_SYNC_HISTORY_HPP +#define REALM_SYNC_HISTORY_HPP + +namespace realm { +namespace sync { + +/// SyncProgress is the progress sent by the server in +/// the download message. The server scans through its +/// history in connection with every download message. +/// scan_server_version is the server_version of the changeset +/// at which the server ended the scan. scan_client_version +/// is the client_version for this client that was last integrated before +/// scan_server_version. +/// latest_server_version is the end of the server history, and +/// latest_server_session_ident is the server_session_ident +/// corresponding to latest_sever_version. +/// latest_client_version is the corresponding client_version. +/// In other words, latest_client_version is the very latest version +/// of a changeset originating from this client. +/// +/// The client persists the entire progress. It is not very important to +/// persist latest_server_version, but for consistency the entire +/// progress is persisted. +struct SyncProgress { + using version_type = HistoryEntry::version_type; + + version_type scan_server_version = 0; + version_type scan_client_version = 0; + version_type latest_server_version = 0; + int_fast64_t latest_server_session_ident = 0; + version_type latest_client_version = 0; +}; + + +class SyncHistory: + public TrivialReplication { +public: + using version_type = TrivialReplication::version_type; + using file_ident_type = HistoryEntry::file_ident_type; + using SyncTransactCallback = void(VersionID old_version, VersionID new_version); + + SyncHistory(const std::string& realm_path); + + /// Get the version of the latest snapshot of the associated Realm, as well + /// as the file identifier pair and the synchronization progress pair as + /// they are stored in that snapshot. + /// + /// The returned current client version is the version of the latest + /// snapshot of the associated SharedGroup object, and is guaranteed to be + /// zero for the initial empty Realm state. + /// + /// The returned file identifier pair (server, client) is the one that was + /// last stored by set_file_ident_pair(). If no identifier pair has been + /// stored yet, \a client_file_ident is set to zero. + /// + /// The returned SyncProgress is the one that was last stored by + /// set_sync_progress(), or {} if set_sync_progress() has never been called + /// for the associated Realm file. + virtual void get_status(version_type& current_client_version, + file_ident_type& server_file_ident, + file_ident_type& client_file_ident, + int_fast64_t& client_file_ident_secret, + SyncProgress& progress) = 0; + + /// Stores the server assigned file identifier pair (server, client) in the + /// associated Realm file, such that it is available via get_status() during + /// future synchronization sessions. It is an error to set this identifier + /// pair more than once per Realm file. + /// + /// \param server_file_ident The server assigned server-side file + /// identifier. This can be any non-zero integer strictly less than 2**64. + /// The server is supposed to choose a cryptic value that cannot easily be + /// guessed by clients (intentionally or not), and its only puspose is to + /// provide a higher level of fidelity during subsequent identification of + /// the server Realm. The server does not have to guarantee that this + /// identifier is unique, but in almost all cases it will be. Since the + /// client will also always specify the path name when referring to a server + /// file, the lack of a uniqueness guarantee is effectively not a problem. + /// + /// \param client_file_ident The server assigned client-side file + /// identifier. A client-side file identifier is a non-zero positive integer + /// strictly less than 2**64. The server guarantees that all client-side + /// file identifiers generated on behalf of a particular server Realm are + /// unique with respect to each other. The server is free to generate + /// identical identifiers for two client files if they are associated with + /// different server Realms. + /// + /// The client is required to obtain the file identifiers before engaging in + /// synchronization proper, and it must store the identifiers and use + /// them to reestablish the connection between the client file and the server + /// file when engaging in future synchronization sessions. + virtual void set_file_ident_pair(file_ident_type server_file_ident, + file_ident_type client_file_ident, + int_fast64_t client_file_ident_secret) = 0; + + /// Stores the SyncProgress progress in the + /// associated Realm file in a way that makes it available via get_status() + /// during future synchronization sessions. Progress is reported by the + /// server in the DOWNLOAD message. + /// + /// See struct SyncProgress for a description of \param progress. + /// + /// `progress.scan_client_version` has an effect on the process by which + /// old history entries are discarded. + /// + /// `progress.scan_client_version` The version produced on this client by the last + /// changeset, that was sent to, and integrated by the server at the time + /// `progress.scan_server_version was produced, or zero if + /// `progress.scan_server_version` is zero. + /// + /// Since all changesets produced after `progres.scan_client_version` are potentially + /// needed during operational transformation of the next changeset received + /// from the server, the implementation of this class must promise to retain + /// all history entries produced after `progress.scan_client_version`. That is, a history + /// entry with a changeset, that produces version V, is guaranteed to be + /// retained as long as V is strictly greater than `progress.scan_client_version`. + /// + /// It is an error to specify a client version that is less than the + /// currently stored version, since there is no way to get discarded history + /// back. + virtual void set_sync_progress(SyncProgress progress) = 0; + + /// Get the first history entry whose changeset produced a version that + /// succeeds `begin_version` and, and does not succeed `end_version`, whose + /// changeset was not produced by integration of a changeset received from + /// the server, and whose changeset was not empty. + /// + /// \param begin_version, end_version The range of versions to consider. If + /// `begin_version` is equal to `end_version`, this is the empty range. If + /// `begin_version` is zero, it means that everything preceeding + /// `end_version` is to be considered, which is again the empty range if + /// `end_version` is also zero. Zero is is special value in that no + /// changeset produces that version. It is an error if `end_version` + /// preceeds `begin_version`, or if `end_version` is zero and + /// `begin_version` is not. + /// + /// \param buffer Owner of memory referenced by entry.changeset upon return. + /// + /// \return The version produced by the changeset of the located history + /// entry, or zero if no history entry exists matching the specified + /// criteria. + virtual version_type find_history_entry_for_upload(version_type begin_version, + version_type end_version, + HistoryEntry& entry, + std::unique_ptr& buffer) const = 0; + + using RemoteChangeset = Transformer::RemoteChangeset; + + /// \brief Integrate a sequence of remote changesets using a single Realm + /// transaction. + /// + /// Each changeset will be transformed as if by a call to + /// Transformer::transform_remote_changeset(), and then applied to the + /// associated Realm. + /// + /// As a final step, each changeset will be added to the local history (list + /// of applied changesets). + /// + /// \param progress is the SyncProgress received in the download message. + /// Progress will be persisted along with the changesets. + /// + /// \param num_changesets The number of passed changesets. Must be non-zero. + /// + /// \param callback An optional callback which will be called with the + /// version immediately processing the sync transaction and that of the sync + /// transaction. + /// + /// \return The new local version produced by the application of the + /// transformed changeset. + virtual version_type integrate_remote_changesets(SyncProgress progress, + const RemoteChangeset* changesets, + size_t num_changesets, + util::Logger* replay_logger, + std::function& callback) = 0; +}; + + +std::unique_ptr make_sync_history(const std::string& realm_path); + + + +// Implementation + +inline SyncHistory::SyncHistory(const std::string& realm_path): + TrivialReplication(realm_path) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_SYNC_HPP diff --git a/Pods/Realm/include/core/realm/sync/metrics.hpp b/Pods/Realm/include/core/realm/sync/metrics.hpp new file mode 100644 index 0000000..6ce5830 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/metrics.hpp @@ -0,0 +1,88 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_METRICS_HPP +#define REALM_SYNC_METRICS_HPP + +#if REALM_HAVE_DOGLESS +# include +#endif + +namespace realm { +namespace sync { + +// FIXME: Consider adding support for specification of sample rate. The Dogless +// API already supports this. +class Metrics { +public: + /// Increment the counter identified by the specified key. + virtual void increment(const char* key) = 0; + + /// Set value of the guage identified by the specified key. + virtual void gauge(const char* key, double value) = 0; + + /// Add the specified value to the guage identified by the specified + /// key. The value is allowed to be negative. + virtual void gauge_relative(const char* key, double value) = 0; + + virtual ~Metrics() {} +}; + +#if REALM_HAVE_DOGLESS + +class DoglessMetrics: public sync::Metrics { +public: + DoglessMetrics(): + m_dogless("realm") // Throws + { +#if !REALM_PLATFORM_APPLE + m_dogless.mtu(dogless::MTU_Jumbo); +#endif + m_dogless.loop_interval(1); + } + + void increment(const char* key) override + { + const char* metric = key; + m_dogless.increment(metric); // Throws + } + + void gauge(const char* key, double value) override + { + const char* metric = key; + m_dogless.gauge(metric, value); // Throws + } + + void gauge_relative(const char* key, double value) override + { + const char* metric = key; + double amount = value; + m_dogless.gauge_relative(metric, amount); // Throws + } + +private: + dogless::BufferedStatsd m_dogless; +}; + +#endif + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_METRICS_HPP diff --git a/Pods/Realm/include/core/realm/sync/protocol.hpp b/Pods/Realm/include/core/realm/sync/protocol.hpp new file mode 100644 index 0000000..cd5c44c --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/protocol.hpp @@ -0,0 +1,124 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2016] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_PROTOCOL_HPP +#define REALM_SYNC_PROTOCOL_HPP + + +// NOTE: The protocol specification is in `/doc/protocol.md` + + +namespace realm { +namespace sync { + +// Protocol versions: +// +// 1 Initial version. +// +// 2 Introduces the UNBOUND message (sent from server to client in +// response to a BIND message). +// +// 3 Introduces the ERROR message (sent from server to client before the +// server closes a connection). Introduces MARK message from client to +// server, and MARK response message from server to client as a way for the +// client to wait for download to complete. +// +// 4 User token and signature are now passed as a single string (see +// /doc/protocol.md for details). Also, `application_ident` parameter +// removed from IDENT message. +// +// 5 IDENT message renamed to CLIENT, and ALLOC message (client->server) +// renamed to IDENT. Also, parameter added to CLIENT +// message. Also, the protocol has been changed to make the clients +// acquisition of a server allocated file identifier pair be part of a +// session from the servers point of view. File identifier and version +// parameters moved from the BIND message to a new IDENT message sent by +// client when it has obtained the file identifier pair. Both the new IDENT +// message and the ALLOC message sent by the server are now properly +// associated with a session. +// +// 6 Server session IDs have been added to the IDENT, DOWNLOAD, and PROGRESS +// messages, and the "Divergent history" error code was added as an +// indication that a server version / session ID pair does not match the +// server's history. +// +// 7 FIXME: Who introduced version 7? Please describe what changed. +// +// 8 Error code (`bad_authentication`) moved from 200-range to 300-range +// because it is now session specific. Other error codes were renumbered. +// +// 9 New format of the DOWNLOAD message to support progress reporting on the +// client +// 10 Error codes reordered (now categorized as either connection or session +// level errors). +// 11 Bugfixes in Link List and ChangeLinkTargets merge rules, that +// make previous versions incompatible. +// 12 FIXME What was 12? +// 13 Bugfixes in Link List and ChangeLinkTargets merge rules, that +// make previous versions incompatible. +// 14 Further bugfixes related to primary keys and link lists. Add support for +// LinkListSwap. +// 15 Deleting an object with a primary key deletes all objects on other +// with the same primary key. +constexpr int get_current_protocol_version() noexcept +{ + return 15; +} + +// Reserve 0 for compatibility with std::error_condition. +enum class Error { + // Connection level and protocol errors + connection_closed = 100, // Connection closed (no error) + other_error = 101, // Other connection level error + unknown_message = 102, // Unknown type of input message + bad_syntax = 103, // Bad syntax in input message head + limits_exceeded = 104, // Limits exceeded in input message + wrong_protocol_version = 105, // Wrong protocol version (CLIENT) + bad_session_ident = 106, // Bad session identifier in input message + reuse_of_session_ident = 107, // Overlapping reuse of session identifier (BIND) + bound_in_other_session = 108, // Client file bound in other session (IDENT) + bad_message_order = 109, // Bad input message order + + // Session level errors + session_closed = 200, // Session closed (no error) + other_session_error = 201, // Other session level error + token_expired = 202, // Access token expired + bad_authentication = 203, // Bad user authentication (BIND, REFRESH) + illegal_realm_path = 204, // Illegal Realm path (BIND) + no_such_realm = 205, // No such Realm (BIND) + permission_denied = 206, // Permission denied (BIND, REFRESH) + bad_server_file_ident = 207, // Bad server file identifier (IDENT) + bad_client_file_ident = 208, // Bad client file identifier (IDENT) + bad_server_version = 209, // Bad server version (IDENT, UPLOAD) + bad_client_version = 210, // Bad client version (IDENT, UPLOAD) + diverging_histories = 211, // Diverging histories (IDENT) + bad_changeset = 212, // Bad changeset (UPLOAD) +}; + +inline constexpr bool is_session_level_error(Error error) +{ + return int(error) >= 200; +} + +const char* get_error_message(Error error_code); + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_PROTOCOL_HPP diff --git a/Pods/Realm/include/core/realm/sync/server.hpp b/Pods/Realm/include/core/realm/sync/server.hpp new file mode 100644 index 0000000..5250abe --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/server.hpp @@ -0,0 +1,166 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2012] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_SERVER_HPP +#define REALM_SYNC_SERVER_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace realm { +namespace sync { + +class Server { +public: + struct Config { + Config() {} + + /// The maximum number of Realm files that will be kept open + /// concurrently by this server. The server keeps a cache of open Realm + /// files for efficiency reasons. + long max_open_files = 256; + + /// An optional logger to be used by the server. If no logger is + /// specified, the server will use an instance of util::StderrLogger + /// with the log level threshold set to util::Logger::Level::info. The + /// server does not require a thread-safe logger, and it guarantees that + /// all logging happens on behalf of start() and run() (which are not + /// allowed to execute concurrently). + util::Logger* logger = nullptr; + + /// An optional sink for recording metrics about the internal operation + /// of the server. Below is a list of counters and gauges that are + /// updated by the server. The server may or may not update additional + /// counters and gauges. + /// + /// Statistics counters Incremented when + /// ------------------------------------------------------------------------ + /// server.started The server was started + /// connection.started A new client connection was established + /// connection.terminated A client connection was terminated + /// session.started A new session was started + /// session.terminated A session was terminated + /// connection.read.failed A connection was closed due to read error + /// connection.write.failed A connection was closed due to write error + /// protocol.upload.received An UPLOAD message was received + /// protocol.download.sent A DOWNLOAD message was sent + /// protocol.connection.errored Connection level protocol error occurred + /// protocol.session.errored Session level protocol error occurred + /// + /// Statistics gauges Continuously updated to reflect + /// -------------------------------------------------------------------------- + /// connection.opened The current total number of connections + /// session.opened The current total number of sessions + /// + Metrics* metrics = nullptr; + + /// FIXME: This seems to be related to the dashboard feature, but it + /// would be nice with some additional explanation (Sebastian). + const char* stats_db = nullptr; + + /// The address at which the listening socket is bound. + /// The address can be a name or on numerical form. + /// Use "localhost" to listen on the loopback interface. + std::string listen_address; + + /// The port at which the listening socket is bound. + /// The port can be a name or on numerical form. + /// Use the empty string to have the system assign a dynamic + /// listening port. + std::string listen_port; + + bool reuse_address = true; + + /// The listening socket accepts TLS/SSL connections if `ssl` is + /// true, and non-secure tcp connections otherwise. + bool ssl = false; + + /// The path of the certificate that will be sent to clients during + /// the SSL/TLS handshake. + /// + /// From the point of view of OpenSSL, this file will be passed to + /// `SSL_CTX_use_certificate_chain_file()`. + /// + /// This option is ignore if `ssl` is false. + std::string ssl_certificate_path; + + /// The path of the private key corresponding to the certificate. + /// + /// From the point of view of OpenSSL, this file will be passed to + /// `SSL_CTX_use_PrivateKey_file()`. + /// + /// This option is ignore if `ssl` is false. + std::string ssl_certificate_key_path; + }; + + Server(const std::string& root_dir, util::Optional public_key, Config = Config()); + Server(Server&&) noexcept; + ~Server() noexcept; + + /// start() binds a listening socket to the address and port specified in + /// Config and starts accepting connections. + /// The resolved endpoint (including the dynamically assigned port, if requested) + /// can be obtained by calling listen_endpoint(). + /// This can be done immediately after start() returns. + void start(); + + /// A helper function, for backwards compatibility, that starts a listening + /// socket without SSL at the specified address and port. + void start(const std::string& listen_address, + const std::string& listen_port, + bool reuse_address = true); + + /// Return the resolved and bound endpoint of the listening socket. + util::network::endpoint listen_endpoint() const; + + /// Run the internal event-loop of the server. At most one thread may + /// execute run() at any given time. It is an error if run() is called + /// before start() has been successfully executed. The call to run() will + /// not return until somebody calls stop(). + void run(); + + /// Stop any thread that is currently executing run(). This function may be + /// called by any thread. + void stop() noexcept; + + /// Must not be called while run() is executing. + uint_fast64_t errors_seen() const noexcept; + + /// Initialise the directory structure as required for correct operation of + /// the server. This is a static function, as it should be run on the \a + /// root_path prior to instantiating the \c Server object. + static void init_directory_structure(const std::string& root_path, util::Logger& logger); + + +private: + class Implementation; + std::unique_ptr m_impl; +}; + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_SERVER_HPP diff --git a/Pods/Realm/include/core/realm/sync/server_configuration.hpp b/Pods/Realm/include/core/realm/sync/server_configuration.hpp new file mode 100644 index 0000000..e295b4f --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/server_configuration.hpp @@ -0,0 +1,56 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_SERVER_CONFIGURATION_HPP +#define REALM_SYNC_SERVER_CONFIGURATION_HPP + +// Realm headers +#include +#include + +namespace realm { +namespace config { + +struct Configuration { + std::string listen_address = "127.0.0.1"; + std::string listen_port = ""; // Empty means choose default based on `ssl`. + realm::util::Optional root_dir; + std::string user_data_dir; + std::string internal_data_dir; + realm::util::Optional public_key_path; + realm::util::Optional config_file_path; + bool reuse_address = true; + bool disable_sync = false; + realm::util::Logger::Level log_level = realm::util::Logger::Level::info; + realm::util::Optional log_path; + std::string stats_db_path; + long max_open_files = 256; + bool ssl = false; + std::string ssl_certificate_path; + std::string ssl_certificate_key_path; +}; + +void show_help(const std::string& program_name); +Configuration build_configuration(int argc, char* argv[]); +Configuration load_configuration(std::string configuration_file_path); + +} // namespace config +} // namespace realm + +#endif // REALM_SYNC_SERVER_CONFIGURATION_HPP diff --git a/Pods/Realm/include/core/realm/sync/transform.hpp b/Pods/Realm/include/core/realm/sync/transform.hpp new file mode 100644 index 0000000..5d9b115 --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/transform.hpp @@ -0,0 +1,371 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2015] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ + +#ifndef REALM_SYNC_TRANSFORM_HPP +#define REALM_SYNC_TRANSFORM_HPP + +#include + +#include +#include +#include + +namespace realm { +namespace sync { + +class HistoryEntry { +public: + using timestamp_type = uint_fast64_t; + using file_ident_type = uint_fast64_t; + using version_type = _impl::History::version_type; + + /// The time of origination of the changes referenced by this history entry, + /// meassured as the number of milliseconds since 2015-01-01T00:00:00Z, not + /// including leap seconds. For changes of local origin, this is the local + /// time at the point when the local transaction was committed. For changes + /// of remote origin, it is the remote time of origin at the client + /// identified by `origin_client_file_ident`. + /// + /// All clients that will be, or are already participating in + /// synchronization must guarantee that their local history is causally + /// consistent. The convergence guarantee offered by the merge system relies + /// strongly on this. + /// + /// FIXME: In its current form, the merge algorithm seems to achieve + /// convergence even without causal consistency. Figure out whether we still + /// want to require it, and if so, why. + /// + /// **Definition:** The local history is *causally consistent* if, and only + /// if every entry, referring to changes of local origin, has an effective + /// timestamp, which is greater than, or equal to the effective timestamp of + /// all preceding entries in the local history. + /// + /// **Definition:** The *effective timestamp* of a history entry is the pair + /// `(origin_timestamp, origin_client_file_ident)` endowed with the standard + /// lexicographic order. Note that this implies that it is impossible for + /// two entries to have equal effective timestamps if they originate from + /// different clients. + timestamp_type origin_timestamp; + + /// For changes of local origin, `origin_client_file_ident` is always + /// zero. For changes of remote origin, this history entry was produced by + /// the integration of a changeset received from a remote peer P. In some + /// cases, that changeset may itself have been produced by the integration + /// on P of a changeset received from another remote peer. In any case, as + /// long as these changes are of remote origin, `origin_client_file_ident` + /// identifies the peer on which they originated, which may or may not be P. + /// + /// More concretely, on the server-side, the remote peer is a client, and + /// and the changes always originate from that client, so + /// `origin_client_file_ident` always refer to that client. Conversely, on + /// the client-side, the remote peer is the server, and the server received + /// the original changeset from a client, so `origin_client_file_ident` + /// refers to that client. + /// + /// Note that *peer* is used colloquially here to refer to a particular + /// synchronization participant. In reality, a synchronization participant + /// is either a server-side file, or a particular client-side file + /// associated with that server-side file. To make things even more + /// confusing, a single client application may contain multiple client-side + /// files associated with the same server-side file. In the same vein, + /// *client* should be understood as client-side file, and *remote peer* as + /// any other file from the set of associated files, even other such files + /// contained within the same client application, if there are any. + file_ident_type origin_client_file_ident; + + /// For changes of local origin, `remote_version` is the version produced on + /// the remote peer by the last changeset integrated locally prior to the + /// production of the changeset referenced by this history entry, or zero if + /// no remote changeset was integrated yet. This only makes sense when there + /// is a unique remote peer, and since that is not the case on the server, + /// the server cannot be the originator of any changes. + /// + /// For changes of remote origin, this history entry was produced by the + /// integration of a changeset directly received from a remote peer P, and + /// `remote_version` is then the version produced on P by that + /// changeset. Note that such changes may have originated from a different + /// peer (not P), but `remote_version` will still be the version produced on + /// P. + /// + /// More concretely, on the server-side, the remote peer is a client, and + /// the changes always originate from that client, and `remote_version` is + /// the `` specified in an UPLOAD message of the + /// client-server communication protocol. Conversely, for changes of remote + /// origin on the client-side, the remote peer is the server, and + /// `remote_version` is the specified in a received + /// DOWNLOAD message. + version_type remote_version; + + /// Referenced memory is not owned by this class. + BinaryData changeset; +}; + + +/// The interface between the sync history and the operational transformer +/// (Transformer). +class TransformHistory { +public: + using timestamp_type = HistoryEntry::timestamp_type; + using file_ident_type = HistoryEntry::file_ident_type; + using version_type = HistoryEntry::version_type; + + /// Get the first history entry whose changeset produced a version that + /// succeeds `begin_version` and, and does not succeed `end_version`, and + /// whose changeset was not produced by integration of a changeset received + /// from the specified remote peer. + /// + /// The ownership of the memory referenced by `entry.changeset` is **not** + /// passed to the caller upon return. The callee retains ownership. + /// + /// \param begin_version, end_version The range of versions to consider. If + /// `begin_version` is equal to `end_version`, this is the empty range. If + /// `begin_version` is zero, it means that everything preceeding + /// `end_version` is to be considered, which is again the empty range if + /// `end_version` is also zero. Zero is is special value in that no + /// changeset produces that version. It is an error if `end_version` + /// preceeds `begin_version`, or if `end_version` is zero and + /// `begin_version` is not. + /// + /// \param not_from_remote_client_file_ident Skip entries whose changeset is + /// produced by integration of changesets received from this remote + /// peer. Zero if the remote peer is the server, otherwise the peer + /// identifier of a client. + /// + /// \param only_nonempty Skip entries with empty changesets. + /// + /// \return The version produced by the changeset of the located history + /// entry, or zero if no history entry exists matching the specified + /// criteria. + virtual version_type find_history_entry(version_type begin_version, version_type end_version, + file_ident_type not_from_remote_client_file_ident, + bool only_nonempty, + HistoryEntry& entry) const noexcept = 0; + + /// Copy a contiguous sequence of bytes from the specified reciprocally + /// transformed changeset into the specified buffer. The targeted history + /// entry is the one whose untransformed changeset produced the specified + /// version. Copying starts at the specified offset within the transform, + /// and will continue until the end of the transform or the end of the + /// buffer, whichever comes first. The first copied byte is always placed in + /// `buffer[0]`. The number of copied bytes is returned. + /// + /// \param remote_client_file_ident Zero if the remote peer is the server, + /// otherwise the peer identifier of a client. + virtual size_t read_reciprocal_transform(version_type version, + file_ident_type remote_client_file_ident, + size_t offset, char* buffer, size_t size) const = 0; + + /// Replace a contiguous chunk of bytes within the specified reciprocally + /// transformed changeset. The targeted history entry is the one whose + /// untransformed changeset produced the specified version. If the new chunk + /// has a different size than the on it replaces, subsequent bytes (those + /// beyond the end of the replaced chunk) are shifted to lower or higher + /// offsets accordingly. If `replaced_size` is equal to `size_t(-1)`, the + /// replaced chunk extends from `offset` to the end of the transform. Let + /// `replaced_size_2` be the actual size of the replaced chunk, then the + /// total number of bytes in the transform will increase by `size - + /// replaced_size_2`. It is an error if `replaced_size` is not `size_t(-1)` + /// and `offset + replaced_size` is greater than the size of the transform. + /// + /// \param remote_client_file_ident See read_reciprocal_transform(). + /// + /// \param offset The index of the first replaced byte relative to the + /// beginning of the transform. + /// + /// \param replaced_size The number of bytes in the replaced chunk. + /// + /// \param data The buffer holding the replacing chunk. + /// + /// \param size The number of bytes in the replacing chunk, which is also + /// the number of bytes that will be read from the specified buffer. + virtual void write_reciprocal_transform(version_type version, + file_ident_type remote_client_file_ident, + size_t offset, size_t replaced_size, + const char* data, size_t size) = 0; + + virtual ~TransformHistory() noexcept {} + +protected: + static bool register_local_time(timestamp_type local_timestamp, + timestamp_type& timestamp_threshold) noexcept; + + static bool register_remote_time(timestamp_type remote_timestamp, + timestamp_type& timestamp_threshold) noexcept; +}; + + +class TransformError; // Exception + + +class Transformer { +public: + using timestamp_type = HistoryEntry::timestamp_type; + using file_ident_type = HistoryEntry::file_ident_type; + using version_type = HistoryEntry::version_type; + + struct RemoteChangeset { + /// The version produced on the remote peer by this changeset. + /// + /// On the server, the remote peer is the client from which the + /// changeset originated, and `remote_version` is the client version + /// produced by the changeset on that client. + /// + /// On a client, the remote peer is the server, and `remote_version` is + /// the server version produced by this changeset on the server. Since + /// the server is never the originator of changes, this changeset must + /// in turn have been produced on the server by integration of a + /// changeset uploaded by some other client. + version_type remote_version; + + /// The last local version that has been integrated into + /// `remote_version`. + /// + /// A local version, L, has been integrated into a remote version, R, + /// when, and only when L is the latest local version such that all + /// preceeding changesets in the local history have been integrated by + /// the remote peer prior to R. + /// + /// On the server, this is the last server version integrated into the + /// client version `remote_version`. On a client, it is the last client + /// version integrated into the server version `remote_version`. + version_type last_integrated_local_version; + + /// The changeset itself. + BinaryData data; + + /// Same meaning as `HistoryEntry::origin_timestamp`. + timestamp_type origin_timestamp; + + /// Same meaning as `HistoryEntry::origin_client_file_ident`. + file_ident_type origin_client_file_ident; + + RemoteChangeset(version_type rv, version_type lv, BinaryData d, timestamp_type ot, + file_ident_type fi); + }; + + /// Produce an operationally transformed version of the specified changeset, + /// which is assumed to be of remote origin, and received from remote peer + /// P. Note that P is not necessarily the peer from which the changes + /// originated. + /// + /// Operational transformation is carried out between the specified + /// changeset and all causally unrelated changesets in the local history. A + /// changeset in the local history is causally unrelated if, and only if it + /// occurs after the local changeset that produced + /// `remote_changeset.last_integrated_local_version` and is not a produced + /// by integration of a changeset received from P. This assumes that + /// `remote_changeset.last_integrated_local_version` is set to the local + /// version produced by the last local changeset, that was integrated by P + /// before P produced the specified changeset. + /// + /// The operational transformation is reciprocal (two-way), so it also + /// transforms the causally unrelated local changesets. This process does + /// not modify the history itself (the changesets available through + /// TransformHistory::get_history_entry()), instead the reciprocally + /// transformed changesets are stored separately, and individually for each + /// remote peer, such that they can participate in transformation of the + /// next incoming changeset from P. Note that the peer identifier of P can + /// be derived from `origin_client_file_ident` and information about whether + /// the local peer is a server or a client. + /// + /// In general, if A and B are two causally unrelated (alternative) + /// changesets based on the same version V, then the operational + /// transformation between A and B produces changesets A' and B' such that + /// both of the concatenated changesets A + B' and B + A' produce the same + /// final state when applied to V. Operational transformation is meaningful + /// only when carried out between alternative changesets based on the same + /// version. + /// + /// \return The size of the transformed version of the specified + /// changeset. Upon return, the changeset itself is stored in the specified + /// output buffer. + /// + /// \throw TransformError Thrown if operational transformation fails due to + /// a problem with the specified changeset. + virtual size_t transform_remote_changeset(TransformHistory&, + version_type current_local_version, + RemoteChangeset changeset, + util::Buffer& output_buffer) = 0; + + virtual ~Transformer() noexcept {} +}; + + +/// \param local_client_file_ident The server assigned local client file +/// identifier. This must be zero on the server-side, and only on the +/// server-side. +std::unique_ptr make_transformer(Transformer::file_ident_type local_client_file_ident); + + + + +// Implementation + +inline bool TransformHistory::register_local_time(timestamp_type local_timestamp, + timestamp_type& timestamp_threshold) noexcept +{ + // Needed to ensure causal consistency. This also guards against + // nonmonotonic local time. + if (timestamp_threshold < local_timestamp) { + timestamp_threshold = local_timestamp; + return true; + } + return false; +} + +inline bool TransformHistory::register_remote_time(timestamp_type remote_timestamp, + timestamp_type& timestamp_threshold) noexcept +{ + // To ensure causal consistency, we need to know the latest remote (or + // local) timestamp seen so far. Adding one to the incoming remote + // timestamp, before using it to bump the `timestamp_threshold`, is a + // simple way of ensuring not only proper ordering among timestamps, but + // also among 'effective timestamps' (which is required), regardless of the + // values of the assiciated client file identifiers. + if (timestamp_threshold < remote_timestamp + 1) { + timestamp_threshold = remote_timestamp + 1; + return true; + } + return false; +} + +class TransformError: public std::runtime_error { +public: + TransformError(const std::string& message): + std::runtime_error(message) + { + } +}; + +inline Transformer::RemoteChangeset::RemoteChangeset(version_type rv, version_type lv, + BinaryData d, timestamp_type ot, + file_ident_type fi): + remote_version(rv), + last_integrated_local_version(lv), + data(d), + origin_timestamp(ot), + origin_client_file_ident(fi) +{ +} + +} // namespace sync +} // namespace realm + +#endif // REALM_SYNC_TRANSFORM_HPP diff --git a/Pods/Realm/include/core/realm/sync/version.hpp b/Pods/Realm/include/core/realm/sync/version.hpp new file mode 100644 index 0000000..04c787f --- /dev/null +++ b/Pods/Realm/include/core/realm/sync/version.hpp @@ -0,0 +1,34 @@ +/************************************************************************* + * + * REALM CONFIDENTIAL + * __________________ + * + * [2011] - [2013] Realm Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of Realm Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to Realm Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from Realm Incorporated. + * + **************************************************************************/ +#ifndef REALM_SYNC_VERSION_HPP +#define REALM_SYNC_VERSION_HPP + +#include + +#define REALM_SYNC_VER_MAJOR 1 +#define REALM_SYNC_VER_MINOR 0 +#define REALM_SYNC_VER_PATCH 0-BETA-2.0 +#define REALM_SYNC_PRODUCT_NAME "realm-sync" + +#define REALM_SYNC_VER_STRING REALM_QUOTE(REALM_SYNC_VER_MAJOR) "." \ + REALM_QUOTE(REALM_SYNC_VER_MINOR) "." REALM_QUOTE(REALM_SYNC_VER_PATCH) +#define REALM_SYNC_VER_CHUNK "[" REALM_SYNC_PRODUCT_NAME "-" REALM_SYNC_VER_STRING "]" + +#endif // REALM_SYNC_VERSION_HPP diff --git a/Pods/Realm/include/core/realm/table.hpp b/Pods/Realm/include/core/realm/table.hpp new file mode 100644 index 0000000..01c44ac --- /dev/null +++ b/Pods/Realm/include/core/realm/table.hpp @@ -0,0 +1,2439 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_TABLE_HPP +#define REALM_TABLE_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace realm { + +class BacklinkColumn; +class BinaryColumy; +class ConstTableView; +class Group; +class LinkColumn; +class LinkColumnBase; +class LinkListColumn; +class LinkView; +class SortDescriptor; +class StringIndex; +class TableView; +class TableViewBase; +class TimestampColumn; +template +class Columns; +template +class SubQuery; +struct LinkTargetInfo; + +struct Link { +}; +typedef Link LinkList; +typedef Link BackLink; + +namespace _impl { +class TableFriend; +} + +class Replication; + + +/// The Table class is non-polymorphic, that is, it has no virtual +/// functions. This is important because it ensures that there is no run-time +/// distinction between a Table instance and an instance of any variation of +/// BasicTable, and this, in turn, makes it valid to cast a pointer from +/// Table to BasicTable even when the instance is constructed as a Table. Of +/// course, this also assumes that BasicTable<> is non-polymorphic, has no +/// destructor, and adds no extra data members. +/// +/// FIXME: Table assignment (from any group to any group) could be made aliasing +/// safe as follows: Start by cloning source table into target allocator. On +/// success, assign, and then deallocate any previous structure at the target. +/// +/// FIXME: It might be desirable to have a 'table move' feature between two +/// places inside the same group (say from a subtable or a mixed column to group +/// level). This could be done in a very efficient manner. +/// +/// FIXME: When compiling in debug mode, all public non-static table functions +/// should REALM_ASSERT(is_attached()). +class Table { +public: + /// Construct a new freestanding top-level table with static + /// lifetime. + /// + /// This constructor should be used only when placing a table + /// instance on the stack, and it is then the responsibility of + /// the application that there are no objects of type TableRef or + /// ConstTableRef that refer to it, or to any of its subtables, + /// when it goes out of scope. To create a top-level table with + /// dynamic lifetime, use Table::create() instead. + Table(Allocator& = Allocator::get_default()); + + /// Construct a copy of the specified table as a new freestanding + /// top-level table with static lifetime. + /// + /// This constructor should be used only when placing a table + /// instance on the stack, and it is then the responsibility of + /// the application that there are no objects of type TableRef or + /// ConstTableRef that refer to it, or to any of its subtables, + /// when it goes out of scope. To create a top-level table with + /// dynamic lifetime, use Table::copy() instead. + Table(const Table&, Allocator& = Allocator::get_default()); + + ~Table() noexcept; + + Allocator& get_alloc() const; + + /// Construct a new freestanding top-level table with dynamic lifetime. + static TableRef create(Allocator& = Allocator::get_default()); + + /// Construct a copy of the specified table as a new freestanding top-level + /// table with dynamic lifetime. + TableRef copy(Allocator& = Allocator::get_default()) const; + + /// Returns true if, and only if this accessor is currently attached to an + /// underlying table. + /// + /// A table accessor may get detached from the underlying row for various + /// reasons (see below). When it does, it no longer refers to anything, and + /// can no longer be used, except for calling is_attached(). The + /// consequences of calling other non-static functions on a detached table + /// accessor are unspecified. Table accessors obtained by calling functions in + /// the Realm API are always in the 'attached' state immediately upon + /// return from those functions. + /// + /// A table accessor of a free-standing table never becomes detached (except + /// during its eventual destruction). A group-level table accessor becomes + /// detached if the underlying table is removed from the group, or when the + /// group accessor is destroyed. A subtable accessor becomes detached if the + /// underlying subtable is removed, or if the parent table accessor is + /// detached. A table accessor does not become detached for any other reason + /// than those mentioned here. + /// + /// FIXME: High level language bindings will probably want to be able to + /// explicitely detach a group and all tables of that group if any modifying + /// operation fails (e.g. memory allocation failure) (and something similar + /// for freestanding tables) since that leaves the group in state where any + /// further access is disallowed. This way they will be able to reliably + /// intercept any attempt at accessing such a failed group. + /// + /// FIXME: The C++ documentation must state that if any modifying operation + /// on a group (incl. tables, subtables, and specs) or on a free standing + /// table (incl. subtables and specs) fails, then any further access to that + /// group (except ~Group()) or freestanding table (except ~Table()) has + /// undefined behaviour and is considered an error on behalf of the + /// application. Note that even Table::is_attached() is disallowed in this + /// case. + bool is_attached() const noexcept; + + /// Get the name of this table, if it has one. Only group-level tables have + /// names. For a table of any other kind, this function returns the empty + /// string. + StringData get_name() const noexcept; + + // Whether or not elements can be null. + bool is_nullable(size_t col_ndx) const; + + //@{ + /// Conventience functions for inspecting the dynamic table type. + /// + /// These functions behave as if they were called on the descriptor returned + /// by get_descriptor(). + size_t get_column_count() const noexcept; + DataType get_column_type(size_t column_ndx) const noexcept; + StringData get_column_name(size_t column_ndx) const noexcept; + size_t get_column_index(StringData name) const noexcept; + //@} + + //@{ + /// Convenience functions for manipulating the dynamic table type. + /// + /// These function must be called only for tables with independent dynamic + /// type. A table has independent dynamic type if the function + /// has_shared_type() returns false. A table that is a direct member of a + /// group has independent dynamic type. So does a free-standing table, and a + /// subtable in a column of type 'mixed'. All other tables have shared + /// dynamic type. The consequences of calling any of these functions for a + /// table with shared dynamic type are undefined. + /// + /// Apart from that, these functions behave as if they were called on the + /// descriptor returned by get_descriptor(). Note especially that the + /// `_link` suffixed functions must be used when inserting link-type + /// columns. + /// + /// If you need to change the shared dynamic type of the subtables in a + /// subtable column, consider using the API offered by the Descriptor class. + /// + /// \sa has_shared_type() + /// \sa get_descriptor() + + size_t add_column(DataType type, StringData name, bool nullable = false, DescriptorRef* subdesc = nullptr); + void insert_column(size_t column_ndx, DataType type, StringData name, bool nullable = false, + DescriptorRef* subdesc = nullptr); + + // Todo, these prototypes only exist for backwards compatibility. We should remove them because they are error + // prone (optional arguments and implicit bool to null-ptr conversion) + size_t add_column(DataType type, StringData name, DescriptorRef* subdesc) + { + return add_column(type, name, false, subdesc); + } + void insert_column(size_t column_ndx, DataType type, StringData name, DescriptorRef* subdesc) + { + insert_column(column_ndx, type, name, false, subdesc); + } + + size_t add_column_link(DataType type, StringData name, Table& target, LinkType link_type = link_Weak); + void insert_column_link(size_t column_ndx, DataType type, StringData name, Table& target, + LinkType link_type = link_Weak); + void remove_column(size_t column_ndx); + void rename_column(size_t column_ndx, StringData new_name); + //@} + + //@{ + + /// has_search_index() returns true if, and only if a search index has been + /// added to the specified column. Rather than throwing, it returns false if + /// the table accessor is detached or the specified index is out of range. + /// + /// add_search_index() adds a search index to the specified column of this + /// table. It has no effect if a search index has already been added to the + /// specified column (idempotency). + /// + /// remove_search_index() removes the search index from the specified column + /// of this table. It has no effect if the specified column has no search + /// index. The search index cannot be removed from the primary key of a + /// table. + /// + /// This table must be a root table; that is, it must have an independent + /// descriptor. Freestanding tables, group-level tables, and subtables in a + /// column of type 'mixed' are all examples of root tables. See add_column() + /// for more on this. + /// + /// \param column_ndx The index of a column of this table. + + bool has_search_index(size_t column_ndx) const noexcept; + void add_search_index(size_t column_ndx); + void remove_search_index(size_t column_ndx); + + //@} + + //@{ + /// Get the dynamic type descriptor for this table. + /// + /// Every table has an associated descriptor that specifies its dynamic + /// type. For simple tables, that is, tables without subtable columns, the + /// dynamic type can be inspected and modified directly using member + /// functions such as get_column_count() and add_column(). For more complex + /// tables, the type is best managed through the associated descriptor + /// object which is returned by this function. + /// + /// \sa has_shared_type() + DescriptorRef get_descriptor(); + ConstDescriptorRef get_descriptor() const; + //@} + + //@{ + /// Get the dynamic type descriptor for the column with the + /// specified index. That column must have type 'table'. + /// + /// This is merely a shorthand for calling `get_subdescriptor(column_ndx)` + /// on the descriptor returned by `get_descriptor()`. + DescriptorRef get_subdescriptor(size_t column_ndx); + ConstDescriptorRef get_subdescriptor(size_t column_ndx) const; + //@} + + //@{ + /// Get access to an arbitrarily nested dynamic type descriptor. + /// + /// The returned descriptor is the one you would get by calling + /// Descriptor::get_subdescriptor() once for each entry in the specified + /// path, starting with the descriptor returned by get_descriptor(). The + /// path is allowed to be empty. + typedef std::vector path_vec; + DescriptorRef get_subdescriptor(const path_vec& path); + ConstDescriptorRef get_subdescriptor(const path_vec& path) const; + //@} + + //@{ + /// Convenience functions for manipulating nested table types. + /// + /// These functions behave as if they were called on the descriptor returned + /// by `get_subdescriptor(path)`. These function must be called only on + /// tables with independent dynamic type. + /// + /// \return The value returned by add_subcolumn(), is the index of + /// the added column within the descriptor referenced by the + /// specified path. + /// + /// \sa Descriptor::add_column() + /// \sa has_shared_type() + size_t add_subcolumn(const path_vec& path, DataType type, StringData name); + void insert_subcolumn(const path_vec& path, size_t column_ndx, DataType type, StringData name); + void remove_subcolumn(const path_vec& path, size_t column_ndx); + void rename_subcolumn(const path_vec& path, size_t column_ndx, StringData new_name); + //@} + + /// Does this table share its type with other tables? + /// + /// Tables that are direct members of groups have independent + /// dynamic types. The same is true for free-standing tables and + /// subtables in coulmns of type 'mixed'. For such tables, this + /// function returns false. + /// + /// When a table has a column of type 'table', the cells in that + /// column contain subtables. All those subtables have the same + /// dynamic type, and they share a single type descriptor. For all + /// such subtables, this function returns true. See + /// Descriptor::is_root() for more on this. + /// + /// Please note that Table functions that modify the dynamic type + /// directly, such as add_column(), are only allowed to be used on + /// tables with non-shared type. If you need to modify a shared + /// type, you will have to do that through the descriptor returned + /// by get_descriptor(), but note that it will then affect all the + /// tables sharing that descriptor. + /// + /// \sa get_descriptor() + /// \sa Descriptor::is_root() + bool has_shared_type() const noexcept; + + + template + Columns column(size_t column); // FIXME: Should this one have been declared noexcept? + template + Columns column(const Table& origin, size_t origin_column_ndx); + + template + SubQuery column(size_t column, Query subquery); + template + SubQuery column(const Table& origin, size_t origin_column_ndx, Query subquery); + + // Table size and deletion + bool is_empty() const noexcept; + size_t size() const noexcept; + + typedef BasicRowExpr
RowExpr; + typedef BasicRowExpr ConstRowExpr; + + RowExpr get(size_t row_ndx) noexcept; + ConstRowExpr get(size_t row_ndx) const noexcept; + + RowExpr front() noexcept; + ConstRowExpr front() const noexcept; + + RowExpr back() noexcept; + ConstRowExpr back() const noexcept; + + RowExpr operator[](size_t row_ndx) noexcept; + ConstRowExpr operator[](size_t row_ndx) const noexcept; + + + //@{ + + /// Row handling. + /// + /// remove() removes the specified row from the table and shifts all rows at + /// higher index to fill the vacated slot. This operation assumes that the + /// table is ordered, and it is therefore allowed only on tables **without** + /// link columns, as link columns are only allowed in unordered tables. + /// + /// move_last_over() removes the specified row from the table, and if it is + /// not the last row in the table, it then moves the last row into the + /// vacated slot. This operation assumes that the table is unordered, and it + /// may therfore be used on tables with link columns. + /// + /// The removal of a row from an unordered table (move_last_over()) may + /// cause other linked rows to be cascade-removed. The clearing of a table + /// may also cause linked rows to be cascade-removed, but in this respect, + /// the effect is exactly as if each row had been removed individually. See + /// Descriptor::set_link_type() for details. + + size_t add_empty_row(size_t num_rows = 1); + void insert_empty_row(size_t row_ndx, size_t num_rows = 1); + void remove(size_t row_ndx); + void remove_last(); + void move_last_over(size_t row_ndx); + void clear(); + void swap_rows(size_t row_ndx_1, size_t row_ndx_2); + //@} + + /// Replaces all links to \a row_ndx with links to \a new_row_ndx. + /// + /// This operation is usually followed by Table::move_last_over() + /// as part of Table::set_int_unique() or Table::set_string_unique() + /// or Table::set_null_unique() detecting a collision. + /// + /// \sa Table::move_last_over() + /// \sa Table::set_int_unique() + /// \sa Table::set_string_unique() + /// \sa Table::set_null_unique() + void merge_rows(size_t row_ndx, size_t new_row_ndx); + + // Get cell values. Will assert if the requested type does not match the column type + int64_t get_int(size_t column_ndx, size_t row_ndx) const noexcept; + bool get_bool(size_t column_ndx, size_t row_ndx) const noexcept; + OldDateTime get_olddatetime(size_t column_ndx, size_t row_ndx) const noexcept; + float get_float(size_t column_ndx, size_t row_ndx) const noexcept; + double get_double(size_t column_ndx, size_t row_ndx) const noexcept; + StringData get_string(size_t column_ndx, size_t row_ndx) const noexcept; + BinaryData get_binary(size_t column_ndx, size_t row_ndx) const noexcept; + Mixed get_mixed(size_t column_ndx, size_t row_ndx) const noexcept; + DataType get_mixed_type(size_t column_ndx, size_t row_ndx) const noexcept; + Timestamp get_timestamp(size_t column_ndx, size_t row_ndx) const noexcept; + + template + T get(size_t c, size_t r) const noexcept; + + size_t get_link(size_t column_ndx, size_t row_ndx) const noexcept; + bool is_null_link(size_t column_ndx, size_t row_ndx) const noexcept; + LinkViewRef get_linklist(size_t column_ndx, size_t row_ndx); + ConstLinkViewRef get_linklist(size_t column_ndx, size_t row_ndx) const; + size_t get_link_count(size_t column_ndx, size_t row_ndx) const noexcept; + bool linklist_is_empty(size_t column_ndx, size_t row_ndx) const noexcept; + bool is_null(size_t column_ndx, size_t row_ndx) const noexcept; + + TableRef get_link_target(size_t column_ndx) noexcept; + ConstTableRef get_link_target(size_t column_ndx) const noexcept; + + template + typename T::RowAccessor get_link_accessor(size_t column_ndx, size_t row_ndx); + + //@{ + + /// Set cell values. + /// + /// It is an error to specify a column index, row index, or string position + /// that is out of range. + /// + /// The number of bytes in a string value must not exceed `max_string_size`, + /// and the number of bytes in a binary data value must not exceed + /// `max_binary_size`. String must also contain valid UTF-8 encodings. These + /// requirements also apply when modifying a string with insert_substring() + /// and remove_substring(), and for strings in a mixed columnt. Passing, or + /// producing an oversized string or binary data value will cause an + /// exception to be thrown. + /// + /// The "unique" variants (set_int_unique(), set_string_unique(), set_null_unique()) + /// are intended to be used in the implementation of primary key support. They + /// check if the given column already contains one or more values that are + /// equal to \a value, and if there are conflicts, it calls + /// Table::merge_rows() for the row_ndx to be replaced by the + /// existing row, followed by a Table::move_last_over() of row_ndx. The + /// return value is always a row index of a row that contains \a value in + /// the specified column, possibly different from \a row_ndx if a conflict + /// occurred. Users intending to implement primary keys must therefore + /// manually check for duplicates if they want to raise an error instead. + /// + /// NOTE: It is an error to call either function after adding elements to a + /// linklist in the object. In general, calling set_int_unique() or + /// set_string_unique() or set_null_unique() should be the first thing that + /// happens after creating a row. These limitations are imposed by limitations + /// in the Realm Object Server and may be relaxed in the future. A violation of + /// these rules results in a LogicError being thrown. + /// + /// add_int() adds a 64-bit signed integer to the current value of the + /// cell. If the addition would cause signed integer overflow or + /// underflow, the addition "wraps around" with semantics similar to + /// unsigned integer arithmetic, such that Table::max_integer + 1 == + /// Table::min_integer and Table::min_integer - 1 == Table::max_integer. + /// Note that the wrapping is platform-independent (all platforms wrap in + /// the same way regardless of integer representation). If the existing + /// value in the cell is null, a LogicError exception is thrown. + /// + /// insert_substring() inserts the specified string into the currently + /// stored string at the specified position. The position must be less than + /// or equal to the size of the currently stored string. + /// + /// remove_substring() removes the specified byte range from the currently + /// stored string. The beginning of the range (\a pos) must be less than or + /// equal to the size of the currently stored string. If the specified range + /// extends beyond the end of the currently stored string, it will be + /// silently clamped. + /// + /// String level modifications performed via insert_substring() and + /// remove_substring() are mergable and subject to operational + /// trsnaformation. That is, the effect of two causally unrelated + /// modifications will in general both be retained during synchronization. + + static const size_t max_string_size = 0xFFFFF8 - Array::header_size - 1; + static const size_t max_binary_size = 0xFFFFF8 - Array::header_size; + + // FIXME: These limits should be chosen independently of the underlying + // platform's choice to define int64_t and independent of the integer + // representation. The current values only work for 2's complement, which is + // not guaranteed by the standard. + static constexpr int_fast64_t max_integer = std::numeric_limits::max(); + static constexpr int_fast64_t min_integer = std::numeric_limits::min(); + + void set_int(size_t column_ndx, size_t row_ndx, int_fast64_t value, bool is_default = false); + size_t set_int_unique(size_t column_ndx, size_t row_ndx, int_fast64_t value); + void set_bool(size_t column_ndx, size_t row_ndx, bool value, bool is_default = false); + void set_olddatetime(size_t column_ndx, size_t row_ndx, OldDateTime value, bool is_default = false); + void set_timestamp(size_t column_ndx, size_t row_ndx, Timestamp value, bool is_default = false); + template + void set_enum(size_t column_ndx, size_t row_ndx, E value); + void set_float(size_t column_ndx, size_t row_ndx, float value, bool is_default = false); + void set_double(size_t column_ndx, size_t row_ndx, double value, bool is_default = false); + void set_string(size_t column_ndx, size_t row_ndx, StringData value, bool is_default = false); + size_t set_string_unique(size_t column_ndx, size_t row_ndx, StringData value); + void set_binary(size_t column_ndx, size_t row_ndx, BinaryData value, bool is_default = false); + void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value, bool is_default = false); + void set_link(size_t column_ndx, size_t row_ndx, size_t target_row_ndx, bool is_default = false); + void nullify_link(size_t column_ndx, size_t row_ndx); + void set_null(size_t column_ndx, size_t row_ndx, bool is_default = false); + void set_null_unique(size_t col_ndx, size_t row_ndx); + + void add_int(size_t column_ndx, size_t row_ndx, int_fast64_t value); + + void insert_substring(size_t col_ndx, size_t row_ndx, size_t pos, StringData); + void remove_substring(size_t col_ndx, size_t row_ndx, size_t pos, size_t substring_size = realm::npos); + + //@} + + /// Assumes that the specified column is a subtable column (in + /// particular, not a mixed column) and that the specified table + /// has a spec that is compatible with that column, that is, the + /// number of columns must be the same, and corresponding columns + /// must have identical data types (as returned by + /// get_column_type()). + void set_subtable(size_t col_ndx, size_t row_ndx, const Table*); + void set_mixed_subtable(size_t col_ndx, size_t row_ndx, const Table*); + + + // Sub-tables (works on columns whose type is either 'subtable' or + // 'mixed', for a value in a mixed column that is not a subtable, + // get_subtable() returns null, get_subtable_size() returns zero, + // and clear_subtable() replaces the value with an empty table.) + TableRef get_subtable(size_t column_ndx, size_t row_ndx); + ConstTableRef get_subtable(size_t column_ndx, size_t row_ndx) const; + size_t get_subtable_size(size_t column_ndx, size_t row_ndx) const noexcept; + void clear_subtable(size_t column_ndx, size_t row_ndx); + + // Backlinks + size_t get_backlink_count(size_t row_ndx, const Table& origin, size_t origin_col_ndx) const noexcept; + size_t get_backlink(size_t row_ndx, const Table& origin, size_t origin_col_ndx, size_t backlink_ndx) const + noexcept; + + + //@{ + + /// If this accessor is attached to a subtable, then that subtable has a + /// parent table, and the subtable either resides in a column of type + /// `table` or of type `mixed` in that parent. In that case + /// get_parent_table() returns a reference to the accessor associated with + /// the parent, and get_parent_row_index() returns the index of the row in + /// which the subtable resides. In all other cases (free-standing and + /// group-level tables), get_parent_table() returns null and + /// get_parent_row_index() returns realm::npos. + /// + /// If this accessor is attached to a subtable, and \a column_ndx_out is + /// specified, then `*column_ndx_out` is set to the index of the column of + /// the parent table in which the subtable resides. If this accessor is not + /// attached to a subtable, then `*column_ndx_out` will retain its original + /// value upon return. + + TableRef get_parent_table(size_t* column_ndx_out = nullptr) noexcept; + ConstTableRef get_parent_table(size_t* column_ndx_out = nullptr) const noexcept; + size_t get_parent_row_index() const noexcept; + + //@} + + + /// Only group-level unordered tables can be used as origins or targets of + /// links. + bool is_group_level() const noexcept; + + /// If this table is a group-level table, then this function returns the + /// index of this table within the group. Otherwise it returns realm::npos. + size_t get_index_in_group() const noexcept; + + // Aggregate functions + size_t count_int(size_t column_ndx, int64_t value) const; + size_t count_string(size_t column_ndx, StringData value) const; + size_t count_float(size_t column_ndx, float value) const; + size_t count_double(size_t column_ndx, double value) const; + + int64_t sum_int(size_t column_ndx) const; + double sum_float(size_t column_ndx) const; + double sum_double(size_t column_ndx) const; + int64_t maximum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + float maximum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + double maximum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + OldDateTime maximum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + Timestamp maximum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + int64_t minimum_int(size_t column_ndx, size_t* return_ndx = nullptr) const; + float minimum_float(size_t column_ndx, size_t* return_ndx = nullptr) const; + double minimum_double(size_t column_ndx, size_t* return_ndx = nullptr) const; + OldDateTime minimum_olddatetime(size_t column_ndx, size_t* return_ndx = nullptr) const; + Timestamp minimum_timestamp(size_t column_ndx, size_t* return_ndx = nullptr) const; + double average_int(size_t column_ndx, size_t* value_count = nullptr) const; + double average_float(size_t column_ndx, size_t* value_count = nullptr) const; + double average_double(size_t column_ndx, size_t* value_count = nullptr) const; + + // Searching + size_t find_first_link(size_t target_row_index) const; + size_t find_first_int(size_t column_ndx, int64_t value) const; + size_t find_first_bool(size_t column_ndx, bool value) const; + size_t find_first_olddatetime(size_t column_ndx, OldDateTime value) const; + size_t find_first_timestamp(size_t column_ndx, Timestamp value) const; + size_t find_first_float(size_t column_ndx, float value) const; + size_t find_first_double(size_t column_ndx, double value) const; + size_t find_first_string(size_t column_ndx, StringData value) const; + size_t find_first_binary(size_t column_ndx, BinaryData value) const; + size_t find_first_null(size_t column_ndx) const; + + TableView find_all_link(size_t target_row_index); + ConstTableView find_all_link(size_t target_row_index) const; + TableView find_all_int(size_t column_ndx, int64_t value); + ConstTableView find_all_int(size_t column_ndx, int64_t value) const; + TableView find_all_bool(size_t column_ndx, bool value); + ConstTableView find_all_bool(size_t column_ndx, bool value) const; + TableView find_all_olddatetime(size_t column_ndx, OldDateTime value); + ConstTableView find_all_olddatetime(size_t column_ndx, OldDateTime value) const; + TableView find_all_float(size_t column_ndx, float value); + ConstTableView find_all_float(size_t column_ndx, float value) const; + TableView find_all_double(size_t column_ndx, double value); + ConstTableView find_all_double(size_t column_ndx, double value) const; + TableView find_all_string(size_t column_ndx, StringData value); + ConstTableView find_all_string(size_t column_ndx, StringData value) const; + TableView find_all_binary(size_t column_ndx, BinaryData value); + ConstTableView find_all_binary(size_t column_ndx, BinaryData value) const; + TableView find_all_null(size_t column_ndx); + ConstTableView find_all_null(size_t column_ndx) const; + + /// The following column types are supported: String, Integer, OldDateTime, Bool + TableView get_distinct_view(size_t column_ndx); + ConstTableView get_distinct_view(size_t column_ndx) const; + + TableView get_sorted_view(size_t column_ndx, bool ascending = true); + ConstTableView get_sorted_view(size_t column_ndx, bool ascending = true) const; + + TableView get_sorted_view(SortDescriptor order); + ConstTableView get_sorted_view(SortDescriptor order) const; + + TableView get_range_view(size_t begin, size_t end); + ConstTableView get_range_view(size_t begin, size_t end) const; + + TableView get_backlink_view(size_t row_ndx, Table* src_table, size_t src_col_ndx); + + + // Pivot / aggregate operation types. Experimental! Please do not document method publicly. + enum AggrType { + aggr_count, + aggr_sum, + aggr_avg, + aggr_min, + aggr_max, + }; + + // Simple pivot aggregate method. Experimental! Please do not document method publicly. + void aggregate(size_t group_by_column, size_t aggr_column, AggrType op, Table& result, + const IntegerColumn* viewrefs = nullptr) const; + + /// Report the current versioning counter for the table. The versioning counter is guaranteed to + /// change when the contents of the table changes after advance_read() or promote_to_write(), or + /// immediately after calls to methods which change the table. The term "change" means "change of + /// value": The storage layout of the table may change, for example due to optimization, but this + /// is not considered a change of a value. This means that you *cannot* use a non-changing version + /// count to indicate that object addresses (e.g. strings, binary data) remain the same. + /// The versioning counter *may* change (but is not required to do so) when another table linked + /// from this table, or linking to this table, is changed. The version counter *may* also change + /// without any apparent reason. + uint_fast64_t get_version_counter() const noexcept; + +private: + template + size_t find_first(size_t column_ndx, T value) const; // called by above methods + template + TableView find_all(size_t column_ndx, T value); + +public: + //@{ + /// Find the lower/upper bound according to a column that is + /// already sorted in ascending order. + /// + /// For an integer column at index 0, and an integer value '`v`', + /// lower_bound_int(0,v) returns the index '`l`' of the first row + /// such that `get_int(0,l) ≥ v`, and upper_bound_int(0,v) + /// returns the index '`u`' of the first row such that + /// `get_int(0,u) > v`. In both cases, if no such row is found, + /// the returned value is the number of rows in the table. + /// + /// 3 3 3 4 4 4 5 6 7 9 9 9 + /// ^ ^ ^ ^ ^ + /// | | | | | + /// | | | | -- Lower and upper bound of 15 + /// | | | | + /// | | | -- Lower and upper bound of 8 + /// | | | + /// | | -- Upper bound of 4 + /// | | + /// | -- Lower bound of 4 + /// | + /// -- Lower and upper bound of 1 + /// + /// These functions are similar to std::lower_bound() and + /// std::upper_bound(). + /// + /// The string versions assume that the column is sorted according + /// to StringData::operator<(). + size_t lower_bound_int(size_t column_ndx, int64_t value) const noexcept; + size_t upper_bound_int(size_t column_ndx, int64_t value) const noexcept; + size_t lower_bound_bool(size_t column_ndx, bool value) const noexcept; + size_t upper_bound_bool(size_t column_ndx, bool value) const noexcept; + size_t lower_bound_float(size_t column_ndx, float value) const noexcept; + size_t upper_bound_float(size_t column_ndx, float value) const noexcept; + size_t lower_bound_double(size_t column_ndx, double value) const noexcept; + size_t upper_bound_double(size_t column_ndx, double value) const noexcept; + size_t lower_bound_string(size_t column_ndx, StringData value) const noexcept; + size_t upper_bound_string(size_t column_ndx, StringData value) const noexcept; + //@} + + // Queries + // Using where(tv) is the new method to perform queries on TableView. The 'tv' can have any order; it does not + // need to be sorted, and, resulting view retains its order. + Query where(TableViewBase* tv = nullptr) + { + return Query(*this, tv); + } + + // FIXME: We need a ConstQuery class or runtime check against modifications in read transaction. + Query where(TableViewBase* tv = nullptr) const + { + return Query(*this, tv); + } + + // Perform queries on a LinkView. The returned Query holds a reference to lv. + Query where(const LinkViewRef& lv) + { + return Query(*this, lv); + } + + Table& link(size_t link_column); + Table& backlink(const Table& origin, size_t origin_col_ndx); + + // Optimizing. enforce == true will enforce enumeration of all string columns; + // enforce == false will auto-evaluate if they should be enumerated or not + void optimize(bool enforce = false); + + /// Write this table (or a slice of this table) to the specified + /// output stream. + /// + /// The output will have the same format as any other Realm + /// database file, such as those produced by Group::write(). In + /// this case, however, the resulting database file will contain + /// exactly one table, and that table will contain only the + /// specified slice of the source table (this table). + /// + /// The new table will always have the same dynamic type (see + /// Descriptor) as the source table (this table), and unless it is + /// overridden (\a override_table_name), the new table will have + /// the same name as the source table (see get_name()). Indexes + /// (see add_search_index()) will not be carried over to the new + /// table. + /// + /// \param out The destination output stream buffer. + /// + /// \param offset Index of first row to include (if `slice_size > + /// 0`). Must be less than, or equal to size(). + /// + /// \param slice_size Number of rows to include. May be zero. If + /// `slice_size > size() - offset`, then the effective size of + /// the written slice will be `size() - offset`. + /// + /// \param override_table_name Custom name to write out instead of + /// the actual table name. + /// + /// \throw std::out_of_range If `offset > size()`. + /// + /// FIXME: While this function does provided a maximally efficient + /// way of serializing part of a table, it offers little in terms + /// of general utility. This is unfortunate, because it pulls + /// quite a large amount of code into the core library to support + /// it. + void write(std::ostream& out, size_t offset = 0, size_t slice_size = npos, + StringData override_table_name = StringData()) const; + + // Conversion + void to_json(std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const; + void to_string(std::ostream& out, size_t limit = 500) const; + void row_to_string(size_t row_ndx, std::ostream& out) const; + + // Get a reference to this table + TableRef get_table_ref() + { + return TableRef(this); + } + ConstTableRef get_table_ref() const + { + return ConstTableRef(this); + } + + /// \brief Compare two tables for equality. + /// + /// Two tables are equal if they have equal descriptors + /// (`Descriptor::operator==()`) and equal contents. Equal descriptors imply + /// that the two tables have the same columns in the same order. Equal + /// contents means that the two tables must have the same number of rows, + /// and that for each row index, the two rows must have the same values in + /// each column. + /// + /// In mixed columns, both the value types and the values are required to be + /// equal. + /// + /// For a particular row and column, if the two values are themselves tables + /// (subtable and mixed columns) value equality implies a recursive + /// invocation of `Table::operator==()`. + bool operator==(const Table&) const; + + /// \brief Compare two tables for inequality. + /// + /// See operator==(). + bool operator!=(const Table& t) const; + + /// A subtable in a column of type 'table' (which shares descriptor with + /// other subtables in the same column) is initially in a degenerate state + /// where it takes up a minimal amout of space. This function returns true + /// if, and only if the table accessor is attached to such a subtable. This + /// function is mainly intended for debugging purposes. + bool is_degenerate() const noexcept; + + // Debug + void verify() const; +#ifdef REALM_DEBUG + void to_dot(std::ostream&, StringData title = StringData()) const; + void print() const; + MemStats stats() const; + void dump_node_structure() const; // To std::cerr (for GDB) + void dump_node_structure(std::ostream&, int level) const; +#endif + + class Parent; + using HandoverPatch = TableHandoverPatch; + static void generate_patch(const Table* ref, std::unique_ptr& patch); + static TableRef create_from_and_consume_patch(std::unique_ptr& patch, Group& group); + +protected: + /// Get a pointer to the accessor of the specified subtable. The + /// accessor will be created if it does not already exist. + /// + /// The returned table pointer must **always** end up being + /// wrapped in some instantiation of BasicTableRef<>. + Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx); + + /// See non-const get_subtable_ptr(). + const Table* get_subtable_ptr(size_t col_ndx, size_t row_ndx) const; + + /// Compare the rows of two tables under the assumption that the two tables + /// have the same number of columns, and the same data type at each column + /// index (as expressed through the DataType enum). + bool compare_rows(const Table&) const; + + void set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const; + + void check_lists_are_empty(size_t row_ndx) const; + +private: + class SliceWriter; + + // Number of rows in this table + size_t m_size; + + // Underlying array structure. `m_top` is in use only for root tables; that + // is, for tables with independent descriptor. `m_columns` contains a ref + // for each column and search index in order of the columns. A search index + // ref always occurs immediately after the ref of the column to which the + // search index belongs. + // + // A subtable column (a column of type `type_table`) is essentially just a + // column of 'refs' pointing to the root node of each subtable. + // + // To save space in the database file, a subtable in such a column always + // starts out in a degenerate form where nothing is allocated on its behalf, + // and a null 'ref' is stored in the corresponding slot of the column. A + // subtable remains in this degenerate state until the first row is added to + // the subtable. + // + // For this scheme to work, it must be (and is) possible to create a table + // accessor that refers to a degenerate subtable. A table accessor (instance + // of `Table`) refers to a degenerate subtable if, and only if `m_columns` + // is unattached. + // + // FIXME: The fact that `m_columns` may be detached means that many + // functions (even non-modifying functions) need to check for that before + // accessing the contents of the table. This incurs a runtime + // overhead. Consider whether this overhead can be eliminated by having + // `Table::m_columns` always attached to something, and then detect the + // degenerate state in a different way. + Array m_top; + Array m_columns; // 2nd slot in m_top (for root tables) + Spec m_spec; // 1st slot in m_top (for root tables) + + // Is guaranteed to be empty for a detached accessor. Otherwise it is empty + // when the table accessor is attached to a degenerate subtable (unattached + // `m_columns`), otherwise it contains precisely one column accessor for + // each column in the table, in order. + // + // In some cases an entry may be null. This is currently possible only in + // connection with Group::advance_transact(), but it means that several + // member functions must be prepared to handle these null entries; in + // particular, detach(), ~Table(), functions called on behalf of detach() + // and ~Table(), and functiones called on behalf of + // Group::advance_transact(). + typedef std::vector column_accessors; + column_accessors m_cols; + + mutable std::atomic m_ref_count; + + // If this table is a root table (has independent descriptor), + // then Table::m_descriptor refers to the accessor of its + // descriptor when, and only when the descriptor accessor + // exists. This is used to ensure that at most one descriptor + // accessor exists for each underlying descriptor at any given + // point in time. Subdescriptors are kept unique by means of a + // registry in the parent descriptor. Table::m_descriptor is + // always null for tables with shared descriptor. + mutable std::weak_ptr m_descriptor; + + // Table view instances + // Access needs to be protected by m_accessor_mutex + typedef std::vector views; + mutable views m_views; + + // Points to first bound row accessor, or is null if there are none. + mutable RowBase* m_row_accessors = nullptr; + + // Mutex which must be locked any time the row accessor chain or m_views is used + mutable util::Mutex m_accessor_mutex; + + // Used for queries: Items are added with link() method during buildup of query + mutable std::vector m_link_chain; + + /// Used only in connection with Group::advance_transact() and + /// Table::refresh_accessor_tree(). + mutable bool m_mark; + + mutable uint_fast64_t m_version; + + void erase_row(size_t row_ndx, bool is_move_last_over); + void batch_erase_rows(const IntegerColumn& row_indexes, bool is_move_last_over); + void do_remove(size_t row_ndx, bool broken_reciprocal_backlinks); + void do_move_last_over(size_t row_ndx, bool broken_reciprocal_backlinks); + void do_swap_rows(size_t row_ndx_1, size_t row_ndx_2); + void do_merge_rows(size_t row_ndx, size_t new_row_ndx); + void do_clear(bool broken_reciprocal_backlinks); + size_t do_set_link(size_t col_ndx, size_t row_ndx, size_t target_row_ndx); + template + size_t do_find_unique(ColType& col, size_t ndx, T&& value, bool& conflict); + template + size_t do_set_unique_null(ColType& col, size_t ndx, bool& conflict); + template + size_t do_set_unique(ColType& column, size_t row_ndx, T&& value, bool& conflict); + + void upgrade_file_format(size_t target_file_format_version); + + // Upgrades OldDateTime columns to Timestamp columns + void upgrade_olddatetime(); + + /// Update the version of this table and all tables which have links to it. + /// This causes all views referring to those tables to go out of sync, so that + /// calls to sync_if_needed() will bring the view up to date by reexecuting the + /// query. + /// + /// \param bump_global chooses whether the global versioning counter must be + /// bumped first as part of the update. This is the normal mode of operation, + /// when a change is made to the table. When calling recursively (following links + /// or going to the parent table), the parameter should be set to false to correctly + /// prune traversal. + void bump_version(bool bump_global = true) const noexcept; + + /// Disable copying assignment. + /// + /// It could easily be implemented by calling assign(), but the + /// non-checking nature of the low-level dynamically typed API + /// makes it too risky to offer this feature as an + /// operator. + /// + /// FIXME: assign() has not yet been implemented, but the + /// intention is that it will copy the rows of the argument table + /// into this table after clearing the original contents, and for + /// target tables without a shared spec, it would also copy the + /// spec. For target tables with shared spec, it would be an error + /// to pass an argument table with an incompatible spec, but + /// assign() would not check for spec compatibility. This would + /// make it ideal as a basis for implementing operator=() for + /// typed tables. + Table& operator=(const Table&); + + /// Used when constructing an accessor whose lifetime is going to be managed + /// by reference counting. The lifetime of accessors of free-standing tables + /// allocated on the stack by the application is not managed by reference + /// counting, so that is a case where this tag must **not** be specified. + class ref_count_tag { + }; + + /// Create an uninitialized accessor whose lifetime is managed by reference + /// counting. + Table(ref_count_tag, Allocator&); + + void init(ref_type top_ref, ArrayParent*, size_t ndx_in_parent, bool skip_create_column_accessors = false); + void init(ConstSubspecRef shared_spec, ArrayParent* parent_column, size_t parent_row_ndx); + + static void do_insert_column(Descriptor&, size_t col_ndx, DataType type, StringData name, + LinkTargetInfo& link_target_info, bool nullable = false); + static void do_insert_column_unless_exists(Descriptor&, size_t col_ndx, DataType type, StringData name, + LinkTargetInfo& link, bool nullable = false, + bool* was_inserted = nullptr); + static void do_erase_column(Descriptor&, size_t col_ndx); + static void do_rename_column(Descriptor&, size_t col_ndx, StringData name); + static void do_move_column(Descriptor&, size_t col_ndx_1, size_t col_ndx_2); + + struct InsertSubtableColumns; + struct EraseSubtableColumns; + struct RenameSubtableColumns; + struct MoveSubtableColumns; + + void insert_root_column(size_t col_ndx, DataType type, StringData name, LinkTargetInfo& link_target, + bool nullable = false); + void erase_root_column(size_t col_ndx); + void move_root_column(size_t from, size_t to); + void do_insert_root_column(size_t col_ndx, ColumnType, StringData name, bool nullable = false); + void do_erase_root_column(size_t col_ndx); + void do_move_root_column(size_t from, size_t to); + void do_set_link_type(size_t col_ndx, LinkType); + void insert_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx, size_t backlink_col_ndx); + void erase_backlink_column(size_t origin_table_ndx, size_t origin_col_ndx); + void update_link_target_tables(size_t old_col_ndx_begin, size_t new_col_ndx_begin); + void update_link_target_tables_after_column_move(size_t moved_from, size_t moved_to); + + struct SubtableUpdater { + virtual void update(const SubtableColumn&, Array& subcolumns) = 0; + virtual void update_accessor(Table&) = 0; + virtual ~SubtableUpdater() + { + } + }; + static void update_subtables(Descriptor&, SubtableUpdater*); + void update_subtables(const size_t* col_path_begin, const size_t* col_path_end, SubtableUpdater*); + + struct AccessorUpdater { + virtual void update(Table&) = 0; + virtual void update_parent(Table&) = 0; + virtual ~AccessorUpdater() + { + } + }; + void update_accessors(const size_t* col_path_begin, const size_t* col_path_end, AccessorUpdater&); + + void create_degen_subtab_columns(); + ColumnBase* create_column_accessor(ColumnType, size_t col_ndx, size_t ndx_in_parent); + void destroy_column_accessors() noexcept; + + /// Called in the context of Group::commit() to ensure that + /// attached table accessors stay valid across a commit. Please + /// note that this works only for non-transactional commits. Table + /// accessors obtained during a transaction are always detached + /// when the transaction ends. + void update_from_parent(size_t old_baseline) noexcept; + + // Support function for conversions + void to_string_header(std::ostream& out, std::vector& widths) const; + void to_string_row(size_t row_ndx, std::ostream& out, const std::vector& widths) const; + + // recursive methods called by to_json, to follow links + void to_json(std::ostream& out, size_t link_depth, std::map& renames, + std::vector& followed) const; + void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth, + std::map& renames, std::vector& followed) const; + void to_json_row(size_t row_ndx, std::ostream& out, size_t link_depth = 0, + std::map* renames = nullptr) const; + + // Detach accessor from underlying table. Caller must ensure that + // a reference count exists upon return, for example by obtaining + // an extra reference count before the call. + // + // This function puts this table accessor into the detached + // state. This detaches it from the underlying structure of array + // nodes. It also recursively detaches accessors for subtables, + // and the type descriptor accessor. When this function returns, + // is_attached() will return false. + // + // This function may be called for a table accessor that is + // already in the detached state (idempotency). + // + // It is also valid to call this function for a table accessor + // that has not yet been detached, but whose underlying structure + // of arrays have changed in an unpredictable/unknown way. This + // kind of change generally happens when a modifying table + // operation fails, and also when one transaction is ended and a + // new one is started. + void detach() noexcept; + + /// Detach and remove all attached row, link list, and subtable + /// accessors. This function does not discard the descriptor accessor, if + /// any, and it does not discard column accessors either. + void discard_child_accessors() noexcept; + + void discard_row_accessors() noexcept; + + // Detach the type descriptor accessor if it exists. + void discard_desc_accessor() noexcept; + + void bind_ptr() const noexcept; + void unbind_ptr() const noexcept; + + void register_view(const TableViewBase* view); + void unregister_view(const TableViewBase* view) noexcept; + void move_registered_view(const TableViewBase* old_addr, const TableViewBase* new_addr) noexcept; + void discard_views() noexcept; + + void register_row_accessor(RowBase*) const noexcept; + void unregister_row_accessor(RowBase*) const noexcept; + void do_unregister_row_accessor(RowBase*) const noexcept; + + class UnbindGuard; + + ColumnType get_real_column_type(size_t column_ndx) const noexcept; + + /// If this table is a group-level table, the parent group is returned, + /// otherwise null is returned. + Group* get_parent_group() const noexcept; + + const ColumnBase& get_column_base(size_t column_ndx) const noexcept; + ColumnBase& get_column_base(size_t column_ndx); + + const ColumnBaseWithIndex& get_column_base_indexed(size_t ndx) const noexcept; + ColumnBaseWithIndex& get_column_base_indexed(size_t ndx); + + template + T& get_column(size_t ndx); + + template + const T& get_column(size_t ndx) const noexcept; + + IntegerColumn& get_column(size_t column_ndx); + const IntegerColumn& get_column(size_t column_ndx) const noexcept; + IntNullColumn& get_column_int_null(size_t column_ndx); + const IntNullColumn& get_column_int_null(size_t column_ndx) const noexcept; + FloatColumn& get_column_float(size_t column_ndx); + const FloatColumn& get_column_float(size_t column_ndx) const noexcept; + DoubleColumn& get_column_double(size_t column_ndx); + const DoubleColumn& get_column_double(size_t column_ndx) const noexcept; + StringColumn& get_column_string(size_t column_ndx); + const StringColumn& get_column_string(size_t column_ndx) const noexcept; + BinaryColumn& get_column_binary(size_t column_ndx); + const BinaryColumn& get_column_binary(size_t column_ndx) const noexcept; + StringEnumColumn& get_column_string_enum(size_t column_ndx); + const StringEnumColumn& get_column_string_enum(size_t column_ndx) const noexcept; + SubtableColumn& get_column_table(size_t column_ndx); + const SubtableColumn& get_column_table(size_t column_ndx) const noexcept; + MixedColumn& get_column_mixed(size_t column_ndx); + const MixedColumn& get_column_mixed(size_t column_ndx) const noexcept; + TimestampColumn& get_column_timestamp(size_t column_ndx); + const TimestampColumn& get_column_timestamp(size_t column_ndx) const noexcept; + const LinkColumnBase& get_column_link_base(size_t ndx) const noexcept; + LinkColumnBase& get_column_link_base(size_t ndx); + const LinkColumn& get_column_link(size_t ndx) const noexcept; + LinkColumn& get_column_link(size_t ndx); + const LinkListColumn& get_column_link_list(size_t ndx) const noexcept; + LinkListColumn& get_column_link_list(size_t ndx); + const BacklinkColumn& get_column_backlink(size_t ndx) const noexcept; + BacklinkColumn& get_column_backlink(size_t ndx); + + void instantiate_before_change(); + void validate_column_type(const ColumnBase& col, ColumnType expected_type, size_t ndx) const; + + static size_t get_size_from_ref(ref_type top_ref, Allocator&) noexcept; + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator&) noexcept; + + const Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) const noexcept; + Table* get_parent_table_ptr(size_t* column_ndx_out = nullptr) noexcept; + + /// Create an empty table with independent spec and return just + /// the reference to the underlying memory. + static ref_type create_empty_table(Allocator&); + + /// Create a column of the specified type, fill it with the + /// specified number of default values, and return just the + /// reference to the underlying memory. + static ref_type create_column(ColumnType column_type, size_t num_default_values, bool nullable, Allocator&); + + /// Construct a copy of the columns array of this table using the + /// specified allocator and return just the ref to that array. + /// + /// In the clone, no string column will be of the enumeration + /// type. + ref_type clone_columns(Allocator&) const; + + /// Construct a complete copy of this table (including its spec) + /// using the specified allocator and return just the ref to the + /// new top array. + ref_type clone(Allocator&) const; + + /// True for `col_type_Link` and `col_type_LinkList`. + static bool is_link_type(ColumnType) noexcept; + + void connect_opposite_link_columns(size_t link_col_ndx, Table& target_table, size_t backlink_col_ndx) noexcept; + + size_t get_num_strong_backlinks(size_t row_ndx) const noexcept; + + //@{ + + /// Cascading removal of strong links. + /// + /// cascade_break_backlinks_to() removes all backlinks pointing to the row + /// at \a row_ndx. Additionally, if this causes the number of **strong** + /// backlinks originating from a particular opposite row (target row of + /// corresponding forward link) to drop to zero, and that row is not already + /// in \a state.rows, then that row is added to \a state.rows, and + /// cascade_break_backlinks_to() is called recursively for it. This + /// operation is the first half of the cascading row removal operation. The + /// second half is performed by passing the resulting contents of \a + /// state.rows to remove_backlink_broken_rows(). + /// + /// Operations that trigger cascading row removal due to explicit removal of + /// one or more rows (the *initiating rows*), should add those rows to \a + /// rows initially, and then call cascade_break_backlinks_to() once for each + /// of them in turn. This is opposed to carrying out the explicit row + /// removals independently, which is also possible, but does require that + /// any initiating rows, that end up in \a state.rows due to link cycles, + /// are removed before passing \a state.rows to + /// remove_backlink_broken_rows(). In the case of clear(), where all rows of + /// a table are explicitly removed, it is better to use + /// cascade_break_backlinks_to_all_rows(), and then carry out the table + /// clearing as an independent step. For operations that trigger cascading + /// row removal for other reasons than explicit row removal, \a state.rows + /// must be empty initially, but cascade_break_backlinks_to() must still be + /// called for each of the initiating rows. + /// + /// When the last non-recursive invocation of cascade_break_backlinks_to() + /// returns, all forward links originating from a row in \a state.rows have + /// had their reciprocal backlinks removed, so remove_backlink_broken_rows() + /// does not perform reciprocal backlink removal at all. Additionally, all + /// remaining backlinks originating from rows in \a state.rows are + /// guaranteed to point to rows that are **not** in \a state.rows. This is + /// true because any backlink that was pointing to a row in \a state.rows + /// has been removed by one of the invocations of + /// cascade_break_backlinks_to(). The set of forward links, that correspond + /// to these remaining backlinks, is precisely the set of forward links that + /// need to be removed/nullified by remove_backlink_broken_rows(), which it + /// does by way of reciprocal forward link removal. Note also, that while + /// all the rows in \a state.rows can have remaining **weak** backlinks + /// originating from them, only the initiating rows in \a state.rows can + /// have remaining **strong** backlinks originating from them. This is true + /// because a non-initiating row is added to \a state.rows only when the + /// last backlink originating from it is lost. + /// + /// Each row removal is replicated individually (as opposed to one + /// replication instruction for the entire cascading operation). This is + /// done because it provides an easy way for Group::advance_transact() to + /// know which tables are affected by the cascade. Note that this has + /// several important consequences: First of all, the replication log + /// receiver must execute the row removal instructions in a non-cascading + /// fashion, meaning that there will be an asymmetry between the two sides + /// in how the effect of the cascade is brought about. While this is fine + /// for simple 1-to-1 replication, it may end up interfering badly with + /// *transaction merging*, when that feature is introduced. Imagine for + /// example that the cascade initiating operation gets canceled during + /// conflict resolution, but some, or all of the induced row removals get to + /// stay. That would break causal consistency. It is important, however, for + /// transaction merging that the cascaded row removals are explicitly + /// mentioned in the replication log, such that they can be used to adjust + /// row indexes during the *operational transform*. + /// + /// cascade_break_backlinks_to_all_rows() has the same affect as calling + /// cascade_break_backlinks_to() once for each row in the table. When + /// calling this function, \a state.stop_on_table must be set to the origin + /// table (origin table of corresponding forward links), and \a + /// state.stop_on_link_list_column must be null. + /// + /// It is immaterial which table remove_backlink_broken_rows() is called on, + /// as long it that table is in the same group as the removed rows. + + void cascade_break_backlinks_to(size_t row_ndx, CascadeState& state); + void cascade_break_backlinks_to_all_rows(CascadeState& state); + void remove_backlink_broken_rows(const CascadeState&); + + //@} + + /// Used by query. Follows chain of link columns and returns final target table + const Table* get_link_chain_target(const std::vector& link_chain) const; + + /// Remove the specified row by the 'move last over' method. + void do_move_last_over(size_t row_ndx); + + // Precondition: 1 <= end - begin + size_t* record_subtable_path(size_t* begin, size_t* end) const noexcept; + + /// Check if an accessor exists for the specified subtable. If it does, + /// return a pointer to it, otherwise return null. This function assumes + /// that the specified column index in a valid index into `m_cols` but does + /// not otherwise assume more than minimal accessor consistency (see + /// AccessorConsistencyLevels.) + Table* get_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; + + /// Unless the column accessor is missing, this function returns the + /// accessor for the target table of the specified link-type column. The + /// column accessor is said to be missing if `m_cols[col_ndx]` is null, and + /// this can happen only during certain operations such as the updating of + /// the accessor tree when a read transaction is advanced. Note that for + /// link type columns, the target table accessor exists when, and only when + /// the origin table accessor exists. This function assumes that the + /// specified column index in a valid index into `m_cols` and that the + /// column is a link-type column. Beyond that, it assume nothing more than + /// minimal accessor consistency (see AccessorConsistencyLevels.) + Table* get_link_target_table_accessor(size_t col_ndx) noexcept; + + void discard_subtable_accessor(size_t col_ndx, size_t row_ndx) noexcept; + + void adj_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + void adj_acc_erase_row(size_t row_ndx) noexcept; + void adj_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + + /// Adjust this table accessor and its subordinates after move_last_over() + /// (or its inverse). + /// + /// First, any row, subtable, or link list accessors registered as being at + /// \a to_row_ndx will be detached, as that row is assumed to have been + /// replaced. Next, any row, subtable, or link list accessors registered as + /// being at \a from_row_ndx, will be reregistered as being at \a + /// to_row_ndx, as the row at \a from_row_ndx is assumed to have been moved + /// to \a to_row_ndx. + /// + /// Crucially, if \a to_row_ndx is equal to \a from_row_ndx, then row, + /// subtable, or link list accessors at that row are **still detached**. + /// + /// Additionally, this function causes all link-adjacent tables to be marked + /// (dirty). Two tables are link-adjacent if one is the target table of a + /// link column of the other table. Note that this marking follows these + /// relations in both directions, but only to a depth of one. + /// + /// When this function is used in connection with move_last_over(), set \a + /// to_row_ndx to the index of the row to be removed, and set \a + /// from_row_ndx to the index of the last row in the table. As mentioned + /// earlier, this function can also be used in connection with the **inverse + /// of** move_last_over(), which is an operation that vacates a row by + /// moving its contents into a new last row of the table. In that case, set + /// \a to_row_ndx to one plus the index of the last row in the table, and + /// set \a from_row_ndx to the index of the row to be vacated. + /// + /// This function is used as part of Table::refresh_accessor_tree() to + /// promote the state of the accessors from Minimal Consistency into + /// Structural Correspondence, so it must be able to execute without + /// accessing the underlying array nodes. + void adj_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + void adj_acc_clear_root_table() noexcept; + void adj_acc_clear_nonroot_table() noexcept; + void adj_row_acc_insert_rows(size_t row_ndx, size_t num_rows) noexcept; + void adj_row_acc_erase_row(size_t row_ndx) noexcept; + void adj_row_acc_swap_rows(size_t row_ndx_1, size_t row_ndx_2) noexcept; + void adj_row_acc_merge_rows(size_t old_row_ndx, size_t new_row_ndx) noexcept; + + /// Called by adj_acc_move_over() to adjust row accessors. + void adj_row_acc_move_over(size_t from_row_ndx, size_t to_row_ndx) noexcept; + + void adj_insert_column(size_t col_ndx); + void adj_erase_column(size_t col_ndx) noexcept; + void adj_move_column(size_t col_ndx_1, size_t col_ndx_2) noexcept; + + bool is_marked() const noexcept; + void mark() noexcept; + void unmark() noexcept; + void recursive_mark() noexcept; + void mark_link_target_tables(size_t col_ndx_begin) noexcept; + void mark_opposite_link_tables() noexcept; + + Replication* get_repl() noexcept; + + void set_ndx_in_parent(size_t ndx_in_parent) noexcept; + + /// Refresh the part of the accessor tree that is rooted at this + /// table. Subtable accessors will be refreshed only if they are marked + /// (Table::m_mark), and this applies recursively to subtables of + /// subtables. All refreshed table accessors (including this one) will be + /// unmarked upon return. + /// + /// The following conditions are necessary and sufficient for the proper + /// operation of this function: + /// + /// - This table must be a group-level table, or a subtable. It must not be + /// a free-standing table (because a free-standing table has no parent). + /// + /// - The `index in parent` property is correct. The `index in parent` + /// property of the table is the `index in parent` property of + /// `m_columns` for subtables with shared descriptor, and the `index in + /// parent` property of `m_top` for all other tables. + /// + /// - If this table has shared descriptor, then the `index in parent` + /// property of the contained spec accessor is correct. + /// + /// - The parent accessor is in a valid state (already refreshed). If the + /// parent is a group, then the group accessor (excluding its table + /// accessors) must be in a valid state. If the parent is a table, then + /// the table accessor (excluding its subtable accessors) must be in a + /// valid state. + /// + /// - Every descendant subtable accessor is marked if it needs to be + /// refreshed, or if it has a descendant accessor that needs to be + /// refreshed. + /// + /// - This table accessor, as well as all its descendant accessors, are in + /// structural correspondence with the underlying node hierarchy whose + /// root ref is stored in the parent (see AccessorConsistencyLevels). + void refresh_accessor_tree(); + + void refresh_column_accessors(size_t col_ndx_begin = 0); + + // Look for link columns starting from col_ndx_begin. + // If a link column is found, follow the link and update it's + // backlink column accessor if it is in different table. + void refresh_link_target_accessors(size_t col_ndx_begin = 0); + + bool is_cross_table_link_target() const noexcept; + +#ifdef REALM_DEBUG + void to_dot_internal(std::ostream&) const; +#endif + + friend class SubtableNode; + friend class _impl::TableFriend; + friend class Query; + template + friend class util::bind_ptr; + template + friend class SimpleQuerySupport; + friend class LangBindHelper; + friend class TableViewBase; + template + friend class Columns; + friend class Columns; + friend class ParentNode; + template + friend class SequentialGetter; + friend class RowBase; + friend class LinksToNode; + friend class LinkMap; + friend class LinkView; + friend class Group; +}; + + +class Table::Parent : public ArrayParent { +public: + ~Parent() noexcept override + { + } + +protected: + virtual StringData get_child_name(size_t child_ndx) const noexcept; + + /// If children are group-level tables, then this function returns the + /// group. Otherwise it returns null. + virtual Group* get_parent_group() noexcept; + + /// If children are subtables, then this function returns the + /// parent table. Otherwise it returns null. + /// + /// If \a column_ndx_out is not null, this function must assign the index of + /// the column within the parent table to `*column_ndx_out` when , and only + /// when this table parent is a column in a parent table. + virtual Table* get_parent_table(size_t* column_ndx_out = nullptr) noexcept; + + /// Must be called whenever a child table accessor is about to be destroyed. + /// + /// Note that the argument is a pointer to the child Table rather than its + /// `ndx_in_parent` property. This is because only minimal accessor + /// consistency can be assumed by this function. + virtual void child_accessor_destroyed(Table* child) noexcept = 0; + + virtual size_t* record_subtable_path(size_t* begin, size_t* end) noexcept; + + friend class Table; +}; + + +// Implementation: + + +inline uint_fast64_t Table::get_version_counter() const noexcept +{ + return m_version; +} + +inline void Table::bump_version(bool bump_global) const noexcept +{ + if (bump_global) { + // This is only set on initial entry through an operation on the same + // table. recursive calls (via parent or via backlinks) must be done + // with bump_global=false. + m_top.get_alloc().bump_global_version(); + } + if (m_top.get_alloc().should_propagate_version(m_version)) { + if (const Table* parent = get_parent_table_ptr()) + parent->bump_version(false); + // Recurse through linked tables, use m_mark to avoid infinite recursion + for (auto& column_ptr : m_cols) { + // We may meet a null pointer in place of a backlink column, pending + // replacement with a new one. This can happen ONLY when creation of + // the corresponding forward link column in the origin table is + // pending as well. In this case it is ok to just ignore the zeroed + // backlink column, because the origin table is guaranteed to also + // be refreshed/marked dirty and hence have it's version bumped. + if (column_ptr != nullptr) + column_ptr->bump_link_origin_table_version(); + } + } +} + +inline void Table::remove(size_t row_ndx) +{ + bool is_move_last_over = false; + erase_row(row_ndx, is_move_last_over); // Throws +} + +inline void Table::move_last_over(size_t row_ndx) +{ + bool is_move_last_over = true; + erase_row(row_ndx, is_move_last_over); // Throws +} + +inline void Table::remove_last() +{ + if (!is_empty()) + remove(size() - 1); +} + +// A good place to start if you want to understand the memory ordering +// chosen for the operations below is http://preshing.com/20130922/acquire-and-release-fences/ +inline void Table::bind_ptr() const noexcept +{ + m_ref_count.fetch_add(1, std::memory_order_relaxed); +} + +inline void Table::unbind_ptr() const noexcept +{ + // The delete operation runs the destructor, and the destructor + // must always see all changes to the object being deleted. + // Within each thread, we know that unbind_ptr will always happen after + // any changes, so it is a convenient place to do a release. + // The release will then be observed by the acquire fence in + // the case where delete is actually called (the count reaches 0) + if (m_ref_count.fetch_sub(1, std::memory_order_release) != 1) + return; + + std::atomic_thread_fence(std::memory_order_acquire); + delete this; +} + +inline void Table::register_view(const TableViewBase* view) +{ + util::LockGuard lock(m_accessor_mutex); + // Casting away constness here - operations done on tableviews + // through m_views are all internal and preserving "some" kind + // of logical constness. + m_views.push_back(const_cast(view)); +} + +inline bool Table::is_attached() const noexcept +{ + // Note that it is not possible to tie the state of attachment of a table to + // the state of attachment of m_top, because tables with shared spec do not + // have a 'top' array. Neither is it possible to tie it to the state of + // attachment of m_columns, because subtables with shared spec start out in + // a degenerate form where they do not have a 'columns' array. For these + // reasons, it is neccessary to define the notion of attachment for a table + // as follows: A table is attached if, and ony if m_column stores a non-null + // parent pointer. This works because even for degenerate subtables, + // m_columns is initialized with the correct parent pointer. + return m_columns.has_parent(); +} + +inline StringData Table::get_name() const noexcept +{ + REALM_ASSERT(is_attached()); + const Array& real_top = m_top.is_attached() ? m_top : m_columns; + ArrayParent* parent = real_top.get_parent(); + if (!parent) + return StringData(""); + size_t index_in_parent = real_top.get_ndx_in_parent(); + REALM_ASSERT(dynamic_cast(parent)); + return static_cast(parent)->get_child_name(index_in_parent); +} + +inline size_t Table::get_column_count() const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec.get_public_column_count(); +} + +inline StringData Table::get_column_name(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, get_column_count()); + return m_spec.get_column_name(ndx); +} + +inline size_t Table::get_column_index(StringData name) const noexcept +{ + REALM_ASSERT(is_attached()); + return m_spec.get_column_index(name); +} + +inline ColumnType Table::get_real_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); + return m_spec.get_column_type(ndx); +} + +inline DataType Table::get_column_type(size_t ndx) const noexcept +{ + REALM_ASSERT_3(ndx, <, m_spec.get_column_count()); + return m_spec.get_public_column_type(ndx); +} + +template +inline Col& Table::get_column(size_t ndx) +{ + ColumnBase& col = get_column_base(ndx); +#ifdef REALM_DEBUG + validate_column_type(col, col_type, ndx); +#endif + REALM_ASSERT(typeid(Col) == typeid(col)); + return static_cast(col); +} + +template +inline const Col& Table::get_column(size_t ndx) const noexcept +{ + const ColumnBase& col = get_column_base(ndx); +#ifdef REALM_DEBUG + validate_column_type(col, col_type, ndx); +#endif + REALM_ASSERT(typeid(Col) == typeid(col)); + return static_cast(col); +} + +inline bool Table::has_shared_type() const noexcept +{ + REALM_ASSERT(is_attached()); + return !m_top.is_attached(); +} + + +class Table::UnbindGuard { +public: + UnbindGuard(Table* table) noexcept + : m_table(table) + { + } + + ~UnbindGuard() noexcept + { + if (m_table) + m_table->unbind_ptr(); + } + + Table& operator*() const noexcept + { + return *m_table; + } + + Table* operator->() const noexcept + { + return m_table; + } + + Table* get() const noexcept + { + return m_table; + } + + Table* release() noexcept + { + Table* table = m_table; + m_table = nullptr; + return table; + } + +private: + Table* m_table; +}; + + +inline Table::Table(Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 1; // Explicitely managed lifetime + + ref_type ref = create_empty_table(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + init(ref, parent, ndx_in_parent); +} + +inline Table::Table(const Table& t, Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 1; // Explicitely managed lifetime + + ref_type ref = t.clone(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + init(ref, parent, ndx_in_parent); +} + +inline Table::Table(ref_count_tag, Allocator& alloc) + : m_top(alloc) + , m_columns(alloc) + , m_spec(alloc) +{ + m_ref_count = 0; // Lifetime managed by reference counting +} + +inline Allocator& Table::get_alloc() const +{ + return m_top.get_alloc(); +} + +inline TableRef Table::create(Allocator& alloc) +{ + std::unique_ptr
table(new Table(ref_count_tag(), alloc)); // Throws + ref_type ref = create_empty_table(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + table->init(ref, parent, ndx_in_parent); // Throws + return table.release()->get_table_ref(); +} + +inline TableRef Table::copy(Allocator& alloc) const +{ + std::unique_ptr
table(new Table(ref_count_tag(), alloc)); // Throws + ref_type ref = clone(alloc); // Throws + Parent* parent = nullptr; + size_t ndx_in_parent = 0; + table->init(ref, parent, ndx_in_parent); // Throws + return table.release()->get_table_ref(); +} + +// For use by queries +template +inline Columns Table::column(size_t column_ndx) +{ + std::vector link_chain = std::move(m_link_chain); + m_link_chain.clear(); + + // Check if user-given template type equals Realm type. Todo, we should clean up and reuse all our + // type traits (all the is_same() cases below). + const Table* table = get_link_chain_target(link_chain); + + realm::DataType ct = table->get_column_type(column_ndx); + if (std::is_same::value && ct != type_Int) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Bool) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_OldDateTime) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Float) + throw(LogicError::type_mismatch); + else if (std::is_same::value && ct != type_Double) + throw(LogicError::type_mismatch); + + if (std::is_same::value || std::is_same::value || std::is_same::value) { + link_chain.push_back(column_ndx); + } + + return Columns(column_ndx, this, std::move(link_chain)); +} + +template +inline Columns Table::column(const Table& origin, size_t origin_col_ndx) +{ + static_assert(std::is_same::value, ""); + + size_t origin_table_ndx = origin.get_index_in_group(); + const Table& current_target_table = *get_link_chain_target(m_link_chain); + size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + + std::vector link_chain = std::move(m_link_chain); + m_link_chain.clear(); + link_chain.push_back(backlink_col_ndx); + + return Columns(backlink_col_ndx, this, std::move(link_chain)); +} + +template +SubQuery Table::column(size_t column_ndx, Query subquery) +{ + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(column_ndx), std::move(subquery)); +} + +template +SubQuery Table::column(const Table& origin, size_t origin_col_ndx, Query subquery) +{ + static_assert(std::is_same::value, "A subquery must involve a link list or backlink column"); + return SubQuery(column(origin, origin_col_ndx), std::move(subquery)); +} + +// For use by queries +inline Table& Table::link(size_t link_column) +{ + m_link_chain.push_back(link_column); + return *this; +} + +inline Table& Table::backlink(const Table& origin, size_t origin_col_ndx) +{ + size_t origin_table_ndx = origin.get_index_in_group(); + const Table& current_target_table = *get_link_chain_target(m_link_chain); + size_t backlink_col_ndx = current_target_table.m_spec.find_backlink_column(origin_table_ndx, origin_col_ndx); + return link(backlink_col_ndx); +} + +inline bool Table::is_empty() const noexcept +{ + return m_size == 0; +} + +inline size_t Table::size() const noexcept +{ + return m_size; +} + +inline Table::RowExpr Table::get(size_t row_ndx) noexcept +{ + REALM_ASSERT_3(row_ndx, <, size()); + return RowExpr(this, row_ndx); +} + +inline Table::ConstRowExpr Table::get(size_t row_ndx) const noexcept +{ + REALM_ASSERT_3(row_ndx, <, size()); + return ConstRowExpr(this, row_ndx); +} + +inline Table::RowExpr Table::front() noexcept +{ + return get(0); +} + +inline Table::ConstRowExpr Table::front() const noexcept +{ + return get(0); +} + +inline Table::RowExpr Table::back() noexcept +{ + return get(m_size - 1); +} + +inline Table::ConstRowExpr Table::back() const noexcept +{ + return get(m_size - 1); +} + +inline Table::RowExpr Table::operator[](size_t row_ndx) noexcept +{ + return get(row_ndx); +} + +inline Table::ConstRowExpr Table::operator[](size_t row_ndx) const noexcept +{ + return get(row_ndx); +} + +inline size_t Table::add_empty_row(size_t num_rows) +{ + size_t row_ndx = m_size; + insert_empty_row(row_ndx, num_rows); // Throws + return row_ndx; // Return index of first new row +} + +inline const Table* Table::get_subtable_ptr(size_t col_ndx, size_t row_ndx) const +{ + return const_cast(this)->get_subtable_ptr(col_ndx, row_ndx); // Throws +} + +inline bool Table::is_null_link(size_t col_ndx, size_t row_ndx) const noexcept +{ + return get_link(col_ndx, row_ndx) == realm::npos; +} + +inline ConstTableRef Table::get_link_target(size_t col_ndx) const noexcept +{ + return const_cast(this)->get_link_target(col_ndx); +} + +template +inline void Table::set_enum(size_t column_ndx, size_t row_ndx, E value) +{ + set_int(column_ndx, row_ndx, value); +} + +inline void Table::nullify_link(size_t col_ndx, size_t row_ndx) +{ + set_link(col_ndx, row_ndx, realm::npos); +} + +inline TableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) +{ + return TableRef(get_subtable_ptr(column_ndx, row_ndx)); +} + +inline ConstTableRef Table::get_subtable(size_t column_ndx, size_t row_ndx) const +{ + return ConstTableRef(get_subtable_ptr(column_ndx, row_ndx)); +} + +inline ConstTableRef Table::get_parent_table(size_t* column_ndx_out) const noexcept +{ + return ConstTableRef(get_parent_table_ptr(column_ndx_out)); +} + +inline TableRef Table::get_parent_table(size_t* column_ndx_out) noexcept +{ + return TableRef(get_parent_table_ptr(column_ndx_out)); +} + +inline bool Table::is_group_level() const noexcept +{ + return bool(get_parent_group()); +} + +inline bool Table::operator==(const Table& t) const +{ + return m_spec == t.m_spec && compare_rows(t); // Throws +} + +inline bool Table::operator!=(const Table& t) const +{ + return !(*this == t); // Throws +} + +inline bool Table::is_degenerate() const noexcept +{ + return !m_columns.is_attached(); +} + +inline void Table::set_into_mixed(Table* parent, size_t col_ndx, size_t row_ndx) const +{ + parent->set_mixed_subtable(col_ndx, row_ndx, this); +} + +inline size_t Table::get_size_from_ref(ref_type top_ref, Allocator& alloc) noexcept +{ + const char* top_header = alloc.translate(top_ref); + std::pair p = Array::get_two(top_header, 0); + ref_type spec_ref = to_ref(p.first), columns_ref = to_ref(p.second); + return get_size_from_ref(spec_ref, columns_ref, alloc); +} + +inline Table* Table::get_parent_table_ptr(size_t* column_ndx_out) noexcept +{ + const Table* parent = const_cast(this)->get_parent_table_ptr(column_ndx_out); + return const_cast(parent); +} + +inline bool Table::is_link_type(ColumnType col_type) noexcept +{ + return col_type == col_type_Link || col_type == col_type_LinkList; +} + +inline size_t* Table::record_subtable_path(size_t* begin, size_t* end) const noexcept +{ + const Array& real_top = m_top.is_attached() ? m_top : m_columns; + size_t index_in_parent = real_top.get_ndx_in_parent(); + REALM_ASSERT_3(begin, <, end); + *begin++ = index_in_parent; + ArrayParent* parent = real_top.get_parent(); + REALM_ASSERT(parent); + REALM_ASSERT(dynamic_cast(parent)); + return static_cast(parent)->record_subtable_path(begin, end); +} + +inline size_t* Table::Parent::record_subtable_path(size_t* begin, size_t*) noexcept +{ + return begin; +} + +template +typename T::RowAccessor Table::get_link_accessor(size_t column_ndx, size_t row_ndx) +{ + size_t row_pos_in_target = get_link(column_ndx, row_ndx); + TableRef target_table = get_link_target(column_ndx); + + Table* table = &*target_table; + T* typed_table = reinterpret_cast(table); + return (*typed_table)[row_pos_in_target]; +} + +inline bool Table::is_marked() const noexcept +{ + return m_mark; +} + +inline void Table::mark() noexcept +{ + m_mark = true; +} + +inline void Table::unmark() noexcept +{ + m_mark = false; +} + +inline Replication* Table::get_repl() noexcept +{ + return m_top.get_alloc().get_replication(); +} + +inline void Table::set_ndx_in_parent(size_t ndx_in_parent) noexcept +{ + if (m_top.is_attached()) { + // Root table (independent descriptor) + m_top.set_ndx_in_parent(ndx_in_parent); + } + else { + // Subtable with shared descriptor + m_columns.set_ndx_in_parent(ndx_in_parent); + } +} + +// This class groups together information about the target of a link column +// This is not a valid link if the target table == nullptr +struct LinkTargetInfo { + LinkTargetInfo(Table* target = nullptr, size_t backlink_ndx = realm::npos) + : m_target_table(target) + , m_backlink_col_ndx(backlink_ndx) + { + } + bool is_valid() const + { + return (m_target_table != nullptr); + } + Table* m_target_table; + size_t m_backlink_col_ndx; // a value of npos indicates the backlink should be appended +}; + +// The purpose of this class is to give internal access to some, but +// not all of the non-public parts of the Table class. +class _impl::TableFriend { +public: + typedef Table::UnbindGuard UnbindGuard; + + static ref_type create_empty_table(Allocator& alloc) + { + return Table::create_empty_table(alloc); // Throws + } + + static ref_type clone(const Table& table, Allocator& alloc) + { + return table.clone(alloc); // Throws + } + + static ref_type clone_columns(const Table& table, Allocator& alloc) + { + return table.clone_columns(alloc); // Throws + } + + static Table* create_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent, size_t ndx_in_parent) + { + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + table->init(top_ref, parent, ndx_in_parent); // Throws + return table.release(); + } + + static Table* create_accessor(ConstSubspecRef shared_spec, Table::Parent* parent_column, size_t parent_row_ndx) + { + Allocator& alloc = shared_spec.get_alloc(); + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + table->init(shared_spec, parent_column, parent_row_ndx); // Throws + return table.release(); + } + + // Intended to be used only by Group::create_table_accessor() + static Table* create_incomplete_accessor(Allocator& alloc, ref_type top_ref, Table::Parent* parent, + size_t ndx_in_parent) + { + std::unique_ptr
table(new Table(Table::ref_count_tag(), alloc)); // Throws + bool skip_create_column_accessors = true; + table->init(top_ref, parent, ndx_in_parent, skip_create_column_accessors); // Throws + return table.release(); + } + + // Intended to be used only by Group::create_table_accessor() + static void complete_accessor(Table& table) + { + table.refresh_column_accessors(); // Throws + } + + static void set_top_parent(Table& table, ArrayParent* parent, size_t ndx_in_parent) noexcept + { + table.m_top.set_parent(parent, ndx_in_parent); + } + + static void update_from_parent(Table& table, size_t old_baseline) noexcept + { + table.update_from_parent(old_baseline); + } + + static void detach(Table& table) noexcept + { + table.detach(); + } + + static void discard_row_accessors(Table& table) noexcept + { + table.discard_row_accessors(); + } + + static void discard_child_accessors(Table& table) noexcept + { + table.discard_child_accessors(); + } + + static void discard_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept + { + table.discard_subtable_accessor(col_ndx, row_ndx); + } + + static void bind_ptr(Table& table) noexcept + { + table.bind_ptr(); + } + + static void unbind_ptr(Table& table) noexcept + { + table.unbind_ptr(); + } + + static bool compare_rows(const Table& a, const Table& b) + { + return a.compare_rows(b); // Throws + } + + static size_t get_size_from_ref(ref_type ref, Allocator& alloc) noexcept + { + return Table::get_size_from_ref(ref, alloc); + } + + static size_t get_size_from_ref(ref_type spec_ref, ref_type columns_ref, Allocator& alloc) noexcept + { + return Table::get_size_from_ref(spec_ref, columns_ref, alloc); + } + + static Spec& get_spec(Table& table) noexcept + { + return table.m_spec; + } + + static const Spec& get_spec(const Table& table) noexcept + { + return table.m_spec; + } + + static ColumnBase& get_column(const Table& table, size_t col_ndx) + { + return *table.m_cols[col_ndx]; + } + + static void do_remove(Table& table, size_t row_ndx) + { + bool broken_reciprocal_backlinks = false; + table.do_remove(row_ndx, broken_reciprocal_backlinks); // Throws + } + + static void do_move_last_over(Table& table, size_t row_ndx) + { + bool broken_reciprocal_backlinks = false; + table.do_move_last_over(row_ndx, broken_reciprocal_backlinks); // Throws + } + + static void do_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) + { + table.do_swap_rows(row_ndx_1, row_ndx_2); // Throws + } + + static void do_merge_rows(Table& table, size_t row_ndx, size_t new_row_ndx) + { + table.do_merge_rows(row_ndx, new_row_ndx); // Throws + } + + static void do_clear(Table& table) + { + bool broken_reciprocal_backlinks = false; + table.do_clear(broken_reciprocal_backlinks); // Throws + } + + static void do_set_link(Table& table, size_t col_ndx, size_t row_ndx, size_t target_row_ndx) + { + table.do_set_link(col_ndx, row_ndx, target_row_ndx); // Throws + } + + static size_t get_num_strong_backlinks(const Table& table, size_t row_ndx) noexcept + { + return table.get_num_strong_backlinks(row_ndx); + } + + static void cascade_break_backlinks_to(Table& table, size_t row_ndx, CascadeState& state) + { + table.cascade_break_backlinks_to(row_ndx, state); // Throws + } + + static void remove_backlink_broken_rows(Table& table, const CascadeState& rows) + { + table.remove_backlink_broken_rows(rows); // Throws + } + + static size_t* record_subtable_path(const Table& table, size_t* begin, size_t* end) noexcept + { + return table.record_subtable_path(begin, end); + } + + static void insert_column(Descriptor& desc, size_t column_ndx, DataType type, StringData name, + LinkTargetInfo& link, bool nullable = false) + { + Table::do_insert_column(desc, column_ndx, type, name, link, nullable); // Throws + } + + static void insert_column_unless_exists(Descriptor& desc, size_t column_ndx, DataType type, StringData name, + LinkTargetInfo link, bool nullable = false, bool* was_inserted = nullptr) + { + Table::do_insert_column_unless_exists(desc, column_ndx, type, name, link, nullable, was_inserted); // Throws + } + + static void erase_column(Descriptor& desc, size_t column_ndx) + { + Table::do_erase_column(desc, column_ndx); // Throws + } + + static void rename_column(Descriptor& desc, size_t column_ndx, StringData name) + { + Table::do_rename_column(desc, column_ndx, name); // Throws + } + + static void move_column(Descriptor& desc, size_t col_ndx_1, size_t col_ndx_2) + { + Table::do_move_column(desc, col_ndx_1, col_ndx_2); // Throws + } + + static void set_link_type(Table& table, size_t column_ndx, LinkType link_type) + { + table.do_set_link_type(column_ndx, link_type); // Throws + } + + static void erase_row(Table& table, size_t row_ndx, bool is_move_last_over) + { + table.erase_row(row_ndx, is_move_last_over); // Throws + } + + static void batch_erase_rows(Table& table, const IntegerColumn& row_indexes, bool is_move_last_over) + { + table.batch_erase_rows(row_indexes, is_move_last_over); // Throws + } + + static Table* get_subtable_accessor(Table& table, size_t col_ndx, size_t row_ndx) noexcept + { + return table.get_subtable_accessor(col_ndx, row_ndx); + } + + static const Table* get_link_target_table_accessor(const Table& table, size_t col_ndx) noexcept + { + return const_cast(table).get_link_target_table_accessor(col_ndx); + } + + static Table* get_link_target_table_accessor(Table& table, size_t col_ndx) noexcept + { + return table.get_link_target_table_accessor(col_ndx); + } + + static void adj_acc_insert_rows(Table& table, size_t row_ndx, size_t num_rows) noexcept + { + table.adj_acc_insert_rows(row_ndx, num_rows); + } + + static void adj_acc_erase_row(Table& table, size_t row_ndx) noexcept + { + table.adj_acc_erase_row(row_ndx); + } + + static void adj_acc_swap_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept + { + table.adj_acc_swap_rows(row_ndx_1, row_ndx_2); + } + + static void adj_acc_merge_rows(Table& table, size_t row_ndx_1, size_t row_ndx_2) noexcept + { + table.adj_acc_merge_rows(row_ndx_1, row_ndx_2); + } + + static void adj_acc_move_over(Table& table, size_t from_row_ndx, size_t to_row_ndx) noexcept + { + table.adj_acc_move_over(from_row_ndx, to_row_ndx); + } + + static void adj_acc_clear_root_table(Table& table) noexcept + { + table.adj_acc_clear_root_table(); + } + + static void adj_acc_clear_nonroot_table(Table& table) noexcept + { + table.adj_acc_clear_nonroot_table(); + } + + static void adj_insert_column(Table& table, size_t col_ndx) + { + table.adj_insert_column(col_ndx); // Throws + } + + static void adj_add_column(Table& table) + { + size_t num_cols = table.m_cols.size(); + table.adj_insert_column(num_cols); // Throws + } + + static void adj_erase_column(Table& table, size_t col_ndx) noexcept + { + table.adj_erase_column(col_ndx); + } + + static void adj_move_column(Table& table, size_t col_ndx_1, size_t col_ndx_2) noexcept + { + table.adj_move_column(col_ndx_1, col_ndx_2); + } + + static bool is_marked(const Table& table) noexcept + { + return table.is_marked(); + } + + static void mark(Table& table) noexcept + { + table.mark(); + } + + static void unmark(Table& table) noexcept + { + table.unmark(); + } + + static void recursive_mark(Table& table) noexcept + { + table.recursive_mark(); + } + + static void mark_link_target_tables(Table& table, size_t col_ndx_begin) noexcept + { + table.mark_link_target_tables(col_ndx_begin); + } + + static void mark_opposite_link_tables(Table& table) noexcept + { + table.mark_opposite_link_tables(); + } + + static DescriptorRef get_root_table_desc_accessor(Table& root_table) noexcept + { + return root_table.m_descriptor.lock(); + } + + typedef Table::AccessorUpdater AccessorUpdater; + static void update_accessors(Table& table, const size_t* col_path_begin, const size_t* col_path_end, + AccessorUpdater& updater) + { + table.update_accessors(col_path_begin, col_path_end, updater); // Throws + } + + static void refresh_accessor_tree(Table& table) + { + table.refresh_accessor_tree(); // Throws + } + + static void set_ndx_in_parent(Table& table, size_t ndx_in_parent) noexcept + { + table.set_ndx_in_parent(ndx_in_parent); + } + + static void set_shared_subspec_ndx_in_parent(Table& table, size_t spec_ndx_in_parent) noexcept + { + table.m_spec.set_ndx_in_parent(spec_ndx_in_parent); + } + + static bool is_link_type(ColumnType type) noexcept + { + return Table::is_link_type(type); + } + + static void bump_version(Table& table, bool bump_global = true) noexcept + { + table.bump_version(bump_global); + } + + static bool is_cross_table_link_target(const Table& table) + { + return table.is_cross_table_link_target(); + } + + static Group* get_parent_group(const Table& table) noexcept + { + return table.get_parent_group(); + } + + static Replication* get_repl(Table& table) noexcept + { + return table.get_repl(); + } + + static void register_view(Table& table, const TableViewBase* view) + { + table.register_view(view); // Throws + } + + static void unregister_view(Table& table, const TableViewBase* view) noexcept + { + table.unregister_view(view); + } +}; + + +} // namespace realm + +#endif // REALM_TABLE_HPP diff --git a/Pods/Realm/include/core/realm/table_accessors.hpp b/Pods/Realm/include/core/realm/table_accessors.hpp new file mode 100644 index 0000000..e3e5cba --- /dev/null +++ b/Pods/Realm/include/core/realm/table_accessors.hpp @@ -0,0 +1,1975 @@ +/************************************************************************* + * + * Copyright 2016 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_TABLE_ACCESSORS_HPP +#define REALM_TABLE_ACCESSORS_HPP + +#include +#include + +#include +#include + +#include + +namespace realm { + + +/// A convenience base class for Spec classes that are to be used with +/// BasicTable. +/// +/// There are two reasons why you might want to derive your spec class +/// from this one. First, it offers short hand names for each of the +/// available column types. Second, it makes it easier when you do not +/// want to specify colum names or convenience methods, since suitable +/// fallbacks are defined here. +struct SpecBase { + typedef int64_t Int; + typedef bool Bool; + typedef realm::OldDateTime OldDateTime; + typedef float Float; + typedef double Double; + typedef realm::StringData String; + typedef realm::BinaryData Binary; + typedef realm::Mixed Mixed; + + template + class Enum { + public: + typedef E enum_type; + Enum(E v) + : m_value(v) + { + } + operator E() const + { + return m_value; + } + + private: + E m_value; + }; + + template + class Subtable { + public: + typedef T table_type; + Subtable(T* t) + : m_table(t) + { + } + operator T*() const + { + return m_table; + } + + private: + T* m_table; + }; + + /// By default, there are no static column names defined for a + /// BasicTable. One may define a set of column mames as follows: + /// + /// \code{.cpp} + /// + /// struct MyTableSpec: SpecBase { + /// typedef TypeAppend::type Columns1; + /// typedef TypeAppend::type Columns; + /// + /// template